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 {
39 constexpr char kHtmlNewLine[] = "<br>";
40 constexpr char kAsciiNewLine[] = "\n";
41 }  // namespace
42 
43 namespace base {
44 
45 namespace {
46 
ReadHistogramArguments(PickleIterator * iter,std::string * histogram_name,int * flags,int * declared_min,int * declared_max,uint32_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                             uint32_t* bucket_count,
53                             uint32_t* range_checksum) {
54   if (!iter->ReadString(histogram_name) ||
55       !iter->ReadInt(flags) ||
56       !iter->ReadInt(declared_min) ||
57       !iter->ReadInt(declared_max) ||
58       !iter->ReadUInt32(bucket_count) ||
59       !iter->ReadUInt32(range_checksum)) {
60     DLOG(ERROR) << "Pickle error decoding Histogram: " << *histogram_name;
61     return false;
62   }
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 // static
101 const uint32_t Histogram::kBucketCount_MAX = 1002u;
102 
103 class Histogram::Factory {
104  public:
Factory(const std::string & name,HistogramBase::Sample minimum,HistogramBase::Sample maximum,uint32_t bucket_count,int32_t flags)105   Factory(const std::string& name,
106           HistogramBase::Sample minimum,
107           HistogramBase::Sample maximum,
108           uint32_t bucket_count,
109           int32_t flags)
110     : Factory(name, HISTOGRAM, minimum, maximum, bucket_count, flags) {}
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,uint32_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           uint32_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(
141         new Histogram(GetPermanentName(name_), minimum_, maximum_, ranges));
142   }
143 
144   // Perform any required datafill on the just-created histogram.  If
145   // overridden, be sure to call the "super" version -- this method may not
146   // always remain empty.
FillHistogram(HistogramBase * histogram)147   virtual void FillHistogram(HistogramBase* histogram) {}
148 
149   // These values are protected (instead of private) because they need to
150   // be accessible to methods of sub-classes in order to avoid passing
151   // unnecessary parameters everywhere.
152   const std::string& name_;
153   const HistogramType histogram_type_;
154   HistogramBase::Sample minimum_;
155   HistogramBase::Sample maximum_;
156   uint32_t bucket_count_;
157   int32_t flags_;
158 
159  private:
160   DISALLOW_COPY_AND_ASSIGN(Factory);
161 };
162 
Build()163 HistogramBase* Histogram::Factory::Build() {
164   HistogramBase* histogram = StatisticsRecorder::FindHistogram(name_);
165   if (!histogram) {
166     // TODO(gayane): |HashMetricName()| is called again in Histogram
167     // constructor. Refactor code to avoid the additional call.
168     bool should_record =
169         StatisticsRecorder::ShouldRecordHistogram(HashMetricName(name_));
170     if (!should_record)
171       return DummyHistogram::GetInstance();
172     // To avoid racy destruction at shutdown, the following will be leaked.
173     const BucketRanges* created_ranges = CreateRanges();
174 
175     const BucketRanges* registered_ranges =
176         StatisticsRecorder::RegisterOrDeleteDuplicateRanges(created_ranges);
177 
178     // In most cases, the bucket-count, minimum, and maximum values are known
179     // when the code is written and so are passed in explicitly. In other
180     // cases (such as with a CustomHistogram), they are calculated dynamically
181     // at run-time. In the latter case, those ctor parameters are zero and
182     // the results extracted from the result of CreateRanges().
183     if (bucket_count_ == 0) {
184       bucket_count_ = static_cast<uint32_t>(registered_ranges->bucket_count());
185       minimum_ = registered_ranges->range(1);
186       maximum_ = registered_ranges->range(bucket_count_ - 1);
187     }
188     DCHECK_EQ(minimum_, registered_ranges->range(1));
189     DCHECK_EQ(maximum_, registered_ranges->range(bucket_count_ - 1));
190 
191     // Try to create the histogram using a "persistent" allocator. As of
192     // 2016-02-25, the availability of such is controlled by a base::Feature
193     // that is off by default. If the allocator doesn't exist or if
194     // allocating from it fails, code below will allocate the histogram from
195     // the process heap.
196     PersistentHistogramAllocator::Reference histogram_ref = 0;
197     std::unique_ptr<HistogramBase> tentative_histogram;
198     PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
199     if (allocator) {
200       tentative_histogram = allocator->AllocateHistogram(
201           histogram_type_,
202           name_,
203           minimum_,
204           maximum_,
205           registered_ranges,
206           flags_,
207           &histogram_ref);
208     }
209 
210     // Handle the case where no persistent allocator is present or the
211     // persistent allocation fails (perhaps because it is full).
212     if (!tentative_histogram) {
213       DCHECK(!histogram_ref);  // Should never have been set.
214       DCHECK(!allocator);  // Shouldn't have failed.
215       flags_ &= ~HistogramBase::kIsPersistent;
216       tentative_histogram = HeapAlloc(registered_ranges);
217       tentative_histogram->SetFlags(flags_);
218     }
219 
220     FillHistogram(tentative_histogram.get());
221 
222     // Register this histogram with the StatisticsRecorder. Keep a copy of
223     // the pointer value to tell later whether the locally created histogram
224     // was registered or deleted. The type is "void" because it could point
225     // to released memory after the following line.
226     const void* tentative_histogram_ptr = tentative_histogram.get();
227     histogram = StatisticsRecorder::RegisterOrDeleteDuplicate(
228         tentative_histogram.release());
229 
230     // Persistent histograms need some follow-up processing.
231     if (histogram_ref) {
232       allocator->FinalizeHistogram(histogram_ref,
233                                    histogram == tentative_histogram_ptr);
234     }
235   }
236 
237   if (histogram_type_ != histogram->GetHistogramType() ||
238       (bucket_count_ != 0 && !histogram->HasConstructionArguments(
239                                  minimum_, maximum_, bucket_count_))) {
240     // The construction arguments do not match the existing histogram.  This can
241     // come about if an extension updates in the middle of a chrome run and has
242     // changed one of them, or simply by bad code within Chrome itself.  A NULL
243     // return would cause Chrome to crash; better to just record it for later
244     // analysis.
245     UmaHistogramSparse("Histogram.MismatchedConstructionArguments",
246                        static_cast<Sample>(HashMetricName(name_)));
247     DLOG(ERROR) << "Histogram " << name_
248                 << " has mismatched construction arguments";
249     return DummyHistogram::GetInstance();
250   }
251   return histogram;
252 }
253 
FactoryGet(const std::string & name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags)254 HistogramBase* Histogram::FactoryGet(const std::string& name,
255                                      Sample minimum,
256                                      Sample maximum,
257                                      uint32_t bucket_count,
258                                      int32_t flags) {
259   bool valid_arguments =
260       InspectConstructionArguments(name, &minimum, &maximum, &bucket_count);
261   DCHECK(valid_arguments) << name;
262 
263   return Factory(name, minimum, maximum, bucket_count, flags).Build();
264 }
265 
FactoryTimeGet(const std::string & name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)266 HistogramBase* Histogram::FactoryTimeGet(const std::string& name,
267                                          TimeDelta minimum,
268                                          TimeDelta maximum,
269                                          uint32_t bucket_count,
270                                          int32_t flags) {
271   DCHECK_LT(minimum.InMilliseconds(), std::numeric_limits<Sample>::max());
272   DCHECK_LT(maximum.InMilliseconds(), std::numeric_limits<Sample>::max());
273   return FactoryGet(name, static_cast<Sample>(minimum.InMilliseconds()),
274                     static_cast<Sample>(maximum.InMilliseconds()), bucket_count,
275                     flags);
276 }
277 
FactoryMicrosecondsTimeGet(const std::string & name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)278 HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const std::string& name,
279                                                      TimeDelta minimum,
280                                                      TimeDelta maximum,
281                                                      uint32_t bucket_count,
282                                                      int32_t flags) {
283   DCHECK_LT(minimum.InMicroseconds(), std::numeric_limits<Sample>::max());
284   DCHECK_LT(maximum.InMicroseconds(), std::numeric_limits<Sample>::max());
285   return FactoryGet(name, static_cast<Sample>(minimum.InMicroseconds()),
286                     static_cast<Sample>(maximum.InMicroseconds()), bucket_count,
287                     flags);
288 }
289 
FactoryGet(const char * name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags)290 HistogramBase* Histogram::FactoryGet(const char* name,
291                                      Sample minimum,
292                                      Sample maximum,
293                                      uint32_t bucket_count,
294                                      int32_t flags) {
295   return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
296 }
297 
FactoryTimeGet(const char * name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)298 HistogramBase* Histogram::FactoryTimeGet(const char* name,
299                                          TimeDelta minimum,
300                                          TimeDelta maximum,
301                                          uint32_t bucket_count,
302                                          int32_t flags) {
303   return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count,
304                         flags);
305 }
306 
FactoryMicrosecondsTimeGet(const char * name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)307 HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const char* name,
308                                                      TimeDelta minimum,
309                                                      TimeDelta maximum,
310                                                      uint32_t bucket_count,
311                                                      int32_t flags) {
312   return FactoryMicrosecondsTimeGet(std::string(name), minimum, maximum,
313                                     bucket_count, flags);
314 }
315 
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)316 std::unique_ptr<HistogramBase> Histogram::PersistentCreate(
317     const char* name,
318     Sample minimum,
319     Sample maximum,
320     const BucketRanges* ranges,
321     const DelayedPersistentAllocation& counts,
322     const DelayedPersistentAllocation& logged_counts,
323     HistogramSamples::Metadata* meta,
324     HistogramSamples::Metadata* logged_meta) {
325   return WrapUnique(new Histogram(name, minimum, maximum, ranges, counts,
326                                   logged_counts, meta, logged_meta));
327 }
328 
329 // Calculate what range of values are held in each bucket.
330 // We have to be careful that we don't pick a ratio between starting points in
331 // consecutive buckets that is sooo small, that the integer bounds are the same
332 // (effectively making one bucket get no values).  We need to avoid:
333 //   ranges(i) == ranges(i + 1)
334 // To avoid that, we just do a fine-grained bucket width as far as we need to
335 // until we get a ratio that moves us along at least 2 units at a time.  From
336 // that bucket onward we do use the exponential growth of buckets.
337 //
338 // static
InitializeBucketRanges(Sample minimum,Sample maximum,BucketRanges * ranges)339 void Histogram::InitializeBucketRanges(Sample minimum,
340                                        Sample maximum,
341                                        BucketRanges* ranges) {
342   double log_max = log(static_cast<double>(maximum));
343   double log_ratio;
344   double log_next;
345   size_t bucket_index = 1;
346   Sample current = minimum;
347   ranges->set_range(bucket_index, current);
348   size_t bucket_count = ranges->bucket_count();
349 
350   while (bucket_count > ++bucket_index) {
351     double log_current;
352     log_current = log(static_cast<double>(current));
353     debug::Alias(&log_current);
354     // Calculate the count'th root of the range.
355     log_ratio = (log_max - log_current) / (bucket_count - bucket_index);
356     // See where the next bucket would start.
357     log_next = log_current + log_ratio;
358     Sample next;
359     next = static_cast<int>(std::round(exp(log_next)));
360     if (next > current)
361       current = next;
362     else
363       ++current;  // Just do a narrow bucket, and keep trying.
364     ranges->set_range(bucket_index, current);
365   }
366   ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
367   ranges->ResetChecksum();
368 }
369 
370 // static
371 const int Histogram::kCommonRaceBasedCountMismatch = 5;
372 
FindCorruption(const HistogramSamples & samples) const373 uint32_t Histogram::FindCorruption(const HistogramSamples& samples) const {
374   int inconsistencies = NO_INCONSISTENCIES;
375   Sample previous_range = -1;  // Bottom range is always 0.
376   for (uint32_t index = 0; index < bucket_count(); ++index) {
377     int new_range = ranges(index);
378     if (previous_range >= new_range)
379       inconsistencies |= BUCKET_ORDER_ERROR;
380     previous_range = new_range;
381   }
382 
383   if (!bucket_ranges()->HasValidChecksum())
384     inconsistencies |= RANGE_CHECKSUM_ERROR;
385 
386   int64_t delta64 = samples.redundant_count() - samples.TotalCount();
387   if (delta64 != 0) {
388     int delta = static_cast<int>(delta64);
389     if (delta != delta64)
390       delta = INT_MAX;  // Flag all giant errors as INT_MAX.
391     if (delta > 0) {
392       if (delta > kCommonRaceBasedCountMismatch)
393         inconsistencies |= COUNT_HIGH_ERROR;
394     } else {
395       DCHECK_GT(0, delta);
396       if (-delta > kCommonRaceBasedCountMismatch)
397         inconsistencies |= COUNT_LOW_ERROR;
398     }
399   }
400   return inconsistencies;
401 }
402 
bucket_ranges() const403 const BucketRanges* Histogram::bucket_ranges() const {
404   return unlogged_samples_->bucket_ranges();
405 }
406 
declared_min() const407 Sample Histogram::declared_min() const {
408   const BucketRanges* ranges = bucket_ranges();
409   if (ranges->bucket_count() < 2)
410     return -1;
411   return ranges->range(1);
412 }
413 
declared_max() const414 Sample Histogram::declared_max() const {
415   const BucketRanges* ranges = bucket_ranges();
416   if (ranges->bucket_count() < 2)
417     return -1;
418   return ranges->range(ranges->bucket_count() - 1);
419 }
420 
ranges(uint32_t i) const421 Sample Histogram::ranges(uint32_t i) const {
422   return bucket_ranges()->range(i);
423 }
424 
bucket_count() const425 uint32_t Histogram::bucket_count() const {
426   return static_cast<uint32_t>(bucket_ranges()->bucket_count());
427 }
428 
429 // static
InspectConstructionArguments(StringPiece name,Sample * minimum,Sample * maximum,uint32_t * bucket_count)430 bool Histogram::InspectConstructionArguments(StringPiece name,
431                                              Sample* minimum,
432                                              Sample* maximum,
433                                              uint32_t* bucket_count) {
434   bool check_okay = true;
435 
436   // Checks below must be done after any min/max swap.
437   if (*minimum > *maximum) {
438     check_okay = false;
439     std::swap(*minimum, *maximum);
440   }
441 
442   // Defensive code for backward compatibility.
443   if (*minimum < 1) {
444     DVLOG(1) << "Histogram: " << name << " has bad minimum: " << *minimum;
445     *minimum = 1;
446     if (*maximum < 1)
447       *maximum = 1;
448   }
449   if (*maximum >= kSampleType_MAX) {
450     DVLOG(1) << "Histogram: " << name << " has bad maximum: " << *maximum;
451     *maximum = kSampleType_MAX - 1;
452   }
453   if (*bucket_count > kBucketCount_MAX) {
454     UmaHistogramSparse("Histogram.TooManyBuckets.1000",
455                        static_cast<Sample>(HashMetricName(name)));
456 
457     // TODO(bcwhite): Clean these up as bugs get fixed. Also look at injecting
458     // whitelist (using hashes) from a higher layer rather than hardcoding
459     // them here.
460     // Blink.UseCounter legitimately has more than 1000 entries in its enum.
461     // Arc.OOMKills: https://crbug.com/916757
462     if (!name.starts_with("Blink.UseCounter") &&
463         !name.starts_with("Arc.OOMKills.")) {
464       DVLOG(1) << "Histogram: " << name
465                << " has bad bucket_count: " << *bucket_count << " (limit "
466                << kBucketCount_MAX << ")";
467 
468       // Assume it's a mistake and limit to 100 buckets, plus under and over.
469       // If the DCHECK doesn't alert the user then hopefully the small number
470       // will be obvious on the dashboard. If not, then it probably wasn't
471       // important.
472       *bucket_count = 102;
473       check_okay = false;
474     }
475   }
476 
477   // Ensure parameters are sane.
478   if (*maximum == *minimum) {
479     check_okay = false;
480     *maximum = *minimum + 1;
481   }
482   if (*bucket_count < 3) {
483     check_okay = false;
484     *bucket_count = 3;
485   }
486   if (*bucket_count > static_cast<uint32_t>(*maximum - *minimum + 2)) {
487     check_okay = false;
488     *bucket_count = static_cast<uint32_t>(*maximum - *minimum + 2);
489   }
490 
491   if (!check_okay) {
492     UmaHistogramSparse("Histogram.BadConstructionArguments",
493                        static_cast<Sample>(HashMetricName(name)));
494   }
495 
496   return check_okay;
497 }
498 
name_hash() const499 uint64_t Histogram::name_hash() const {
500   return unlogged_samples_->id();
501 }
502 
GetHistogramType() const503 HistogramType Histogram::GetHistogramType() const {
504   return HISTOGRAM;
505 }
506 
HasConstructionArguments(Sample expected_minimum,Sample expected_maximum,uint32_t expected_bucket_count) const507 bool Histogram::HasConstructionArguments(Sample expected_minimum,
508                                          Sample expected_maximum,
509                                          uint32_t expected_bucket_count) const {
510   return (expected_bucket_count == bucket_count() &&
511           expected_minimum == declared_min() &&
512           expected_maximum == declared_max());
513 }
514 
Add(int value)515 void Histogram::Add(int value) {
516   AddCount(value, 1);
517 }
518 
AddCount(int value,int count)519 void Histogram::AddCount(int value, int count) {
520   DCHECK_EQ(0, ranges(0));
521   DCHECK_EQ(kSampleType_MAX, ranges(bucket_count()));
522 
523   if (value > kSampleType_MAX - 1)
524     value = kSampleType_MAX - 1;
525   if (value < 0)
526     value = 0;
527   if (count <= 0) {
528     NOTREACHED();
529     return;
530   }
531   unlogged_samples_->Accumulate(value, count);
532 
533   if (UNLIKELY(StatisticsRecorder::have_active_callbacks()))
534     FindAndRunCallback(value);
535 }
536 
SnapshotSamples() const537 std::unique_ptr<HistogramSamples> Histogram::SnapshotSamples() const {
538   return SnapshotAllSamples();
539 }
540 
SnapshotDelta()541 std::unique_ptr<HistogramSamples> Histogram::SnapshotDelta() {
542 #if DCHECK_IS_ON()
543   DCHECK(!final_delta_created_);
544 #endif
545 
546   // The code below has subtle thread-safety guarantees! All changes to
547   // the underlying SampleVectors use atomic integer operations, which guarantee
548   // eventual consistency, but do not guarantee full synchronization between
549   // different entries in the SampleVector. In particular, this means that
550   // concurrent updates to the histogram might result in the reported sum not
551   // matching the individual bucket counts; or there being some buckets that are
552   // logically updated "together", but end up being only partially updated when
553   // a snapshot is captured. Note that this is why it's important to subtract
554   // exactly the snapshotted unlogged samples, rather than simply resetting the
555   // vector: this way, the next snapshot will include any concurrent updates
556   // missed by the current snapshot.
557 
558   std::unique_ptr<HistogramSamples> snapshot = SnapshotUnloggedSamples();
559   unlogged_samples_->Subtract(*snapshot);
560   logged_samples_->Add(*snapshot);
561 
562   return snapshot;
563 }
564 
SnapshotFinalDelta() const565 std::unique_ptr<HistogramSamples> Histogram::SnapshotFinalDelta() const {
566 #if DCHECK_IS_ON()
567   DCHECK(!final_delta_created_);
568   final_delta_created_ = true;
569 #endif
570 
571   return SnapshotUnloggedSamples();
572 }
573 
AddSamples(const HistogramSamples & samples)574 void Histogram::AddSamples(const HistogramSamples& samples) {
575   unlogged_samples_->Add(samples);
576 }
577 
AddSamplesFromPickle(PickleIterator * iter)578 bool Histogram::AddSamplesFromPickle(PickleIterator* iter) {
579   return unlogged_samples_->AddFromPickle(iter);
580 }
581 
582 // The following methods provide a graphical histogram display.
WriteHTMLGraph(std::string * output) const583 void Histogram::WriteHTMLGraph(std::string* output) const {
584   // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
585 
586   // Get local (stack) copies of all effectively volatile class data so that we
587   // are consistent across our output activities.
588   std::unique_ptr<SampleVector> snapshot = SnapshotAllSamples();
589   output->append("<PRE>");
590   output->append("<h4>");
591   WriteAsciiHeader(*snapshot, output);
592   output->append("</h4>");
593   WriteAsciiBody(*snapshot, true, kHtmlNewLine, output);
594   output->append("</PRE>");
595 }
596 
WriteAscii(std::string * output) const597 void Histogram::WriteAscii(std::string* output) const {
598   // Get local (stack) copies of all effectively volatile class data so that we
599   // are consistent across our output activities.
600   std::unique_ptr<SampleVector> snapshot = SnapshotAllSamples();
601   WriteAsciiHeader(*snapshot, output);
602   output->append(kAsciiNewLine);
603   WriteAsciiBody(*snapshot, true, kAsciiNewLine, output);
604 }
605 
ValidateHistogramContents() const606 void Histogram::ValidateHistogramContents() const {
607   CHECK(unlogged_samples_);
608   CHECK(unlogged_samples_->bucket_ranges());
609   CHECK(logged_samples_);
610   CHECK(logged_samples_->bucket_ranges());
611   CHECK_NE(0U, logged_samples_->id());
612 }
613 
SerializeInfoImpl(Pickle * pickle) const614 void Histogram::SerializeInfoImpl(Pickle* pickle) const {
615   DCHECK(bucket_ranges()->HasValidChecksum());
616   pickle->WriteString(histogram_name());
617   pickle->WriteInt(flags());
618   pickle->WriteInt(declared_min());
619   pickle->WriteInt(declared_max());
620   pickle->WriteUInt32(bucket_count());
621   pickle->WriteUInt32(bucket_ranges()->checksum());
622 }
623 
624 // TODO(bcwhite): Remove minimum/maximum parameters from here and call chain.
Histogram(const char * name,Sample minimum,Sample maximum,const BucketRanges * ranges)625 Histogram::Histogram(const char* name,
626                      Sample minimum,
627                      Sample maximum,
628                      const BucketRanges* ranges)
629     : HistogramBase(name) {
630   DCHECK(ranges) << name << ": " << minimum << "-" << maximum;
631   unlogged_samples_.reset(new SampleVector(HashMetricName(name), ranges));
632   logged_samples_.reset(new SampleVector(unlogged_samples_->id(), ranges));
633 }
634 
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)635 Histogram::Histogram(const char* name,
636                      Sample minimum,
637                      Sample maximum,
638                      const BucketRanges* ranges,
639                      const DelayedPersistentAllocation& counts,
640                      const DelayedPersistentAllocation& logged_counts,
641                      HistogramSamples::Metadata* meta,
642                      HistogramSamples::Metadata* logged_meta)
643     : HistogramBase(name) {
644   DCHECK(ranges) << name << ": " << minimum << "-" << maximum;
645   unlogged_samples_.reset(
646       new PersistentSampleVector(HashMetricName(name), ranges, meta, counts));
647   logged_samples_.reset(new PersistentSampleVector(
648       unlogged_samples_->id(), ranges, logged_meta, logged_counts));
649 }
650 
651 Histogram::~Histogram() = default;
652 
PrintEmptyBucket(uint32_t index) const653 bool Histogram::PrintEmptyBucket(uint32_t index) const {
654   return true;
655 }
656 
657 // Use the actual bucket widths (like a linear histogram) until the widths get
658 // over some transition value, and then use that transition width.  Exponentials
659 // get so big so fast (and we don't expect to see a lot of entries in the large
660 // buckets), so we need this to make it possible to see what is going on and
661 // not have 0-graphical-height buckets.
GetBucketSize(Count current,uint32_t i) const662 double Histogram::GetBucketSize(Count current, uint32_t i) const {
663   DCHECK_GT(ranges(i + 1), ranges(i));
664   static const double kTransitionWidth = 5;
665   double denominator = ranges(i + 1) - ranges(i);
666   if (denominator > kTransitionWidth)
667     denominator = kTransitionWidth;  // Stop trying to normalize.
668   return current/denominator;
669 }
670 
GetAsciiBucketRange(uint32_t i) const671 const std::string Histogram::GetAsciiBucketRange(uint32_t i) const {
672   return GetSimpleAsciiBucketRange(ranges(i));
673 }
674 
675 //------------------------------------------------------------------------------
676 // Private methods
677 
678 // static
DeserializeInfoImpl(PickleIterator * iter)679 HistogramBase* Histogram::DeserializeInfoImpl(PickleIterator* iter) {
680   std::string histogram_name;
681   int flags;
682   int declared_min;
683   int declared_max;
684   uint32_t bucket_count;
685   uint32_t range_checksum;
686 
687   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
688                               &declared_max, &bucket_count, &range_checksum)) {
689     return nullptr;
690   }
691 
692   // Find or create the local version of the histogram in this process.
693   HistogramBase* histogram = Histogram::FactoryGet(
694       histogram_name, declared_min, declared_max, bucket_count, flags);
695   if (!histogram)
696     return nullptr;
697 
698   // The serialized histogram might be corrupted.
699   if (!ValidateRangeChecksum(*histogram, range_checksum))
700     return nullptr;
701 
702   return histogram;
703 }
704 
SnapshotAllSamples() const705 std::unique_ptr<SampleVector> Histogram::SnapshotAllSamples() const {
706   std::unique_ptr<SampleVector> samples = SnapshotUnloggedSamples();
707   samples->Add(*logged_samples_);
708   return samples;
709 }
710 
SnapshotUnloggedSamples() const711 std::unique_ptr<SampleVector> Histogram::SnapshotUnloggedSamples() const {
712   std::unique_ptr<SampleVector> samples(
713       new SampleVector(unlogged_samples_->id(), bucket_ranges()));
714   samples->Add(*unlogged_samples_);
715   return samples;
716 }
717 
WriteAsciiBody(const SampleVector & snapshot,bool graph_it,const std::string & newline,std::string * output) const718 void Histogram::WriteAsciiBody(const SampleVector& snapshot,
719                                bool graph_it,
720                                const std::string& newline,
721                                std::string* output) const {
722   Count sample_count = snapshot.TotalCount();
723 
724   // Prepare to normalize graphical rendering of bucket contents.
725   double max_size = 0;
726   if (graph_it)
727     max_size = GetPeakBucketSize(snapshot);
728 
729   // Calculate space needed to print bucket range numbers.  Leave room to print
730   // nearly the largest bucket range without sliding over the histogram.
731   uint32_t largest_non_empty_bucket = bucket_count() - 1;
732   while (0 == snapshot.GetCountAtIndex(largest_non_empty_bucket)) {
733     if (0 == largest_non_empty_bucket)
734       break;  // All buckets are empty.
735     --largest_non_empty_bucket;
736   }
737 
738   // Calculate largest print width needed for any of our bucket range displays.
739   size_t print_width = 1;
740   for (uint32_t i = 0; i < bucket_count(); ++i) {
741     if (snapshot.GetCountAtIndex(i)) {
742       size_t width = GetAsciiBucketRange(i).size() + 1;
743       if (width > print_width)
744         print_width = width;
745     }
746   }
747 
748   int64_t remaining = sample_count;
749   int64_t past = 0;
750   // Output the actual histogram graph.
751   for (uint32_t i = 0; i < bucket_count(); ++i) {
752     Count current = snapshot.GetCountAtIndex(i);
753     if (!current && !PrintEmptyBucket(i))
754       continue;
755     remaining -= current;
756     std::string range = GetAsciiBucketRange(i);
757     output->append(range);
758     for (size_t j = 0; range.size() + j < print_width + 1; ++j)
759       output->push_back(' ');
760     if (0 == current && i < bucket_count() - 1 &&
761         0 == snapshot.GetCountAtIndex(i + 1)) {
762       while (i < bucket_count() - 1 && 0 == snapshot.GetCountAtIndex(i + 1)) {
763         ++i;
764       }
765       output->append("... ");
766       output->append(newline);
767       continue;  // No reason to plot emptiness.
768     }
769     double current_size = GetBucketSize(current, i);
770     if (graph_it)
771       WriteAsciiBucketGraph(current_size, max_size, output);
772     WriteAsciiBucketContext(past, current, remaining, i, output);
773     output->append(newline);
774     past += current;
775   }
776   DCHECK_EQ(sample_count, past);
777 }
778 
GetPeakBucketSize(const SampleVectorBase & samples) const779 double Histogram::GetPeakBucketSize(const SampleVectorBase& samples) const {
780   double max = 0;
781   for (uint32_t i = 0; i < bucket_count() ; ++i) {
782     double current_size = GetBucketSize(samples.GetCountAtIndex(i), i);
783     if (current_size > max)
784       max = current_size;
785   }
786   return max;
787 }
788 
WriteAsciiHeader(const SampleVectorBase & samples,std::string * output) const789 void Histogram::WriteAsciiHeader(const SampleVectorBase& samples,
790                                  std::string* output) const {
791   Count sample_count = samples.TotalCount();
792 
793   StringAppendF(output, "Histogram: %s recorded %d samples", histogram_name(),
794                 sample_count);
795   if (sample_count == 0) {
796     DCHECK_EQ(samples.sum(), 0);
797   } else {
798     double mean = static_cast<float>(samples.sum()) / sample_count;
799     StringAppendF(output, ", mean = %.1f", mean);
800   }
801   if (flags())
802     StringAppendF(output, " (flags = 0x%x)", flags());
803 }
804 
WriteAsciiBucketContext(const int64_t past,const Count current,const int64_t remaining,const uint32_t i,std::string * output) const805 void Histogram::WriteAsciiBucketContext(const int64_t past,
806                                         const Count current,
807                                         const int64_t remaining,
808                                         const uint32_t i,
809                                         std::string* output) const {
810   double scaled_sum = (past + current + remaining) / 100.0;
811   WriteAsciiBucketValue(current, scaled_sum, output);
812   if (0 < i) {
813     double percentage = past / scaled_sum;
814     StringAppendF(output, " {%3.1f%%}", percentage);
815   }
816 }
817 
GetParameters(DictionaryValue * params) const818 void Histogram::GetParameters(DictionaryValue* params) const {
819   params->SetString("type", HistogramTypeToString(GetHistogramType()));
820   params->SetIntKey("min", declared_min());
821   params->SetIntKey("max", declared_max());
822   params->SetIntKey("bucket_count", static_cast<int>(bucket_count()));
823 }
824 
GetCountAndBucketData(Count * count,int64_t * sum,ListValue * buckets) const825 void Histogram::GetCountAndBucketData(Count* count,
826                                       int64_t* sum,
827                                       ListValue* buckets) const {
828   std::unique_ptr<SampleVector> snapshot = SnapshotAllSamples();
829   *count = snapshot->TotalCount();
830   *sum = snapshot->sum();
831   uint32_t index = 0;
832   for (uint32_t i = 0; i < bucket_count(); ++i) {
833     Sample count_at_index = snapshot->GetCountAtIndex(i);
834     if (count_at_index > 0) {
835       std::unique_ptr<DictionaryValue> bucket_value(new DictionaryValue());
836       bucket_value->SetIntKey("low", ranges(i));
837       if (i != bucket_count() - 1)
838         bucket_value->SetIntKey("high", ranges(i + 1));
839       bucket_value->SetIntKey("count", count_at_index);
840       buckets->Set(index, std::move(bucket_value));
841       ++index;
842     }
843   }
844 }
845 
846 //------------------------------------------------------------------------------
847 // LinearHistogram: This histogram uses a traditional set of evenly spaced
848 // buckets.
849 //------------------------------------------------------------------------------
850 
851 class LinearHistogram::Factory : public Histogram::Factory {
852  public:
Factory(const std::string & name,HistogramBase::Sample minimum,HistogramBase::Sample maximum,uint32_t bucket_count,int32_t flags,const DescriptionPair * descriptions)853   Factory(const std::string& name,
854           HistogramBase::Sample minimum,
855           HistogramBase::Sample maximum,
856           uint32_t bucket_count,
857           int32_t flags,
858           const DescriptionPair* descriptions)
859     : Histogram::Factory(name, LINEAR_HISTOGRAM, minimum, maximum,
860                          bucket_count, flags) {
861     descriptions_ = descriptions;
862   }
863 
864  protected:
CreateRanges()865   BucketRanges* CreateRanges() override {
866     BucketRanges* ranges = new BucketRanges(bucket_count_ + 1);
867     LinearHistogram::InitializeBucketRanges(minimum_, maximum_, ranges);
868     return ranges;
869   }
870 
HeapAlloc(const BucketRanges * ranges)871   std::unique_ptr<HistogramBase> HeapAlloc(
872       const BucketRanges* ranges) override {
873     return WrapUnique(new LinearHistogram(GetPermanentName(name_), minimum_,
874                                           maximum_, ranges));
875   }
876 
FillHistogram(HistogramBase * base_histogram)877   void FillHistogram(HistogramBase* base_histogram) override {
878     Histogram::Factory::FillHistogram(base_histogram);
879     // Normally, |base_histogram| should have type LINEAR_HISTOGRAM or be
880     // inherited from it. However, if it's expired, it will actually be a
881     // DUMMY_HISTOGRAM. Skip filling in that case.
882     if (base_histogram->GetHistogramType() == DUMMY_HISTOGRAM)
883       return;
884     LinearHistogram* histogram = static_cast<LinearHistogram*>(base_histogram);
885     // Set range descriptions.
886     if (descriptions_) {
887       for (int i = 0; descriptions_[i].description; ++i) {
888         histogram->bucket_description_[descriptions_[i].sample] =
889             descriptions_[i].description;
890       }
891     }
892   }
893 
894  private:
895   const DescriptionPair* descriptions_;
896 
897   DISALLOW_COPY_AND_ASSIGN(Factory);
898 };
899 
900 LinearHistogram::~LinearHistogram() = default;
901 
FactoryGet(const std::string & name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags)902 HistogramBase* LinearHistogram::FactoryGet(const std::string& name,
903                                            Sample minimum,
904                                            Sample maximum,
905                                            uint32_t bucket_count,
906                                            int32_t flags) {
907   return FactoryGetWithRangeDescription(name, minimum, maximum, bucket_count,
908                                         flags, NULL);
909 }
910 
FactoryTimeGet(const std::string & name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)911 HistogramBase* LinearHistogram::FactoryTimeGet(const std::string& name,
912                                                TimeDelta minimum,
913                                                TimeDelta maximum,
914                                                uint32_t bucket_count,
915                                                int32_t flags) {
916   DCHECK_LT(minimum.InMilliseconds(), std::numeric_limits<Sample>::max());
917   DCHECK_LT(maximum.InMilliseconds(), std::numeric_limits<Sample>::max());
918   return FactoryGet(name, static_cast<Sample>(minimum.InMilliseconds()),
919                     static_cast<Sample>(maximum.InMilliseconds()), bucket_count,
920                     flags);
921 }
922 
FactoryGet(const char * name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags)923 HistogramBase* LinearHistogram::FactoryGet(const char* name,
924                                            Sample minimum,
925                                            Sample maximum,
926                                            uint32_t bucket_count,
927                                            int32_t flags) {
928   return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
929 }
930 
FactoryTimeGet(const char * name,TimeDelta minimum,TimeDelta maximum,uint32_t bucket_count,int32_t flags)931 HistogramBase* LinearHistogram::FactoryTimeGet(const char* name,
932                                                TimeDelta minimum,
933                                                TimeDelta maximum,
934                                                uint32_t bucket_count,
935                                                int32_t flags) {
936   return FactoryTimeGet(std::string(name),  minimum, maximum, bucket_count,
937                         flags);
938 }
939 
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)940 std::unique_ptr<HistogramBase> LinearHistogram::PersistentCreate(
941     const char* name,
942     Sample minimum,
943     Sample maximum,
944     const BucketRanges* ranges,
945     const DelayedPersistentAllocation& counts,
946     const DelayedPersistentAllocation& logged_counts,
947     HistogramSamples::Metadata* meta,
948     HistogramSamples::Metadata* logged_meta) {
949   return WrapUnique(new LinearHistogram(name, minimum, maximum, ranges, counts,
950                                         logged_counts, meta, logged_meta));
951 }
952 
FactoryGetWithRangeDescription(const std::string & name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t flags,const DescriptionPair descriptions[])953 HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
954     const std::string& name,
955     Sample minimum,
956     Sample maximum,
957     uint32_t bucket_count,
958     int32_t flags,
959     const DescriptionPair descriptions[]) {
960   // Originally, histograms were required to have at least one sample value
961   // plus underflow and overflow buckets. For single-entry enumerations,
962   // that one value is usually zero (which IS the underflow bucket)
963   // resulting in a |maximum| value of 1 (the exclusive upper-bound) and only
964   // the two outlier buckets. Handle this by making max==2 and buckets==3.
965   // This usually won't have any cost since the single-value-optimization
966   // will be used until the count exceeds 16 bits.
967   if (maximum == 1 && bucket_count == 2) {
968     maximum = 2;
969     bucket_count = 3;
970   }
971 
972   bool valid_arguments = Histogram::InspectConstructionArguments(
973       name, &minimum, &maximum, &bucket_count);
974   DCHECK(valid_arguments) << name;
975 
976   return Factory(name, minimum, maximum, bucket_count, flags, descriptions)
977       .Build();
978 }
979 
GetHistogramType() const980 HistogramType LinearHistogram::GetHistogramType() const {
981   return LINEAR_HISTOGRAM;
982 }
983 
LinearHistogram(const char * name,Sample minimum,Sample maximum,const BucketRanges * ranges)984 LinearHistogram::LinearHistogram(const char* name,
985                                  Sample minimum,
986                                  Sample maximum,
987                                  const BucketRanges* ranges)
988     : Histogram(name, minimum, maximum, ranges) {}
989 
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)990 LinearHistogram::LinearHistogram(
991     const char* name,
992     Sample minimum,
993     Sample maximum,
994     const BucketRanges* ranges,
995     const DelayedPersistentAllocation& counts,
996     const DelayedPersistentAllocation& logged_counts,
997     HistogramSamples::Metadata* meta,
998     HistogramSamples::Metadata* logged_meta)
999     : Histogram(name,
1000                 minimum,
1001                 maximum,
1002                 ranges,
1003                 counts,
1004                 logged_counts,
1005                 meta,
1006                 logged_meta) {}
1007 
GetBucketSize(Count current,uint32_t i) const1008 double LinearHistogram::GetBucketSize(Count current, uint32_t i) const {
1009   DCHECK_GT(ranges(i + 1), ranges(i));
1010   // Adjacent buckets with different widths would have "surprisingly" many (few)
1011   // samples in a histogram if we didn't normalize this way.
1012   double denominator = ranges(i + 1) - ranges(i);
1013   return current/denominator;
1014 }
1015 
GetAsciiBucketRange(uint32_t i) const1016 const std::string LinearHistogram::GetAsciiBucketRange(uint32_t i) const {
1017   int range = ranges(i);
1018   BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
1019   if (it == bucket_description_.end())
1020     return Histogram::GetAsciiBucketRange(i);
1021   return it->second;
1022 }
1023 
PrintEmptyBucket(uint32_t index) const1024 bool LinearHistogram::PrintEmptyBucket(uint32_t index) const {
1025   return bucket_description_.find(ranges(index)) == bucket_description_.end();
1026 }
1027 
1028 // static
InitializeBucketRanges(Sample minimum,Sample maximum,BucketRanges * ranges)1029 void LinearHistogram::InitializeBucketRanges(Sample minimum,
1030                                              Sample maximum,
1031                                              BucketRanges* ranges) {
1032   double min = minimum;
1033   double max = maximum;
1034   size_t bucket_count = ranges->bucket_count();
1035 
1036   for (size_t i = 1; i < bucket_count; ++i) {
1037     double linear_range =
1038         (min * (bucket_count - 1 - i) + max * (i - 1)) / (bucket_count - 2);
1039     uint32_t range = static_cast<Sample>(linear_range + 0.5);
1040     ranges->set_range(i, range);
1041   }
1042   ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX);
1043   ranges->ResetChecksum();
1044 }
1045 
1046 // static
DeserializeInfoImpl(PickleIterator * iter)1047 HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) {
1048   std::string histogram_name;
1049   int flags;
1050   int declared_min;
1051   int declared_max;
1052   uint32_t bucket_count;
1053   uint32_t range_checksum;
1054 
1055   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
1056                               &declared_max, &bucket_count, &range_checksum)) {
1057     return nullptr;
1058   }
1059 
1060   HistogramBase* histogram = LinearHistogram::FactoryGet(
1061       histogram_name, declared_min, declared_max, bucket_count, flags);
1062   if (!histogram)
1063     return nullptr;
1064 
1065   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
1066     // The serialized histogram might be corrupted.
1067     return nullptr;
1068   }
1069   return histogram;
1070 }
1071 
1072 //------------------------------------------------------------------------------
1073 // ScaledLinearHistogram: This is a wrapper around a LinearHistogram that
1074 // scales input counts.
1075 //------------------------------------------------------------------------------
1076 
ScaledLinearHistogram(const char * name,Sample minimum,Sample maximum,uint32_t bucket_count,int32_t scale,int32_t flags)1077 ScaledLinearHistogram::ScaledLinearHistogram(const char* name,
1078                                              Sample minimum,
1079                                              Sample maximum,
1080                                              uint32_t bucket_count,
1081                                              int32_t scale,
1082                                              int32_t flags)
1083     : histogram_(static_cast<LinearHistogram*>(
1084           LinearHistogram::FactoryGet(name,
1085                                       minimum,
1086                                       maximum,
1087                                       bucket_count,
1088                                       flags))),
1089       scale_(scale) {
1090   DCHECK(histogram_);
1091   DCHECK_LT(1, scale);
1092   DCHECK_EQ(1, minimum);
1093   CHECK_EQ(static_cast<Sample>(bucket_count), maximum - minimum + 2)
1094       << " ScaledLinearHistogram requires buckets of size 1";
1095 
1096   remainders_.resize(histogram_->bucket_count(), 0);
1097 }
1098 
1099 ScaledLinearHistogram::~ScaledLinearHistogram() = default;
1100 
AddScaledCount(Sample value,int count)1101 void ScaledLinearHistogram::AddScaledCount(Sample value, int count) {
1102   if (count == 0)
1103     return;
1104   if (count < 0) {
1105     NOTREACHED();
1106     return;
1107   }
1108   const int32_t max_value =
1109       static_cast<int32_t>(histogram_->bucket_count() - 1);
1110   if (value > max_value)
1111     value = max_value;
1112   if (value < 0)
1113     value = 0;
1114 
1115   int scaled_count = count / scale_;
1116   subtle::Atomic32 remainder = count - scaled_count * scale_;
1117 
1118   // ScaledLinearHistogram currently requires 1-to-1 mappings between value
1119   // and bucket which alleviates the need to do a bucket lookup here (something
1120   // that is internal to the HistogramSamples object).
1121   if (remainder > 0) {
1122     remainder =
1123         subtle::NoBarrier_AtomicIncrement(&remainders_[value], remainder);
1124     // If remainder passes 1/2 scale, increment main count (thus rounding up).
1125     // The remainder is decremented by the full scale, though, which will
1126     // cause it to go negative and thus requrire another increase by the full
1127     // scale amount before another bump of the scaled count.
1128     if (remainder >= scale_ / 2) {
1129       scaled_count += 1;
1130       subtle::NoBarrier_AtomicIncrement(&remainders_[value], -scale_);
1131     }
1132   }
1133 
1134   if (scaled_count > 0)
1135     histogram_->AddCount(value, scaled_count);
1136 }
1137 
1138 //------------------------------------------------------------------------------
1139 // This section provides implementation for BooleanHistogram.
1140 //------------------------------------------------------------------------------
1141 
1142 class BooleanHistogram::Factory : public Histogram::Factory {
1143  public:
Factory(const std::string & name,int32_t flags)1144   Factory(const std::string& name, int32_t flags)
1145     : Histogram::Factory(name, BOOLEAN_HISTOGRAM, 1, 2, 3, flags) {}
1146 
1147  protected:
CreateRanges()1148   BucketRanges* CreateRanges() override {
1149     BucketRanges* ranges = new BucketRanges(3 + 1);
1150     LinearHistogram::InitializeBucketRanges(1, 2, ranges);
1151     return ranges;
1152   }
1153 
HeapAlloc(const BucketRanges * ranges)1154   std::unique_ptr<HistogramBase> HeapAlloc(
1155       const BucketRanges* ranges) override {
1156     return WrapUnique(new BooleanHistogram(GetPermanentName(name_), ranges));
1157   }
1158 
1159  private:
1160   DISALLOW_COPY_AND_ASSIGN(Factory);
1161 };
1162 
FactoryGet(const std::string & name,int32_t flags)1163 HistogramBase* BooleanHistogram::FactoryGet(const std::string& name,
1164                                             int32_t flags) {
1165   return Factory(name, flags).Build();
1166 }
1167 
FactoryGet(const char * name,int32_t flags)1168 HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32_t flags) {
1169   return FactoryGet(std::string(name), flags);
1170 }
1171 
PersistentCreate(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1172 std::unique_ptr<HistogramBase> BooleanHistogram::PersistentCreate(
1173     const char* name,
1174     const BucketRanges* ranges,
1175     const DelayedPersistentAllocation& counts,
1176     const DelayedPersistentAllocation& logged_counts,
1177     HistogramSamples::Metadata* meta,
1178     HistogramSamples::Metadata* logged_meta) {
1179   return WrapUnique(new BooleanHistogram(name, ranges, counts, logged_counts,
1180                                          meta, logged_meta));
1181 }
1182 
GetHistogramType() const1183 HistogramType BooleanHistogram::GetHistogramType() const {
1184   return BOOLEAN_HISTOGRAM;
1185 }
1186 
BooleanHistogram(const char * name,const BucketRanges * ranges)1187 BooleanHistogram::BooleanHistogram(const char* name, const BucketRanges* ranges)
1188     : LinearHistogram(name, 1, 2, ranges) {}
1189 
BooleanHistogram(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1190 BooleanHistogram::BooleanHistogram(
1191     const char* name,
1192     const BucketRanges* ranges,
1193     const DelayedPersistentAllocation& counts,
1194     const DelayedPersistentAllocation& logged_counts,
1195     HistogramSamples::Metadata* meta,
1196     HistogramSamples::Metadata* logged_meta)
1197     : LinearHistogram(name,
1198                       1,
1199                       2,
1200                       ranges,
1201                       counts,
1202                       logged_counts,
1203                       meta,
1204                       logged_meta) {}
1205 
DeserializeInfoImpl(PickleIterator * iter)1206 HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) {
1207   std::string histogram_name;
1208   int flags;
1209   int declared_min;
1210   int declared_max;
1211   uint32_t bucket_count;
1212   uint32_t range_checksum;
1213 
1214   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
1215                               &declared_max, &bucket_count, &range_checksum)) {
1216     return nullptr;
1217   }
1218 
1219   HistogramBase* histogram = BooleanHistogram::FactoryGet(
1220       histogram_name, flags);
1221   if (!histogram)
1222     return nullptr;
1223 
1224   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
1225     // The serialized histogram might be corrupted.
1226     return nullptr;
1227   }
1228   return histogram;
1229 }
1230 
1231 //------------------------------------------------------------------------------
1232 // CustomHistogram:
1233 //------------------------------------------------------------------------------
1234 
1235 class CustomHistogram::Factory : public Histogram::Factory {
1236  public:
Factory(const std::string & name,const std::vector<Sample> * custom_ranges,int32_t flags)1237   Factory(const std::string& name,
1238           const std::vector<Sample>* custom_ranges,
1239           int32_t flags)
1240     : Histogram::Factory(name, CUSTOM_HISTOGRAM, 0, 0, 0, flags) {
1241     custom_ranges_ = custom_ranges;
1242   }
1243 
1244  protected:
CreateRanges()1245   BucketRanges* CreateRanges() override {
1246     // Remove the duplicates in the custom ranges array.
1247     std::vector<int> ranges = *custom_ranges_;
1248     ranges.push_back(0);  // Ensure we have a zero value.
1249     ranges.push_back(HistogramBase::kSampleType_MAX);
1250     std::sort(ranges.begin(), ranges.end());
1251     ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
1252 
1253     BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
1254     for (uint32_t i = 0; i < ranges.size(); i++) {
1255       bucket_ranges->set_range(i, ranges[i]);
1256     }
1257     bucket_ranges->ResetChecksum();
1258     return bucket_ranges;
1259   }
1260 
HeapAlloc(const BucketRanges * ranges)1261   std::unique_ptr<HistogramBase> HeapAlloc(
1262       const BucketRanges* ranges) override {
1263     return WrapUnique(new CustomHistogram(GetPermanentName(name_), ranges));
1264   }
1265 
1266  private:
1267   const std::vector<Sample>* custom_ranges_;
1268 
1269   DISALLOW_COPY_AND_ASSIGN(Factory);
1270 };
1271 
FactoryGet(const std::string & name,const std::vector<Sample> & custom_ranges,int32_t flags)1272 HistogramBase* CustomHistogram::FactoryGet(
1273     const std::string& name,
1274     const std::vector<Sample>& custom_ranges,
1275     int32_t flags) {
1276   CHECK(ValidateCustomRanges(custom_ranges));
1277 
1278   return Factory(name, &custom_ranges, flags).Build();
1279 }
1280 
FactoryGet(const char * name,const std::vector<Sample> & custom_ranges,int32_t flags)1281 HistogramBase* CustomHistogram::FactoryGet(
1282     const char* name,
1283     const std::vector<Sample>& custom_ranges,
1284     int32_t flags) {
1285   return FactoryGet(std::string(name), custom_ranges, flags);
1286 }
1287 
PersistentCreate(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1288 std::unique_ptr<HistogramBase> CustomHistogram::PersistentCreate(
1289     const char* name,
1290     const BucketRanges* ranges,
1291     const DelayedPersistentAllocation& counts,
1292     const DelayedPersistentAllocation& logged_counts,
1293     HistogramSamples::Metadata* meta,
1294     HistogramSamples::Metadata* logged_meta) {
1295   return WrapUnique(new CustomHistogram(name, ranges, counts, logged_counts,
1296                                         meta, logged_meta));
1297 }
1298 
GetHistogramType() const1299 HistogramType CustomHistogram::GetHistogramType() const {
1300   return CUSTOM_HISTOGRAM;
1301 }
1302 
1303 // static
ArrayToCustomEnumRanges(base::span<const Sample> values)1304 std::vector<Sample> CustomHistogram::ArrayToCustomEnumRanges(
1305     base::span<const Sample> values) {
1306   std::vector<Sample> all_values;
1307   for (Sample value : values) {
1308     all_values.push_back(value);
1309 
1310     // Ensure that a guard bucket is added. If we end up with duplicate
1311     // values, FactoryGet will take care of removing them.
1312     all_values.push_back(value + 1);
1313   }
1314   return all_values;
1315 }
1316 
CustomHistogram(const char * name,const BucketRanges * ranges)1317 CustomHistogram::CustomHistogram(const char* name, const BucketRanges* ranges)
1318     : Histogram(name,
1319                 ranges->range(1),
1320                 ranges->range(ranges->bucket_count() - 1),
1321                 ranges) {}
1322 
CustomHistogram(const char * name,const BucketRanges * ranges,const DelayedPersistentAllocation & counts,const DelayedPersistentAllocation & logged_counts,HistogramSamples::Metadata * meta,HistogramSamples::Metadata * logged_meta)1323 CustomHistogram::CustomHistogram(
1324     const char* name,
1325     const BucketRanges* ranges,
1326     const DelayedPersistentAllocation& counts,
1327     const DelayedPersistentAllocation& logged_counts,
1328     HistogramSamples::Metadata* meta,
1329     HistogramSamples::Metadata* logged_meta)
1330     : Histogram(name,
1331                 ranges->range(1),
1332                 ranges->range(ranges->bucket_count() - 1),
1333                 ranges,
1334                 counts,
1335                 logged_counts,
1336                 meta,
1337                 logged_meta) {}
1338 
SerializeInfoImpl(Pickle * pickle) const1339 void CustomHistogram::SerializeInfoImpl(Pickle* pickle) const {
1340   Histogram::SerializeInfoImpl(pickle);
1341 
1342   // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't
1343   // write them.
1344   for (uint32_t i = 1; i < bucket_ranges()->bucket_count(); ++i)
1345     pickle->WriteInt(bucket_ranges()->range(i));
1346 }
1347 
GetBucketSize(Count current,uint32_t i) const1348 double CustomHistogram::GetBucketSize(Count current, uint32_t i) const {
1349   // If this is a histogram of enum values, normalizing the bucket count
1350   // by the bucket range is not helpful, so just return the bucket count.
1351   return current;
1352 }
1353 
1354 // static
DeserializeInfoImpl(PickleIterator * iter)1355 HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) {
1356   std::string histogram_name;
1357   int flags;
1358   int declared_min;
1359   int declared_max;
1360   uint32_t bucket_count;
1361   uint32_t range_checksum;
1362 
1363   if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min,
1364                               &declared_max, &bucket_count, &range_checksum)) {
1365     return nullptr;
1366   }
1367 
1368   // First and last ranges are not serialized.
1369   std::vector<Sample> sample_ranges(bucket_count - 1);
1370 
1371   for (uint32_t i = 0; i < sample_ranges.size(); ++i) {
1372     if (!iter->ReadInt(&sample_ranges[i]))
1373       return nullptr;
1374   }
1375 
1376   HistogramBase* histogram = CustomHistogram::FactoryGet(
1377       histogram_name, sample_ranges, flags);
1378   if (!histogram)
1379     return nullptr;
1380 
1381   if (!ValidateRangeChecksum(*histogram, range_checksum)) {
1382     // The serialized histogram might be corrupted.
1383     return nullptr;
1384   }
1385   return histogram;
1386 }
1387 
1388 // static
ValidateCustomRanges(const std::vector<Sample> & custom_ranges)1389 bool CustomHistogram::ValidateCustomRanges(
1390     const std::vector<Sample>& custom_ranges) {
1391   bool has_valid_range = false;
1392   for (uint32_t i = 0; i < custom_ranges.size(); i++) {
1393     Sample sample = custom_ranges[i];
1394     if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1)
1395       return false;
1396     if (sample != 0)
1397       has_valid_range = true;
1398   }
1399   return has_valid_range;
1400 }
1401 
1402 }  // namespace base
1403