• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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 <string>
18 #include <utility>
19 
20 #include "base/compiler_specific.h"
21 #include "base/debug/alias.h"
22 #include "base/logging.h"
23 #include "base/memory/ptr_util.h"
24 #include "base/metrics/dummy_histogram.h"
25 #include "base/metrics/histogram_functions.h"
26 #include "base/metrics/metrics_hashes.h"
27 #include "base/metrics/persistent_histogram_allocator.h"
28 #include "base/metrics/persistent_memory_allocator.h"
29 #include "base/metrics/sample_vector.h"
30 #include "base/metrics/statistics_recorder.h"
31 #include "base/pickle.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/stringprintf.h"
34 #include "base/synchronization/lock.h"
35 #include "base/values.h"
36 #include "build/build_config.h"
37 
38 namespace base {
39 
40 namespace {
41 
ReadHistogramArguments(PickleIterator * iter,std::string * histogram_name,int * flags,int * declared_min,int * declared_max,uint32_t * bucket_count,uint32_t * range_checksum)42 bool ReadHistogramArguments(PickleIterator* iter,
43                             std::string* histogram_name,
44                             int* flags,
45                             int* declared_min,
46                             int* declared_max,
47                             uint32_t* bucket_count,
48                             uint32_t* range_checksum) {
49   if (!iter->ReadString(histogram_name) ||
50       !iter->ReadInt(flags) ||
51       !iter->ReadInt(declared_min) ||
52       !iter->ReadInt(declared_max) ||
53       !iter->ReadUInt32(bucket_count) ||
54       !iter->ReadUInt32(range_checksum)) {
55     DLOG(ERROR) << "Pickle error decoding Histogram: " << *histogram_name;
56     return false;
57   }
58 
59   // Since these fields may have come from an untrusted renderer, do additional
60   // checks above and beyond those in Histogram::Initialize()
61   if (*declared_max <= 0 ||
62       *declared_min <= 0 ||
63       *declared_max < *declared_min ||
64       INT_MAX / sizeof(HistogramBase::Count) <= *bucket_count ||
65       *bucket_count < 2) {
66     DLOG(ERROR) << "Values error decoding Histogram: " << histogram_name;
67     return false;
68   }
69 
70   // We use the arguments to find or create the local version of the histogram
71   // in this process, so we need to clear any IPC flag.
72   *flags &= ~HistogramBase::kIPCSerializationSourceFlag;
73 
74   return true;
75 }
76 
ValidateRangeChecksum(const HistogramBase & histogram,uint32_t range_checksum)77 bool ValidateRangeChecksum(const HistogramBase& histogram,
78                            uint32_t range_checksum) {
79   // Normally, |histogram| should have type HISTOGRAM or be inherited from it.
80   // However, if it's expired, it will actually be a DUMMY_HISTOGRAM.
81   // Skip the checks in that case.
82   if (histogram.GetHistogramType() == DUMMY_HISTOGRAM)
83     return true;
84   const Histogram& casted_histogram =
85       static_cast<const Histogram&>(histogram);
86 
87   return casted_histogram.bucket_ranges()->checksum() == range_checksum;
88 }
89 
90 }  // namespace
91 
92 typedef HistogramBase::Count Count;
93 typedef HistogramBase::Sample Sample;
94 
95 // static
96 const uint32_t Histogram::kBucketCount_MAX = 16384u;
97 
98 class Histogram::Factory {
99  public:
Factory(const std::string & name,HistogramBase::Sample minimum,HistogramBase::Sample maximum,uint32_t bucket_count,int32_t flags)100   Factory(const std::string& name,
101           HistogramBase::Sample minimum,
102           HistogramBase::Sample maximum,
103           uint32_t bucket_count,
104           int32_t flags)
105     : Factory(name, HISTOGRAM, minimum, maximum, bucket_count, flags) {}
106   virtual ~Factory() = default;
107 
108   // Create histogram based on construction parameters. Caller takes
109   // ownership of the returned object.
110   HistogramBase* Build();
111 
112  protected:
Factory(const std::string & name,HistogramType histogram_type,HistogramBase::Sample minimum,HistogramBase::Sample maximum,uint32_t bucket_count,int32_t flags)113   Factory(const std::string& name,
114           HistogramType histogram_type,
115           HistogramBase::Sample minimum,
116           HistogramBase::Sample maximum,
117           uint32_t bucket_count,
118           int32_t flags)
119     : name_(name),
120       histogram_type_(histogram_type),
121       minimum_(minimum),
122       maximum_(maximum),
123       bucket_count_(bucket_count),
124       flags_(flags) {}
125 
126   // Create a BucketRanges structure appropriate for this histogram.
CreateRanges()127   virtual BucketRanges* CreateRanges() {
128     BucketRanges* ranges = new BucketRanges(bucket_count_ + 1);
129     Histogram::InitializeBucketRanges(minimum_, maximum_, ranges);
130     return ranges;
131   }
132 
133   // Allocate the correct Histogram object off the heap (in case persistent
134   // memory is not available).
HeapAlloc(const BucketRanges * ranges)135   virtual std::unique_ptr<HistogramBase> HeapAlloc(const BucketRanges* ranges) {
136     return WrapUnique(
137         new Histogram(GetPermanentName(name_), minimum_, maximum_, ranges));
138   }
139 
140   // Perform any required datafill on the just-created histogram.  If
141   // overridden, be sure to call the "super" version -- this method may not
142   // always remain empty.
FillHistogram(HistogramBase * histogram)143   virtual void FillHistogram(HistogramBase* histogram) {}
144 
145   // These values are protected (instead of private) because they need to
146   // be accessible to methods of sub-classes in order to avoid passing
147   // unnecessary parameters everywhere.
148   const std::string& name_;
149   const HistogramType histogram_type_;
150   HistogramBase::Sample minimum_;
151   HistogramBase::Sample maximum_;
152   uint32_t bucket_count_;
153   int32_t flags_;
154 
155  private:
156   DISALLOW_COPY_AND_ASSIGN(Factory);
157 };
158 
Build()159 HistogramBase* Histogram::Factory::Build() {
160   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name_);
161   if (!histogram) {
162     // TODO(gayane): |HashMetricName()| is called again in Histogram
163     // constructor. Refactor code to avoid the additional call.
164     bool should_record =
165         StatisticsRecorder::ShouldRecordHistogram(HashMetricName(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     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_ = static_cast<uint32_t>(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_,
197           name_,
198           minimum_,
199           maximum_,
200           registered_ranges,
201           flags_,
202           &histogram_ref);
203     }
204 
205     // Handle the case where no persistent allocator is present or the
206     // persistent allocation fails (perhaps because it is full).
207     if (!tentative_histogram) {
208       DCHECK(!histogram_ref);  // Should never have been set.
209       DCHECK(!allocator);  // Shouldn't have failed.
210       flags_ &= ~HistogramBase::kIsPersistent;
211       tentative_histogram = HeapAlloc(registered_ranges);
212       tentative_histogram->SetFlags(flags_);
213     }
214 
215     FillHistogram(tentative_histogram.get());
216 
217     // Register this histogram with the StatisticsRecorder. Keep a copy of
218     // the pointer value to tell later whether the locally created histogram
219     // was registered or deleted. The type is "void" because it could point
220     // to released memory after the following line.
221     const void* tentative_histogram_ptr = tentative_histogram.get();
222     histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(
223         tentative_histogram.release());
224 
225     // Persistent histograms need some follow-up processing.
226     if (histogram_ref) {
227       allocator->FinalizeHistogram(histogram_ref,
228                                    histogram == tentative_histogram_ptr);
229     }
230   }
231 
232   if (histogram_type_ != histogram->GetHistogramType() ||
233       (bucket_count_ != 0 && !histogram->HasConstructionArguments(
234                                  minimum_, maximum_, bucket_count_))) {
235     // The construction arguments do not match the existing histogram.  This can
236     // come about if an extension updates in the middle of a chrome run and has
237     // changed one of them, or simply by bad code within Chrome itself.  A NULL
238     // return would cause Chrome to crash; better to just record it for later
239     // analysis.
240     UmaHistogramSparse("Histogram.MismatchedConstructionArguments",
241                        static_cast<Sample>(HashMetricName(name_)));
242     DLOG(ERROR) << "Histogram " << name_
243                 << " has mismatched construction arguments";
244     return DummyHistogram::GetInstance();
245   }
246   return histogram;
247 }
248 
FactoryGet(const std::string & name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags)249 HistogramBase* Histogram::FactoryGet(const std::string& name,
250                                      Sample minimum,
251                                      Sample maximum,
252                                      uint32_t bucket_count,
253                                      int32_t flags) {
254   bool valid_arguments =
255       InspectConstructionArguments(name, &minimum, &maximum, &bucket_count);
256   DCHECK(valid_arguments);
257 
258   return Factory(name, minimum, maximum, bucket_count, flags).Build();
259 }
260 
FactoryTimeGet(const std::string & name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)261 HistogramBase* Histogram::FactoryTimeGet(const std::string& name,
262                                          TimeDelta minimum,
263                                          TimeDelta maximum,
264                                          uint32_t bucket_count,
265                                          int32_t flags) {
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,uint32_t bucket_count,int32_t flags)271 HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const std::string& name,
272                                                      TimeDelta minimum,
273                                                      TimeDelta maximum,
274                                                      uint32_t bucket_count,
275                                                      int32_t flags) {
276   return FactoryGet(name, static_cast<Sample>(minimum.InMicroseconds()),
277                     static_cast<Sample>(maximum.InMicroseconds()), bucket_count,
278                     flags);
279 }
280 
FactoryGet(const char * name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags)281 HistogramBase* Histogram::FactoryGet(const char* name,
282                                      Sample minimum,
283                                      Sample maximum,
284                                      uint32_t bucket_count,
285                                      int32_t flags) {
286   return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
287 }
288 
FactoryTimeGet(const char * name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)289 HistogramBase* Histogram::FactoryTimeGet(const char* name,
290                                          TimeDelta minimum,
291                                          TimeDelta maximum,
292                                          uint32_t bucket_count,
293                                          int32_t flags) {
294   return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count,
295                         flags);
296 }
297 
FactoryMicrosecondsTimeGet(const char * name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)298 HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const char* name,
299                                                      TimeDelta minimum,
300                                                      TimeDelta maximum,
301                                                      uint32_t bucket_count,
302                                                      int32_t flags) {
303   return FactoryMicrosecondsTimeGet(std::string(name), minimum, maximum,
304                                     bucket_count, flags);
305 }
306 
PersistentCreate(const char * name,Sample minimum,Sample maximum,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)307 std::unique_ptr<HistogramBase> Histogram::PersistentCreate(
308     const char* name,
309     Sample minimum,
310     Sample maximum,
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(new Histogram(name, minimum, maximum, ranges, counts,
317                                   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   while (bucket_count > ++bucket_index) {
341     double log_current;
342     log_current = log(static_cast<double>(current));
343     // Calculate the count'th root of the range.
344     log_ratio = (log_max - log_current) / (bucket_count - bucket_index);
345     // See where the next bucket would start.
346     log_next = log_current + log_ratio;
347     Sample next;
348     next = static_cast<int>(std::round(exp(log_next)));
349     if (next > current)
350       current = next;
351     else
352       ++current;  // Just do a narrow bucket, and keep trying.
353     ranges->set_range(bucket_index, current);
354   }
355   ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
356   ranges->ResetChecksum();
357 }
358 
359 // static
360 const int Histogram::kCommonRaceBasedCountMismatch = 5;
361 
FindCorruption(const HistogramSamples & samples) const362 uint32_t Histogram::FindCorruption(const HistogramSamples& samples) const {
363   int inconsistencies = NO_INCONSISTENCIES;
364   Sample previous_range = -1;  // Bottom range is always 0.
365   for (uint32_t index = 0; index < bucket_count(); ++index) {
366     int new_range = ranges(index);
367     if (previous_range >= new_range)
368       inconsistencies |= BUCKET_ORDER_ERROR;
369     previous_range = new_range;
370   }
371 
372   if (!bucket_ranges()->HasValidChecksum())
373     inconsistencies |= RANGE_CHECKSUM_ERROR;
374 
375   int64_t delta64 = samples.redundant_count() - samples.TotalCount();
376   if (delta64 != 0) {
377     int delta = static_cast<int>(delta64);
378     if (delta != delta64)
379       delta = INT_MAX;  // Flag all giant errors as INT_MAX.
380     if (delta > 0) {
381       if (delta > kCommonRaceBasedCountMismatch)
382         inconsistencies |= COUNT_HIGH_ERROR;
383     } else {
384       DCHECK_GT(0, delta);
385       if (-delta > kCommonRaceBasedCountMismatch)
386         inconsistencies |= COUNT_LOW_ERROR;
387     }
388   }
389   return inconsistencies;
390 }
391 
bucket_ranges() const392 const BucketRanges* Histogram::bucket_ranges() const {
393   return unlogged_samples_->bucket_ranges();
394 }
395 
declared_min() const396 Sample Histogram::declared_min() const {
397   const BucketRanges* ranges = bucket_ranges();
398   if (ranges->bucket_count() < 2)
399     return -1;
400   return ranges->range(1);
401 }
402 
declared_max() const403 Sample Histogram::declared_max() const {
404   const BucketRanges* ranges = bucket_ranges();
405   if (ranges->bucket_count() < 2)
406     return -1;
407   return ranges->range(ranges->bucket_count() - 1);
408 }
409 
ranges(uint32_t i) const410 Sample Histogram::ranges(uint32_t i) const {
411   return bucket_ranges()->range(i);
412 }
413 
bucket_count() const414 uint32_t Histogram::bucket_count() const {
415   return static_cast<uint32_t>(bucket_ranges()->bucket_count());
416 }
417 
418 // static
InspectConstructionArguments(StringPiece name,Sample * minimum,Sample * maximum,uint32_t * bucket_count)419 bool Histogram::InspectConstructionArguments(StringPiece name,
420                                              Sample* minimum,
421                                              Sample* maximum,
422                                              uint32_t* bucket_count) {
423   // Defensive code for backward compatibility.
424   if (*minimum < 1) {
425     DVLOG(1) << "Histogram: " << name << " has bad minimum: " << *minimum;
426     *minimum = 1;
427   }
428   if (*maximum >= kSampleType_MAX) {
429     DVLOG(1) << "Histogram: " << name << " has bad maximum: " << *maximum;
430     *maximum = kSampleType_MAX - 1;
431   }
432   if (*bucket_count >= kBucketCount_MAX) {
433     DVLOG(1) << "Histogram: " << name << " has bad bucket_count: "
434              << *bucket_count;
435     *bucket_count = kBucketCount_MAX - 1;
436   }
437 
438   bool check_okay = true;
439 
440   if (*minimum > *maximum) {
441     check_okay = false;
442     std::swap(*minimum, *maximum);
443   }
444   if (*maximum == *minimum) {
445     check_okay = false;
446     *maximum = *minimum + 1;
447   }
448   if (*bucket_count < 3) {
449     check_okay = false;
450     *bucket_count = 3;
451   }
452   // Very high bucket counts are wasteful. Use a sparse histogram instead.
453   // Value of 10002 equals a user-supplied value of 10k + 2 overflow buckets.
454   constexpr uint32_t kMaxBucketCount = 10002;
455   if (*bucket_count > kMaxBucketCount) {
456     check_okay = false;
457     *bucket_count = kMaxBucketCount;
458   }
459   if (*bucket_count > static_cast<uint32_t>(*maximum - *minimum + 2)) {
460     check_okay = false;
461     *bucket_count = static_cast<uint32_t>(*maximum - *minimum + 2);
462   }
463 
464   if (!check_okay) {
465     UmaHistogramSparse("Histogram.BadConstructionArguments",
466                        static_cast<Sample>(HashMetricName(name)));
467   }
468 
469   return check_okay;
470 }
471 
name_hash() const472 uint64_t Histogram::name_hash() const {
473   return unlogged_samples_->id();
474 }
475 
GetHistogramType() const476 HistogramType Histogram::GetHistogramType() const {
477   return HISTOGRAM;
478 }
479 
HasConstructionArguments(Sample expected_minimum,Sample expected_maximum,uint32_t expected_bucket_count) const480 bool Histogram::HasConstructionArguments(Sample expected_minimum,
481                                          Sample expected_maximum,
482                                          uint32_t expected_bucket_count) const {
483   return (expected_bucket_count == bucket_count() &&
484           expected_minimum == declared_min() &&
485           expected_maximum == declared_max());
486 }
487 
Add(int value)488 void Histogram::Add(int value) {
489   AddCount(value, 1);
490 }
491 
AddCount(int value,int count)492 void Histogram::AddCount(int value, int count) {
493   DCHECK_EQ(0, ranges(0));
494   DCHECK_EQ(kSampleType_MAX, ranges(bucket_count()));
495 
496   if (value > kSampleType_MAX - 1)
497     value = kSampleType_MAX - 1;
498   if (value < 0)
499     value = 0;
500   if (count <= 0) {
501     NOTREACHED();
502     return;
503   }
504   unlogged_samples_->Accumulate(value, count);
505 
506   FindAndRunCallback(value);
507 }
508 
SnapshotSamples() const509 std::unique_ptr<HistogramSamples> Histogram::SnapshotSamples() const {
510   return SnapshotAllSamples();
511 }
512 
SnapshotDelta()513 std::unique_ptr<HistogramSamples> Histogram::SnapshotDelta() {
514 #if DCHECK_IS_ON()
515   DCHECK(!final_delta_created_);
516 #endif
517 
518   // The code below has subtle thread-safety guarantees! All changes to
519   // the underlying SampleVectors use atomic integer operations, which guarantee
520   // eventual consistency, but do not guarantee full synchronization between
521   // different entries in the SampleVector. In particular, this means that
522   // concurrent updates to the histogram might result in the reported sum not
523   // matching the individual bucket counts; or there being some buckets that are
524   // logically updated "together", but end up being only partially updated when
525   // a snapshot is captured. Note that this is why it's important to subtract
526   // exactly the snapshotted unlogged samples, rather than simply resetting the
527   // vector: this way, the next snapshot will include any concurrent updates
528   // missed by the current snapshot.
529 
530   std::unique_ptr<HistogramSamples> snapshot = SnapshotUnloggedSamples();
531   unlogged_samples_->Subtract(*snapshot);
532   logged_samples_->Add(*snapshot);
533 
534   return snapshot;
535 }
536 
SnapshotFinalDelta() const537 std::unique_ptr<HistogramSamples> Histogram::SnapshotFinalDelta() const {
538 #if DCHECK_IS_ON()
539   DCHECK(!final_delta_created_);
540   final_delta_created_ = true;
541 #endif
542 
543   return SnapshotUnloggedSamples();
544 }
545 
AddSamples(const HistogramSamples & samples)546 void Histogram::AddSamples(const HistogramSamples& samples) {
547   unlogged_samples_->Add(samples);
548 }
549 
AddSamplesFromPickle(PickleIterator * iter)550 bool Histogram::AddSamplesFromPickle(PickleIterator* iter) {
551   return unlogged_samples_->AddFromPickle(iter);
552 }
553 
554 // The following methods provide a graphical histogram display.
WriteHTMLGraph(std::string * output) const555 void Histogram::WriteHTMLGraph(std::string* output) const {
556   // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
557   output->append("<PRE>");
558   WriteAsciiImpl(true, "<br>", output);
559   output->append("</PRE>");
560 }
561 
WriteAscii(std::string * output) const562 void Histogram::WriteAscii(std::string* output) const {
563   WriteAsciiImpl(true, "\n", output);
564 }
565 
ValidateHistogramContents() const566 void Histogram::ValidateHistogramContents() const {
567   CHECK(unlogged_samples_);
568   CHECK(unlogged_samples_->bucket_ranges());
569   CHECK(logged_samples_);
570   CHECK(logged_samples_->bucket_ranges());
571   CHECK_NE(0U, logged_samples_->id());
572 }
573 
SerializeInfoImpl(Pickle * pickle) const574 void Histogram::SerializeInfoImpl(Pickle* pickle) const {
575   DCHECK(bucket_ranges()->HasValidChecksum());
576   pickle->WriteString(histogram_name());
577   pickle->WriteInt(flags());
578   pickle->WriteInt(declared_min());
579   pickle->WriteInt(declared_max());
580   pickle->WriteUInt32(bucket_count());
581   pickle->WriteUInt32(bucket_ranges()->checksum());
582 }
583 
584 // TODO(bcwhite): Remove minimum/maximum parameters from here and call chain.
Histogram(const char * name,Sample minimum,Sample maximum,const BucketRanges * ranges)585 Histogram::Histogram(const char* name,
586                      Sample minimum,
587                      Sample maximum,
588                      const BucketRanges* ranges)
589     : HistogramBase(name) {
590   DCHECK(ranges) << name << ": " << minimum << "-" << maximum;
591   unlogged_samples_.reset(new SampleVector(HashMetricName(name), ranges));
592   logged_samples_.reset(new SampleVector(unlogged_samples_->id(), ranges));
593 }
594 
Histogram(const char * name,Sample minimum,Sample maximum,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)595 Histogram::Histogram(const char* name,
596                      Sample minimum,
597                      Sample maximum,
598                      const BucketRanges* ranges,
599                      const DelayedPersistentAllocation& counts,
600                      const DelayedPersistentAllocation& logged_counts,
601                      HistogramSamples::Metadata* meta,
602                      HistogramSamples::Metadata* logged_meta)
603     : HistogramBase(name) {
604   DCHECK(ranges) << name << ": " << minimum << "-" << maximum;
605   unlogged_samples_.reset(
606       new PersistentSampleVector(HashMetricName(name), ranges, meta, counts));
607   logged_samples_.reset(new PersistentSampleVector(
608       unlogged_samples_->id(), ranges, logged_meta, logged_counts));
609 }
610 
611 Histogram::~Histogram() = default;
612 
PrintEmptyBucket(uint32_t index) const613 bool Histogram::PrintEmptyBucket(uint32_t index) const {
614   return true;
615 }
616 
617 // Use the actual bucket widths (like a linear histogram) until the widths get
618 // over some transition value, and then use that transition width.  Exponentials
619 // get so big so fast (and we don't expect to see a lot of entries in the large
620 // buckets), so we need this to make it possible to see what is going on and
621 // not have 0-graphical-height buckets.
GetBucketSize(Count current,uint32_t i) const622 double Histogram::GetBucketSize(Count current, uint32_t i) const {
623   DCHECK_GT(ranges(i + 1), ranges(i));
624   static const double kTransitionWidth = 5;
625   double denominator = ranges(i + 1) - ranges(i);
626   if (denominator > kTransitionWidth)
627     denominator = kTransitionWidth;  // Stop trying to normalize.
628   return current/denominator;
629 }
630 
GetAsciiBucketRange(uint32_t i) const631 const std::string Histogram::GetAsciiBucketRange(uint32_t i) const {
632   return GetSimpleAsciiBucketRange(ranges(i));
633 }
634 
635 //------------------------------------------------------------------------------
636 // Private methods
637 
638 // static
DeserializeInfoImpl(PickleIterator * iter)639 HistogramBase* Histogram::DeserializeInfoImpl(PickleIterator* iter) {
640   std::string histogram_name;
641   int flags;
642   int declared_min;
643   int declared_max;
644   uint32_t bucket_count;
645   uint32_t range_checksum;
646 
647   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
648                               &declared_max, &bucket_count, &range_checksum)) {
649     return nullptr;
650   }
651 
652   // Find or create the local version of the histogram in this process.
653   HistogramBase* histogram = Histogram::FactoryGet(
654       histogram_name, declared_min, declared_max, bucket_count, flags);
655   if (!histogram)
656     return nullptr;
657 
658   // The serialized histogram might be corrupted.
659   if (!ValidateRangeChecksum(*histogram, range_checksum))
660     return nullptr;
661 
662   return histogram;
663 }
664 
SnapshotAllSamples() const665 std::unique_ptr<SampleVector> Histogram::SnapshotAllSamples() const {
666   std::unique_ptr<SampleVector> samples = SnapshotUnloggedSamples();
667   samples->Add(*logged_samples_);
668   return samples;
669 }
670 
SnapshotUnloggedSamples() const671 std::unique_ptr<SampleVector> Histogram::SnapshotUnloggedSamples() const {
672   std::unique_ptr<SampleVector> samples(
673       new SampleVector(unlogged_samples_->id(), bucket_ranges()));
674   samples->Add(*unlogged_samples_);
675   return samples;
676 }
677 
WriteAsciiImpl(bool graph_it,const std::string & newline,std::string * output) const678 void Histogram::WriteAsciiImpl(bool graph_it,
679                                const std::string& newline,
680                                std::string* output) const {
681   // Get local (stack) copies of all effectively volatile class data so that we
682   // are consistent across our output activities.
683   std::unique_ptr<SampleVector> snapshot = SnapshotAllSamples();
684   Count sample_count = snapshot->TotalCount();
685 
686   WriteAsciiHeader(*snapshot, sample_count, output);
687   output->append(newline);
688 
689   // Prepare to normalize graphical rendering of bucket contents.
690   double max_size = 0;
691   if (graph_it)
692     max_size = GetPeakBucketSize(*snapshot);
693 
694   // Calculate space needed to print bucket range numbers.  Leave room to print
695   // nearly the largest bucket range without sliding over the histogram.
696   uint32_t largest_non_empty_bucket = bucket_count() - 1;
697   while (0 == snapshot->GetCountAtIndex(largest_non_empty_bucket)) {
698     if (0 == largest_non_empty_bucket)
699       break;  // All buckets are empty.
700     --largest_non_empty_bucket;
701   }
702 
703   // Calculate largest print width needed for any of our bucket range displays.
704   size_t print_width = 1;
705   for (uint32_t i = 0; i < bucket_count(); ++i) {
706     if (snapshot->GetCountAtIndex(i)) {
707       size_t width = GetAsciiBucketRange(i).size() + 1;
708       if (width > print_width)
709         print_width = width;
710     }
711   }
712 
713   int64_t remaining = sample_count;
714   int64_t past = 0;
715   // Output the actual histogram graph.
716   for (uint32_t i = 0; i < bucket_count(); ++i) {
717     Count current = snapshot->GetCountAtIndex(i);
718     if (!current && !PrintEmptyBucket(i))
719       continue;
720     remaining -= current;
721     std::string range = GetAsciiBucketRange(i);
722     output->append(range);
723     for (size_t j = 0; range.size() + j < print_width + 1; ++j)
724       output->push_back(' ');
725     if (0 == current && i < bucket_count() - 1 &&
726         0 == snapshot->GetCountAtIndex(i + 1)) {
727       while (i < bucket_count() - 1 &&
728              0 == snapshot->GetCountAtIndex(i + 1)) {
729         ++i;
730       }
731       output->append("... ");
732       output->append(newline);
733       continue;  // No reason to plot emptiness.
734     }
735     double current_size = GetBucketSize(current, i);
736     if (graph_it)
737       WriteAsciiBucketGraph(current_size, max_size, output);
738     WriteAsciiBucketContext(past, current, remaining, i, output);
739     output->append(newline);
740     past += current;
741   }
742   DCHECK_EQ(sample_count, past);
743 }
744 
GetPeakBucketSize(const SampleVectorBase & samples) const745 double Histogram::GetPeakBucketSize(const SampleVectorBase& samples) const {
746   double max = 0;
747   for (uint32_t i = 0; i < bucket_count() ; ++i) {
748     double current_size = GetBucketSize(samples.GetCountAtIndex(i), i);
749     if (current_size > max)
750       max = current_size;
751   }
752   return max;
753 }
754 
WriteAsciiHeader(const SampleVectorBase & samples,Count sample_count,std::string * output) const755 void Histogram::WriteAsciiHeader(const SampleVectorBase& samples,
756                                  Count sample_count,
757                                  std::string* output) const {
758   StringAppendF(output, "Histogram: %s recorded %d samples", histogram_name(),
759                 sample_count);
760   if (sample_count == 0) {
761     DCHECK_EQ(samples.sum(), 0);
762   } else {
763     double mean = static_cast<float>(samples.sum()) / sample_count;
764     StringAppendF(output, ", mean = %.1f", mean);
765   }
766   if (flags())
767     StringAppendF(output, " (flags = 0x%x)", flags());
768 }
769 
WriteAsciiBucketContext(const int64_t past,const Count current,const int64_t remaining,const uint32_t i,std::string * output) const770 void Histogram::WriteAsciiBucketContext(const int64_t past,
771                                         const Count current,
772                                         const int64_t remaining,
773                                         const uint32_t i,
774                                         std::string* output) const {
775   double scaled_sum = (past + current + remaining) / 100.0;
776   WriteAsciiBucketValue(current, scaled_sum, output);
777   if (0 < i) {
778     double percentage = past / scaled_sum;
779     StringAppendF(output, " {%3.1f%%}", percentage);
780   }
781 }
782 
GetParameters(DictionaryValue * params) const783 void Histogram::GetParameters(DictionaryValue* params) const {
784   params->SetString("type", HistogramTypeToString(GetHistogramType()));
785   params->SetInteger("min", declared_min());
786   params->SetInteger("max", declared_max());
787   params->SetInteger("bucket_count", static_cast<int>(bucket_count()));
788 }
789 
GetCountAndBucketData(Count * count,int64_t * sum,ListValue * buckets) const790 void Histogram::GetCountAndBucketData(Count* count,
791                                       int64_t* sum,
792                                       ListValue* buckets) const {
793   std::unique_ptr<SampleVector> snapshot = SnapshotAllSamples();
794   *count = snapshot->TotalCount();
795   *sum = snapshot->sum();
796   uint32_t index = 0;
797   for (uint32_t i = 0; i < bucket_count(); ++i) {
798     Sample count_at_index = snapshot->GetCountAtIndex(i);
799     if (count_at_index > 0) {
800       std::unique_ptr<DictionaryValue> bucket_value(new DictionaryValue());
801       bucket_value->SetInteger("low", ranges(i));
802       if (i != bucket_count() - 1)
803         bucket_value->SetInteger("high", ranges(i + 1));
804       bucket_value->SetInteger("count", count_at_index);
805       buckets->Set(index, std::move(bucket_value));
806       ++index;
807     }
808   }
809 }
810 
811 //------------------------------------------------------------------------------
812 // LinearHistogram: This histogram uses a traditional set of evenly spaced
813 // buckets.
814 //------------------------------------------------------------------------------
815 
816 class LinearHistogram::Factory : public Histogram::Factory {
817  public:
Factory(const std::string & name,HistogramBase::Sample minimum,HistogramBase::Sample maximum,uint32_t bucket_count,int32_t flags,const DescriptionPair * descriptions)818   Factory(const std::string& name,
819           HistogramBase::Sample minimum,
820           HistogramBase::Sample maximum,
821           uint32_t bucket_count,
822           int32_t flags,
823           const DescriptionPair* descriptions)
824     : Histogram::Factory(name, LINEAR_HISTOGRAM, minimum, maximum,
825                          bucket_count, flags) {
826     descriptions_ = descriptions;
827   }
828   ~Factory() override = default;
829 
830  protected:
CreateRanges()831   BucketRanges* CreateRanges() override {
832     BucketRanges* ranges = new BucketRanges(bucket_count_ + 1);
833     LinearHistogram::InitializeBucketRanges(minimum_, maximum_, ranges);
834     return ranges;
835   }
836 
HeapAlloc(const BucketRanges * ranges)837   std::unique_ptr<HistogramBase> HeapAlloc(
838       const BucketRanges* ranges) override {
839     return WrapUnique(new LinearHistogram(GetPermanentName(name_), minimum_,
840                                           maximum_, ranges));
841   }
842 
FillHistogram(HistogramBase * base_histogram)843   void FillHistogram(HistogramBase* base_histogram) override {
844     Histogram::Factory::FillHistogram(base_histogram);
845     // Normally, |base_histogram| should have type LINEAR_HISTOGRAM or be
846     // inherited from it. However, if it's expired, it will actually be a
847     // DUMMY_HISTOGRAM. Skip filling in that case.
848     if (base_histogram->GetHistogramType() == DUMMY_HISTOGRAM)
849       return;
850     LinearHistogram* histogram = static_cast<LinearHistogram*>(base_histogram);
851     // Set range descriptions.
852     if (descriptions_) {
853       for (int i = 0; descriptions_[i].description; ++i) {
854         histogram->bucket_description_[descriptions_[i].sample] =
855             descriptions_[i].description;
856       }
857     }
858   }
859 
860  private:
861   const DescriptionPair* descriptions_;
862 
863   DISALLOW_COPY_AND_ASSIGN(Factory);
864 };
865 
866 LinearHistogram::~LinearHistogram() = default;
867 
FactoryGet(const std::string & name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags)868 HistogramBase* LinearHistogram::FactoryGet(const std::string& name,
869                                            Sample minimum,
870                                            Sample maximum,
871                                            uint32_t bucket_count,
872                                            int32_t flags) {
873   return FactoryGetWithRangeDescription(name, minimum, maximum, bucket_count,
874                                         flags, NULL);
875 }
876 
FactoryTimeGet(const std::string & name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)877 HistogramBase* LinearHistogram::FactoryTimeGet(const std::string& name,
878                                                TimeDelta minimum,
879                                                TimeDelta maximum,
880                                                uint32_t bucket_count,
881                                                int32_t flags) {
882   return FactoryGet(name, static_cast<Sample>(minimum.InMilliseconds()),
883                     static_cast<Sample>(maximum.InMilliseconds()), bucket_count,
884                     flags);
885 }
886 
FactoryGet(const char * name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags)887 HistogramBase* LinearHistogram::FactoryGet(const char* name,
888                                            Sample minimum,
889                                            Sample maximum,
890                                            uint32_t bucket_count,
891                                            int32_t flags) {
892   return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
893 }
894 
FactoryTimeGet(const char * name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)895 HistogramBase* LinearHistogram::FactoryTimeGet(const char* name,
896                                                TimeDelta minimum,
897                                                TimeDelta maximum,
898                                                uint32_t bucket_count,
899                                                int32_t flags) {
900   return FactoryTimeGet(std::string(name),  minimum, maximum, bucket_count,
901                         flags);
902 }
903 
PersistentCreate(const char * name,Sample minimum,Sample maximum,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)904 std::unique_ptr<HistogramBase> LinearHistogram::PersistentCreate(
905     const char* name,
906     Sample minimum,
907     Sample maximum,
908     const BucketRanges* ranges,
909     const DelayedPersistentAllocation& counts,
910     const DelayedPersistentAllocation& logged_counts,
911     HistogramSamples::Metadata* meta,
912     HistogramSamples::Metadata* logged_meta) {
913   return WrapUnique(new LinearHistogram(name, minimum, maximum, ranges, counts,
914                                         logged_counts, meta, logged_meta));
915 }
916 
FactoryGetWithRangeDescription(const std::string & name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags,const DescriptionPair descriptions[])917 HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
918     const std::string& name,
919     Sample minimum,
920     Sample maximum,
921     uint32_t bucket_count,
922     int32_t flags,
923     const DescriptionPair descriptions[]) {
924   bool valid_arguments = Histogram::InspectConstructionArguments(
925       name, &minimum, &maximum, &bucket_count);
926   DCHECK(valid_arguments);
927 
928   return Factory(name, minimum, maximum, bucket_count, flags, descriptions)
929       .Build();
930 }
931 
GetHistogramType() const932 HistogramType LinearHistogram::GetHistogramType() const {
933   return LINEAR_HISTOGRAM;
934 }
935 
LinearHistogram(const char * name,Sample minimum,Sample maximum,const BucketRanges * ranges)936 LinearHistogram::LinearHistogram(const char* name,
937                                  Sample minimum,
938                                  Sample maximum,
939                                  const BucketRanges* ranges)
940     : Histogram(name, minimum, maximum, ranges) {}
941 
LinearHistogram(const char * name,Sample minimum,Sample maximum,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)942 LinearHistogram::LinearHistogram(
943     const char* name,
944     Sample minimum,
945     Sample maximum,
946     const BucketRanges* ranges,
947     const DelayedPersistentAllocation& counts,
948     const DelayedPersistentAllocation& logged_counts,
949     HistogramSamples::Metadata* meta,
950     HistogramSamples::Metadata* logged_meta)
951     : Histogram(name,
952                 minimum,
953                 maximum,
954                 ranges,
955                 counts,
956                 logged_counts,
957                 meta,
958                 logged_meta) {}
959 
GetBucketSize(Count current,uint32_t i) const960 double LinearHistogram::GetBucketSize(Count current, uint32_t i) const {
961   DCHECK_GT(ranges(i + 1), ranges(i));
962   // Adjacent buckets with different widths would have "surprisingly" many (few)
963   // samples in a histogram if we didn't normalize this way.
964   double denominator = ranges(i + 1) - ranges(i);
965   return current/denominator;
966 }
967 
GetAsciiBucketRange(uint32_t i) const968 const std::string LinearHistogram::GetAsciiBucketRange(uint32_t i) const {
969   int range = ranges(i);
970   BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
971   if (it == bucket_description_.end())
972     return Histogram::GetAsciiBucketRange(i);
973   return it->second;
974 }
975 
PrintEmptyBucket(uint32_t index) const976 bool LinearHistogram::PrintEmptyBucket(uint32_t index) const {
977   return bucket_description_.find(ranges(index)) == bucket_description_.end();
978 }
979 
980 // static
InitializeBucketRanges(Sample minimum,Sample maximum,BucketRanges * ranges)981 void LinearHistogram::InitializeBucketRanges(Sample minimum,
982                                              Sample maximum,
983                                              BucketRanges* ranges) {
984   double min = minimum;
985   double max = maximum;
986   size_t bucket_count = ranges->bucket_count();
987   for (size_t i = 1; i < bucket_count; ++i) {
988     double linear_range =
989         (min * (bucket_count - 1 - i) + max * (i - 1)) / (bucket_count - 2);
990     ranges->set_range(i, static_cast<Sample>(linear_range + 0.5));
991   }
992   ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
993   ranges->ResetChecksum();
994 }
995 
996 // static
DeserializeInfoImpl(PickleIterator * iter)997 HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) {
998   std::string histogram_name;
999   int flags;
1000   int declared_min;
1001   int declared_max;
1002   uint32_t bucket_count;
1003   uint32_t range_checksum;
1004 
1005   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
1006                               &declared_max, &bucket_count, &range_checksum)) {
1007     return nullptr;
1008   }
1009 
1010   HistogramBase* histogram = LinearHistogram::FactoryGet(
1011       histogram_name, declared_min, declared_max, bucket_count, flags);
1012   if (!histogram)
1013     return nullptr;
1014 
1015   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
1016     // The serialized histogram might be corrupted.
1017     return nullptr;
1018   }
1019   return histogram;
1020 }
1021 
1022 //------------------------------------------------------------------------------
1023 // ScaledLinearHistogram: This is a wrapper around a LinearHistogram that
1024 // scales input counts.
1025 //------------------------------------------------------------------------------
1026 
ScaledLinearHistogram(const char * name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t scale,int32_t flags)1027 ScaledLinearHistogram::ScaledLinearHistogram(const char* name,
1028                                              Sample minimum,
1029                                              Sample maximum,
1030                                              uint32_t bucket_count,
1031                                              int32_t scale,
1032                                              int32_t flags)
1033     : histogram_(static_cast<LinearHistogram*>(
1034           LinearHistogram::FactoryGet(name,
1035                                       minimum,
1036                                       maximum,
1037                                       bucket_count,
1038                                       flags))),
1039       scale_(scale) {
1040   DCHECK(histogram_);
1041   DCHECK_LT(1, scale);
1042   DCHECK_EQ(1, minimum);
1043   CHECK_EQ(static_cast<Sample>(bucket_count), maximum - minimum + 2)
1044       << " ScaledLinearHistogram requires buckets of size 1";
1045 
1046   remainders_.resize(histogram_->bucket_count(), 0);
1047 }
1048 
1049 ScaledLinearHistogram::~ScaledLinearHistogram() = default;
1050 
AddScaledCount(Sample value,int count)1051 void ScaledLinearHistogram::AddScaledCount(Sample value, int count) {
1052   if (count == 0)
1053     return;
1054   if (count < 0) {
1055     NOTREACHED();
1056     return;
1057   }
1058   const int32_t max_value =
1059       static_cast<int32_t>(histogram_->bucket_count() - 1);
1060   if (value > max_value)
1061     value = max_value;
1062   if (value < 0)
1063     value = 0;
1064 
1065   int scaled_count = count / scale_;
1066   subtle::Atomic32 remainder = count - scaled_count * scale_;
1067 
1068   // ScaledLinearHistogram currently requires 1-to-1 mappings between value
1069   // and bucket which alleviates the need to do a bucket lookup here (something
1070   // that is internal to the HistogramSamples object).
1071   if (remainder > 0) {
1072     remainder =
1073         subtle::NoBarrier_AtomicIncrement(&remainders_[value], remainder);
1074     // If remainder passes 1/2 scale, increment main count (thus rounding up).
1075     // The remainder is decremented by the full scale, though, which will
1076     // cause it to go negative and thus requrire another increase by the full
1077     // scale amount before another bump of the scaled count.
1078     if (remainder >= scale_ / 2) {
1079       scaled_count += 1;
1080       subtle::NoBarrier_AtomicIncrement(&remainders_[value], -scale_);
1081     }
1082   }
1083 
1084   if (scaled_count > 0)
1085     histogram_->AddCount(value, scaled_count);
1086 }
1087 
1088 //------------------------------------------------------------------------------
1089 // This section provides implementation for BooleanHistogram.
1090 //------------------------------------------------------------------------------
1091 
1092 class BooleanHistogram::Factory : public Histogram::Factory {
1093  public:
Factory(const std::string & name,int32_t flags)1094   Factory(const std::string& name, int32_t flags)
1095     : Histogram::Factory(name, BOOLEAN_HISTOGRAM, 1, 2, 3, flags) {}
1096   ~Factory() override = default;
1097 
1098  protected:
CreateRanges()1099   BucketRanges* CreateRanges() override {
1100     BucketRanges* ranges = new BucketRanges(3 + 1);
1101     LinearHistogram::InitializeBucketRanges(1, 2, ranges);
1102     return ranges;
1103   }
1104 
HeapAlloc(const BucketRanges * ranges)1105   std::unique_ptr<HistogramBase> HeapAlloc(
1106       const BucketRanges* ranges) override {
1107     return WrapUnique(new BooleanHistogram(GetPermanentName(name_), ranges));
1108   }
1109 
1110  private:
1111   DISALLOW_COPY_AND_ASSIGN(Factory);
1112 };
1113 
FactoryGet(const std::string & name,int32_t flags)1114 HistogramBase* BooleanHistogram::FactoryGet(const std::string& name,
1115                                             int32_t flags) {
1116   return Factory(name, flags).Build();
1117 }
1118 
FactoryGet(const char * name,int32_t flags)1119 HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32_t flags) {
1120   return FactoryGet(std::string(name), flags);
1121 }
1122 
PersistentCreate(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1123 std::unique_ptr<HistogramBase> BooleanHistogram::PersistentCreate(
1124     const char* name,
1125     const BucketRanges* ranges,
1126     const DelayedPersistentAllocation& counts,
1127     const DelayedPersistentAllocation& logged_counts,
1128     HistogramSamples::Metadata* meta,
1129     HistogramSamples::Metadata* logged_meta) {
1130   return WrapUnique(new BooleanHistogram(name, ranges, counts, logged_counts,
1131                                          meta, logged_meta));
1132 }
1133 
GetHistogramType() const1134 HistogramType BooleanHistogram::GetHistogramType() const {
1135   return BOOLEAN_HISTOGRAM;
1136 }
1137 
BooleanHistogram(const char * name,const BucketRanges * ranges)1138 BooleanHistogram::BooleanHistogram(const char* name, const BucketRanges* ranges)
1139     : LinearHistogram(name, 1, 2, ranges) {}
1140 
BooleanHistogram(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1141 BooleanHistogram::BooleanHistogram(
1142     const char* name,
1143     const BucketRanges* ranges,
1144     const DelayedPersistentAllocation& counts,
1145     const DelayedPersistentAllocation& logged_counts,
1146     HistogramSamples::Metadata* meta,
1147     HistogramSamples::Metadata* logged_meta)
1148     : LinearHistogram(name,
1149                       1,
1150                       2,
1151                       ranges,
1152                       counts,
1153                       logged_counts,
1154                       meta,
1155                       logged_meta) {}
1156 
DeserializeInfoImpl(PickleIterator * iter)1157 HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) {
1158   std::string histogram_name;
1159   int flags;
1160   int declared_min;
1161   int declared_max;
1162   uint32_t bucket_count;
1163   uint32_t range_checksum;
1164 
1165   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
1166                               &declared_max, &bucket_count, &range_checksum)) {
1167     return nullptr;
1168   }
1169 
1170   HistogramBase* histogram = BooleanHistogram::FactoryGet(
1171       histogram_name, flags);
1172   if (!histogram)
1173     return nullptr;
1174 
1175   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
1176     // The serialized histogram might be corrupted.
1177     return nullptr;
1178   }
1179   return histogram;
1180 }
1181 
1182 //------------------------------------------------------------------------------
1183 // CustomHistogram:
1184 //------------------------------------------------------------------------------
1185 
1186 class CustomHistogram::Factory : public Histogram::Factory {
1187  public:
Factory(const std::string & name,const std::vector<Sample> * custom_ranges,int32_t flags)1188   Factory(const std::string& name,
1189           const std::vector<Sample>* custom_ranges,
1190           int32_t flags)
1191     : Histogram::Factory(name, CUSTOM_HISTOGRAM, 0, 0, 0, flags) {
1192     custom_ranges_ = custom_ranges;
1193   }
1194   ~Factory() override = default;
1195 
1196  protected:
CreateRanges()1197   BucketRanges* CreateRanges() override {
1198     // Remove the duplicates in the custom ranges array.
1199     std::vector<int> ranges = *custom_ranges_;
1200     ranges.push_back(0);  // Ensure we have a zero value.
1201     ranges.push_back(HistogramBase::kSampleType_MAX);
1202     std::sort(ranges.begin(), ranges.end());
1203     ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
1204 
1205     BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
1206     for (uint32_t i = 0; i < ranges.size(); i++) {
1207       bucket_ranges->set_range(i, ranges[i]);
1208     }
1209     bucket_ranges->ResetChecksum();
1210     return bucket_ranges;
1211   }
1212 
HeapAlloc(const BucketRanges * ranges)1213   std::unique_ptr<HistogramBase> HeapAlloc(
1214       const BucketRanges* ranges) override {
1215     return WrapUnique(new CustomHistogram(GetPermanentName(name_), ranges));
1216   }
1217 
1218  private:
1219   const std::vector<Sample>* custom_ranges_;
1220 
1221   DISALLOW_COPY_AND_ASSIGN(Factory);
1222 };
1223 
FactoryGet(const std::string & name,const std::vector<Sample> & custom_ranges,int32_t flags)1224 HistogramBase* CustomHistogram::FactoryGet(
1225     const std::string& name,
1226     const std::vector<Sample>& custom_ranges,
1227     int32_t flags) {
1228   CHECK(ValidateCustomRanges(custom_ranges));
1229 
1230   return Factory(name, &custom_ranges, flags).Build();
1231 }
1232 
FactoryGet(const char * name,const std::vector<Sample> & custom_ranges,int32_t flags)1233 HistogramBase* CustomHistogram::FactoryGet(
1234     const char* name,
1235     const std::vector<Sample>& custom_ranges,
1236     int32_t flags) {
1237   return FactoryGet(std::string(name), custom_ranges, flags);
1238 }
1239 
PersistentCreate(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1240 std::unique_ptr<HistogramBase> CustomHistogram::PersistentCreate(
1241     const char* name,
1242     const BucketRanges* ranges,
1243     const DelayedPersistentAllocation& counts,
1244     const DelayedPersistentAllocation& logged_counts,
1245     HistogramSamples::Metadata* meta,
1246     HistogramSamples::Metadata* logged_meta) {
1247   return WrapUnique(new CustomHistogram(name, ranges, counts, logged_counts,
1248                                         meta, logged_meta));
1249 }
1250 
GetHistogramType() const1251 HistogramType CustomHistogram::GetHistogramType() const {
1252   return CUSTOM_HISTOGRAM;
1253 }
1254 
1255 // static
ArrayToCustomEnumRanges(base::span<const Sample> values)1256 std::vector<Sample> CustomHistogram::ArrayToCustomEnumRanges(
1257     base::span<const Sample> values) {
1258   std::vector<Sample> all_values;
1259   for (Sample value : values) {
1260     all_values.push_back(value);
1261 
1262     // Ensure that a guard bucket is added. If we end up with duplicate
1263     // values, FactoryGet will take care of removing them.
1264     all_values.push_back(value + 1);
1265   }
1266   return all_values;
1267 }
1268 
CustomHistogram(const char * name,const BucketRanges * ranges)1269 CustomHistogram::CustomHistogram(const char* name, const BucketRanges* ranges)
1270     : Histogram(name,
1271                 ranges->range(1),
1272                 ranges->range(ranges->bucket_count() - 1),
1273                 ranges) {}
1274 
CustomHistogram(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1275 CustomHistogram::CustomHistogram(
1276     const char* name,
1277     const BucketRanges* ranges,
1278     const DelayedPersistentAllocation& counts,
1279     const DelayedPersistentAllocation& logged_counts,
1280     HistogramSamples::Metadata* meta,
1281     HistogramSamples::Metadata* logged_meta)
1282     : Histogram(name,
1283                 ranges->range(1),
1284                 ranges->range(ranges->bucket_count() - 1),
1285                 ranges,
1286                 counts,
1287                 logged_counts,
1288                 meta,
1289                 logged_meta) {}
1290 
SerializeInfoImpl(Pickle * pickle) const1291 void CustomHistogram::SerializeInfoImpl(Pickle* pickle) const {
1292   Histogram::SerializeInfoImpl(pickle);
1293 
1294   // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't
1295   // write them.
1296   for (uint32_t i = 1; i < bucket_ranges()->bucket_count(); ++i)
1297     pickle->WriteInt(bucket_ranges()->range(i));
1298 }
1299 
GetBucketSize(Count current,uint32_t i) const1300 double CustomHistogram::GetBucketSize(Count current, uint32_t i) const {
1301   // If this is a histogram of enum values, normalizing the bucket count
1302   // by the bucket range is not helpful, so just return the bucket count.
1303   return current;
1304 }
1305 
1306 // static
DeserializeInfoImpl(PickleIterator * iter)1307 HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) {
1308   std::string histogram_name;
1309   int flags;
1310   int declared_min;
1311   int declared_max;
1312   uint32_t bucket_count;
1313   uint32_t range_checksum;
1314 
1315   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
1316                               &declared_max, &bucket_count, &range_checksum)) {
1317     return nullptr;
1318   }
1319 
1320   // First and last ranges are not serialized.
1321   std::vector<Sample> sample_ranges(bucket_count - 1);
1322 
1323   for (uint32_t i = 0; i < sample_ranges.size(); ++i) {
1324     if (!iter->ReadInt(&sample_ranges[i]))
1325       return nullptr;
1326   }
1327 
1328   HistogramBase* histogram = CustomHistogram::FactoryGet(
1329       histogram_name, sample_ranges, flags);
1330   if (!histogram)
1331     return nullptr;
1332 
1333   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
1334     // The serialized histogram might be corrupted.
1335     return nullptr;
1336   }
1337   return histogram;
1338 }
1339 
1340 // static
ValidateCustomRanges(const std::vector<Sample> & custom_ranges)1341 bool CustomHistogram::ValidateCustomRanges(
1342     const std::vector<Sample>& custom_ranges) {
1343   bool has_valid_range = false;
1344   for (uint32_t i = 0; i < custom_ranges.size(); i++) {
1345     Sample sample = custom_ranges[i];
1346     if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1)
1347       return false;
1348     if (sample != 0)
1349       has_valid_range = true;
1350   }
1351   return has_valid_range;
1352 }
1353 
1354 }  // namespace base
1355