xref: /aosp_15_r20/external/cronet/base/metrics/histogram.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Histogram is an object that aggregates statistics, and can summarize them in
6 // various forms, including ASCII graphical, HTML, and numerically (as a
7 // vector of numbers corresponding to each of the aggregating buckets).
8 // See header file for details and examples.
9 
10 #include "base/metrics/histogram.h"
11 
12 #include <inttypes.h>
13 #include <limits.h>
14 #include <math.h>
15 
16 #include <algorithm>
17 #include <memory>
18 #include <string>
19 #include <string_view>
20 #include <utility>
21 
22 #include "base/compiler_specific.h"
23 #include "base/debug/alias.h"
24 #include "base/logging.h"
25 #include "base/memory/ptr_util.h"
26 #include "base/memory/raw_ptr.h"
27 #include "base/memory/raw_ref.h"
28 #include "base/metrics/dummy_histogram.h"
29 #include "base/metrics/histogram_functions.h"
30 #include "base/metrics/metrics_hashes.h"
31 #include "base/metrics/persistent_histogram_allocator.h"
32 #include "base/metrics/persistent_memory_allocator.h"
33 #include "base/metrics/sample_vector.h"
34 #include "base/metrics/statistics_recorder.h"
35 #include "base/notreached.h"
36 #include "base/pickle.h"
37 #include "base/ranges/algorithm.h"
38 #include "base/strings/string_util.h"
39 #include "base/strings/utf_string_conversions.h"
40 #include "base/synchronization/lock.h"
41 #include "base/values.h"
42 #include "build/build_config.h"
43 
44 namespace base {
45 
46 namespace {
47 
ReadHistogramArguments(PickleIterator * iter,std::string * histogram_name,int * flags,int * declared_min,int * declared_max,size_t * bucket_count,uint32_t * range_checksum)48 bool ReadHistogramArguments(PickleIterator* iter,
49                             std::string* histogram_name,
50                             int* flags,
51                             int* declared_min,
52                             int* declared_max,
53                             size_t* bucket_count,
54                             uint32_t* range_checksum) {
55   uint32_t bucket_count_u32;
56   if (!iter->ReadString(histogram_name) || !iter->ReadInt(flags) ||
57       !iter->ReadInt(declared_min) || !iter->ReadInt(declared_max) ||
58       !iter->ReadUInt32(&bucket_count_u32) ||
59       !iter->ReadUInt32(range_checksum)) {
60     DLOG(ERROR) << "Pickle error decoding Histogram: " << *histogram_name;
61     return false;
62   }
63   *bucket_count = bucket_count_u32;
64 
65   // Since these fields may have come from an untrusted renderer, do additional
66   // checks above and beyond those in Histogram::Initialize()
67   if (*declared_max <= 0 ||
68       *declared_min <= 0 ||
69       *declared_max < *declared_min ||
70       INT_MAX / sizeof(HistogramBase::Count) <= *bucket_count ||
71       *bucket_count < 2) {
72     DLOG(ERROR) << "Values error decoding Histogram: " << histogram_name;
73     return false;
74   }
75 
76   // We use the arguments to find or create the local version of the histogram
77   // in this process, so we need to clear any IPC flag.
78   *flags &= ~HistogramBase::kIPCSerializationSourceFlag;
79 
80   return true;
81 }
82 
ValidateRangeChecksum(const HistogramBase & histogram,uint32_t range_checksum)83 bool ValidateRangeChecksum(const HistogramBase& histogram,
84                            uint32_t range_checksum) {
85   // Normally, |histogram| should have type HISTOGRAM or be inherited from it.
86   // However, if it's expired, it will actually be a DUMMY_HISTOGRAM.
87   // Skip the checks in that case.
88   if (histogram.GetHistogramType() == DUMMY_HISTOGRAM)
89     return true;
90   const Histogram& casted_histogram =
91       static_cast<const Histogram&>(histogram);
92 
93   return casted_histogram.bucket_ranges()->checksum() == range_checksum;
94 }
95 
96 }  // namespace
97 
98 typedef HistogramBase::Count Count;
99 typedef HistogramBase::Sample Sample;
100 
101 class Histogram::Factory {
102  public:
Factory(std::string_view name,HistogramBase::Sample minimum,HistogramBase::Sample maximum,size_t bucket_count,int32_t flags)103   Factory(std::string_view name,
104           HistogramBase::Sample minimum,
105           HistogramBase::Sample maximum,
106           size_t bucket_count,
107           int32_t flags)
108       : Factory(name, HISTOGRAM, minimum, maximum, bucket_count, flags) {}
109 
110   Factory(const Factory&) = delete;
111   Factory& operator=(const Factory&) = delete;
112 
113   // Create histogram based on construction parameters. Caller takes
114   // ownership of the returned object.
115   HistogramBase* Build();
116 
117  protected:
Factory(std::string_view name,HistogramType histogram_type,HistogramBase::Sample minimum,HistogramBase::Sample maximum,size_t bucket_count,int32_t flags)118   Factory(std::string_view name,
119           HistogramType histogram_type,
120           HistogramBase::Sample minimum,
121           HistogramBase::Sample maximum,
122           size_t bucket_count,
123           int32_t flags)
124       : name_(name),
125         histogram_type_(histogram_type),
126         minimum_(minimum),
127         maximum_(maximum),
128         bucket_count_(bucket_count),
129         flags_(flags) {}
130 
131   // Create a BucketRanges structure appropriate for this histogram.
CreateRanges()132   virtual BucketRanges* CreateRanges() {
133     BucketRanges* ranges = new BucketRanges(bucket_count_ + 1);
134     Histogram::InitializeBucketRanges(minimum_, maximum_, ranges);
135     return ranges;
136   }
137 
138   // Allocate the correct Histogram object off the heap (in case persistent
139   // memory is not available).
HeapAlloc(const BucketRanges * ranges)140   virtual std::unique_ptr<HistogramBase> HeapAlloc(const BucketRanges* ranges) {
141     return WrapUnique(new Histogram(GetPermanentName(name_), ranges));
142   }
143 
144   // Perform any required datafill on the just-created histogram.  If
145   // overridden, be sure to call the "super" version -- this method may not
146   // always remain empty.
FillHistogram(HistogramBase * histogram)147   virtual void FillHistogram(HistogramBase* histogram) {}
148 
149   // These values are protected (instead of private) because they need to
150   // be accessible to methods of sub-classes in order to avoid passing
151   // unnecessary parameters everywhere.
152   const std::string_view name_;
153   const HistogramType histogram_type_;
154   HistogramBase::Sample minimum_;
155   HistogramBase::Sample maximum_;
156   size_t bucket_count_;
157   int32_t flags_;
158 };
159 
Build()160 HistogramBase* Histogram::Factory::Build() {
161   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name_);
162   if (!histogram) {
163     // constructor. Refactor code to avoid the additional call.
164     bool should_record = StatisticsRecorder::ShouldRecordHistogram(
165         HashMetricNameAs32Bits(name_));
166     if (!should_record)
167       return DummyHistogram::GetInstance();
168     // To avoid racy destruction at shutdown, the following will be leaked.
169     const BucketRanges* created_ranges = CreateRanges();
170 
171     const BucketRanges* registered_ranges =
172         StatisticsRecorder::RegisterOrDeleteDuplicateRanges(created_ranges);
173 
174     // In most cases, the bucket-count, minimum, and maximum values are known
175     // when the code is written and so are passed in explicitly. In other
176     // cases (such as with a CustomHistogram), they are calculated dynamically
177     // at run-time. In the latter case, those ctor parameters are zero and
178     // the results extracted from the result of CreateRanges().
179     if (bucket_count_ == 0) {
180       bucket_count_ = registered_ranges->bucket_count();
181       minimum_ = registered_ranges->range(1);
182       maximum_ = registered_ranges->range(bucket_count_ - 1);
183     }
184     DCHECK_EQ(minimum_, registered_ranges->range(1));
185     DCHECK_EQ(maximum_, registered_ranges->range(bucket_count_ - 1));
186 
187     // Try to create the histogram using a "persistent" allocator. As of
188     // 2016-02-25, the availability of such is controlled by a base::Feature
189     // that is off by default. If the allocator doesn't exist or if
190     // allocating from it fails, code below will allocate the histogram from
191     // the process heap.
192     PersistentHistogramAllocator::Reference histogram_ref = 0;
193     std::unique_ptr<HistogramBase> tentative_histogram;
194     PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
195     if (allocator) {
196       tentative_histogram = allocator->AllocateHistogram(
197           histogram_type_, name_, minimum_, maximum_, registered_ranges, flags_,
198           &histogram_ref);
199     }
200 
201     // Handle the case where no persistent allocator is present or the
202     // persistent allocation fails (perhaps because it is full).
203     if (!tentative_histogram) {
204       DCHECK(!histogram_ref);  // Should never have been set.
205       flags_ &= ~HistogramBase::kIsPersistent;
206       tentative_histogram = HeapAlloc(registered_ranges);
207       tentative_histogram->SetFlags(flags_);
208     }
209 
210     FillHistogram(tentative_histogram.get());
211 
212     // Register this histogram with the StatisticsRecorder. Keep a copy of
213     // the pointer value to tell later whether the locally created histogram
214     // was registered or deleted. The type is "void" because it could point
215     // to released memory after the following line.
216     const void* tentative_histogram_ptr = tentative_histogram.get();
217     histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(
218         tentative_histogram.release());
219 
220     // Persistent histograms need some follow-up processing.
221     if (histogram_ref) {
222       allocator->FinalizeHistogram(histogram_ref,
223                                    histogram == tentative_histogram_ptr);
224     }
225   }
226 
227   if (histogram_type_ != histogram->GetHistogramType() ||
228       (bucket_count_ != 0 && !histogram->HasConstructionArguments(
229                                  minimum_, maximum_, bucket_count_))) {
230     // The construction arguments do not match the existing histogram.  This can
231     // come about if an extension updates in the middle of a chrome run and has
232     // changed one of them, or simply by bad code within Chrome itself.  A NULL
233     // return would cause Chrome to crash; better to just record it for later
234     // analysis.
235     UmaHistogramSparse("Histogram.MismatchedConstructionArguments",
236                        static_cast<Sample>(HashMetricName(name_)));
237     DLOG(ERROR) << "Histogram " << name_
238                 << " has mismatched construction arguments";
239     return DummyHistogram::GetInstance();
240   }
241   return histogram;
242 }
243 
FactoryGet(const std::string & name,Sample minimum,Sample maximum,size_t bucket_count,int32_t flags)244 HistogramBase* Histogram::FactoryGet(const std::string& name,
245                                      Sample minimum,
246                                      Sample maximum,
247                                      size_t bucket_count,
248                                      int32_t flags) {
249   return FactoryGetInternal(name, minimum, maximum, bucket_count, flags);
250 }
251 
FactoryTimeGet(const std::string & name,TimeDelta minimum,TimeDelta maximum,size_t bucket_count,int32_t flags)252 HistogramBase* Histogram::FactoryTimeGet(const std::string& name,
253                                          TimeDelta minimum,
254                                          TimeDelta maximum,
255                                          size_t bucket_count,
256                                          int32_t flags) {
257   return FactoryTimeGetInternal(name, minimum, maximum, bucket_count, flags);
258 }
259 
FactoryMicrosecondsTimeGet(const std::string & name,TimeDelta minimum,TimeDelta maximum,size_t bucket_count,int32_t flags)260 HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const std::string& name,
261                                                      TimeDelta minimum,
262                                                      TimeDelta maximum,
263                                                      size_t bucket_count,
264                                                      int32_t flags) {
265   return FactoryMicrosecondsTimeGetInternal(name, minimum, maximum,
266                                             bucket_count, flags);
267 }
268 
FactoryGet(const char * name,Sample minimum,Sample maximum,size_t bucket_count,int32_t flags)269 HistogramBase* Histogram::FactoryGet(const char* name,
270                                      Sample minimum,
271                                      Sample maximum,
272                                      size_t bucket_count,
273                                      int32_t flags) {
274   return FactoryGetInternal(name, minimum, maximum, bucket_count, flags);
275 }
276 
FactoryTimeGet(const char * name,TimeDelta minimum,TimeDelta maximum,size_t bucket_count,int32_t flags)277 HistogramBase* Histogram::FactoryTimeGet(const char* name,
278                                          TimeDelta minimum,
279                                          TimeDelta maximum,
280                                          size_t bucket_count,
281                                          int32_t flags) {
282   return FactoryTimeGetInternal(name, minimum, maximum, bucket_count, flags);
283 }
284 
FactoryMicrosecondsTimeGet(const char * name,TimeDelta minimum,TimeDelta maximum,size_t bucket_count,int32_t flags)285 HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const char* name,
286                                                      TimeDelta minimum,
287                                                      TimeDelta maximum,
288                                                      size_t bucket_count,
289                                                      int32_t flags) {
290   return FactoryMicrosecondsTimeGetInternal(name, minimum, maximum,
291                                             bucket_count, flags);
292 }
293 
PersistentCreate(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)294 std::unique_ptr<HistogramBase> Histogram::PersistentCreate(
295     const char* name,
296     const BucketRanges* ranges,
297     const DelayedPersistentAllocation& counts,
298     const DelayedPersistentAllocation& logged_counts,
299     HistogramSamples::Metadata* meta,
300     HistogramSamples::Metadata* logged_meta) {
301   return WrapUnique(
302       new Histogram(name, ranges, counts, logged_counts, meta, logged_meta));
303 }
304 
305 // Calculate what range of values are held in each bucket.
306 // We have to be careful that we don't pick a ratio between starting points in
307 // consecutive buckets that is sooo small, that the integer bounds are the same
308 // (effectively making one bucket get no values).  We need to avoid:
309 //   ranges(i) == ranges(i + 1)
310 // To avoid that, we just do a fine-grained bucket width as far as we need to
311 // until we get a ratio that moves us along at least 2 units at a time.  From
312 // that bucket onward we do use the exponential growth of buckets.
313 //
314 // static
InitializeBucketRanges(Sample minimum,Sample maximum,BucketRanges * ranges)315 void Histogram::InitializeBucketRanges(Sample minimum,
316                                        Sample maximum,
317                                        BucketRanges* ranges) {
318   double log_max = log(static_cast<double>(maximum));
319   double log_ratio;
320   double log_next;
321   size_t bucket_index = 1;
322   Sample current = minimum;
323   ranges->set_range(bucket_index, current);
324   size_t bucket_count = ranges->bucket_count();
325 
326   while (bucket_count > ++bucket_index) {
327     double log_current;
328     log_current = log(static_cast<double>(current));
329     debug::Alias(&log_current);
330     // Calculate the count'th root of the range.
331     log_ratio = (log_max - log_current) / (bucket_count - bucket_index);
332     // See where the next bucket would start.
333     log_next = log_current + log_ratio;
334     Sample next;
335     next = static_cast<int>(std::round(exp(log_next)));
336     if (next > current)
337       current = next;
338     else
339       ++current;  // Just do a narrow bucket, and keep trying.
340     ranges->set_range(bucket_index, current);
341   }
342   ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
343   ranges->ResetChecksum();
344 }
345 
346 // static
347 const int Histogram::kCommonRaceBasedCountMismatch = 5;
348 
FindCorruption(const HistogramSamples & samples) const349 uint32_t Histogram::FindCorruption(const HistogramSamples& samples) const {
350   uint32_t inconsistencies = NO_INCONSISTENCIES;
351   Sample previous_range = -1;  // Bottom range is always 0.
352   for (size_t index = 0; index < bucket_count(); ++index) {
353     int new_range = ranges(index);
354     if (previous_range >= new_range)
355       inconsistencies |= BUCKET_ORDER_ERROR;
356     previous_range = new_range;
357   }
358 
359   if (!bucket_ranges()->HasValidChecksum())
360     inconsistencies |= RANGE_CHECKSUM_ERROR;
361 
362   int64_t delta64 = samples.redundant_count() - samples.TotalCount();
363   if (delta64 != 0) {
364     int delta = static_cast<int>(delta64);
365     if (delta != delta64)
366       delta = INT_MAX;  // Flag all giant errors as INT_MAX.
367     if (delta > 0) {
368       if (delta > kCommonRaceBasedCountMismatch)
369         inconsistencies |= COUNT_HIGH_ERROR;
370     } else {
371       DCHECK_GT(0, delta);
372       if (-delta > kCommonRaceBasedCountMismatch)
373         inconsistencies |= COUNT_LOW_ERROR;
374     }
375   }
376   return inconsistencies;
377 }
378 
bucket_ranges() const379 const BucketRanges* Histogram::bucket_ranges() const {
380   return unlogged_samples_->bucket_ranges();
381 }
382 
declared_min() const383 Sample Histogram::declared_min() const {
384   const BucketRanges* ranges = bucket_ranges();
385   if (ranges->bucket_count() < 2)
386     return -1;
387   return ranges->range(1);
388 }
389 
declared_max() const390 Sample Histogram::declared_max() const {
391   const BucketRanges* ranges = bucket_ranges();
392   if (ranges->bucket_count() < 2)
393     return -1;
394   return ranges->range(ranges->bucket_count() - 1);
395 }
396 
ranges(size_t i) const397 Sample Histogram::ranges(size_t i) const {
398   return bucket_ranges()->range(i);
399 }
400 
bucket_count() const401 size_t Histogram::bucket_count() const {
402   return bucket_ranges()->bucket_count();
403 }
404 
405 // static
InspectConstructionArguments(std::string_view name,Sample * minimum,Sample * maximum,size_t * bucket_count)406 bool Histogram::InspectConstructionArguments(std::string_view name,
407                                              Sample* minimum,
408                                              Sample* maximum,
409                                              size_t* bucket_count) {
410   bool check_okay = true;
411 
412   // Checks below must be done after any min/max swap.
413   if (*minimum > *maximum) {
414     DLOG(ERROR) << "Histogram: " << name << " has swapped minimum/maximum";
415     check_okay = false;
416     std::swap(*minimum, *maximum);
417   }
418 
419   // Defensive code for backward compatibility.
420   if (*minimum < 1) {
421     // TODO(crbug.com/1288842): Temporarily disabled during cleanup.
422     // DLOG(ERROR) << "Histogram: " << name << " has bad minimum: " << *minimum;
423     *minimum = 1;
424     if (*maximum < 1)
425       *maximum = 1;
426   }
427   if (*maximum >= kSampleType_MAX) {
428     DLOG(ERROR) << "Histogram: " << name << " has bad maximum: " << *maximum;
429     *maximum = kSampleType_MAX - 1;
430   }
431   if (*bucket_count > kBucketCount_MAX) {
432     UmaHistogramSparse("Histogram.TooManyBuckets.1000",
433                        static_cast<Sample>(HashMetricName(name)));
434 
435     // Blink.UseCounter legitimately has more than 1000 entries in its enum.
436     if (!StartsWith(name, "Blink.UseCounter")) {
437       DLOG(ERROR) << "Histogram: " << name
438                   << " has bad bucket_count: " << *bucket_count << " (limit "
439                   << kBucketCount_MAX << ")";
440 
441       // Assume it's a mistake and limit to 100 buckets, plus under and over.
442       // If the DCHECK doesn't alert the user then hopefully the small number
443       // will be obvious on the dashboard. If not, then it probably wasn't
444       // important.
445       *bucket_count = 102;
446       check_okay = false;
447     }
448   }
449 
450   // Ensure parameters are sane.
451   if (*maximum == *minimum) {
452     check_okay = false;
453     *maximum = *minimum + 1;
454   }
455   if (*bucket_count < 3) {
456     check_okay = false;
457     *bucket_count = 3;
458   }
459   // The swap at the top of the function guarantees this cast is safe.
460   const size_t max_buckets = static_cast<size_t>(*maximum - *minimum + 2);
461   if (*bucket_count > max_buckets) {
462     check_okay = false;
463     *bucket_count = max_buckets;
464   }
465 
466   if (!check_okay) {
467     UmaHistogramSparse("Histogram.BadConstructionArguments",
468                        static_cast<Sample>(HashMetricName(name)));
469   }
470 
471   return check_okay;
472 }
473 
name_hash() const474 uint64_t Histogram::name_hash() const {
475   return unlogged_samples_->id();
476 }
477 
GetHistogramType() const478 HistogramType Histogram::GetHistogramType() const {
479   return HISTOGRAM;
480 }
481 
HasConstructionArguments(Sample expected_minimum,Sample expected_maximum,size_t expected_bucket_count) const482 bool Histogram::HasConstructionArguments(Sample expected_minimum,
483                                          Sample expected_maximum,
484                                          size_t expected_bucket_count) const {
485   return (expected_bucket_count == bucket_count() &&
486           expected_minimum == declared_min() &&
487           expected_maximum == declared_max());
488 }
489 
Add(int value)490 void Histogram::Add(int value) {
491   AddCount(value, 1);
492 }
493 
AddCount(int value,int count)494 void Histogram::AddCount(int value, int count) {
495   DCHECK_EQ(0, ranges(0));
496   DCHECK_EQ(kSampleType_MAX, ranges(bucket_count()));
497 
498   if (value > kSampleType_MAX - 1)
499     value = kSampleType_MAX - 1;
500   if (value < 0)
501     value = 0;
502   if (count <= 0) {
503     NOTREACHED();
504     return;
505   }
506   unlogged_samples_->Accumulate(value, count);
507 
508   if (UNLIKELY(StatisticsRecorder::have_active_callbacks()))
509     FindAndRunCallbacks(value);
510 }
511 
SnapshotSamples() const512 std::unique_ptr<HistogramSamples> Histogram::SnapshotSamples() const {
513   return SnapshotAllSamples();
514 }
515 
SnapshotUnloggedSamples() const516 std::unique_ptr<HistogramSamples> Histogram::SnapshotUnloggedSamples() const {
517   return SnapshotUnloggedSamplesImpl();
518 }
519 
MarkSamplesAsLogged(const HistogramSamples & samples)520 void Histogram::MarkSamplesAsLogged(const HistogramSamples& samples) {
521   // |final_delta_created_| only exists when DCHECK is on.
522 #if DCHECK_IS_ON()
523   DCHECK(!final_delta_created_);
524 #endif
525 
526   unlogged_samples_->Subtract(samples);
527   logged_samples_->Add(samples);
528 }
529 
SnapshotDelta()530 std::unique_ptr<HistogramSamples> Histogram::SnapshotDelta() {
531   // |final_delta_created_| only exists when DCHECK is on.
532 #if DCHECK_IS_ON()
533   DCHECK(!final_delta_created_);
534 #endif
535 
536   // The code below has subtle thread-safety guarantees! All changes to
537   // the underlying SampleVectors use atomic integer operations, which guarantee
538   // eventual consistency, but do not guarantee full synchronization between
539   // different entries in the SampleVector. In particular, this means that
540   // concurrent updates to the histogram might result in the reported sum not
541   // matching the individual bucket counts; or there being some buckets that are
542   // logically updated "together", but end up being only partially updated when
543   // a snapshot is captured. Note that this is why it's important to subtract
544   // exactly the snapshotted unlogged samples, rather than simply resetting the
545   // vector: this way, the next snapshot will include any concurrent updates
546   // missed by the current snapshot.
547 
548   std::unique_ptr<HistogramSamples> snapshot =
549       std::make_unique<SampleVector>(unlogged_samples_->id(), bucket_ranges());
550   snapshot->Extract(*unlogged_samples_);
551   logged_samples_->Add(*snapshot);
552 
553   return snapshot;
554 }
555 
SnapshotFinalDelta() const556 std::unique_ptr<HistogramSamples> Histogram::SnapshotFinalDelta() const {
557   // |final_delta_created_| only exists when DCHECK is on.
558 #if DCHECK_IS_ON()
559   DCHECK(!final_delta_created_);
560   final_delta_created_ = true;
561 #endif
562 
563   return SnapshotUnloggedSamples();
564 }
565 
AddSamples(const HistogramSamples & samples)566 void Histogram::AddSamples(const HistogramSamples& samples) {
567   unlogged_samples_->Add(samples);
568 }
569 
AddSamplesFromPickle(PickleIterator * iter)570 bool Histogram::AddSamplesFromPickle(PickleIterator* iter) {
571   return unlogged_samples_->AddFromPickle(iter);
572 }
573 
ToGraphDict() const574 base::Value::Dict Histogram::ToGraphDict() const {
575   std::unique_ptr<SampleVector> snapshot = SnapshotAllSamples();
576   return snapshot->ToGraphDict(histogram_name(), flags());
577 }
578 
SerializeInfoImpl(Pickle * pickle) const579 void Histogram::SerializeInfoImpl(Pickle* pickle) const {
580   DCHECK(bucket_ranges()->HasValidChecksum());
581   pickle->WriteString(histogram_name());
582   pickle->WriteInt(flags());
583   pickle->WriteInt(declared_min());
584   pickle->WriteInt(declared_max());
585   // Limited to kBucketCount_MAX, which fits in a uint32_t.
586   pickle->WriteUInt32(static_cast<uint32_t>(bucket_count()));
587   pickle->WriteUInt32(bucket_ranges()->checksum());
588 }
589 
Histogram(const char * name,const BucketRanges * ranges)590 Histogram::Histogram(const char* name, const BucketRanges* ranges)
591     : HistogramBase(name) {
592   DCHECK(ranges) << name;
593   unlogged_samples_ =
594       std::make_unique<SampleVector>(HashMetricName(name), ranges);
595   logged_samples_ =
596       std::make_unique<SampleVector>(unlogged_samples_->id(), ranges);
597 }
598 
Histogram(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)599 Histogram::Histogram(const char* name,
600                      const BucketRanges* ranges,
601                      const DelayedPersistentAllocation& counts,
602                      const DelayedPersistentAllocation& logged_counts,
603                      HistogramSamples::Metadata* meta,
604                      HistogramSamples::Metadata* logged_meta)
605     : HistogramBase(name) {
606   DCHECK(ranges) << name;
607   unlogged_samples_ = std::make_unique<PersistentSampleVector>(
608       HashMetricName(name), ranges, meta, counts);
609   logged_samples_ = std::make_unique<PersistentSampleVector>(
610       unlogged_samples_->id(), ranges, logged_meta, logged_counts);
611 }
612 
613 Histogram::~Histogram() = default;
614 
GetAsciiBucketRange(size_t i) const615 const std::string Histogram::GetAsciiBucketRange(size_t i) const {
616   return GetSimpleAsciiBucketRange(ranges(i));
617 }
618 
619 //------------------------------------------------------------------------------
620 // Private methods
621 
622 // static
DeserializeInfoImpl(PickleIterator * iter)623 HistogramBase* Histogram::DeserializeInfoImpl(PickleIterator* iter) {
624   std::string histogram_name;
625   int flags;
626   int declared_min;
627   int declared_max;
628   size_t bucket_count;
629   uint32_t range_checksum;
630 
631   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
632                               &declared_max, &bucket_count, &range_checksum)) {
633     return nullptr;
634   }
635 
636   // Find or create the local version of the histogram in this process.
637   HistogramBase* histogram = Histogram::FactoryGet(
638       histogram_name, declared_min, declared_max, bucket_count, flags);
639   if (!histogram)
640     return nullptr;
641 
642   // The serialized histogram might be corrupted.
643   if (!ValidateRangeChecksum(*histogram, range_checksum))
644     return nullptr;
645 
646   return histogram;
647 }
648 
649 // static
FactoryGetInternal(std::string_view name,Sample minimum,Sample maximum,size_t bucket_count,int32_t flags)650 HistogramBase* Histogram::FactoryGetInternal(std::string_view name,
651                                              Sample minimum,
652                                              Sample maximum,
653                                              size_t bucket_count,
654                                              int32_t flags) {
655   bool valid_arguments =
656       InspectConstructionArguments(name, &minimum, &maximum, &bucket_count);
657   DCHECK(valid_arguments) << name;
658   if (!valid_arguments) {
659     DLOG(ERROR) << "Histogram " << name << " dropped for invalid parameters.";
660     return DummyHistogram::GetInstance();
661   }
662 
663   return Factory(name, minimum, maximum, bucket_count, flags).Build();
664 }
665 
666 // static
FactoryTimeGetInternal(std::string_view name,TimeDelta minimum,TimeDelta maximum,size_t bucket_count,int32_t flags)667 HistogramBase* Histogram::FactoryTimeGetInternal(std::string_view name,
668                                                  TimeDelta minimum,
669                                                  TimeDelta maximum,
670                                                  size_t bucket_count,
671                                                  int32_t flags) {
672   DCHECK_LT(minimum.InMilliseconds(), std::numeric_limits<Sample>::max());
673   DCHECK_LT(maximum.InMilliseconds(), std::numeric_limits<Sample>::max());
674   return FactoryGetInternal(name, static_cast<Sample>(minimum.InMilliseconds()),
675                             static_cast<Sample>(maximum.InMilliseconds()),
676                             bucket_count, flags);
677 }
678 
679 // static
FactoryMicrosecondsTimeGetInternal(std::string_view name,TimeDelta minimum,TimeDelta maximum,size_t bucket_count,int32_t flags)680 HistogramBase* Histogram::FactoryMicrosecondsTimeGetInternal(
681     std::string_view name,
682     TimeDelta minimum,
683     TimeDelta maximum,
684     size_t bucket_count,
685     int32_t flags) {
686   DCHECK_LT(minimum.InMicroseconds(), std::numeric_limits<Sample>::max());
687   DCHECK_LT(maximum.InMicroseconds(), std::numeric_limits<Sample>::max());
688   return FactoryGetInternal(name, static_cast<Sample>(minimum.InMicroseconds()),
689                             static_cast<Sample>(maximum.InMicroseconds()),
690                             bucket_count, flags);
691 }
692 
SnapshotAllSamples() const693 std::unique_ptr<SampleVector> Histogram::SnapshotAllSamples() const {
694   std::unique_ptr<SampleVector> samples = SnapshotUnloggedSamplesImpl();
695   samples->Add(*logged_samples_);
696   return samples;
697 }
698 
SnapshotUnloggedSamplesImpl() const699 std::unique_ptr<SampleVector> Histogram::SnapshotUnloggedSamplesImpl() const {
700   std::unique_ptr<SampleVector> samples(
701       new SampleVector(unlogged_samples_->id(), bucket_ranges()));
702   samples->Add(*unlogged_samples_);
703   return samples;
704 }
705 
GetParameters() const706 Value::Dict Histogram::GetParameters() const {
707   Value::Dict params;
708   params.Set("type", HistogramTypeToString(GetHistogramType()));
709   params.Set("min", declared_min());
710   params.Set("max", declared_max());
711   params.Set("bucket_count", static_cast<int>(bucket_count()));
712   return params;
713 }
714 
715 //------------------------------------------------------------------------------
716 // LinearHistogram: This histogram uses a traditional set of evenly spaced
717 // buckets.
718 //------------------------------------------------------------------------------
719 
720 class LinearHistogram::Factory : public Histogram::Factory {
721  public:
Factory(std::string_view name,HistogramBase::Sample minimum,HistogramBase::Sample maximum,size_t bucket_count,int32_t flags,const DescriptionPair * descriptions)722   Factory(std::string_view name,
723           HistogramBase::Sample minimum,
724           HistogramBase::Sample maximum,
725           size_t bucket_count,
726           int32_t flags,
727           const DescriptionPair* descriptions)
728       : Histogram::Factory(name,
729                            LINEAR_HISTOGRAM,
730                            minimum,
731                            maximum,
732                            bucket_count,
733                            flags) {
734     descriptions_ = descriptions;
735   }
736 
737   Factory(const Factory&) = delete;
738   Factory& operator=(const Factory&) = delete;
739 
740  protected:
CreateRanges()741   BucketRanges* CreateRanges() override {
742     BucketRanges* ranges = new BucketRanges(bucket_count_ + 1);
743     LinearHistogram::InitializeBucketRanges(minimum_, maximum_, ranges);
744     return ranges;
745   }
746 
HeapAlloc(const BucketRanges * ranges)747   std::unique_ptr<HistogramBase> HeapAlloc(
748       const BucketRanges* ranges) override {
749     return WrapUnique(new LinearHistogram(GetPermanentName(name_), ranges));
750   }
751 
FillHistogram(HistogramBase * base_histogram)752   void FillHistogram(HistogramBase* base_histogram) override {
753     Histogram::Factory::FillHistogram(base_histogram);
754     // Normally, |base_histogram| should have type LINEAR_HISTOGRAM or be
755     // inherited from it. However, if it's expired, it will actually be a
756     // DUMMY_HISTOGRAM. Skip filling in that case.
757     if (base_histogram->GetHistogramType() == DUMMY_HISTOGRAM)
758       return;
759     LinearHistogram* histogram = static_cast<LinearHistogram*>(base_histogram);
760     // Set range descriptions.
761     if (descriptions_) {
762       for (int i = 0; descriptions_[i].description; ++i) {
763         histogram->bucket_description_[descriptions_[i].sample] =
764             descriptions_[i].description;
765       }
766     }
767   }
768 
769  private:
770   raw_ptr<const DescriptionPair, AllowPtrArithmetic> descriptions_;
771 };
772 
773 LinearHistogram::~LinearHistogram() = default;
774 
FactoryGet(const std::string & name,Sample minimum,Sample maximum,size_t bucket_count,int32_t flags)775 HistogramBase* LinearHistogram::FactoryGet(const std::string& name,
776                                            Sample minimum,
777                                            Sample maximum,
778                                            size_t bucket_count,
779                                            int32_t flags) {
780   return FactoryGetInternal(name, minimum, maximum, bucket_count, flags);
781 }
782 
FactoryTimeGet(const std::string & name,TimeDelta minimum,TimeDelta maximum,size_t bucket_count,int32_t flags)783 HistogramBase* LinearHistogram::FactoryTimeGet(const std::string& name,
784                                                TimeDelta minimum,
785                                                TimeDelta maximum,
786                                                size_t bucket_count,
787                                                int32_t flags) {
788   return FactoryTimeGetInternal(name, minimum, maximum, bucket_count, flags);
789 }
790 
FactoryGet(const char * name,Sample minimum,Sample maximum,size_t bucket_count,int32_t flags)791 HistogramBase* LinearHistogram::FactoryGet(const char* name,
792                                            Sample minimum,
793                                            Sample maximum,
794                                            size_t bucket_count,
795                                            int32_t flags) {
796   return FactoryGetInternal(name, minimum, maximum, bucket_count, flags);
797 }
798 
FactoryTimeGet(const char * name,TimeDelta minimum,TimeDelta maximum,size_t bucket_count,int32_t flags)799 HistogramBase* LinearHistogram::FactoryTimeGet(const char* name,
800                                                TimeDelta minimum,
801                                                TimeDelta maximum,
802                                                size_t bucket_count,
803                                                int32_t flags) {
804   return FactoryTimeGetInternal(name, minimum, maximum, bucket_count, flags);
805 }
806 
PersistentCreate(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)807 std::unique_ptr<HistogramBase> LinearHistogram::PersistentCreate(
808     const char* name,
809     const BucketRanges* ranges,
810     const DelayedPersistentAllocation& counts,
811     const DelayedPersistentAllocation& logged_counts,
812     HistogramSamples::Metadata* meta,
813     HistogramSamples::Metadata* logged_meta) {
814   return WrapUnique(new LinearHistogram(name, ranges, counts, logged_counts,
815                                         meta, logged_meta));
816 }
817 
FactoryGetWithRangeDescription(std::string_view name,Sample minimum,Sample maximum,size_t bucket_count,int32_t flags,const DescriptionPair descriptions[])818 HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
819     std::string_view name,
820     Sample minimum,
821     Sample maximum,
822     size_t bucket_count,
823     int32_t flags,
824     const DescriptionPair descriptions[]) {
825   // Originally, histograms were required to have at least one sample value
826   // plus underflow and overflow buckets. For single-entry enumerations,
827   // that one value is usually zero (which IS the underflow bucket)
828   // resulting in a |maximum| value of 1 (the exclusive upper-bound) and only
829   // the two outlier buckets. Handle this by making max==2 and buckets==3.
830   // This usually won't have any cost since the single-value-optimization
831   // will be used until the count exceeds 16 bits.
832   if (maximum == 1 && bucket_count == 2) {
833     maximum = 2;
834     bucket_count = 3;
835   }
836 
837   bool valid_arguments = Histogram::InspectConstructionArguments(
838       name, &minimum, &maximum, &bucket_count);
839   DCHECK(valid_arguments) << name;
840   if (!valid_arguments) {
841     DLOG(ERROR) << "Histogram " << name << " dropped for invalid parameters.";
842     return DummyHistogram::GetInstance();
843   }
844 
845   return Factory(name, minimum, maximum, bucket_count, flags, descriptions)
846       .Build();
847 }
848 
GetHistogramType() const849 HistogramType LinearHistogram::GetHistogramType() const {
850   return LINEAR_HISTOGRAM;
851 }
852 
LinearHistogram(const char * name,const BucketRanges * ranges)853 LinearHistogram::LinearHistogram(const char* name, const BucketRanges* ranges)
854     : Histogram(name, ranges) {}
855 
LinearHistogram(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)856 LinearHistogram::LinearHistogram(
857     const char* name,
858     const BucketRanges* ranges,
859     const DelayedPersistentAllocation& counts,
860     const DelayedPersistentAllocation& logged_counts,
861     HistogramSamples::Metadata* meta,
862     HistogramSamples::Metadata* logged_meta)
863     : Histogram(name, ranges, counts, logged_counts, meta, logged_meta) {}
864 
GetAsciiBucketRange(size_t i) const865 const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const {
866   int range = ranges(i);
867   BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
868   if (it == bucket_description_.end())
869     return Histogram::GetAsciiBucketRange(i);
870   return it->second;
871 }
872 
873 // static
InitializeBucketRanges(Sample minimum,Sample maximum,BucketRanges * ranges)874 void LinearHistogram::InitializeBucketRanges(Sample minimum,
875                                              Sample maximum,
876                                              BucketRanges* ranges) {
877   double min = minimum;
878   double max = maximum;
879   size_t bucket_count = ranges->bucket_count();
880 
881   for (size_t i = 1; i < bucket_count; ++i) {
882     double linear_range =
883         (min * (bucket_count - 1 - i) + max * (i - 1)) / (bucket_count - 2);
884     auto range = static_cast<Sample>(linear_range + 0.5);
885     ranges->set_range(i, range);
886   }
887   ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
888   ranges->ResetChecksum();
889 }
890 
891 // static
FactoryGetInternal(std::string_view name,Sample minimum,Sample maximum,size_t bucket_count,int32_t flags)892 HistogramBase* LinearHistogram::FactoryGetInternal(std::string_view name,
893                                                    Sample minimum,
894                                                    Sample maximum,
895                                                    size_t bucket_count,
896                                                    int32_t flags) {
897   return FactoryGetWithRangeDescription(name, minimum, maximum, bucket_count,
898                                         flags, nullptr);
899 }
900 
901 // static
FactoryTimeGetInternal(std::string_view name,TimeDelta minimum,TimeDelta maximum,size_t bucket_count,int32_t flags)902 HistogramBase* LinearHistogram::FactoryTimeGetInternal(std::string_view name,
903                                                        TimeDelta minimum,
904                                                        TimeDelta maximum,
905                                                        size_t bucket_count,
906                                                        int32_t flags) {
907   DCHECK_LT(minimum.InMilliseconds(), std::numeric_limits<Sample>::max());
908   DCHECK_LT(maximum.InMilliseconds(), std::numeric_limits<Sample>::max());
909   return FactoryGetInternal(name, static_cast<Sample>(minimum.InMilliseconds()),
910                             static_cast<Sample>(maximum.InMilliseconds()),
911                             bucket_count, flags);
912 }
913 
914 // static
DeserializeInfoImpl(PickleIterator * iter)915 HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) {
916   std::string histogram_name;
917   int flags;
918   int declared_min;
919   int declared_max;
920   size_t bucket_count;
921   uint32_t range_checksum;
922 
923   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
924                               &declared_max, &bucket_count, &range_checksum)) {
925     return nullptr;
926   }
927 
928   HistogramBase* histogram = LinearHistogram::FactoryGet(
929       histogram_name, declared_min, declared_max, bucket_count, flags);
930   if (!histogram)
931     return nullptr;
932 
933   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
934     // The serialized histogram might be corrupted.
935     return nullptr;
936   }
937   return histogram;
938 }
939 
940 //------------------------------------------------------------------------------
941 // ScaledLinearHistogram: This is a wrapper around a LinearHistogram that
942 // scales input counts.
943 //------------------------------------------------------------------------------
944 
ScaledLinearHistogram(const std::string & name,Sample minimum,Sample maximum,size_t bucket_count,int32_t scale,int32_t flags)945 ScaledLinearHistogram::ScaledLinearHistogram(const std::string& name,
946                                              Sample minimum,
947                                              Sample maximum,
948                                              size_t bucket_count,
949                                              int32_t scale,
950                                              int32_t flags)
951     : ScaledLinearHistogram(name.data(),
952                             minimum,
953                             maximum,
954                             bucket_count,
955                             scale,
956                             flags) {}
957 
ScaledLinearHistogram(const char * name,Sample minimum,Sample maximum,size_t bucket_count,int32_t scale,int32_t flags)958 ScaledLinearHistogram::ScaledLinearHistogram(const char* name,
959                                              Sample minimum,
960                                              Sample maximum,
961                                              size_t bucket_count,
962                                              int32_t scale,
963                                              int32_t flags)
964     : histogram_(LinearHistogram::FactoryGet(name,
965                                              minimum,
966                                              maximum,
967                                              bucket_count,
968                                              flags)),
969       scale_(scale) {
970   DCHECK(histogram_);
971   DCHECK_LT(1, scale);
972   DCHECK_EQ(1, minimum);
973   CHECK_EQ(static_cast<Sample>(bucket_count), maximum - minimum + 2)
974       << " ScaledLinearHistogram requires buckets of size 1";
975 
976   // Normally, |histogram_| should have type LINEAR_HISTOGRAM or be
977   // inherited from it. However, if it's expired, it will be DUMMY_HISTOGRAM.
978   if (histogram_->GetHistogramType() == DUMMY_HISTOGRAM)
979     return;
980 
981   DCHECK_EQ(histogram_->GetHistogramType(), LINEAR_HISTOGRAM);
982   LinearHistogram* histogram = static_cast<LinearHistogram*>(histogram_);
983   remainders_.resize(histogram->bucket_count(), 0);
984 }
985 
986 ScaledLinearHistogram::~ScaledLinearHistogram() = default;
987 
AddScaledCount(Sample value,int64_t count)988 void ScaledLinearHistogram::AddScaledCount(Sample value, int64_t count) {
989   if (histogram_->GetHistogramType() == DUMMY_HISTOGRAM)
990     return;
991   if (count == 0)
992     return;
993   if (count < 0) {
994     DUMP_WILL_BE_NOTREACHED_NORETURN();
995     return;
996   }
997 
998   DCHECK_EQ(histogram_->GetHistogramType(), LINEAR_HISTOGRAM);
999   LinearHistogram* histogram = static_cast<LinearHistogram*>(histogram_);
1000   const auto max_value = static_cast<Sample>(histogram->bucket_count() - 1);
1001   value = std::clamp(value, 0, max_value);
1002 
1003   int64_t scaled_count = count / scale_;
1004   subtle::Atomic32 remainder = static_cast<int>(count - scaled_count * scale_);
1005 
1006   // ScaledLinearHistogram currently requires 1-to-1 mappings between value
1007   // and bucket which alleviates the need to do a bucket lookup here (something
1008   // that is internal to the HistogramSamples object).
1009   if (remainder > 0) {
1010     remainder = subtle::NoBarrier_AtomicIncrement(
1011         &remainders_[static_cast<size_t>(value)], remainder);
1012     // If remainder passes 1/2 scale, increment main count (thus rounding up).
1013     // The remainder is decremented by the full scale, though, which will
1014     // cause it to go negative and thus requrire another increase by the full
1015     // scale amount before another bump of the scaled count.
1016     if (remainder >= scale_ / 2) {
1017       scaled_count += 1;
1018       subtle::NoBarrier_AtomicIncrement(
1019           &remainders_[static_cast<size_t>(value)], -scale_);
1020     }
1021   }
1022 
1023   if (scaled_count > 0) {
1024     DCHECK(scaled_count <= std::numeric_limits<int>::max());
1025     histogram->AddCount(value, static_cast<int>(scaled_count));
1026   }
1027 }
1028 
1029 //------------------------------------------------------------------------------
1030 // This section provides implementation for BooleanHistogram.
1031 //------------------------------------------------------------------------------
1032 
1033 class BooleanHistogram::Factory : public Histogram::Factory {
1034  public:
Factory(std::string_view name,int32_t flags)1035   Factory(std::string_view name, int32_t flags)
1036       : Histogram::Factory(name, BOOLEAN_HISTOGRAM, 1, 2, 3, flags) {}
1037 
1038   Factory(const Factory&) = delete;
1039   Factory& operator=(const Factory&) = delete;
1040 
1041  protected:
CreateRanges()1042   BucketRanges* CreateRanges() override {
1043     BucketRanges* ranges = new BucketRanges(3 + 1);
1044     LinearHistogram::InitializeBucketRanges(1, 2, ranges);
1045     return ranges;
1046   }
1047 
HeapAlloc(const BucketRanges * ranges)1048   std::unique_ptr<HistogramBase> HeapAlloc(
1049       const BucketRanges* ranges) override {
1050     return WrapUnique(new BooleanHistogram(GetPermanentName(name_), ranges));
1051   }
1052 };
1053 
FactoryGet(const std::string & name,int32_t flags)1054 HistogramBase* BooleanHistogram::FactoryGet(const std::string& name,
1055                                             int32_t flags) {
1056   return FactoryGetInternal(name, flags);
1057 }
1058 
FactoryGet(const char * name,int32_t flags)1059 HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32_t flags) {
1060   return FactoryGetInternal(name, flags);
1061 }
1062 
PersistentCreate(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1063 std::unique_ptr<HistogramBase> BooleanHistogram::PersistentCreate(
1064     const char* name,
1065     const BucketRanges* ranges,
1066     const DelayedPersistentAllocation& counts,
1067     const DelayedPersistentAllocation& logged_counts,
1068     HistogramSamples::Metadata* meta,
1069     HistogramSamples::Metadata* logged_meta) {
1070   return WrapUnique(new BooleanHistogram(name, ranges, counts, logged_counts,
1071                                          meta, logged_meta));
1072 }
1073 
GetHistogramType() const1074 HistogramType BooleanHistogram::GetHistogramType() const {
1075   return BOOLEAN_HISTOGRAM;
1076 }
1077 
1078 // static
FactoryGetInternal(std::string_view name,int32_t flags)1079 HistogramBase* BooleanHistogram::FactoryGetInternal(std::string_view name,
1080                                                     int32_t flags) {
1081   return Factory(name, flags).Build();
1082 }
1083 
BooleanHistogram(const char * name,const BucketRanges * ranges)1084 BooleanHistogram::BooleanHistogram(const char* name, const BucketRanges* ranges)
1085     : LinearHistogram(name, ranges) {}
1086 
BooleanHistogram(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1087 BooleanHistogram::BooleanHistogram(
1088     const char* name,
1089     const BucketRanges* ranges,
1090     const DelayedPersistentAllocation& counts,
1091     const DelayedPersistentAllocation& logged_counts,
1092     HistogramSamples::Metadata* meta,
1093     HistogramSamples::Metadata* logged_meta)
1094     : LinearHistogram(name, ranges, counts, logged_counts, meta, logged_meta) {}
1095 
DeserializeInfoImpl(PickleIterator * iter)1096 HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) {
1097   std::string histogram_name;
1098   int flags;
1099   int declared_min;
1100   int declared_max;
1101   size_t bucket_count;
1102   uint32_t range_checksum;
1103 
1104   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
1105                               &declared_max, &bucket_count, &range_checksum)) {
1106     return nullptr;
1107   }
1108 
1109   HistogramBase* histogram = BooleanHistogram::FactoryGet(
1110       histogram_name, flags);
1111   if (!histogram)
1112     return nullptr;
1113 
1114   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
1115     // The serialized histogram might be corrupted.
1116     return nullptr;
1117   }
1118   return histogram;
1119 }
1120 
1121 //------------------------------------------------------------------------------
1122 // CustomHistogram:
1123 //------------------------------------------------------------------------------
1124 
1125 class CustomHistogram::Factory : public Histogram::Factory {
1126  public:
Factory(std::string_view name,const std::vector<Sample> * custom_ranges,int32_t flags)1127   Factory(std::string_view name,
1128           const std::vector<Sample>* custom_ranges,
1129           int32_t flags)
1130       : Histogram::Factory(name, CUSTOM_HISTOGRAM, 0, 0, 0, flags) {
1131     custom_ranges_ = custom_ranges;
1132   }
1133 
1134   Factory(const Factory&) = delete;
1135   Factory& operator=(const Factory&) = delete;
1136 
1137  protected:
CreateRanges()1138   BucketRanges* CreateRanges() override {
1139     // Remove the duplicates in the custom ranges array.
1140     std::vector<int> ranges = *custom_ranges_;
1141     ranges.push_back(0);  // Ensure we have a zero value.
1142     ranges.push_back(HistogramBase::kSampleType_MAX);
1143     ranges::sort(ranges);
1144     ranges.erase(ranges::unique(ranges), ranges.end());
1145 
1146     BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
1147     for (size_t i = 0; i < ranges.size(); i++) {
1148       bucket_ranges->set_range(i, ranges[i]);
1149     }
1150     bucket_ranges->ResetChecksum();
1151     return bucket_ranges;
1152   }
1153 
HeapAlloc(const BucketRanges * ranges)1154   std::unique_ptr<HistogramBase> HeapAlloc(
1155       const BucketRanges* ranges) override {
1156     return WrapUnique(new CustomHistogram(GetPermanentName(name_), ranges));
1157   }
1158 
1159  private:
1160   raw_ptr<const std::vector<Sample>> custom_ranges_;
1161 };
1162 
FactoryGet(const std::string & name,const std::vector<Sample> & custom_ranges,int32_t flags)1163 HistogramBase* CustomHistogram::FactoryGet(
1164     const std::string& name,
1165     const std::vector<Sample>& custom_ranges,
1166     int32_t flags) {
1167   return FactoryGetInternal(name, custom_ranges, flags);
1168 }
1169 
FactoryGet(const char * name,const std::vector<Sample> & custom_ranges,int32_t flags)1170 HistogramBase* CustomHistogram::FactoryGet(
1171     const char* name,
1172     const std::vector<Sample>& custom_ranges,
1173     int32_t flags) {
1174   return FactoryGetInternal(name, custom_ranges, flags);
1175 }
1176 
PersistentCreate(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1177 std::unique_ptr<HistogramBase> CustomHistogram::PersistentCreate(
1178     const char* name,
1179     const BucketRanges* ranges,
1180     const DelayedPersistentAllocation& counts,
1181     const DelayedPersistentAllocation& logged_counts,
1182     HistogramSamples::Metadata* meta,
1183     HistogramSamples::Metadata* logged_meta) {
1184   return WrapUnique(new CustomHistogram(name, ranges, counts, logged_counts,
1185                                         meta, logged_meta));
1186 }
1187 
GetHistogramType() const1188 HistogramType CustomHistogram::GetHistogramType() const {
1189   return CUSTOM_HISTOGRAM;
1190 }
1191 
1192 // static
ArrayToCustomEnumRanges(base::span<const Sample> values)1193 std::vector<Sample> CustomHistogram::ArrayToCustomEnumRanges(
1194     base::span<const Sample> values) {
1195   std::vector<Sample> all_values;
1196   for (Sample value : values) {
1197     all_values.push_back(value);
1198 
1199     // Ensure that a guard bucket is added. If we end up with duplicate
1200     // values, FactoryGet will take care of removing them.
1201     all_values.push_back(value + 1);
1202   }
1203   return all_values;
1204 }
1205 
CustomHistogram(const char * name,const BucketRanges * ranges)1206 CustomHistogram::CustomHistogram(const char* name, const BucketRanges* ranges)
1207     : Histogram(name, ranges) {}
1208 
CustomHistogram(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1209 CustomHistogram::CustomHistogram(
1210     const char* name,
1211     const BucketRanges* ranges,
1212     const DelayedPersistentAllocation& counts,
1213     const DelayedPersistentAllocation& logged_counts,
1214     HistogramSamples::Metadata* meta,
1215     HistogramSamples::Metadata* logged_meta)
1216     : Histogram(name, ranges, counts, logged_counts, meta, logged_meta) {}
1217 
SerializeInfoImpl(Pickle * pickle) const1218 void CustomHistogram::SerializeInfoImpl(Pickle* pickle) const {
1219   Histogram::SerializeInfoImpl(pickle);
1220 
1221   // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't
1222   // write them.
1223   for (size_t i = 1; i < bucket_ranges()->bucket_count(); ++i)
1224     pickle->WriteInt(bucket_ranges()->range(i));
1225 }
1226 
1227 // static
DeserializeInfoImpl(PickleIterator * iter)1228 HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) {
1229   std::string histogram_name;
1230   int flags;
1231   int declared_min;
1232   int declared_max;
1233   size_t bucket_count;
1234   uint32_t range_checksum;
1235 
1236   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
1237                               &declared_max, &bucket_count, &range_checksum)) {
1238     return nullptr;
1239   }
1240 
1241   // First and last ranges are not serialized.
1242   std::vector<Sample> sample_ranges(bucket_count - 1);
1243 
1244   for (Sample& sample : sample_ranges) {
1245     if (!iter->ReadInt(&sample))
1246       return nullptr;
1247   }
1248 
1249   HistogramBase* histogram = CustomHistogram::FactoryGet(
1250       histogram_name, sample_ranges, flags);
1251   if (!histogram)
1252     return nullptr;
1253 
1254   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
1255     // The serialized histogram might be corrupted.
1256     return nullptr;
1257   }
1258   return histogram;
1259 }
1260 
1261 // static
FactoryGetInternal(std::string_view name,const std::vector<Sample> & custom_ranges,int32_t flags)1262 HistogramBase* CustomHistogram::FactoryGetInternal(
1263     std::string_view name,
1264     const std::vector<Sample>& custom_ranges,
1265     int32_t flags) {
1266   CHECK(ValidateCustomRanges(custom_ranges));
1267 
1268   return Factory(name, &custom_ranges, flags).Build();
1269 }
1270 
1271 // static
ValidateCustomRanges(const std::vector<Sample> & custom_ranges)1272 bool CustomHistogram::ValidateCustomRanges(
1273     const std::vector<Sample>& custom_ranges) {
1274   bool has_valid_range = false;
1275   for (Sample sample : custom_ranges) {
1276     if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1)
1277       return false;
1278     if (sample != 0)
1279       has_valid_range = true;
1280   }
1281   return has_valid_range;
1282 }
1283 
1284 }  // namespace base
1285