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