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 "base/check.h"
8 #include "base/numerics/safe_conversions.h"
9
10 namespace base {
11
12 typedef HistogramBase::Count Count;
13 typedef HistogramBase::Sample Sample;
14
15 namespace {
16
17 // An iterator for going through a SampleMap. The logic here is identical
18 // to that of the iterator for PersistentSampleMap but with different data
19 // structures. Changes here likely need to be duplicated there.
20 template <typename T, typename I>
21 class IteratorTemplate : public SampleCountIterator {
22 public:
IteratorTemplate(T & sample_counts)23 explicit IteratorTemplate(T& sample_counts)
24 : iter_(sample_counts.begin()), end_(sample_counts.end()) {
25 SkipEmptyBuckets();
26 }
27
28 ~IteratorTemplate() override;
29
30 // SampleCountIterator:
Done() const31 bool Done() const override { return iter_ == end_; }
Next()32 void Next() override {
33 DCHECK(!Done());
34 ++iter_;
35 SkipEmptyBuckets();
36 }
37 void Get(HistogramBase::Sample* min,
38 int64_t* max,
39 HistogramBase::Count* count) override;
40
41 private:
SkipEmptyBuckets()42 void SkipEmptyBuckets() {
43 while (!Done() && iter_->second == 0) {
44 ++iter_;
45 }
46 }
47
48 I iter_;
49 const I end_;
50 };
51
52 typedef std::map<HistogramBase::Sample, HistogramBase::Count> SampleToCountMap;
53 typedef IteratorTemplate<const SampleToCountMap,
54 SampleToCountMap::const_iterator>
55 SampleMapIterator;
56
57 template <>
58 SampleMapIterator::~IteratorTemplate() = default;
59
60 // Get() for an iterator of a SampleMap.
61 template <>
Get(Sample * min,int64_t * max,Count * count)62 void SampleMapIterator::Get(Sample* min, int64_t* max, Count* count) {
63 DCHECK(!Done());
64 *min = iter_->first;
65 *max = strict_cast<int64_t>(iter_->first) + 1;
66 // We do not have to do the following atomically -- if the caller needs thread
67 // safety, they should use a lock. And since this is in local memory, if a
68 // lock is used, we know the value would not be concurrently modified by a
69 // different process (in contrast to PersistentSampleMap, where the value in
70 // shared memory may be modified concurrently by a subprocess).
71 *count = iter_->second;
72 }
73
74 typedef IteratorTemplate<SampleToCountMap, SampleToCountMap::iterator>
75 ExtractingSampleMapIterator;
76
77 template <>
~IteratorTemplate()78 ExtractingSampleMapIterator::~IteratorTemplate() {
79 // Ensure that the user has consumed all the samples in order to ensure no
80 // samples are lost.
81 DCHECK(Done());
82 }
83
84 // Get() for an extracting iterator of a SampleMap.
85 template <>
Get(Sample * min,int64_t * max,Count * count)86 void ExtractingSampleMapIterator::Get(Sample* min, int64_t* max, Count* count) {
87 DCHECK(!Done());
88 *min = iter_->first;
89 *max = strict_cast<int64_t>(iter_->first) + 1;
90 // We do not have to do the following atomically -- if the caller needs thread
91 // safety, they should use a lock. And since this is in local memory, if a
92 // lock is used, we know the value would not be concurrently modified by a
93 // different process (in contrast to PersistentSampleMap, where the value in
94 // shared memory may be modified concurrently by a subprocess).
95 *count = iter_->second;
96 iter_->second = 0;
97 }
98
99 } // namespace
100
SampleMap()101 SampleMap::SampleMap() : SampleMap(0) {}
102
SampleMap(uint64_t id)103 SampleMap::SampleMap(uint64_t id)
104 : HistogramSamples(id, std::make_unique<LocalMetadata>()) {}
105
106 SampleMap::~SampleMap() = default;
107
Accumulate(Sample value,Count count)108 void SampleMap::Accumulate(Sample value, Count count) {
109 // We do not have to do the following atomically -- if the caller needs
110 // thread safety, they should use a lock. And since this is in local memory,
111 // if a lock is used, we know the value would not be concurrently modified
112 // by a different process (in contrast to PersistentSampleMap, where the
113 // value in shared memory may be modified concurrently by a subprocess).
114 sample_counts_[value] += count;
115 IncreaseSumAndCount(strict_cast<int64_t>(count) * value, count);
116 }
117
GetCount(Sample value) const118 Count SampleMap::GetCount(Sample value) const {
119 auto it = sample_counts_.find(value);
120 if (it == sample_counts_.end())
121 return 0;
122 return it->second;
123 }
124
TotalCount() const125 Count SampleMap::TotalCount() const {
126 Count count = 0;
127 for (const auto& entry : sample_counts_) {
128 count += entry.second;
129 }
130 return count;
131 }
132
Iterator() const133 std::unique_ptr<SampleCountIterator> SampleMap::Iterator() const {
134 return std::make_unique<SampleMapIterator>(sample_counts_);
135 }
136
ExtractingIterator()137 std::unique_ptr<SampleCountIterator> SampleMap::ExtractingIterator() {
138 return std::make_unique<ExtractingSampleMapIterator>(sample_counts_);
139 }
140
AddSubtractImpl(SampleCountIterator * iter,Operator op)141 bool SampleMap::AddSubtractImpl(SampleCountIterator* iter, Operator op) {
142 Sample min;
143 int64_t max;
144 Count count;
145 for (; !iter->Done(); iter->Next()) {
146 iter->Get(&min, &max, &count);
147 if (strict_cast<int64_t>(min) + 1 != max)
148 return false; // SparseHistogram only supports bucket with size 1.
149
150 // We do not have to do the following atomically -- if the caller needs
151 // thread safety, they should use a lock. And since this is in local memory,
152 // if a lock is used, we know the value would not be concurrently modified
153 // by a different process (in contrast to PersistentSampleMap, where the
154 // value in shared memory may be modified concurrently by a subprocess).
155 sample_counts_[min] += (op == HistogramSamples::ADD) ? count : -count;
156 }
157 return true;
158 }
159
160 } // namespace base
161