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