1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/metrics/sample_map.h"
6
7 #include <type_traits>
8
9 #include "base/check.h"
10 #include "base/numerics/safe_conversions.h"
11
12 namespace base {
13
14 typedef HistogramBase::Count Count;
15 typedef HistogramBase::Sample Sample;
16
17 namespace {
18
19 // An iterator for going through a SampleMap. The logic here is identical
20 // to that of the iterator for PersistentSampleMap but with different data
21 // structures. Changes here likely need to be duplicated there.
22 template <typename T, typename I>
23 class IteratorTemplate : public SampleCountIterator {
24 public:
IteratorTemplate(T & sample_counts)25 explicit IteratorTemplate(T& sample_counts)
26 : iter_(sample_counts.begin()), end_(sample_counts.end()) {
27 SkipEmptyBuckets();
28 }
29
30 ~IteratorTemplate() override;
31
32 // SampleCountIterator:
Done() const33 bool Done() const override { return iter_ == end_; }
Next()34 void Next() override {
35 DCHECK(!Done());
36 ++iter_;
37 SkipEmptyBuckets();
38 }
39 void Get(HistogramBase::Sample* min,
40 int64_t* max,
41 HistogramBase::Count* count) override;
42
43 private:
SkipEmptyBuckets()44 void SkipEmptyBuckets() {
45 while (!Done() && iter_->second == 0) {
46 ++iter_;
47 }
48 }
49
50 I iter_;
51 const I end_;
52 };
53
54 typedef std::map<HistogramBase::Sample, HistogramBase::Count> SampleToCountMap;
55 typedef IteratorTemplate<const SampleToCountMap,
56 SampleToCountMap::const_iterator>
57 SampleMapIterator;
58
59 template <>
60 SampleMapIterator::~IteratorTemplate() = default;
61
62 // Get() for an iterator of a SampleMap.
63 template <>
Get(Sample * min,int64_t * max,Count * count)64 void SampleMapIterator::Get(Sample* min, int64_t* max, Count* count) {
65 DCHECK(!Done());
66 *min = iter_->first;
67 *max = strict_cast<int64_t>(iter_->first) + 1;
68 // We do not have to do the following atomically -- if the caller needs thread
69 // safety, they should use a lock. And since this is in local memory, if a
70 // lock is used, we know the value would not be concurrently modified by a
71 // different process (in contrast to PersistentSampleMap, where the value in
72 // shared memory may be modified concurrently by a subprocess).
73 *count = iter_->second;
74 }
75
76 typedef IteratorTemplate<SampleToCountMap, SampleToCountMap::iterator>
77 ExtractingSampleMapIterator;
78
79 template <>
~IteratorTemplate()80 ExtractingSampleMapIterator::~IteratorTemplate() {
81 // Ensure that the user has consumed all the samples in order to ensure no
82 // samples are lost.
83 DCHECK(Done());
84 }
85
86 // Get() for an extracting iterator of a SampleMap.
87 template <>
Get(Sample * min,int64_t * max,Count * count)88 void ExtractingSampleMapIterator::Get(Sample* min, int64_t* max, Count* count) {
89 DCHECK(!Done());
90 *min = iter_->first;
91 *max = strict_cast<int64_t>(iter_->first) + 1;
92 // We do not have to do the following atomically -- if the caller needs thread
93 // safety, they should use a lock. And since this is in local memory, if a
94 // lock is used, we know the value would not be concurrently modified by a
95 // different process (in contrast to PersistentSampleMap, where the value in
96 // shared memory may be modified concurrently by a subprocess).
97 *count = iter_->second;
98 iter_->second = 0;
99 }
100
101 } // namespace
102
SampleMap()103 SampleMap::SampleMap() : SampleMap(0) {}
104
SampleMap(uint64_t id)105 SampleMap::SampleMap(uint64_t id)
106 : HistogramSamples(id, std::make_unique<LocalMetadata>()) {}
107
108 SampleMap::~SampleMap() = default;
109
Accumulate(Sample value,Count count)110 void SampleMap::Accumulate(Sample value, Count count) {
111 // We do not have to do the following atomically -- if the caller needs
112 // thread safety, they should use a lock. And since this is in local memory,
113 // if a lock is used, we know the value would not be concurrently modified
114 // by a different process (in contrast to PersistentSampleMap, where the
115 // value in shared memory may be modified concurrently by a subprocess).
116 sample_counts_[value] += count;
117 IncreaseSumAndCount(strict_cast<int64_t>(count) * value, count);
118 }
119
GetCount(Sample value) const120 Count SampleMap::GetCount(Sample value) const {
121 auto it = sample_counts_.find(value);
122 if (it == sample_counts_.end())
123 return 0;
124 return it->second;
125 }
126
TotalCount() const127 Count SampleMap::TotalCount() const {
128 Count count = 0;
129 for (const auto& entry : sample_counts_) {
130 count += entry.second;
131 }
132 return count;
133 }
134
Iterator() const135 std::unique_ptr<SampleCountIterator> SampleMap::Iterator() const {
136 return std::make_unique<SampleMapIterator>(sample_counts_);
137 }
138
ExtractingIterator()139 std::unique_ptr<SampleCountIterator> SampleMap::ExtractingIterator() {
140 return std::make_unique<ExtractingSampleMapIterator>(sample_counts_);
141 }
142
IsDefinitelyEmpty() const143 bool SampleMap::IsDefinitelyEmpty() const {
144 // If |sample_counts_| is empty (no entry was ever inserted), then return
145 // true. If it does contain some entries, then it may or may not have samples
146 // (e.g. it's possible all entries have a bucket count of 0). Just return
147 // false in this case. If we are wrong, this will just make the caller perform
148 // some extra work thinking that |this| is non-empty.
149 return HistogramSamples::IsDefinitelyEmpty() && sample_counts_.empty();
150 }
151
AddSubtractImpl(SampleCountIterator * iter,Operator op)152 bool SampleMap::AddSubtractImpl(SampleCountIterator* iter, Operator op) {
153 Sample min;
154 int64_t max;
155 Count count;
156 for (; !iter->Done(); iter->Next()) {
157 iter->Get(&min, &max, &count);
158 if (strict_cast<int64_t>(min) + 1 != max) {
159 return false; // SparseHistogram only supports bucket with size 1.
160 }
161
162 // Note that we do not need to check that count != 0, since Next() above
163 // will skip empty buckets.
164
165 // We do not have to do the following atomically -- if the caller needs
166 // thread safety, they should use a lock. And since this is in local memory,
167 // if a lock is used, we know the value would not be concurrently modified
168 // by a different process (in contrast to PersistentSampleMap, where the
169 // value in shared memory may be modified concurrently by a subprocess).
170 Count& sample_ref = sample_counts_[min];
171 if (op == HistogramSamples::ADD) {
172 sample_ref = base::WrappingAdd(sample_ref, count);
173 } else {
174 sample_ref = base::WrappingSub(sample_ref, count);
175 }
176 }
177 return true;
178 }
179
180 } // namespace base
181