xref: /aosp_15_r20/external/cronet/base/metrics/sample_map.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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