xref: /aosp_15_r20/external/cronet/base/metrics/histogram_samples.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/histogram_samples.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <limits>
8*6777b538SAndroid Build Coastguard Worker #include <string_view>
9*6777b538SAndroid Build Coastguard Worker #include <utility>
10*6777b538SAndroid Build Coastguard Worker 
11*6777b538SAndroid Build Coastguard Worker #include "base/compiler_specific.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/memory/raw_ptr.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_functions.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_macros.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/numerics/clamped_math.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
17*6777b538SAndroid Build Coastguard Worker #include "base/numerics/safe_math.h"
18*6777b538SAndroid Build Coastguard Worker #include "base/pickle.h"
19*6777b538SAndroid Build Coastguard Worker #include "base/strings/strcat.h"
20*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_number_conversions.h"
21*6777b538SAndroid Build Coastguard Worker #include "base/strings/stringprintf.h"
22*6777b538SAndroid Build Coastguard Worker 
23*6777b538SAndroid Build Coastguard Worker namespace base {
24*6777b538SAndroid Build Coastguard Worker 
25*6777b538SAndroid Build Coastguard Worker namespace {
26*6777b538SAndroid Build Coastguard Worker 
27*6777b538SAndroid Build Coastguard Worker // A shorthand constant for the max value of size_t.
28*6777b538SAndroid Build Coastguard Worker constexpr size_t kSizeMax = std::numeric_limits<size_t>::max();
29*6777b538SAndroid Build Coastguard Worker 
30*6777b538SAndroid Build Coastguard Worker // A constant stored in an AtomicSingleSample (as_atomic) to indicate that the
31*6777b538SAndroid Build Coastguard Worker // sample is "disabled" and no further accumulation should be done with it. The
32*6777b538SAndroid Build Coastguard Worker // value is chosen such that it will be MAX_UINT16 for both |bucket| & |count|,
33*6777b538SAndroid Build Coastguard Worker // and thus less likely to conflict with real use. Conflicts are explicitly
34*6777b538SAndroid Build Coastguard Worker // handled in the code but it's worth making them as unlikely as possible.
35*6777b538SAndroid Build Coastguard Worker constexpr int32_t kDisabledSingleSample = -1;
36*6777b538SAndroid Build Coastguard Worker 
37*6777b538SAndroid Build Coastguard Worker class SampleCountPickleIterator : public SampleCountIterator {
38*6777b538SAndroid Build Coastguard Worker  public:
39*6777b538SAndroid Build Coastguard Worker   explicit SampleCountPickleIterator(PickleIterator* iter);
40*6777b538SAndroid Build Coastguard Worker 
41*6777b538SAndroid Build Coastguard Worker   bool Done() const override;
42*6777b538SAndroid Build Coastguard Worker   void Next() override;
43*6777b538SAndroid Build Coastguard Worker   void Get(HistogramBase::Sample* min,
44*6777b538SAndroid Build Coastguard Worker            int64_t* max,
45*6777b538SAndroid Build Coastguard Worker            HistogramBase::Count* count) override;
46*6777b538SAndroid Build Coastguard Worker 
47*6777b538SAndroid Build Coastguard Worker  private:
48*6777b538SAndroid Build Coastguard Worker   const raw_ptr<PickleIterator> iter_;
49*6777b538SAndroid Build Coastguard Worker 
50*6777b538SAndroid Build Coastguard Worker   HistogramBase::Sample min_;
51*6777b538SAndroid Build Coastguard Worker   int64_t max_;
52*6777b538SAndroid Build Coastguard Worker   HistogramBase::Count count_;
53*6777b538SAndroid Build Coastguard Worker   bool is_done_;
54*6777b538SAndroid Build Coastguard Worker };
55*6777b538SAndroid Build Coastguard Worker 
SampleCountPickleIterator(PickleIterator * iter)56*6777b538SAndroid Build Coastguard Worker SampleCountPickleIterator::SampleCountPickleIterator(PickleIterator* iter)
57*6777b538SAndroid Build Coastguard Worker     : iter_(iter),
58*6777b538SAndroid Build Coastguard Worker       is_done_(false) {
59*6777b538SAndroid Build Coastguard Worker   Next();
60*6777b538SAndroid Build Coastguard Worker }
61*6777b538SAndroid Build Coastguard Worker 
Done() const62*6777b538SAndroid Build Coastguard Worker bool SampleCountPickleIterator::Done() const {
63*6777b538SAndroid Build Coastguard Worker   return is_done_;
64*6777b538SAndroid Build Coastguard Worker }
65*6777b538SAndroid Build Coastguard Worker 
Next()66*6777b538SAndroid Build Coastguard Worker void SampleCountPickleIterator::Next() {
67*6777b538SAndroid Build Coastguard Worker   DCHECK(!Done());
68*6777b538SAndroid Build Coastguard Worker   if (!iter_->ReadInt(&min_) || !iter_->ReadInt64(&max_) ||
69*6777b538SAndroid Build Coastguard Worker       !iter_->ReadInt(&count_)) {
70*6777b538SAndroid Build Coastguard Worker     is_done_ = true;
71*6777b538SAndroid Build Coastguard Worker   }
72*6777b538SAndroid Build Coastguard Worker }
73*6777b538SAndroid Build Coastguard Worker 
Get(HistogramBase::Sample * min,int64_t * max,HistogramBase::Count * count)74*6777b538SAndroid Build Coastguard Worker void SampleCountPickleIterator::Get(HistogramBase::Sample* min,
75*6777b538SAndroid Build Coastguard Worker                                     int64_t* max,
76*6777b538SAndroid Build Coastguard Worker                                     HistogramBase::Count* count) {
77*6777b538SAndroid Build Coastguard Worker   DCHECK(!Done());
78*6777b538SAndroid Build Coastguard Worker   *min = min_;
79*6777b538SAndroid Build Coastguard Worker   *max = max_;
80*6777b538SAndroid Build Coastguard Worker   *count = count_;
81*6777b538SAndroid Build Coastguard Worker }
82*6777b538SAndroid Build Coastguard Worker 
83*6777b538SAndroid Build Coastguard Worker }  // namespace
84*6777b538SAndroid Build Coastguard Worker 
85*6777b538SAndroid Build Coastguard Worker static_assert(sizeof(HistogramSamples::AtomicSingleSample) ==
86*6777b538SAndroid Build Coastguard Worker                   sizeof(subtle::Atomic32),
87*6777b538SAndroid Build Coastguard Worker               "AtomicSingleSample isn't 32 bits");
88*6777b538SAndroid Build Coastguard Worker 
Load() const89*6777b538SAndroid Build Coastguard Worker HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Load()
90*6777b538SAndroid Build Coastguard Worker     const {
91*6777b538SAndroid Build Coastguard Worker   AtomicSingleSample single_sample(subtle::Acquire_Load(&as_atomic));
92*6777b538SAndroid Build Coastguard Worker 
93*6777b538SAndroid Build Coastguard Worker   // If the sample was extracted/disabled, it's still zero to the outside.
94*6777b538SAndroid Build Coastguard Worker   if (single_sample.as_atomic == kDisabledSingleSample)
95*6777b538SAndroid Build Coastguard Worker     single_sample.as_atomic = 0;
96*6777b538SAndroid Build Coastguard Worker 
97*6777b538SAndroid Build Coastguard Worker   return single_sample.as_parts;
98*6777b538SAndroid Build Coastguard Worker }
99*6777b538SAndroid Build Coastguard Worker 
Extract(AtomicSingleSample new_value)100*6777b538SAndroid Build Coastguard Worker HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Extract(
101*6777b538SAndroid Build Coastguard Worker     AtomicSingleSample new_value) {
102*6777b538SAndroid Build Coastguard Worker   DCHECK(new_value.as_atomic != kDisabledSingleSample)
103*6777b538SAndroid Build Coastguard Worker       << "Disabling an AtomicSingleSample should be done through "
104*6777b538SAndroid Build Coastguard Worker          "ExtractAndDisable().";
105*6777b538SAndroid Build Coastguard Worker 
106*6777b538SAndroid Build Coastguard Worker   AtomicSingleSample old_value;
107*6777b538SAndroid Build Coastguard Worker 
108*6777b538SAndroid Build Coastguard Worker   // Because a concurrent call may modify and/or disable this object as we are
109*6777b538SAndroid Build Coastguard Worker   // trying to extract its value, a compare-and-swap loop must be done to ensure
110*6777b538SAndroid Build Coastguard Worker   // that the value was not changed between the reading and writing (and to
111*6777b538SAndroid Build Coastguard Worker   // prevent accidentally re-enabling this object).
112*6777b538SAndroid Build Coastguard Worker   while (true) {
113*6777b538SAndroid Build Coastguard Worker     old_value.as_atomic = subtle::Acquire_Load(&as_atomic);
114*6777b538SAndroid Build Coastguard Worker 
115*6777b538SAndroid Build Coastguard Worker     // If this object was already disabled, return an empty sample and keep it
116*6777b538SAndroid Build Coastguard Worker     // disabled.
117*6777b538SAndroid Build Coastguard Worker     if (old_value.as_atomic == kDisabledSingleSample) {
118*6777b538SAndroid Build Coastguard Worker       old_value.as_atomic = 0;
119*6777b538SAndroid Build Coastguard Worker       return old_value.as_parts;
120*6777b538SAndroid Build Coastguard Worker     }
121*6777b538SAndroid Build Coastguard Worker 
122*6777b538SAndroid Build Coastguard Worker     // Extract the single-sample from memory. |existing| is what was in that
123*6777b538SAndroid Build Coastguard Worker     // memory location at the time of the call; if it doesn't match |original|
124*6777b538SAndroid Build Coastguard Worker     // (i.e., the single-sample was concurrently modified during this
125*6777b538SAndroid Build Coastguard Worker     // iteration), then the swap did not happen, so try again.
126*6777b538SAndroid Build Coastguard Worker     subtle::Atomic32 existing = subtle::Release_CompareAndSwap(
127*6777b538SAndroid Build Coastguard Worker         &as_atomic, old_value.as_atomic, new_value.as_atomic);
128*6777b538SAndroid Build Coastguard Worker     if (existing == old_value.as_atomic) {
129*6777b538SAndroid Build Coastguard Worker       return old_value.as_parts;
130*6777b538SAndroid Build Coastguard Worker     }
131*6777b538SAndroid Build Coastguard Worker   }
132*6777b538SAndroid Build Coastguard Worker }
133*6777b538SAndroid Build Coastguard Worker 
134*6777b538SAndroid Build Coastguard Worker HistogramSamples::SingleSample
ExtractAndDisable()135*6777b538SAndroid Build Coastguard Worker HistogramSamples::AtomicSingleSample::ExtractAndDisable() {
136*6777b538SAndroid Build Coastguard Worker   AtomicSingleSample old_value(
137*6777b538SAndroid Build Coastguard Worker       subtle::NoBarrier_AtomicExchange(&as_atomic, kDisabledSingleSample));
138*6777b538SAndroid Build Coastguard Worker   // If this object was already disabled, return an empty sample.
139*6777b538SAndroid Build Coastguard Worker   if (old_value.as_atomic == kDisabledSingleSample) {
140*6777b538SAndroid Build Coastguard Worker     old_value.as_atomic = 0;
141*6777b538SAndroid Build Coastguard Worker   }
142*6777b538SAndroid Build Coastguard Worker   return old_value.as_parts;
143*6777b538SAndroid Build Coastguard Worker }
144*6777b538SAndroid Build Coastguard Worker 
Accumulate(size_t bucket,HistogramBase::Count count)145*6777b538SAndroid Build Coastguard Worker bool HistogramSamples::AtomicSingleSample::Accumulate(
146*6777b538SAndroid Build Coastguard Worker     size_t bucket,
147*6777b538SAndroid Build Coastguard Worker     HistogramBase::Count count) {
148*6777b538SAndroid Build Coastguard Worker   if (count == 0)
149*6777b538SAndroid Build Coastguard Worker     return true;
150*6777b538SAndroid Build Coastguard Worker 
151*6777b538SAndroid Build Coastguard Worker   // Convert the parameters to 16-bit variables because it's all 16-bit below.
152*6777b538SAndroid Build Coastguard Worker   // To support decrements/subtractions, divide the |count| into sign/value and
153*6777b538SAndroid Build Coastguard Worker   // do the proper operation below. The alternative is to change the single-
154*6777b538SAndroid Build Coastguard Worker   // sample's count to be a signed integer (int16_t) and just add an int16_t
155*6777b538SAndroid Build Coastguard Worker   // |count16| but that is somewhat wasteful given that the single-sample is
156*6777b538SAndroid Build Coastguard Worker   // never expected to have a count less than zero.
157*6777b538SAndroid Build Coastguard Worker   if (count < -std::numeric_limits<uint16_t>::max() ||
158*6777b538SAndroid Build Coastguard Worker       count > std::numeric_limits<uint16_t>::max() ||
159*6777b538SAndroid Build Coastguard Worker       bucket > std::numeric_limits<uint16_t>::max()) {
160*6777b538SAndroid Build Coastguard Worker     return false;
161*6777b538SAndroid Build Coastguard Worker   }
162*6777b538SAndroid Build Coastguard Worker   bool count_is_negative = count < 0;
163*6777b538SAndroid Build Coastguard Worker   uint16_t count16 = static_cast<uint16_t>(count_is_negative ? -count : count);
164*6777b538SAndroid Build Coastguard Worker   uint16_t bucket16 = static_cast<uint16_t>(bucket);
165*6777b538SAndroid Build Coastguard Worker 
166*6777b538SAndroid Build Coastguard Worker   // A local, unshared copy of the single-sample is necessary so the parts
167*6777b538SAndroid Build Coastguard Worker   // can be manipulated without worrying about atomicity.
168*6777b538SAndroid Build Coastguard Worker   AtomicSingleSample single_sample;
169*6777b538SAndroid Build Coastguard Worker 
170*6777b538SAndroid Build Coastguard Worker   bool sample_updated;
171*6777b538SAndroid Build Coastguard Worker   do {
172*6777b538SAndroid Build Coastguard Worker     subtle::Atomic32 original = subtle::Acquire_Load(&as_atomic);
173*6777b538SAndroid Build Coastguard Worker     if (original == kDisabledSingleSample)
174*6777b538SAndroid Build Coastguard Worker       return false;
175*6777b538SAndroid Build Coastguard Worker     single_sample.as_atomic = original;
176*6777b538SAndroid Build Coastguard Worker     if (single_sample.as_atomic != 0) {
177*6777b538SAndroid Build Coastguard Worker       // Only the same bucket (parameter and stored) can be counted multiple
178*6777b538SAndroid Build Coastguard Worker       // times.
179*6777b538SAndroid Build Coastguard Worker       if (single_sample.as_parts.bucket != bucket16)
180*6777b538SAndroid Build Coastguard Worker         return false;
181*6777b538SAndroid Build Coastguard Worker     } else {
182*6777b538SAndroid Build Coastguard Worker       // The |single_ sample| was zero so becomes the |bucket| parameter, the
183*6777b538SAndroid Build Coastguard Worker       // contents of which were checked above to fit in 16 bits.
184*6777b538SAndroid Build Coastguard Worker       single_sample.as_parts.bucket = bucket16;
185*6777b538SAndroid Build Coastguard Worker     }
186*6777b538SAndroid Build Coastguard Worker 
187*6777b538SAndroid Build Coastguard Worker     // Update count, making sure that it doesn't overflow.
188*6777b538SAndroid Build Coastguard Worker     CheckedNumeric<uint16_t> new_count(single_sample.as_parts.count);
189*6777b538SAndroid Build Coastguard Worker     if (count_is_negative)
190*6777b538SAndroid Build Coastguard Worker       new_count -= count16;
191*6777b538SAndroid Build Coastguard Worker     else
192*6777b538SAndroid Build Coastguard Worker       new_count += count16;
193*6777b538SAndroid Build Coastguard Worker     if (!new_count.AssignIfValid(&single_sample.as_parts.count))
194*6777b538SAndroid Build Coastguard Worker       return false;
195*6777b538SAndroid Build Coastguard Worker 
196*6777b538SAndroid Build Coastguard Worker     // Don't let this become equivalent to the "disabled" value.
197*6777b538SAndroid Build Coastguard Worker     if (single_sample.as_atomic == kDisabledSingleSample)
198*6777b538SAndroid Build Coastguard Worker       return false;
199*6777b538SAndroid Build Coastguard Worker 
200*6777b538SAndroid Build Coastguard Worker     // Store the updated single-sample back into memory. |existing| is what
201*6777b538SAndroid Build Coastguard Worker     // was in that memory location at the time of the call; if it doesn't
202*6777b538SAndroid Build Coastguard Worker     // match |original| then the swap didn't happen so loop again.
203*6777b538SAndroid Build Coastguard Worker     subtle::Atomic32 existing = subtle::Release_CompareAndSwap(
204*6777b538SAndroid Build Coastguard Worker         &as_atomic, original, single_sample.as_atomic);
205*6777b538SAndroid Build Coastguard Worker     sample_updated = (existing == original);
206*6777b538SAndroid Build Coastguard Worker   } while (!sample_updated);
207*6777b538SAndroid Build Coastguard Worker 
208*6777b538SAndroid Build Coastguard Worker   return true;
209*6777b538SAndroid Build Coastguard Worker }
210*6777b538SAndroid Build Coastguard Worker 
IsDisabled() const211*6777b538SAndroid Build Coastguard Worker bool HistogramSamples::AtomicSingleSample::IsDisabled() const {
212*6777b538SAndroid Build Coastguard Worker   return subtle::Acquire_Load(&as_atomic) == kDisabledSingleSample;
213*6777b538SAndroid Build Coastguard Worker }
214*6777b538SAndroid Build Coastguard Worker 
LocalMetadata()215*6777b538SAndroid Build Coastguard Worker HistogramSamples::LocalMetadata::LocalMetadata() {
216*6777b538SAndroid Build Coastguard Worker   // This is the same way it's done for persistent metadata since no ctor
217*6777b538SAndroid Build Coastguard Worker   // is called for the data members in that case.
218*6777b538SAndroid Build Coastguard Worker   memset(this, 0, sizeof(*this));
219*6777b538SAndroid Build Coastguard Worker }
220*6777b538SAndroid Build Coastguard Worker 
HistogramSamples(uint64_t id,Metadata * meta)221*6777b538SAndroid Build Coastguard Worker HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta)
222*6777b538SAndroid Build Coastguard Worker     : meta_(meta) {
223*6777b538SAndroid Build Coastguard Worker   DCHECK(meta_->id == 0 || meta_->id == id);
224*6777b538SAndroid Build Coastguard Worker 
225*6777b538SAndroid Build Coastguard Worker   // It's possible that |meta| is contained in initialized, read-only memory
226*6777b538SAndroid Build Coastguard Worker   // so it's essential that no write be done in that case.
227*6777b538SAndroid Build Coastguard Worker   if (!meta_->id)
228*6777b538SAndroid Build Coastguard Worker     meta_->id = id;
229*6777b538SAndroid Build Coastguard Worker }
230*6777b538SAndroid Build Coastguard Worker 
HistogramSamples(uint64_t id,std::unique_ptr<Metadata> meta)231*6777b538SAndroid Build Coastguard Worker HistogramSamples::HistogramSamples(uint64_t id, std::unique_ptr<Metadata> meta)
232*6777b538SAndroid Build Coastguard Worker     : HistogramSamples(id, meta.get()) {
233*6777b538SAndroid Build Coastguard Worker   meta_owned_ = std::move(meta);
234*6777b538SAndroid Build Coastguard Worker }
235*6777b538SAndroid Build Coastguard Worker 
236*6777b538SAndroid Build Coastguard Worker // This mustn't do anything with |meta_|. It was passed to the ctor and may
237*6777b538SAndroid Build Coastguard Worker // be invalid by the time this dtor gets called.
238*6777b538SAndroid Build Coastguard Worker HistogramSamples::~HistogramSamples() = default;
239*6777b538SAndroid Build Coastguard Worker 
Add(const HistogramSamples & other)240*6777b538SAndroid Build Coastguard Worker void HistogramSamples::Add(const HistogramSamples& other) {
241*6777b538SAndroid Build Coastguard Worker   IncreaseSumAndCount(other.sum(), other.redundant_count());
242*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<SampleCountIterator> it = other.Iterator();
243*6777b538SAndroid Build Coastguard Worker   bool success = AddSubtractImpl(it.get(), ADD);
244*6777b538SAndroid Build Coastguard Worker   DCHECK(success);
245*6777b538SAndroid Build Coastguard Worker }
246*6777b538SAndroid Build Coastguard Worker 
AddFromPickle(PickleIterator * iter)247*6777b538SAndroid Build Coastguard Worker bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
248*6777b538SAndroid Build Coastguard Worker   int64_t sum;
249*6777b538SAndroid Build Coastguard Worker   HistogramBase::Count redundant_count;
250*6777b538SAndroid Build Coastguard Worker 
251*6777b538SAndroid Build Coastguard Worker   if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
252*6777b538SAndroid Build Coastguard Worker     return false;
253*6777b538SAndroid Build Coastguard Worker 
254*6777b538SAndroid Build Coastguard Worker   IncreaseSumAndCount(sum, redundant_count);
255*6777b538SAndroid Build Coastguard Worker 
256*6777b538SAndroid Build Coastguard Worker   SampleCountPickleIterator pickle_iter(iter);
257*6777b538SAndroid Build Coastguard Worker   return AddSubtractImpl(&pickle_iter, ADD);
258*6777b538SAndroid Build Coastguard Worker }
259*6777b538SAndroid Build Coastguard Worker 
Subtract(const HistogramSamples & other)260*6777b538SAndroid Build Coastguard Worker void HistogramSamples::Subtract(const HistogramSamples& other) {
261*6777b538SAndroid Build Coastguard Worker   IncreaseSumAndCount(-other.sum(), -other.redundant_count());
262*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<SampleCountIterator> it = other.Iterator();
263*6777b538SAndroid Build Coastguard Worker   bool success = AddSubtractImpl(it.get(), SUBTRACT);
264*6777b538SAndroid Build Coastguard Worker   DCHECK(success);
265*6777b538SAndroid Build Coastguard Worker }
266*6777b538SAndroid Build Coastguard Worker 
Extract(HistogramSamples & other)267*6777b538SAndroid Build Coastguard Worker void HistogramSamples::Extract(HistogramSamples& other) {
268*6777b538SAndroid Build Coastguard Worker   static_assert(sizeof(other.meta_->sum) == 8);
269*6777b538SAndroid Build Coastguard Worker 
270*6777b538SAndroid Build Coastguard Worker #ifdef ARCH_CPU_64_BITS
271*6777b538SAndroid Build Coastguard Worker   // NoBarrier_AtomicExchange() is only defined for 64-bit types if
272*6777b538SAndroid Build Coastguard Worker   // the ARCH_CPU_64_BITS macro is set.
273*6777b538SAndroid Build Coastguard Worker   subtle::Atomic64 other_sum =
274*6777b538SAndroid Build Coastguard Worker       subtle::NoBarrier_AtomicExchange(&other.meta_->sum, 0);
275*6777b538SAndroid Build Coastguard Worker #else
276*6777b538SAndroid Build Coastguard Worker   // |sum| is only atomic on 64 bit archs. Make |other_sum| volatile so that
277*6777b538SAndroid Build Coastguard Worker   // the following code is not optimized or rearranged to be something like:
278*6777b538SAndroid Build Coastguard Worker   //     IncreaseSumAndCount(other.meta_->sum, ...);
279*6777b538SAndroid Build Coastguard Worker   //     other.meta_->sum = 0;
280*6777b538SAndroid Build Coastguard Worker   // Or:
281*6777b538SAndroid Build Coastguard Worker   //     int64_t other_sum = other.meta_->sum;
282*6777b538SAndroid Build Coastguard Worker   //     other.meta_->sum = 0;
283*6777b538SAndroid Build Coastguard Worker   //     IncreaseSumAndCount(other_sum, ...);
284*6777b538SAndroid Build Coastguard Worker   // Which do not guarantee eventual consistency anymore (other.meta_->sum may
285*6777b538SAndroid Build Coastguard Worker   // be modified concurrently at any time). However, despite this, eventual
286*6777b538SAndroid Build Coastguard Worker   // consistency is still not guaranteed here because performing 64-bit
287*6777b538SAndroid Build Coastguard Worker   // operations (loading, storing, adding, etc.) on a 32-bit machine cannot be
288*6777b538SAndroid Build Coastguard Worker   // done atomically, but this at least reduces the odds of inconsistencies, at
289*6777b538SAndroid Build Coastguard Worker   // the cost of a few extra instructions.
290*6777b538SAndroid Build Coastguard Worker   volatile int64_t other_sum = other.meta_->sum;
291*6777b538SAndroid Build Coastguard Worker   other.meta_->sum -= other_sum;
292*6777b538SAndroid Build Coastguard Worker #endif  // ARCH_CPU_64_BITS
293*6777b538SAndroid Build Coastguard Worker   HistogramBase::AtomicCount other_redundant_count =
294*6777b538SAndroid Build Coastguard Worker       subtle::NoBarrier_AtomicExchange(&other.meta_->redundant_count, 0);
295*6777b538SAndroid Build Coastguard Worker   IncreaseSumAndCount(other_sum, other_redundant_count);
296*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<SampleCountIterator> it = other.ExtractingIterator();
297*6777b538SAndroid Build Coastguard Worker   bool success = AddSubtractImpl(it.get(), ADD);
298*6777b538SAndroid Build Coastguard Worker   DCHECK(success);
299*6777b538SAndroid Build Coastguard Worker }
300*6777b538SAndroid Build Coastguard Worker 
IsDefinitelyEmpty() const301*6777b538SAndroid Build Coastguard Worker bool HistogramSamples::IsDefinitelyEmpty() const {
302*6777b538SAndroid Build Coastguard Worker   return sum() == 0 && redundant_count() == 0;
303*6777b538SAndroid Build Coastguard Worker }
304*6777b538SAndroid Build Coastguard Worker 
Serialize(Pickle * pickle) const305*6777b538SAndroid Build Coastguard Worker void HistogramSamples::Serialize(Pickle* pickle) const {
306*6777b538SAndroid Build Coastguard Worker   pickle->WriteInt64(sum());
307*6777b538SAndroid Build Coastguard Worker   pickle->WriteInt(redundant_count());
308*6777b538SAndroid Build Coastguard Worker 
309*6777b538SAndroid Build Coastguard Worker   HistogramBase::Sample min;
310*6777b538SAndroid Build Coastguard Worker   int64_t max;
311*6777b538SAndroid Build Coastguard Worker   HistogramBase::Count count;
312*6777b538SAndroid Build Coastguard Worker   for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done();
313*6777b538SAndroid Build Coastguard Worker        it->Next()) {
314*6777b538SAndroid Build Coastguard Worker     it->Get(&min, &max, &count);
315*6777b538SAndroid Build Coastguard Worker     pickle->WriteInt(min);
316*6777b538SAndroid Build Coastguard Worker     pickle->WriteInt64(max);
317*6777b538SAndroid Build Coastguard Worker     pickle->WriteInt(count);
318*6777b538SAndroid Build Coastguard Worker   }
319*6777b538SAndroid Build Coastguard Worker }
320*6777b538SAndroid Build Coastguard Worker 
AccumulateSingleSample(HistogramBase::Sample value,HistogramBase::Count count,size_t bucket)321*6777b538SAndroid Build Coastguard Worker bool HistogramSamples::AccumulateSingleSample(HistogramBase::Sample value,
322*6777b538SAndroid Build Coastguard Worker                                               HistogramBase::Count count,
323*6777b538SAndroid Build Coastguard Worker                                               size_t bucket) {
324*6777b538SAndroid Build Coastguard Worker   if (single_sample().Accumulate(bucket, count)) {
325*6777b538SAndroid Build Coastguard Worker     // Success. Update the (separate) sum and redundant-count.
326*6777b538SAndroid Build Coastguard Worker     IncreaseSumAndCount(strict_cast<int64_t>(value) * count, count);
327*6777b538SAndroid Build Coastguard Worker     return true;
328*6777b538SAndroid Build Coastguard Worker   }
329*6777b538SAndroid Build Coastguard Worker   return false;
330*6777b538SAndroid Build Coastguard Worker }
331*6777b538SAndroid Build Coastguard Worker 
IncreaseSumAndCount(int64_t sum,HistogramBase::Count count)332*6777b538SAndroid Build Coastguard Worker void HistogramSamples::IncreaseSumAndCount(int64_t sum,
333*6777b538SAndroid Build Coastguard Worker                                            HistogramBase::Count count) {
334*6777b538SAndroid Build Coastguard Worker #ifdef ARCH_CPU_64_BITS
335*6777b538SAndroid Build Coastguard Worker   subtle::NoBarrier_AtomicIncrement(&meta_->sum, sum);
336*6777b538SAndroid Build Coastguard Worker #else
337*6777b538SAndroid Build Coastguard Worker   meta_->sum += sum;
338*6777b538SAndroid Build Coastguard Worker #endif
339*6777b538SAndroid Build Coastguard Worker   subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, count);
340*6777b538SAndroid Build Coastguard Worker }
341*6777b538SAndroid Build Coastguard Worker 
RecordNegativeSample(NegativeSampleReason reason,HistogramBase::Count increment)342*6777b538SAndroid Build Coastguard Worker void HistogramSamples::RecordNegativeSample(NegativeSampleReason reason,
343*6777b538SAndroid Build Coastguard Worker                                             HistogramBase::Count increment) {
344*6777b538SAndroid Build Coastguard Worker   UMA_HISTOGRAM_ENUMERATION("UMA.NegativeSamples.Reason", reason,
345*6777b538SAndroid Build Coastguard Worker                             MAX_NEGATIVE_SAMPLE_REASONS);
346*6777b538SAndroid Build Coastguard Worker   UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.NegativeSamples.Increment", increment, 1,
347*6777b538SAndroid Build Coastguard Worker                               1 << 30, 100);
348*6777b538SAndroid Build Coastguard Worker   UmaHistogramSparse("UMA.NegativeSamples.Histogram",
349*6777b538SAndroid Build Coastguard Worker                      static_cast<int32_t>(id()));
350*6777b538SAndroid Build Coastguard Worker }
351*6777b538SAndroid Build Coastguard Worker 
ToGraphDict(std::string_view histogram_name,int32_t flags) const352*6777b538SAndroid Build Coastguard Worker base::Value::Dict HistogramSamples::ToGraphDict(std::string_view histogram_name,
353*6777b538SAndroid Build Coastguard Worker                                                 int32_t flags) const {
354*6777b538SAndroid Build Coastguard Worker   base::Value::Dict dict;
355*6777b538SAndroid Build Coastguard Worker   dict.Set("name", histogram_name);
356*6777b538SAndroid Build Coastguard Worker   dict.Set("header", GetAsciiHeader(histogram_name, flags));
357*6777b538SAndroid Build Coastguard Worker   dict.Set("body", GetAsciiBody());
358*6777b538SAndroid Build Coastguard Worker   return dict;
359*6777b538SAndroid Build Coastguard Worker }
360*6777b538SAndroid Build Coastguard Worker 
GetAsciiHeader(std::string_view histogram_name,int32_t flags) const361*6777b538SAndroid Build Coastguard Worker std::string HistogramSamples::GetAsciiHeader(std::string_view histogram_name,
362*6777b538SAndroid Build Coastguard Worker                                              int32_t flags) const {
363*6777b538SAndroid Build Coastguard Worker   std::string output;
364*6777b538SAndroid Build Coastguard Worker   StrAppend(&output, {"Histogram: ", histogram_name, " recorded ",
365*6777b538SAndroid Build Coastguard Worker                       NumberToString(TotalCount()), " samples"});
366*6777b538SAndroid Build Coastguard Worker   if (flags)
367*6777b538SAndroid Build Coastguard Worker     StringAppendF(&output, " (flags = 0x%x)", flags);
368*6777b538SAndroid Build Coastguard Worker   return output;
369*6777b538SAndroid Build Coastguard Worker }
370*6777b538SAndroid Build Coastguard Worker 
GetAsciiBody() const371*6777b538SAndroid Build Coastguard Worker std::string HistogramSamples::GetAsciiBody() const {
372*6777b538SAndroid Build Coastguard Worker   HistogramBase::Count total_count = TotalCount();
373*6777b538SAndroid Build Coastguard Worker   double scaled_total_count = total_count / 100.0;
374*6777b538SAndroid Build Coastguard Worker 
375*6777b538SAndroid Build Coastguard Worker   // Determine how wide the largest bucket range is (how many digits to print),
376*6777b538SAndroid Build Coastguard Worker   // so that we'll be able to right-align starts for the graphical bars.
377*6777b538SAndroid Build Coastguard Worker   // Determine which bucket has the largest sample count so that we can
378*6777b538SAndroid Build Coastguard Worker   // normalize the graphical bar-width relative to that sample count.
379*6777b538SAndroid Build Coastguard Worker   HistogramBase::Count largest_count = 0;
380*6777b538SAndroid Build Coastguard Worker   HistogramBase::Sample largest_sample = 0;
381*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<SampleCountIterator> it = Iterator();
382*6777b538SAndroid Build Coastguard Worker   while (!it->Done()) {
383*6777b538SAndroid Build Coastguard Worker     HistogramBase::Sample min;
384*6777b538SAndroid Build Coastguard Worker     int64_t max;
385*6777b538SAndroid Build Coastguard Worker     HistogramBase::Count count;
386*6777b538SAndroid Build Coastguard Worker     it->Get(&min, &max, &count);
387*6777b538SAndroid Build Coastguard Worker     if (min > largest_sample)
388*6777b538SAndroid Build Coastguard Worker       largest_sample = min;
389*6777b538SAndroid Build Coastguard Worker     if (count > largest_count)
390*6777b538SAndroid Build Coastguard Worker       largest_count = count;
391*6777b538SAndroid Build Coastguard Worker     it->Next();
392*6777b538SAndroid Build Coastguard Worker   }
393*6777b538SAndroid Build Coastguard Worker   // Scale histogram bucket counts to take at most 72 characters.
394*6777b538SAndroid Build Coastguard Worker   // Note: Keep in sync w/ kLineLength sample_vector.cc
395*6777b538SAndroid Build Coastguard Worker   const double kLineLength = 72;
396*6777b538SAndroid Build Coastguard Worker   double scaling_factor = 1;
397*6777b538SAndroid Build Coastguard Worker   if (largest_count > kLineLength)
398*6777b538SAndroid Build Coastguard Worker     scaling_factor = kLineLength / largest_count;
399*6777b538SAndroid Build Coastguard Worker   size_t print_width = GetSimpleAsciiBucketRange(largest_sample).size() + 1;
400*6777b538SAndroid Build Coastguard Worker 
401*6777b538SAndroid Build Coastguard Worker   // iterate over each item and display them
402*6777b538SAndroid Build Coastguard Worker   it = Iterator();
403*6777b538SAndroid Build Coastguard Worker   std::string output;
404*6777b538SAndroid Build Coastguard Worker   while (!it->Done()) {
405*6777b538SAndroid Build Coastguard Worker     HistogramBase::Sample min;
406*6777b538SAndroid Build Coastguard Worker     int64_t max;
407*6777b538SAndroid Build Coastguard Worker     HistogramBase::Count count;
408*6777b538SAndroid Build Coastguard Worker     it->Get(&min, &max, &count);
409*6777b538SAndroid Build Coastguard Worker 
410*6777b538SAndroid Build Coastguard Worker     // value is min, so display it
411*6777b538SAndroid Build Coastguard Worker     std::string range = GetSimpleAsciiBucketRange(min);
412*6777b538SAndroid Build Coastguard Worker     output.append(range);
413*6777b538SAndroid Build Coastguard Worker     if (const auto range_size = range.size(); print_width >= range_size) {
414*6777b538SAndroid Build Coastguard Worker       output.append(print_width + 1 - range_size, ' ');
415*6777b538SAndroid Build Coastguard Worker     }
416*6777b538SAndroid Build Coastguard Worker     HistogramBase::Count current_size = round(count * scaling_factor);
417*6777b538SAndroid Build Coastguard Worker     WriteAsciiBucketGraph(current_size, kLineLength, &output);
418*6777b538SAndroid Build Coastguard Worker     WriteAsciiBucketValue(count, scaled_total_count, &output);
419*6777b538SAndroid Build Coastguard Worker     output.append(1, '\n');
420*6777b538SAndroid Build Coastguard Worker     it->Next();
421*6777b538SAndroid Build Coastguard Worker   }
422*6777b538SAndroid Build Coastguard Worker   return output;
423*6777b538SAndroid Build Coastguard Worker }
424*6777b538SAndroid Build Coastguard Worker 
425*6777b538SAndroid Build Coastguard Worker // static
WriteAsciiBucketGraph(double x_count,int line_length,std::string * output)426*6777b538SAndroid Build Coastguard Worker void HistogramSamples::WriteAsciiBucketGraph(double x_count,
427*6777b538SAndroid Build Coastguard Worker                                              int line_length,
428*6777b538SAndroid Build Coastguard Worker                                              std::string* output) {
429*6777b538SAndroid Build Coastguard Worker   output->reserve(ClampAdd(output->size(), ClampAdd(line_length, 1)));
430*6777b538SAndroid Build Coastguard Worker 
431*6777b538SAndroid Build Coastguard Worker   const size_t count = ClampRound<size_t>(x_count);
432*6777b538SAndroid Build Coastguard Worker   output->append(count, '-');
433*6777b538SAndroid Build Coastguard Worker   output->append(1, 'O');
434*6777b538SAndroid Build Coastguard Worker   if (const auto len = as_unsigned(line_length); count < len) {
435*6777b538SAndroid Build Coastguard Worker     output->append(len - count, ' ');
436*6777b538SAndroid Build Coastguard Worker   }
437*6777b538SAndroid Build Coastguard Worker }
438*6777b538SAndroid Build Coastguard Worker 
WriteAsciiBucketValue(HistogramBase::Count current,double scaled_sum,std::string * output) const439*6777b538SAndroid Build Coastguard Worker void HistogramSamples::WriteAsciiBucketValue(HistogramBase::Count current,
440*6777b538SAndroid Build Coastguard Worker                                              double scaled_sum,
441*6777b538SAndroid Build Coastguard Worker                                              std::string* output) const {
442*6777b538SAndroid Build Coastguard Worker   StringAppendF(output, " (%d = %3.1f%%)", current, current / scaled_sum);
443*6777b538SAndroid Build Coastguard Worker }
444*6777b538SAndroid Build Coastguard Worker 
GetSimpleAsciiBucketRange(HistogramBase::Sample sample) const445*6777b538SAndroid Build Coastguard Worker const std::string HistogramSamples::GetSimpleAsciiBucketRange(
446*6777b538SAndroid Build Coastguard Worker     HistogramBase::Sample sample) const {
447*6777b538SAndroid Build Coastguard Worker   return StringPrintf("%d", sample);
448*6777b538SAndroid Build Coastguard Worker }
449*6777b538SAndroid Build Coastguard Worker 
450*6777b538SAndroid Build Coastguard Worker SampleCountIterator::~SampleCountIterator() = default;
451*6777b538SAndroid Build Coastguard Worker 
GetBucketIndex(size_t * index) const452*6777b538SAndroid Build Coastguard Worker bool SampleCountIterator::GetBucketIndex(size_t* index) const {
453*6777b538SAndroid Build Coastguard Worker   DCHECK(!Done());
454*6777b538SAndroid Build Coastguard Worker   return false;
455*6777b538SAndroid Build Coastguard Worker }
456*6777b538SAndroid Build Coastguard Worker 
SingleSampleIterator(HistogramBase::Sample min,int64_t max,HistogramBase::Count count,size_t bucket_index,bool value_was_extracted)457*6777b538SAndroid Build Coastguard Worker SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
458*6777b538SAndroid Build Coastguard Worker                                            int64_t max,
459*6777b538SAndroid Build Coastguard Worker                                            HistogramBase::Count count,
460*6777b538SAndroid Build Coastguard Worker                                            size_t bucket_index,
461*6777b538SAndroid Build Coastguard Worker                                            bool value_was_extracted)
462*6777b538SAndroid Build Coastguard Worker     : min_(min),
463*6777b538SAndroid Build Coastguard Worker       max_(max),
464*6777b538SAndroid Build Coastguard Worker       bucket_index_(bucket_index),
465*6777b538SAndroid Build Coastguard Worker       count_(count),
466*6777b538SAndroid Build Coastguard Worker       value_was_extracted_(value_was_extracted) {}
467*6777b538SAndroid Build Coastguard Worker 
~SingleSampleIterator()468*6777b538SAndroid Build Coastguard Worker SingleSampleIterator::~SingleSampleIterator() {
469*6777b538SAndroid Build Coastguard Worker   // Because this object may have been instantiated in such a way that the
470*6777b538SAndroid Build Coastguard Worker   // samples it is holding were already extracted from the underlying data, we
471*6777b538SAndroid Build Coastguard Worker   // add a DCHECK to ensure that in those cases, users of this iterator read the
472*6777b538SAndroid Build Coastguard Worker   // samples, otherwise they may be lost.
473*6777b538SAndroid Build Coastguard Worker   DCHECK(!value_was_extracted_ || Done());
474*6777b538SAndroid Build Coastguard Worker }
475*6777b538SAndroid Build Coastguard Worker 
Done() const476*6777b538SAndroid Build Coastguard Worker bool SingleSampleIterator::Done() const {
477*6777b538SAndroid Build Coastguard Worker   return count_ == 0;
478*6777b538SAndroid Build Coastguard Worker }
479*6777b538SAndroid Build Coastguard Worker 
Next()480*6777b538SAndroid Build Coastguard Worker void SingleSampleIterator::Next() {
481*6777b538SAndroid Build Coastguard Worker   DCHECK(!Done());
482*6777b538SAndroid Build Coastguard Worker   count_ = 0;
483*6777b538SAndroid Build Coastguard Worker }
484*6777b538SAndroid Build Coastguard Worker 
Get(HistogramBase::Sample * min,int64_t * max,HistogramBase::Count * count)485*6777b538SAndroid Build Coastguard Worker void SingleSampleIterator::Get(HistogramBase::Sample* min,
486*6777b538SAndroid Build Coastguard Worker                                int64_t* max,
487*6777b538SAndroid Build Coastguard Worker                                HistogramBase::Count* count) {
488*6777b538SAndroid Build Coastguard Worker   DCHECK(!Done());
489*6777b538SAndroid Build Coastguard Worker   *min = min_;
490*6777b538SAndroid Build Coastguard Worker   *max = max_;
491*6777b538SAndroid Build Coastguard Worker   *count = count_;
492*6777b538SAndroid Build Coastguard Worker }
493*6777b538SAndroid Build Coastguard Worker 
GetBucketIndex(size_t * index) const494*6777b538SAndroid Build Coastguard Worker bool SingleSampleIterator::GetBucketIndex(size_t* index) const {
495*6777b538SAndroid Build Coastguard Worker   DCHECK(!Done());
496*6777b538SAndroid Build Coastguard Worker   if (bucket_index_ == kSizeMax)
497*6777b538SAndroid Build Coastguard Worker     return false;
498*6777b538SAndroid Build Coastguard Worker   *index = bucket_index_;
499*6777b538SAndroid Build Coastguard Worker   return true;
500*6777b538SAndroid Build Coastguard Worker }
501*6777b538SAndroid Build Coastguard Worker 
502*6777b538SAndroid Build Coastguard Worker }  // namespace base
503