Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(213)

Side by Side Diff: base/metrics/histogram.h

Issue 10829466: SampleSet -> HistogramSamples (will be reused by SparseHistogram) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // Histogram is an object that aggregates statistics, and can summarize them in 5 // Histogram is an object that aggregates statistics, and can summarize them in
6 // various forms, including ASCII graphical, HTML, and numerically (as a 6 // various forms, including ASCII graphical, HTML, and numerically (as a
7 // vector of numbers corresponding to each of the aggregating buckets). 7 // vector of numbers corresponding to each of the aggregating buckets).
8 8
9 // It supports calls to accumulate either time intervals (which are processed 9 // It supports calls to accumulate either time intervals (which are processed
10 // as integral number of milliseconds), or arbitrary integral units. 10 // as integral number of milliseconds), or arbitrary integral units.
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 #include <map> 61 #include <map>
62 #include <string> 62 #include <string>
63 #include <vector> 63 #include <vector>
64 64
65 #include "base/atomicops.h" 65 #include "base/atomicops.h"
66 #include "base/base_export.h" 66 #include "base/base_export.h"
67 #include "base/basictypes.h" 67 #include "base/basictypes.h"
68 #include "base/compiler_specific.h" 68 #include "base/compiler_specific.h"
69 #include "base/gtest_prod_util.h" 69 #include "base/gtest_prod_util.h"
70 #include "base/logging.h" 70 #include "base/logging.h"
71 #include "base/memory/scoped_ptr.h"
71 #include "base/metrics/bucket_ranges.h" 72 #include "base/metrics/bucket_ranges.h"
72 #include "base/metrics/histogram_base.h" 73 #include "base/metrics/histogram_base.h"
74 #include "base/metrics/histogram_samples.h"
73 #include "base/time.h" 75 #include "base/time.h"
74 76
75 class Pickle; 77 class Pickle;
76 class PickleIterator; 78 class PickleIterator;
77 79
78 namespace base { 80 namespace base {
79 81
80 class Lock; 82 class Lock;
81 //------------------------------------------------------------------------------ 83 //------------------------------------------------------------------------------
82 // Histograms are often put in areas where they are called many many times, and 84 // Histograms are often put in areas where they are called many many times, and
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after
337 base::Histogram::kUmaTargetedHistogramFlag)) 339 base::Histogram::kUmaTargetedHistogramFlag))
338 340
339 //------------------------------------------------------------------------------ 341 //------------------------------------------------------------------------------
340 342
341 class BooleanHistogram; 343 class BooleanHistogram;
342 class BucketRanges; 344 class BucketRanges;
343 class CustomHistogram; 345 class CustomHistogram;
344 class Histogram; 346 class Histogram;
345 class LinearHistogram; 347 class LinearHistogram;
346 348
349 class BASE_EXPORT_PRIVATE BucketHistogramSamples : public HistogramSamples {
350 public:
351 explicit BucketHistogramSamples(const BucketRanges* bucket_ranges);
352 virtual ~BucketHistogramSamples();
353
354 // Implements HistogramSamples:
Ilya Sherman 2012/08/29 08:48:16 nit: This is typically written as "HistogramSample
kaiwang 2012/08/29 22:42:16 Done.
355 virtual void Accumulate(HistogramBase::Sample value,
356 HistogramBase::Count count) OVERRIDE;
357 virtual HistogramBase::Count GetCount(
358 HistogramBase::Sample value) const OVERRIDE;
359 virtual HistogramBase::Count TotalCount() const OVERRIDE;
360 virtual scoped_ptr<SampleCountIterator> Iterator() const OVERRIDE;
361
362 // Get count of a specific bucket.
363 HistogramBase::Count GetCountFromBucketIndex(size_t bucket_index) const;
364
365 protected:
366 virtual bool AddSubtractImpl(SampleCountIterator* iter,
367 bool is_add) OVERRIDE;
368
369 static size_t GetBucketIndex(HistogramBase::Sample value,
370 const BucketRanges* bucket_ranges);
Ilya Sherman 2012/08/29 08:48:16 nit: Why is this a static function rather than an
Ilya Sherman 2012/08/29 08:48:16 Can this be private?
kaiwang 2012/08/29 22:42:16 Will change it to private. For static/non-static,
Ilya Sherman 2012/08/29 23:41:27 As discussed offline, Chromium code usually prefer
kaiwang 2012/08/30 03:13:22 Sorry I forgot a reason to make it a non-static me
371
372 private:
373 FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
374
375 std::vector<HistogramBase::Count> counts_;
376
377 // Shares the same BucketRanges with Histogram object.
378 const BucketRanges* const bucket_ranges_;
379
380 DISALLOW_COPY_AND_ASSIGN(BucketHistogramSamples);
381 };
382
383 class BASE_EXPORT_PRIVATE BucketHistogramSamplesIterator
384 : public SampleCountIterator {
385 public:
386 BucketHistogramSamplesIterator(
387 const std::vector<HistogramBase::Count>* counts,
388 const BucketRanges* bucket_ranges);
389
390 // Implements SampleCountIterator.
391 virtual bool Done() OVERRIDE;
392 virtual void Next() OVERRIDE;
393 virtual void Get(HistogramBase::Sample* min,
394 HistogramBase::Sample* max,
395 HistogramBase::Count* count) OVERRIDE;
396 private:
397 void ForwardToNextNonemptyBucket();
398
399 const std::vector<HistogramBase::Count>* counts_;
400 const BucketRanges* bucket_ranges_;
401
402 int index_;
403 bool is_done_;
404 };
405
347 class BASE_EXPORT Histogram : public HistogramBase { 406 class BASE_EXPORT Histogram : public HistogramBase {
348 public: 407 public:
349 // Initialize maximum number of buckets in histograms as 16,384. 408 // Initialize maximum number of buckets in histograms as 16,384.
350 static const size_t kBucketCount_MAX; 409 static const size_t kBucketCount_MAX;
351 410
352 typedef std::vector<Count> Counts; 411 typedef std::vector<Count> Counts;
353 412
354 // These enums are used to facilitate deserialization of renderer histograms 413 // These enums are used to facilitate deserialization of renderer histograms
355 // into the browser. 414 // into the browser.
356 enum ClassType { 415 enum ClassType {
(...skipping 19 matching lines...) Expand all
376 435
377 NEVER_EXCEEDED_VALUE = 0x10 436 NEVER_EXCEEDED_VALUE = 0x10
378 }; 437 };
379 438
380 struct DescriptionPair { 439 struct DescriptionPair {
381 Sample sample; 440 Sample sample;
382 const char* description; // Null means end of a list of pairs. 441 const char* description; // Null means end of a list of pairs.
383 }; 442 };
384 443
385 //---------------------------------------------------------------------------- 444 //----------------------------------------------------------------------------
386 // Statistic values, developed over the life of the histogram.
387
388 class BASE_EXPORT SampleSet {
389 public:
390 explicit SampleSet(size_t size);
391 SampleSet();
392 ~SampleSet();
393
394 void Resize(size_t size);
395
396 // Accessor for histogram to make routine additions.
397 void Accumulate(Sample value, Count count, size_t index);
398
399 // Accessor methods.
400 size_t size() const { return counts_.size(); }
401 Count counts(size_t i) const { return counts_[i]; }
402 Count TotalCount() const;
403 int64 sum() const { return sum_; }
404 int64 redundant_count() const { return redundant_count_; }
405
406 // Arithmetic manipulation of corresponding elements of the set.
407 void Add(const SampleSet& other);
408 void Subtract(const SampleSet& other);
409
410 bool Serialize(Pickle* pickle) const;
411 bool Deserialize(PickleIterator* iter);
412
413 protected:
414 // Actual histogram data is stored in buckets, showing the count of values
415 // that fit into each bucket.
416 Counts counts_;
417
418 // Save simple stats locally. Note that this MIGHT get done in base class
419 // without shared memory at some point.
420 int64 sum_; // sum of samples.
421
422 private:
423 // Allow tests to corrupt our innards for testing purposes.
424 FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
425
426 // To help identify memory corruption, we reduntantly save the number of
427 // samples we've accumulated into all of our buckets. We can compare this
428 // count to the sum of the counts in all buckets, and detect problems. Note
429 // that due to races in histogram accumulation (if a histogram is indeed
430 // updated on several threads simultaneously), the tallies might mismatch,
431 // and also the snapshotting code may asynchronously get a mismatch (though
432 // generally either race based mismatch cause is VERY rare).
433 int64 redundant_count_;
434 };
435
436 //----------------------------------------------------------------------------
437 // For a valid histogram, input should follow these restrictions: 445 // For a valid histogram, input should follow these restrictions:
438 // minimum > 0 (if a minimum below 1 is specified, it will implicitly be 446 // minimum > 0 (if a minimum below 1 is specified, it will implicitly be
439 // normalized up to 1) 447 // normalized up to 1)
440 // maximum > minimum 448 // maximum > minimum
441 // buckets > 2 [minimum buckets needed: underflow, overflow and the range] 449 // buckets > 2 [minimum buckets needed: underflow, overflow and the range]
442 // Additionally, 450 // Additionally,
443 // buckets <= (maximum - minimum + 2) - this is to ensure that we don't have 451 // buckets <= (maximum - minimum + 2) - this is to ensure that we don't have
444 // more buckets than the range of numbers; having more buckets than 1 per 452 // more buckets than the range of numbers; having more buckets than 1 per
445 // value in the range would be nonsensical. 453 // value in the range would be nonsensical.
446 static Histogram* FactoryGet(const std::string& name, 454 static Histogram* FactoryGet(const std::string& name,
(...skipping 19 matching lines...) Expand all
466 virtual void Add(Sample value) OVERRIDE; 474 virtual void Add(Sample value) OVERRIDE;
467 475
468 // This method is an interface, used only by BooleanHistogram. 476 // This method is an interface, used only by BooleanHistogram.
469 virtual void AddBoolean(bool value); 477 virtual void AddBoolean(bool value);
470 478
471 // Accept a TimeDelta to increment. 479 // Accept a TimeDelta to increment.
472 void AddTime(TimeDelta time) { 480 void AddTime(TimeDelta time) {
473 Add(static_cast<int>(time.InMilliseconds())); 481 Add(static_cast<int>(time.InMilliseconds()));
474 } 482 }
475 483
476 void AddSampleSet(const SampleSet& sample); 484 void AddSamples(const HistogramSamples& samples);
485 bool AddSamples(PickleIterator* iter);
Ilya Sherman 2012/08/29 08:48:16 Optional nit: Perhaps |AddSamplesFromPickle()|?
kaiwang 2012/08/29 22:42:16 Done.
477 486
478 // This method is an interface, used only by LinearHistogram. 487 // This method is an interface, used only by LinearHistogram.
479 virtual void SetRangeDescriptions(const DescriptionPair descriptions[]); 488 virtual void SetRangeDescriptions(const DescriptionPair descriptions[]);
480 489
481 // The following methods provide graphical histogram displays. 490 // The following methods provide graphical histogram displays.
482 virtual void WriteHTMLGraph(std::string* output) const OVERRIDE; 491 virtual void WriteHTMLGraph(std::string* output) const OVERRIDE;
483 virtual void WriteAscii(std::string* output) const OVERRIDE; 492 virtual void WriteAscii(std::string* output) const OVERRIDE;
484 493
485 // Convenience methods for serializing/deserializing the histograms. 494 // Convenience methods for serializing/deserializing the histograms.
486 // Histograms from Renderer process are serialized and sent to the browser. 495 // Histograms from Renderer process are serialized and sent to the browser.
487 // Browser process reconstructs the histogram from the pickled version 496 // Browser process reconstructs the histogram from the pickled version
488 // accumulates the browser-side shadow copy of histograms (that mirror 497 // accumulates the browser-side shadow copy of histograms (that mirror
489 // histograms created in the renderer). 498 // histograms created in the renderer).
490 499
491 // Serialize the given snapshot of a Histogram into a String. Uses 500 // Serialize the given snapshot of a Histogram into a String. Uses
492 // Pickle class to flatten the object. 501 // Pickle class to flatten the object.
493 static std::string SerializeHistogramInfo(const Histogram& histogram, 502 static std::string SerializeHistogramInfo(const Histogram& histogram,
494 const SampleSet& snapshot); 503 const HistogramSamples& snapshot);
495 504
496 // The following method accepts a list of pickled histograms and 505 // The following method accepts a list of pickled histograms and
497 // builds a histogram and updates shadow copy of histogram data in the 506 // builds a histogram and updates shadow copy of histogram data in the
498 // browser process. 507 // browser process.
499 static bool DeserializeHistogramInfo(const std::string& histogram_info); 508 static bool DeserializeHistogramInfo(const std::string& histogram_info);
500 509
510 // This constant if for FindCorruption. Since snapshots of histograms are
511 // taken asynchronously relative to sampling, and out counting code currently
512 // does not prevent race conditions, it is pretty likely that we'll catch a
513 // redundant count that doesn't match the sample count. We allow for a
514 // certain amount of slop before flagging this as an inconsistency. Even with
515 // an inconsistency, we'll snapshot it again (for UMA in about a half hour),
516 // so we'll eventually get the data, if it was not the result of a corruption.
517 static const int kCommonRaceBasedCountMismatch;
518
501 // Check to see if bucket ranges, counts and tallies in the snapshot are 519 // Check to see if bucket ranges, counts and tallies in the snapshot are
502 // consistent with the bucket ranges and checksums in our histogram. This can 520 // consistent with the bucket ranges and checksums in our histogram. This can
503 // produce a false-alarm if a race occurred in the reading of the data during 521 // produce a false-alarm if a race occurred in the reading of the data during
504 // a SnapShot process, but should otherwise be false at all times (unless we 522 // a SnapShot process, but should otherwise be false at all times (unless we
505 // have memory over-writes, or DRAM failures). 523 // have memory over-writes, or DRAM failures).
506 virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const; 524 virtual Inconsistencies FindCorruption(const HistogramSamples& samples) const;
507 525
508 //---------------------------------------------------------------------------- 526 //----------------------------------------------------------------------------
509 // Accessors for factory constuction, serialization and testing. 527 // Accessors for factory constuction, serialization and testing.
510 //---------------------------------------------------------------------------- 528 //----------------------------------------------------------------------------
511 virtual ClassType histogram_type() const; 529 virtual ClassType histogram_type() const;
512 Sample declared_min() const { return declared_min_; } 530 Sample declared_min() const { return declared_min_; }
513 Sample declared_max() const { return declared_max_; } 531 Sample declared_max() const { return declared_max_; }
514 virtual Sample ranges(size_t i) const; 532 virtual Sample ranges(size_t i) const;
515 virtual size_t bucket_count() const; 533 virtual size_t bucket_count() const;
516 const BucketRanges* bucket_ranges() const { return bucket_ranges_; } 534 const BucketRanges* bucket_ranges() const { return bucket_ranges_; }
517 535
518 // Snapshot the current complete set of sample data. 536 // Snapshot the current complete set of sample data.
519 // Override with atomic/locked snapshot if needed. 537 // Override with atomic/locked snapshot if needed.
520 virtual void SnapshotSample(SampleSet* sample) const; 538 virtual scoped_ptr<BucketHistogramSamples> SnapshotSamples() const;
521 539
522 virtual bool HasConstructionArguments(Sample minimum, 540 virtual bool HasConstructionArguments(Sample minimum,
523 Sample maximum, 541 Sample maximum,
524 size_t bucket_count); 542 size_t bucket_count);
525 protected: 543 protected:
526 // |bucket_count| and |ranges| should contain the underflow and overflow 544 // |bucket_count| and |ranges| should contain the underflow and overflow
527 // buckets. See top comments for example. 545 // buckets. See top comments for example.
528 Histogram(const std::string& name, 546 Histogram(const std::string& name,
529 Sample minimum, 547 Sample minimum,
530 Sample maximum, 548 Sample maximum,
(...skipping 15 matching lines...) Expand all
546 564
547 // Serialize the histogram's ranges to |*pickle|, returning true on success. 565 // Serialize the histogram's ranges to |*pickle|, returning true on success.
548 // Most subclasses can leave this no-op implementation, but some will want to 566 // Most subclasses can leave this no-op implementation, but some will want to
549 // override it, especially if the ranges cannot be re-derived from other 567 // override it, especially if the ranges cannot be re-derived from other
550 // serialized parameters. 568 // serialized parameters.
551 virtual bool SerializeRanges(Pickle* pickle) const; 569 virtual bool SerializeRanges(Pickle* pickle) const;
552 570
553 // Method to override to skip the display of the i'th bucket if it's empty. 571 // Method to override to skip the display of the i'th bucket if it's empty.
554 virtual bool PrintEmptyBucket(size_t index) const; 572 virtual bool PrintEmptyBucket(size_t index) const;
555 573
556 //----------------------------------------------------------------------------
557 // Methods to override to create histogram with different bucket widths.
558 //----------------------------------------------------------------------------
559 // Find bucket to increment for sample value.
560 virtual size_t BucketIndex(Sample value) const;
561 // Get normalized size, relative to the ranges(i). 574 // Get normalized size, relative to the ranges(i).
562 virtual double GetBucketSize(Count current, size_t i) const; 575 virtual double GetBucketSize(Count current, size_t i) const;
563 576
564 // Return a string description of what goes in a given bucket. 577 // Return a string description of what goes in a given bucket.
565 // Most commonly this is the numeric value, but in derived classes it may 578 // Most commonly this is the numeric value, but in derived classes it may
566 // be a name (or string description) given to the bucket. 579 // be a name (or string description) given to the bucket.
567 virtual const std::string GetAsciiBucketRange(size_t it) const; 580 virtual const std::string GetAsciiBucketRange(size_t it) const;
568 581
569 //----------------------------------------------------------------------------
570 // Methods to override to create thread safe histogram.
571 //----------------------------------------------------------------------------
572 // Update all our internal data, including histogram
573 virtual void Accumulate(Sample value, Count count, size_t index);
574
575 private: 582 private:
576 // Allow tests to corrupt our innards for testing purposes. 583 // Allow tests to corrupt our innards for testing purposes.
577 FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptBucketBounds); 584 FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptBucketBounds);
578 FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); 585 FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
579 FRIEND_TEST_ALL_PREFIXES(HistogramTest, Crc32SampleHash); 586 FRIEND_TEST_ALL_PREFIXES(HistogramTest, Crc32SampleHash);
580 FRIEND_TEST_ALL_PREFIXES(HistogramTest, Crc32TableTest); 587 FRIEND_TEST_ALL_PREFIXES(HistogramTest, Crc32TableTest);
581 588
582 friend class StatisticsRecorder; // To allow it to delete duplicates. 589 friend class StatisticsRecorder; // To allow it to delete duplicates.
583 friend class StatisticsRecorderTest; 590 friend class StatisticsRecorderTest;
584 591
585 //---------------------------------------------------------------------------- 592 //----------------------------------------------------------------------------
586 // Helpers for emitting Ascii graphic. Each method appends data to output. 593 // Helpers for emitting Ascii graphic. Each method appends data to output.
587 594
588 void WriteAsciiImpl(bool graph_it, 595 void WriteAsciiImpl(bool graph_it,
589 const std::string& newline, 596 const std::string& newline,
590 std::string* output) const; 597 std::string* output) const;
591 598
592 // Find out how large the (graphically) the largest bucket will appear to be. 599 // Find out how large (graphically) the largest bucket will appear to be.
593 double GetPeakBucketSize(const SampleSet& snapshot) const; 600 double GetPeakBucketSize(const BucketHistogramSamples& samples) const;
594 601
595 // Write a common header message describing this histogram. 602 // Write a common header message describing this histogram.
596 void WriteAsciiHeader(const SampleSet& snapshot, 603 void WriteAsciiHeader(const BucketHistogramSamples& samples,
597 Count sample_count, std::string* output) const; 604 Count sample_count,
605 std::string* output) const;
598 606
599 // Write information about previous, current, and next buckets. 607 // Write information about previous, current, and next buckets.
600 // Information such as cumulative percentage, etc. 608 // Information such as cumulative percentage, etc.
601 void WriteAsciiBucketContext(const int64 past, const Count current, 609 void WriteAsciiBucketContext(const int64 past, const Count current,
602 const int64 remaining, const size_t i, 610 const int64 remaining, const size_t i,
603 std::string* output) const; 611 std::string* output) const;
604 612
605 // Write textual description of the bucket contents (relative to histogram). 613 // Write textual description of the bucket contents (relative to histogram).
606 // Output is the count in the buckets, as well as the percentage. 614 // Output is the count in the buckets, as well as the percentage.
607 void WriteAsciiBucketValue(Count current, double scaled_sum, 615 void WriteAsciiBucketValue(Count current, double scaled_sum,
608 std::string* output) const; 616 std::string* output) const;
609 617
610 // Produce actual graph (set of blank vs non blank char's) for a bucket. 618 // Produce actual graph (set of blank vs non blank char's) for a bucket.
611 void WriteAsciiBucketGraph(double current_size, double max_size, 619 void WriteAsciiBucketGraph(double current_size, double max_size,
612 std::string* output) const; 620 std::string* output) const;
613 621
614 // Does not own this object. Should get from StatisticsRecorder. 622 // Does not own this object. Should get from StatisticsRecorder.
615 const BucketRanges* bucket_ranges_; 623 const BucketRanges* bucket_ranges_;
616 624
617 Sample declared_min_; // Less than this goes into counts_[0] 625 Sample declared_min_; // Less than this goes into counts_[0]
618 Sample declared_max_; // Over this goes into counts_[bucket_count_ - 1]. 626 Sample declared_max_; // Over this goes into counts_[bucket_count_ - 1].
619 size_t bucket_count_; // Dimension of counts_[]. 627 size_t bucket_count_; // Dimension of counts_[].
620 628
621 // Finally, provide the state that changes with the addition of each new 629 // Finally, provide the state that changes with the addition of each new
622 // sample. 630 // sample.
623 SampleSet sample_; 631 scoped_ptr<BucketHistogramSamples> samples_;
624 632
625 DISALLOW_COPY_AND_ASSIGN(Histogram); 633 DISALLOW_COPY_AND_ASSIGN(Histogram);
626 }; 634 };
627 635
628 //------------------------------------------------------------------------------ 636 //------------------------------------------------------------------------------
629 637
630 // LinearHistogram is a more traditional histogram, with evenly spaced 638 // LinearHistogram is a more traditional histogram, with evenly spaced
631 // buckets. 639 // buckets.
632 class BASE_EXPORT LinearHistogram : public Histogram { 640 class BASE_EXPORT LinearHistogram : public Histogram {
633 public: 641 public:
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
744 static bool ValidateCustomRanges(const std::vector<Sample>& custom_ranges); 752 static bool ValidateCustomRanges(const std::vector<Sample>& custom_ranges);
745 static BucketRanges* CreateBucketRangesFromCustomRanges( 753 static BucketRanges* CreateBucketRangesFromCustomRanges(
746 const std::vector<Sample>& custom_ranges); 754 const std::vector<Sample>& custom_ranges);
747 755
748 DISALLOW_COPY_AND_ASSIGN(CustomHistogram); 756 DISALLOW_COPY_AND_ASSIGN(CustomHistogram);
749 }; 757 };
750 758
751 } // namespace base 759 } // namespace base
752 760
753 #endif // BASE_METRICS_HISTOGRAM_H_ 761 #endif // BASE_METRICS_HISTOGRAM_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698