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