1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180

base / metrics / sample_map.cc [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/metrics/sample_map.h"

#include <type_traits>

#include "base/check.h"
#include "base/numerics/safe_conversions.h"

namespace base {

typedef HistogramBase::Count Count;
typedef HistogramBase::Sample Sample;

namespace {

// An iterator for going through a SampleMap. The logic here is identical
// to that of the iterator for PersistentSampleMap but with different data
// structures. Changes here likely need to be duplicated there.
template <typename T, typename I>
class IteratorTemplate : public SampleCountIterator {
 public:
  explicit IteratorTemplate(T& sample_counts)
      : iter_(sample_counts.begin()), end_(sample_counts.end()) {
    SkipEmptyBuckets();
  }

  ~IteratorTemplate() override;

  // SampleCountIterator:
  bool Done() const override { return iter_ == end_; }
  void Next() override {
    DCHECK(!Done());
    ++iter_;
    SkipEmptyBuckets();
  }
  void Get(HistogramBase::Sample* min,
           int64_t* max,
           HistogramBase::Count* count) override;

 private:
  void SkipEmptyBuckets() {
    while (!Done() && iter_->second == 0) {
      ++iter_;
    }
  }

  I iter_;
  const I end_;
};

typedef std::map<HistogramBase::Sample, HistogramBase::Count> SampleToCountMap;
typedef IteratorTemplate<const SampleToCountMap,
                         SampleToCountMap::const_iterator>
    SampleMapIterator;

template <>
SampleMapIterator::~IteratorTemplate() = default;

// Get() for an iterator of a SampleMap.
template <>
void SampleMapIterator::Get(Sample* min, int64_t* max, Count* count) {
  DCHECK(!Done());
  *min = iter_->first;
  *max = strict_cast<int64_t>(iter_->first) + 1;
  // We do not have to do the following atomically -- if the caller needs thread
  // safety, they should use a lock. And since this is in local memory, if a
  // lock is used, we know the value would not be concurrently modified by a
  // different process (in contrast to PersistentSampleMap, where the value in
  // shared memory may be modified concurrently by a subprocess).
  *count = iter_->second;
}

typedef IteratorTemplate<SampleToCountMap, SampleToCountMap::iterator>
    ExtractingSampleMapIterator;

template <>
ExtractingSampleMapIterator::~IteratorTemplate() {
  // Ensure that the user has consumed all the samples in order to ensure no
  // samples are lost.
  DCHECK(Done());
}

// Get() for an extracting iterator of a SampleMap.
template <>
void ExtractingSampleMapIterator::Get(Sample* min, int64_t* max, Count* count) {
  DCHECK(!Done());
  *min = iter_->first;
  *max = strict_cast<int64_t>(iter_->first) + 1;
  // We do not have to do the following atomically -- if the caller needs thread
  // safety, they should use a lock. And since this is in local memory, if a
  // lock is used, we know the value would not be concurrently modified by a
  // different process (in contrast to PersistentSampleMap, where the value in
  // shared memory may be modified concurrently by a subprocess).
  *count = iter_->second;
  iter_->second = 0;
}

}  // namespace

SampleMap::SampleMap() : SampleMap(0) {}

SampleMap::SampleMap(uint64_t id)
    : HistogramSamples(id, std::make_unique<LocalMetadata>()) {}

SampleMap::~SampleMap() = default;

void SampleMap::Accumulate(Sample value, Count count) {
  // We do not have to do the following atomically -- if the caller needs
  // thread safety, they should use a lock. And since this is in local memory,
  // if a lock is used, we know the value would not be concurrently modified
  // by a different process (in contrast to PersistentSampleMap, where the
  // value in shared memory may be modified concurrently by a subprocess).
  sample_counts_[value] += count;
  IncreaseSumAndCount(strict_cast<int64_t>(count) * value, count);
}

Count SampleMap::GetCount(Sample value) const {
  auto it = sample_counts_.find(value);
  if (it == sample_counts_.end())
    return 0;
  return it->second;
}

Count SampleMap::TotalCount() const {
  Count count = 0;
  for (const auto& entry : sample_counts_) {
    count += entry.second;
  }
  return count;
}

std::unique_ptr<SampleCountIterator> SampleMap::Iterator() const {
  return std::make_unique<SampleMapIterator>(sample_counts_);
}

std::unique_ptr<SampleCountIterator> SampleMap::ExtractingIterator() {
  return std::make_unique<ExtractingSampleMapIterator>(sample_counts_);
}

bool SampleMap::IsDefinitelyEmpty() const {
  // If |sample_counts_| is empty (no entry was ever inserted), then return
  // true. If it does contain some entries, then it may or may not have samples
  // (e.g. it's possible all entries have a bucket count of 0). Just return
  // false in this case. If we are wrong, this will just make the caller perform
  // some extra work thinking that |this| is non-empty.
  return HistogramSamples::IsDefinitelyEmpty() && sample_counts_.empty();
}

bool SampleMap::AddSubtractImpl(SampleCountIterator* iter, Operator op) {
  Sample min;
  int64_t max;
  Count count;
  for (; !iter->Done(); iter->Next()) {
    iter->Get(&min, &max, &count);
    if (strict_cast<int64_t>(min) + 1 != max) {
      return false;  // SparseHistogram only supports bucket with size 1.
    }

    // Note that we do not need to check that count != 0, since Next() above
    // will skip empty buckets.

    // We do not have to do the following atomically -- if the caller needs
    // thread safety, they should use a lock. And since this is in local memory,
    // if a lock is used, we know the value would not be concurrently modified
    // by a different process (in contrast to PersistentSampleMap, where the
    // value in shared memory may be modified concurrently by a subprocess).
    Count& sample_ref = sample_counts_[min];
    if (op == HistogramSamples::ADD) {
      sample_ref = base::WrappingAdd(sample_ref, count);
    } else {
      sample_ref = base::WrappingSub(sample_ref, count);
    }
  }
  return true;
}

}  // namespace base