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 9 // It supports calls to accumulate either time intervals (which are processed 10 // as integral number of milliseconds), or arbitrary integral units. 11 12 // For Histogram (exponential histogram), LinearHistogram and CustomHistogram, 13 // the minimum for a declared range is 1 (instead of 0), while the maximum is 14 // (HistogramBase::kSampleType_MAX - 1). However, there will always be underflow 15 // and overflow buckets added automatically, so a 0 bucket will always exist 16 // even when a minimum value of 1 is specified. 17 18 // Each use of a histogram with the same name will reference the same underlying 19 // data, so it is safe to record to the same histogram from multiple locations 20 // in the code. It is a runtime error if all uses of the same histogram do not 21 // agree exactly in type, bucket size and range. 22 23 // For Histogram and LinearHistogram, the maximum for a declared range should 24 // always be larger (not equal) than minimal range. Zero and 25 // HistogramBase::kSampleType_MAX are implicitly added as first and last ranges, 26 // so the smallest legal bucket_count is 3. However CustomHistogram can have 27 // bucket count as 2 (when you give a custom ranges vector containing only 1 28 // range). 29 // For these 3 kinds of histograms, the max bucket count is always 30 // (Histogram::kBucketCount_MAX - 1). 31 32 // The buckets layout of class Histogram is exponential. For example, buckets 33 // might contain (sequentially) the count of values in the following intervals: 34 // [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity) 35 // That bucket allocation would actually result from construction of a histogram 36 // for values between 1 and 64, with 8 buckets, such as: 37 // Histogram count("some name", 1, 64, 8); 38 // Note that the underflow bucket [0,1) and the overflow bucket [64,infinity) 39 // are also counted by the constructor in the user supplied "bucket_count" 40 // argument. 41 // The above example has an exponential ratio of 2 (doubling the bucket width 42 // in each consecutive bucket). The Histogram class automatically calculates 43 // the smallest ratio that it can use to construct the number of buckets 44 // selected in the constructor. An another example, if you had 50 buckets, 45 // and millisecond time values from 1 to 10000, then the ratio between 46 // consecutive bucket widths will be approximately somewhere around the 50th 47 // root of 10000. This approach provides very fine grain (narrow) buckets 48 // at the low end of the histogram scale, but allows the histogram to cover a 49 // gigantic range with the addition of very few buckets. 50 51 // Usually we use macros to define and use a histogram, which are defined in 52 // base/metrics/histogram_macros.h. Note: Callers should include that header 53 // directly if they only access the histogram APIs through macros. 54 // 55 // Macros use a pattern involving a function static variable, that is a pointer 56 // to a histogram. This static is explicitly initialized on any thread 57 // that detects a uninitialized (NULL) pointer. The potentially racy 58 // initialization is not a problem as it is always set to point to the same 59 // value (i.e., the FactoryGet always returns the same value). FactoryGet 60 // is also completely thread safe, which results in a completely thread safe, 61 // and relatively fast, set of counters. To avoid races at shutdown, the static 62 // pointer is NOT deleted, and we leak the histograms at process termination. 63 64 #ifndef BASE_METRICS_HISTOGRAM_H_ 65 #define BASE_METRICS_HISTOGRAM_H_ 66 67 #include <stddef.h> 68 #include <stdint.h> 69 70 #include <map> 71 #include <memory> 72 #include <string> 73 #include <string_view> 74 #include <vector> 75 76 #include "base/base_export.h" 77 #include "base/compiler_specific.h" 78 #include "base/containers/span.h" 79 #include "base/dcheck_is_on.h" 80 #include "base/gtest_prod_util.h" 81 #include "base/memory/raw_ptr.h" 82 #include "base/metrics/bucket_ranges.h" 83 #include "base/metrics/histogram_base.h" 84 #include "base/metrics/histogram_samples.h" 85 #include "base/time/time.h" 86 #include "base/values.h" 87 88 namespace base { 89 90 class BooleanHistogram; 91 class CustomHistogram; 92 class DelayedPersistentAllocation; 93 class Histogram; 94 class HistogramTest; 95 class LinearHistogram; 96 class Pickle; 97 class PickleIterator; 98 class SampleVector; 99 class SampleVectorBase; 100 101 class BASE_EXPORT Histogram : public HistogramBase { 102 public: 103 // Initialize maximum number of buckets in histograms as 1000, plus over and 104 // under. This must be a value that fits in a uint32_t (since that's how we 105 // serialize bucket counts) as well as a Sample (since samples can be up to 106 // this value). 107 static constexpr size_t kBucketCount_MAX = 1002; 108 109 typedef std::vector<Count> Counts; 110 111 Histogram(const Histogram&) = delete; 112 Histogram& operator=(const Histogram&) = delete; 113 114 ~Histogram() override; 115 116 //---------------------------------------------------------------------------- 117 // For a valid histogram, input should follow these restrictions: 118 // minimum > 0 (if a minimum below 1 is specified, it will implicitly be 119 // normalized up to 1) 120 // maximum > minimum 121 // buckets > 2 [minimum buckets needed: underflow, overflow and the range] 122 // Additionally, 123 // buckets <= (maximum - minimum + 2) - this is to ensure that we don't have 124 // more buckets than the range of numbers; having more buckets than 1 per 125 // value in the range would be nonsensical. 126 static HistogramBase* FactoryGet(const std::string& name, 127 Sample minimum, 128 Sample maximum, 129 size_t bucket_count, 130 int32_t flags); 131 static HistogramBase* FactoryTimeGet(const std::string& name, 132 base::TimeDelta minimum, 133 base::TimeDelta maximum, 134 size_t bucket_count, 135 int32_t flags); 136 static HistogramBase* FactoryMicrosecondsTimeGet(const std::string& name, 137 base::TimeDelta minimum, 138 base::TimeDelta maximum, 139 size_t bucket_count, 140 int32_t flags); 141 142 // Overloads of the above functions that take a const char* |name| param, to 143 // avoid code bloat from the std::string constructor being inlined into call 144 // sites. 145 static HistogramBase* FactoryGet(const char* name, 146 Sample minimum, 147 Sample maximum, 148 size_t bucket_count, 149 int32_t flags); 150 static HistogramBase* FactoryTimeGet(const char* name, 151 base::TimeDelta minimum, 152 base::TimeDelta maximum, 153 size_t bucket_count, 154 int32_t flags); 155 static HistogramBase* FactoryMicrosecondsTimeGet(const char* name, 156 base::TimeDelta minimum, 157 base::TimeDelta maximum, 158 size_t bucket_count, 159 int32_t flags); 160 161 // Create a histogram using data in persistent storage. 162 static std::unique_ptr<HistogramBase> PersistentCreate( 163 const char* name, 164 const BucketRanges* ranges, 165 const DelayedPersistentAllocation& counts, 166 const DelayedPersistentAllocation& logged_counts, 167 HistogramSamples::Metadata* meta, 168 HistogramSamples::Metadata* logged_meta); 169 170 static void InitializeBucketRanges(Sample minimum, 171 Sample maximum, 172 BucketRanges* ranges); 173 174 // This constant if for FindCorruption. Since snapshots of histograms are 175 // taken asynchronously relative to sampling, and our counting code currently 176 // does not prevent race conditions, it is pretty likely that we'll catch a 177 // redundant count that doesn't match the sample count. We allow for a 178 // certain amount of slop before flagging this as an inconsistency. Even with 179 // an inconsistency, we'll snapshot it again (for UMA in about a half hour), 180 // so we'll eventually get the data, if it was not the result of a corruption. 181 static const int kCommonRaceBasedCountMismatch; 182 183 // Check to see if bucket ranges, counts and tallies in the snapshot are 184 // consistent with the bucket ranges and checksums in our histogram. This can 185 // produce a false-alarm if a race occurred in the reading of the data during 186 // a SnapShot process, but should otherwise be false at all times (unless we 187 // have memory over-writes, or DRAM failures). Flag definitions are located 188 // under "enum Inconsistency" in base/metrics/histogram_base.h. 189 uint32_t FindCorruption(const HistogramSamples& samples) const override; 190 191 //---------------------------------------------------------------------------- 192 // Accessors for factory construction, serialization and testing. 193 //---------------------------------------------------------------------------- 194 const BucketRanges* bucket_ranges() const; 195 Sample declared_min() const; 196 Sample declared_max() const; 197 virtual Sample ranges(size_t i) const; 198 virtual size_t bucket_count() const; 199 200 // This function validates histogram construction arguments. It returns false 201 // if some of the arguments are bad but also corrects them so they should 202 // function on non-dcheck builds without crashing. 203 // Note. Currently it allow some bad input, e.g. 0 as minimum, but silently 204 // converts it to good input: 1. 205 static bool InspectConstructionArguments(std::string_view name, 206 Sample* minimum, 207 Sample* maximum, 208 size_t* bucket_count); 209 210 // HistogramBase implementation: 211 uint64_t name_hash() const override; 212 HistogramType GetHistogramType() const override; 213 bool HasConstructionArguments(Sample expected_minimum, 214 Sample expected_maximum, 215 size_t expected_bucket_count) const override; 216 void Add(Sample value) override; 217 void AddCount(Sample value, int count) override; 218 std::unique_ptr<HistogramSamples> SnapshotSamples() const override; 219 std::unique_ptr<HistogramSamples> SnapshotUnloggedSamples() const override; 220 void MarkSamplesAsLogged(const HistogramSamples& samples) final; 221 std::unique_ptr<HistogramSamples> SnapshotDelta() override; 222 std::unique_ptr<HistogramSamples> SnapshotFinalDelta() const override; 223 void AddSamples(const HistogramSamples& samples) override; 224 bool AddSamplesFromPickle(base::PickleIterator* iter) override; 225 base::Value::Dict ToGraphDict() const override; 226 227 protected: 228 // This class, defined entirely within the .cc file, contains all the 229 // common logic for building a Histogram and can be overridden by more 230 // specific types to alter details of how the creation is done. It is 231 // defined as an embedded class (rather than an anonymous one) so it 232 // can access the protected constructors. 233 class Factory; 234 235 // |ranges| should contain the underflow and overflow buckets. See top 236 // comments for example. 237 Histogram(const char* name, const BucketRanges* ranges); 238 239 // Traditionally, histograms allocate their own memory for the bucket 240 // vector but "shared" histograms use memory regions allocated from a 241 // special memory segment that is passed in here. It is assumed that 242 // the life of this memory is managed externally and exceeds the lifetime 243 // of this object. Practically, this memory is never released until the 244 // process exits and the OS cleans it up. 245 Histogram(const char* name, 246 const BucketRanges* ranges, 247 const DelayedPersistentAllocation& counts, 248 const DelayedPersistentAllocation& logged_counts, 249 HistogramSamples::Metadata* meta, 250 HistogramSamples::Metadata* logged_meta); 251 252 // HistogramBase implementation: 253 void SerializeInfoImpl(base::Pickle* pickle) const override; 254 255 // Return a string description of what goes in a given bucket. 256 // Most commonly this is the numeric value, but in derived classes it may 257 // be a name (or string description) given to the bucket. 258 virtual const std::string GetAsciiBucketRange(size_t it) const; 259 260 private: 261 // Allow tests to corrupt our innards for testing purposes. 262 friend class HistogramTest; 263 friend class HistogramThreadsafeTest; 264 FRIEND_TEST_ALL_PREFIXES(HistogramTest, BoundsTest); 265 FRIEND_TEST_ALL_PREFIXES(HistogramTest, BucketPlacementTest); 266 FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); 267 268 friend class StatisticsRecorder; // To allow it to delete duplicates. 269 friend class StatisticsRecorderTest; 270 271 friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( 272 base::PickleIterator* iter); 273 static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); 274 275 static HistogramBase* FactoryGetInternal(std::string_view name, 276 Sample minimum, 277 Sample maximum, 278 size_t bucket_count, 279 int32_t flags); 280 static HistogramBase* FactoryTimeGetInternal(std::string_view name, 281 base::TimeDelta minimum, 282 base::TimeDelta maximum, 283 size_t bucket_count, 284 int32_t flags); 285 static HistogramBase* FactoryMicrosecondsTimeGetInternal( 286 std::string_view name, 287 base::TimeDelta minimum, 288 base::TimeDelta maximum, 289 size_t bucket_count, 290 int32_t flags); 291 292 // Create a snapshot containing all samples (both logged and unlogged). 293 // Implementation of SnapshotSamples method with a more specific type for 294 // internal use. 295 std::unique_ptr<SampleVector> SnapshotAllSamples() const; 296 297 // Returns a copy of unlogged samples as the underlying SampleVector class, 298 // instead of the HistogramSamples base class. Used for tests and to avoid 299 // virtual dispatch from some callsites. 300 std::unique_ptr<SampleVector> SnapshotUnloggedSamplesImpl() const; 301 302 // Writes the type, min, max, and bucket count information of the histogram in 303 // |params|. 304 Value::Dict GetParameters() const override; 305 306 // Samples that have not yet been logged with SnapshotDelta(). 307 std::unique_ptr<SampleVectorBase> unlogged_samples_; 308 309 // Accumulation of all samples that have been logged with SnapshotDelta(). 310 std::unique_ptr<SampleVectorBase> logged_samples_; 311 312 #if DCHECK_IS_ON() // Don't waste memory if it won't be used. 313 // Flag to indicate if PrepareFinalDelta has been previously called. It is 314 // used to DCHECK that a final delta is not created multiple times. 315 mutable bool final_delta_created_ = false; 316 #endif 317 }; 318 319 //------------------------------------------------------------------------------ 320 321 // LinearHistogram is a more traditional histogram, with evenly spaced 322 // buckets. 323 class BASE_EXPORT LinearHistogram : public Histogram { 324 public: 325 LinearHistogram(const LinearHistogram&) = delete; 326 LinearHistogram& operator=(const LinearHistogram&) = delete; 327 328 ~LinearHistogram() override; 329 330 /* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit 331 default underflow bucket. */ 332 static HistogramBase* FactoryGet(const std::string& name, 333 Sample minimum, 334 Sample maximum, 335 size_t bucket_count, 336 int32_t flags); 337 static HistogramBase* FactoryTimeGet(const std::string& name, 338 TimeDelta minimum, 339 TimeDelta maximum, 340 size_t bucket_count, 341 int32_t flags); 342 343 // Overloads of the above two functions that take a const char* |name| param, 344 // to avoid code bloat from the std::string constructor being inlined into 345 // call sites. 346 static HistogramBase* FactoryGet(const char* name, 347 Sample minimum, 348 Sample maximum, 349 size_t bucket_count, 350 int32_t flags); 351 static HistogramBase* FactoryTimeGet(const char* name, 352 TimeDelta minimum, 353 TimeDelta maximum, 354 size_t bucket_count, 355 int32_t flags); 356 357 // Create a histogram using data in persistent storage. 358 static std::unique_ptr<HistogramBase> PersistentCreate( 359 const char* name, 360 const BucketRanges* ranges, 361 const DelayedPersistentAllocation& counts, 362 const DelayedPersistentAllocation& logged_counts, 363 HistogramSamples::Metadata* meta, 364 HistogramSamples::Metadata* logged_meta); 365 366 struct DescriptionPair { 367 Sample sample; 368 const char* description; // Null means end of a list of pairs. 369 }; 370 371 // Create a LinearHistogram and store a list of number/text values for use in 372 // writing the histogram graph. 373 // |descriptions| can be NULL, which means no special descriptions to set. If 374 // it's not NULL, the last element in the array must has a NULL in its 375 // "description" field. 376 static HistogramBase* FactoryGetWithRangeDescription( 377 std::string_view name, 378 Sample minimum, 379 Sample maximum, 380 size_t bucket_count, 381 int32_t flags, 382 const DescriptionPair descriptions[]); 383 384 static void InitializeBucketRanges(Sample minimum, 385 Sample maximum, 386 BucketRanges* ranges); 387 388 // Overridden from Histogram: 389 HistogramType GetHistogramType() const override; 390 391 protected: 392 class Factory; 393 394 LinearHistogram(const char* name, const BucketRanges* ranges); 395 396 LinearHistogram(const char* name, 397 const BucketRanges* ranges, 398 const DelayedPersistentAllocation& counts, 399 const DelayedPersistentAllocation& logged_counts, 400 HistogramSamples::Metadata* meta, 401 HistogramSamples::Metadata* logged_meta); 402 403 // If we have a description for a bucket, then return that. Otherwise 404 // let parent class provide a (numeric) description. 405 const std::string GetAsciiBucketRange(size_t i) const override; 406 407 private: 408 friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( 409 base::PickleIterator* iter); 410 static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); 411 412 static HistogramBase* FactoryGetInternal(std::string_view name, 413 Sample minimum, 414 Sample maximum, 415 size_t bucket_count, 416 int32_t flags); 417 static HistogramBase* FactoryTimeGetInternal(std::string_view name, 418 TimeDelta minimum, 419 TimeDelta maximum, 420 size_t bucket_count, 421 int32_t flags); 422 423 // For some ranges, we store a printable description of a bucket range. 424 // If there is no description, then GetAsciiBucketRange() uses parent class 425 // to provide a description. 426 typedef std::map<Sample, std::string> BucketDescriptionMap; 427 BucketDescriptionMap bucket_description_; 428 }; 429 430 //------------------------------------------------------------------------------ 431 432 // ScaledLinearHistogram is a wrapper around a linear histogram that scales the 433 // counts down by some factor. Remainder values are kept locally but lost when 434 // uploaded or serialized. The integral counts are rounded up/down so should 435 // average to the correct value when many reports are added. 436 // 437 // This is most useful when adding many counts at once via AddCount() that can 438 // cause overflows of the 31-bit counters, usually with an enum as the value. 439 class BASE_EXPORT ScaledLinearHistogram { 440 using AtomicCount = Histogram::AtomicCount; 441 using Sample = Histogram::Sample; 442 443 public: 444 // Currently only works with "exact" linear histograms: minimum=1, maximum=N, 445 // and bucket_count=N+1. 446 ScaledLinearHistogram(const char* name, 447 Sample minimum, 448 Sample maximum, 449 size_t bucket_count, 450 int32_t scale, 451 int32_t flags); 452 ScaledLinearHistogram(const std::string& name, 453 Sample minimum, 454 Sample maximum, 455 size_t bucket_count, 456 int32_t scale, 457 int32_t flags); 458 459 ScaledLinearHistogram(const ScaledLinearHistogram&) = delete; 460 ScaledLinearHistogram& operator=(const ScaledLinearHistogram&) = delete; 461 462 ~ScaledLinearHistogram(); 463 464 // Like AddCount() but actually accumulates |count|/|scale| and increments 465 // the accumulated remainder by |count|%|scale|. An additional increment 466 // is done when the remainder has grown sufficiently large. 467 // The value after scaling must fit into 32-bit signed integer. 468 void AddScaledCount(Sample value, int64_t count); 469 scale()470 int32_t scale() const { return scale_; } histogram()471 HistogramBase* histogram() { return histogram_; } 472 473 private: 474 // Pointer to the underlying histogram. Ownership of it remains with 475 // the statistics-recorder. This is typed as HistogramBase because it may be a 476 // DummyHistogram if expired. 477 const raw_ptr<HistogramBase> histogram_; 478 479 // The scale factor of the sample counts. 480 const int32_t scale_; 481 482 // A vector of "remainder" counts indexed by bucket number. These values 483 // may be negative as the scaled count is actually bumped once the 484 // remainder is 1/2 way to the scale value (thus "rounding"). 485 std::vector<AtomicCount> remainders_; 486 }; 487 488 //------------------------------------------------------------------------------ 489 490 // BooleanHistogram is a histogram for booleans. 491 class BASE_EXPORT BooleanHistogram : public LinearHistogram { 492 public: 493 static HistogramBase* FactoryGet(const std::string& name, int32_t flags); 494 495 // Overload of the above function that takes a const char* |name| param, 496 // to avoid code bloat from the std::string constructor being inlined into 497 // call sites. 498 static HistogramBase* FactoryGet(const char* name, int32_t flags); 499 500 BooleanHistogram(const BooleanHistogram&) = delete; 501 BooleanHistogram& operator=(const BooleanHistogram&) = delete; 502 503 // Create a histogram using data in persistent storage. 504 static std::unique_ptr<HistogramBase> PersistentCreate( 505 const char* name, 506 const BucketRanges* ranges, 507 const DelayedPersistentAllocation& counts, 508 const DelayedPersistentAllocation& logged_counts, 509 HistogramSamples::Metadata* meta, 510 HistogramSamples::Metadata* logged_meta); 511 512 HistogramType GetHistogramType() const override; 513 514 protected: 515 class Factory; 516 517 private: 518 static HistogramBase* FactoryGetInternal(std::string_view name, 519 int32_t flags); 520 521 BooleanHistogram(const char* name, const BucketRanges* ranges); 522 BooleanHistogram(const char* name, 523 const BucketRanges* ranges, 524 const DelayedPersistentAllocation& counts, 525 const DelayedPersistentAllocation& logged_counts, 526 HistogramSamples::Metadata* meta, 527 HistogramSamples::Metadata* logged_meta); 528 529 friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( 530 base::PickleIterator* iter); 531 static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); 532 }; 533 534 //------------------------------------------------------------------------------ 535 536 // CustomHistogram is a histogram for a set of custom integers. 537 class BASE_EXPORT CustomHistogram : public Histogram { 538 public: 539 // |custom_ranges| contains a vector of limits on ranges. Each limit should be 540 // > 0 and < kSampleType_MAX. (Currently 0 is still accepted for backward 541 // compatibility). The limits can be unordered or contain duplication, but 542 // client should not depend on this. 543 static HistogramBase* FactoryGet(const std::string& name, 544 const std::vector<Sample>& custom_ranges, 545 int32_t flags); 546 547 // Overload of the above function that takes a const char* |name| param, 548 // to avoid code bloat from the std::string constructor being inlined into 549 // call sites. 550 static HistogramBase* FactoryGet(const char* name, 551 const std::vector<Sample>& custom_ranges, 552 int32_t flags); 553 554 CustomHistogram(const CustomHistogram&) = delete; 555 CustomHistogram& operator=(const CustomHistogram&) = delete; 556 557 // Create a histogram using data in persistent storage. 558 static std::unique_ptr<HistogramBase> PersistentCreate( 559 const char* name, 560 const BucketRanges* ranges, 561 const DelayedPersistentAllocation& counts, 562 const DelayedPersistentAllocation& logged_counts, 563 HistogramSamples::Metadata* meta, 564 HistogramSamples::Metadata* logged_meta); 565 566 // Overridden from Histogram: 567 HistogramType GetHistogramType() const override; 568 569 // Helper method for transforming an array of valid enumeration values 570 // to the std::vector<int> expected by UMA_HISTOGRAM_CUSTOM_ENUMERATION. 571 // This function ensures that a guard bucket exists right after any 572 // valid sample value (unless the next higher sample is also a valid value), 573 // so that invalid samples never fall into the same bucket as valid samples. 574 static std::vector<Sample> ArrayToCustomEnumRanges( 575 base::span<const Sample> values); 576 577 protected: 578 class Factory; 579 580 CustomHistogram(const char* name, const BucketRanges* ranges); 581 582 CustomHistogram(const char* name, 583 const BucketRanges* ranges, 584 const DelayedPersistentAllocation& counts, 585 const DelayedPersistentAllocation& logged_counts, 586 HistogramSamples::Metadata* meta, 587 HistogramSamples::Metadata* logged_meta); 588 589 // HistogramBase implementation: 590 void SerializeInfoImpl(base::Pickle* pickle) const override; 591 592 private: 593 friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( 594 base::PickleIterator* iter); 595 static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); 596 597 static HistogramBase* FactoryGetInternal( 598 std::string_view name, 599 const std::vector<Sample>& custom_ranges, 600 int32_t flags); 601 602 static bool ValidateCustomRanges(const std::vector<Sample>& custom_ranges); 603 }; 604 605 } // namespace base 606 607 #endif // BASE_METRICS_HISTOGRAM_H_ 608