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