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