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