Home | History | Annotate | Download | only in metrics
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Test of Histogram class
      6 
      7 #include <climits>
      8 #include <algorithm>
      9 #include <vector>
     10 
     11 #include "base/logging.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/metrics/bucket_ranges.h"
     14 #include "base/metrics/histogram.h"
     15 #include "base/metrics/sample_vector.h"
     16 #include "base/metrics/statistics_recorder.h"
     17 #include "base/pickle.h"
     18 #include "base/time/time.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 using std::vector;
     22 
     23 namespace base {
     24 
     25 class HistogramTest : public testing::Test {
     26  protected:
     27   virtual void SetUp() {
     28     // Each test will have a clean state (no Histogram / BucketRanges
     29     // registered).
     30     InitializeStatisticsRecorder();
     31   }
     32 
     33   virtual void TearDown() {
     34     UninitializeStatisticsRecorder();
     35   }
     36 
     37   void InitializeStatisticsRecorder() {
     38     statistics_recorder_ = new StatisticsRecorder();
     39   }
     40 
     41   void UninitializeStatisticsRecorder() {
     42     delete statistics_recorder_;
     43     statistics_recorder_ = NULL;
     44   }
     45 
     46   StatisticsRecorder* statistics_recorder_;
     47 };
     48 
     49 // Check for basic syntax and use.
     50 TEST_F(HistogramTest, BasicTest) {
     51   // Try basic construction
     52   HistogramBase* histogram = Histogram::FactoryGet(
     53       "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
     54   EXPECT_TRUE(histogram);
     55 
     56   HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
     57       "TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
     58   EXPECT_TRUE(linear_histogram);
     59 
     60   vector<int> custom_ranges;
     61   custom_ranges.push_back(1);
     62   custom_ranges.push_back(5);
     63   HistogramBase* custom_histogram = CustomHistogram::FactoryGet(
     64       "TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags);
     65   EXPECT_TRUE(custom_histogram);
     66 
     67   // Use standard macros (but with fixed samples)
     68   HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1));
     69   HISTOGRAM_COUNTS("Test3Histogram", 30);
     70 
     71   DHISTOGRAM_TIMES("Test4Histogram", TimeDelta::FromDays(1));
     72   DHISTOGRAM_COUNTS("Test5Histogram", 30);
     73 
     74   HISTOGRAM_ENUMERATION("Test6Histogram", 129, 130);
     75 }
     76 
     77 // Check that the macro correctly matches histograms by name and records their
     78 // data together.
     79 TEST_F(HistogramTest, NameMatchTest) {
     80   HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10);
     81   HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10);
     82   HistogramBase* histogram = LinearHistogram::FactoryGet(
     83       "DuplicatedHistogram", 1, 101, 102, HistogramBase::kNoFlags);
     84 
     85   scoped_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
     86   EXPECT_EQ(2, samples->TotalCount());
     87   EXPECT_EQ(2, samples->GetCount(10));
     88 }
     89 
     90 TEST_F(HistogramTest, ExponentialRangesTest) {
     91   // Check that we got a nice exponential when there was enough rooom.
     92   BucketRanges ranges(9);
     93   Histogram::InitializeBucketRanges(1, 64, &ranges);
     94   EXPECT_EQ(0, ranges.range(0));
     95   int power_of_2 = 1;
     96   for (int i = 1; i < 8; i++) {
     97     EXPECT_EQ(power_of_2, ranges.range(i));
     98     power_of_2 *= 2;
     99   }
    100   EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8));
    101 
    102   // Check the corresponding Histogram will use the correct ranges.
    103   Histogram* histogram = static_cast<Histogram*>(
    104       Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags));
    105   EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges()));
    106 
    107   // When bucket count is limited, exponential ranges will partially look like
    108   // linear.
    109   BucketRanges ranges2(16);
    110   Histogram::InitializeBucketRanges(1, 32, &ranges2);
    111 
    112   EXPECT_EQ(0, ranges2.range(0));
    113   EXPECT_EQ(1, ranges2.range(1));
    114   EXPECT_EQ(2, ranges2.range(2));
    115   EXPECT_EQ(3, ranges2.range(3));
    116   EXPECT_EQ(4, ranges2.range(4));
    117   EXPECT_EQ(5, ranges2.range(5));
    118   EXPECT_EQ(6, ranges2.range(6));
    119   EXPECT_EQ(7, ranges2.range(7));
    120   EXPECT_EQ(9, ranges2.range(8));
    121   EXPECT_EQ(11, ranges2.range(9));
    122   EXPECT_EQ(14, ranges2.range(10));
    123   EXPECT_EQ(17, ranges2.range(11));
    124   EXPECT_EQ(21, ranges2.range(12));
    125   EXPECT_EQ(26, ranges2.range(13));
    126   EXPECT_EQ(32, ranges2.range(14));
    127   EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(15));
    128 
    129   // Check the corresponding Histogram will use the correct ranges.
    130   Histogram* histogram2 = static_cast<Histogram*>(
    131       Histogram::FactoryGet("Histogram2", 1, 32, 15, HistogramBase::kNoFlags));
    132   EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges()));
    133 }
    134 
    135 TEST_F(HistogramTest, LinearRangesTest) {
    136   BucketRanges ranges(9);
    137   LinearHistogram::InitializeBucketRanges(1, 7, &ranges);
    138   // Gets a nice linear set of bucket ranges.
    139   for (int i = 0; i < 8; i++)
    140     EXPECT_EQ(i, ranges.range(i));
    141   EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8));
    142 
    143   // The correspoding LinearHistogram should use the correct ranges.
    144   Histogram* histogram = static_cast<Histogram*>(
    145       LinearHistogram::FactoryGet("Linear", 1, 7, 8, HistogramBase::kNoFlags));
    146   EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges()));
    147 
    148   // Linear ranges are not divisible.
    149   BucketRanges ranges2(6);
    150   LinearHistogram::InitializeBucketRanges(1, 6, &ranges2);
    151   EXPECT_EQ(0, ranges2.range(0));
    152   EXPECT_EQ(1, ranges2.range(1));
    153   EXPECT_EQ(3, ranges2.range(2));
    154   EXPECT_EQ(4, ranges2.range(3));
    155   EXPECT_EQ(6, ranges2.range(4));
    156   EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(5));
    157   // The correspoding LinearHistogram should use the correct ranges.
    158   Histogram* histogram2 = static_cast<Histogram*>(
    159       LinearHistogram::FactoryGet("Linear2", 1, 6, 5, HistogramBase::kNoFlags));
    160   EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges()));
    161 }
    162 
    163 TEST_F(HistogramTest, ArrayToCustomRangesTest) {
    164   const HistogramBase::Sample ranges[3] = {5, 10, 20};
    165   vector<HistogramBase::Sample> ranges_vec =
    166       CustomHistogram::ArrayToCustomRanges(ranges, 3);
    167   ASSERT_EQ(6u, ranges_vec.size());
    168   EXPECT_EQ(5, ranges_vec[0]);
    169   EXPECT_EQ(6, ranges_vec[1]);
    170   EXPECT_EQ(10, ranges_vec[2]);
    171   EXPECT_EQ(11, ranges_vec[3]);
    172   EXPECT_EQ(20, ranges_vec[4]);
    173   EXPECT_EQ(21, ranges_vec[5]);
    174 }
    175 
    176 TEST_F(HistogramTest, CustomHistogramTest) {
    177   // A well prepared custom ranges.
    178   vector<HistogramBase::Sample> custom_ranges;
    179   custom_ranges.push_back(1);
    180   custom_ranges.push_back(2);
    181 
    182   Histogram* histogram = static_cast<Histogram*>(
    183       CustomHistogram::FactoryGet("TestCustomHistogram1", custom_ranges,
    184                                   HistogramBase::kNoFlags));
    185   const BucketRanges* ranges = histogram->bucket_ranges();
    186   ASSERT_EQ(4u, ranges->size());
    187   EXPECT_EQ(0, ranges->range(0));  // Auto added.
    188   EXPECT_EQ(1, ranges->range(1));
    189   EXPECT_EQ(2, ranges->range(2));
    190   EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3));  // Auto added.
    191 
    192   // A unordered custom ranges.
    193   custom_ranges.clear();
    194   custom_ranges.push_back(2);
    195   custom_ranges.push_back(1);
    196   histogram = static_cast<Histogram*>(
    197       CustomHistogram::FactoryGet("TestCustomHistogram2", custom_ranges,
    198                                   HistogramBase::kNoFlags));
    199   ranges = histogram->bucket_ranges();
    200   ASSERT_EQ(4u, ranges->size());
    201   EXPECT_EQ(0, ranges->range(0));
    202   EXPECT_EQ(1, ranges->range(1));
    203   EXPECT_EQ(2, ranges->range(2));
    204   EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3));
    205 
    206   // A custom ranges with duplicated values.
    207   custom_ranges.clear();
    208   custom_ranges.push_back(4);
    209   custom_ranges.push_back(1);
    210   custom_ranges.push_back(4);
    211   histogram = static_cast<Histogram*>(
    212       CustomHistogram::FactoryGet("TestCustomHistogram3", custom_ranges,
    213                                   HistogramBase::kNoFlags));
    214   ranges = histogram->bucket_ranges();
    215   ASSERT_EQ(4u, ranges->size());
    216   EXPECT_EQ(0, ranges->range(0));
    217   EXPECT_EQ(1, ranges->range(1));
    218   EXPECT_EQ(4, ranges->range(2));
    219   EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3));
    220 }
    221 
    222 TEST_F(HistogramTest, CustomHistogramWithOnly2Buckets) {
    223   // This test exploits the fact that the CustomHistogram can have 2 buckets,
    224   // while the base class Histogram is *supposed* to have at least 3 buckets.
    225   // We should probably change the restriction on the base class (or not inherit
    226   // the base class!).
    227 
    228   vector<HistogramBase::Sample> custom_ranges;
    229   custom_ranges.push_back(4);
    230 
    231   Histogram* histogram = static_cast<Histogram*>(
    232       CustomHistogram::FactoryGet("2BucketsCustomHistogram", custom_ranges,
    233                                   HistogramBase::kNoFlags));
    234   const BucketRanges* ranges = histogram->bucket_ranges();
    235   ASSERT_EQ(3u, ranges->size());
    236   EXPECT_EQ(0, ranges->range(0));
    237   EXPECT_EQ(4, ranges->range(1));
    238   EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2));
    239 }
    240 
    241 // Make sure histogram handles out-of-bounds data gracefully.
    242 TEST_F(HistogramTest, BoundsTest) {
    243   const size_t kBucketCount = 50;
    244   Histogram* histogram = static_cast<Histogram*>(
    245       Histogram::FactoryGet("Bounded", 10, 100, kBucketCount,
    246                             HistogramBase::kNoFlags));
    247 
    248   // Put two samples "out of bounds" above and below.
    249   histogram->Add(5);
    250   histogram->Add(-50);
    251 
    252   histogram->Add(100);
    253   histogram->Add(10000);
    254 
    255   // Verify they landed in the underflow, and overflow buckets.
    256   scoped_ptr<SampleVector> samples = histogram->SnapshotSampleVector();
    257   EXPECT_EQ(2, samples->GetCountAtIndex(0));
    258   EXPECT_EQ(0, samples->GetCountAtIndex(1));
    259   size_t array_size = histogram->bucket_count();
    260   EXPECT_EQ(kBucketCount, array_size);
    261   EXPECT_EQ(0, samples->GetCountAtIndex(array_size - 2));
    262   EXPECT_EQ(2, samples->GetCountAtIndex(array_size - 1));
    263 
    264   vector<int> custom_ranges;
    265   custom_ranges.push_back(10);
    266   custom_ranges.push_back(50);
    267   custom_ranges.push_back(100);
    268   Histogram* test_custom_histogram = static_cast<Histogram*>(
    269       CustomHistogram::FactoryGet("TestCustomRangeBoundedHistogram",
    270                                   custom_ranges, HistogramBase::kNoFlags));
    271 
    272   // Put two samples "out of bounds" above and below.
    273   test_custom_histogram->Add(5);
    274   test_custom_histogram->Add(-50);
    275   test_custom_histogram->Add(100);
    276   test_custom_histogram->Add(1000);
    277   test_custom_histogram->Add(INT_MAX);
    278 
    279   // Verify they landed in the underflow, and overflow buckets.
    280   scoped_ptr<SampleVector> custom_samples =
    281       test_custom_histogram->SnapshotSampleVector();
    282   EXPECT_EQ(2, custom_samples->GetCountAtIndex(0));
    283   EXPECT_EQ(0, custom_samples->GetCountAtIndex(1));
    284   size_t bucket_count = test_custom_histogram->bucket_count();
    285   EXPECT_EQ(0, custom_samples->GetCountAtIndex(bucket_count - 2));
    286   EXPECT_EQ(3, custom_samples->GetCountAtIndex(bucket_count - 1));
    287 }
    288 
    289 // Check to be sure samples land as expected is "correct" buckets.
    290 TEST_F(HistogramTest, BucketPlacementTest) {
    291   Histogram* histogram = static_cast<Histogram*>(
    292       Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags));
    293 
    294   // Add i+1 samples to the i'th bucket.
    295   histogram->Add(0);
    296   int power_of_2 = 1;
    297   for (int i = 1; i < 8; i++) {
    298     for (int j = 0; j <= i; j++)
    299       histogram->Add(power_of_2);
    300     power_of_2 *= 2;
    301   }
    302 
    303   // Check to see that the bucket counts reflect our additions.
    304   scoped_ptr<SampleVector> samples = histogram->SnapshotSampleVector();
    305   for (int i = 0; i < 8; i++)
    306     EXPECT_EQ(i + 1, samples->GetCountAtIndex(i));
    307 }
    308 
    309 TEST_F(HistogramTest, CorruptSampleCounts) {
    310   Histogram* histogram = static_cast<Histogram*>(
    311       Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags));
    312 
    313   // Add some samples.
    314   histogram->Add(20);
    315   histogram->Add(40);
    316 
    317   scoped_ptr<SampleVector> snapshot = histogram->SnapshotSampleVector();
    318   EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES,
    319             histogram->FindCorruption(*snapshot));
    320   EXPECT_EQ(2, snapshot->redundant_count());
    321   EXPECT_EQ(2, snapshot->TotalCount());
    322 
    323   snapshot->counts_[3] += 100;  // Sample count won't match redundant count.
    324   EXPECT_EQ(HistogramBase::COUNT_LOW_ERROR,
    325             histogram->FindCorruption(*snapshot));
    326   snapshot->counts_[2] -= 200;
    327   EXPECT_EQ(HistogramBase::COUNT_HIGH_ERROR,
    328             histogram->FindCorruption(*snapshot));
    329 
    330   // But we can't spot a corruption if it is compensated for.
    331   snapshot->counts_[1] += 100;
    332   EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES,
    333             histogram->FindCorruption(*snapshot));
    334 }
    335 
    336 TEST_F(HistogramTest, CorruptBucketBounds) {
    337   Histogram* histogram = static_cast<Histogram*>(
    338       Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags));
    339 
    340   scoped_ptr<SampleVector> snapshot = histogram->SnapshotSampleVector();
    341   EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES,
    342             histogram->FindCorruption(*snapshot));
    343 
    344   BucketRanges* bucket_ranges =
    345       const_cast<BucketRanges*>(histogram->bucket_ranges());
    346   HistogramBase::Sample tmp = bucket_ranges->range(1);
    347   bucket_ranges->set_range(1, bucket_ranges->range(2));
    348   bucket_ranges->set_range(2, tmp);
    349   EXPECT_EQ(
    350       HistogramBase::BUCKET_ORDER_ERROR | HistogramBase::RANGE_CHECKSUM_ERROR,
    351       histogram->FindCorruption(*snapshot));
    352 
    353   bucket_ranges->set_range(2, bucket_ranges->range(1));
    354   bucket_ranges->set_range(1, tmp);
    355   EXPECT_EQ(0, histogram->FindCorruption(*snapshot));
    356 
    357   // Show that two simple changes don't offset each other
    358   bucket_ranges->set_range(3, bucket_ranges->range(3) + 1);
    359   EXPECT_EQ(HistogramBase::RANGE_CHECKSUM_ERROR,
    360             histogram->FindCorruption(*snapshot));
    361 
    362   bucket_ranges->set_range(4, bucket_ranges->range(4) - 1);
    363   EXPECT_EQ(HistogramBase::RANGE_CHECKSUM_ERROR,
    364             histogram->FindCorruption(*snapshot));
    365 
    366   // Repair histogram so that destructor won't DCHECK().
    367   bucket_ranges->set_range(3, bucket_ranges->range(3) - 1);
    368   bucket_ranges->set_range(4, bucket_ranges->range(4) + 1);
    369 }
    370 
    371 TEST_F(HistogramTest, HistogramSerializeInfo) {
    372   Histogram* histogram = static_cast<Histogram*>(
    373       Histogram::FactoryGet("Histogram", 1, 64, 8,
    374                             HistogramBase::kIPCSerializationSourceFlag));
    375   Pickle pickle;
    376   histogram->SerializeInfo(&pickle);
    377 
    378   PickleIterator iter(pickle);
    379 
    380   int type;
    381   EXPECT_TRUE(iter.ReadInt(&type));
    382   EXPECT_EQ(HISTOGRAM, type);
    383 
    384   std::string name;
    385   EXPECT_TRUE(iter.ReadString(&name));
    386   EXPECT_EQ("Histogram", name);
    387 
    388   int flag;
    389   EXPECT_TRUE(iter.ReadInt(&flag));
    390   EXPECT_EQ(HistogramBase::kIPCSerializationSourceFlag, flag);
    391 
    392   int min;
    393   EXPECT_TRUE(iter.ReadInt(&min));
    394   EXPECT_EQ(1, min);
    395 
    396   int max;
    397   EXPECT_TRUE(iter.ReadInt(&max));
    398   EXPECT_EQ(64, max);
    399 
    400   int64 bucket_count;
    401   EXPECT_TRUE(iter.ReadInt64(&bucket_count));
    402   EXPECT_EQ(8, bucket_count);
    403 
    404   uint32 checksum;
    405   EXPECT_TRUE(iter.ReadUInt32(&checksum));
    406   EXPECT_EQ(histogram->bucket_ranges()->checksum(), checksum);
    407 
    408   // No more data in the pickle.
    409   EXPECT_FALSE(iter.SkipBytes(1));
    410 }
    411 
    412 TEST_F(HistogramTest, CustomHistogramSerializeInfo) {
    413   vector<int> custom_ranges;
    414   custom_ranges.push_back(10);
    415   custom_ranges.push_back(100);
    416 
    417   HistogramBase* custom_histogram = CustomHistogram::FactoryGet(
    418       "TestCustomRangeBoundedHistogram",
    419       custom_ranges,
    420       HistogramBase::kNoFlags);
    421   Pickle pickle;
    422   custom_histogram->SerializeInfo(&pickle);
    423 
    424   // Validate the pickle.
    425   PickleIterator iter(pickle);
    426 
    427   int i;
    428   std::string s;
    429   int64 bucket_count;
    430   uint32 ui32;
    431   EXPECT_TRUE(iter.ReadInt(&i) && iter.ReadString(&s) && iter.ReadInt(&i) &&
    432               iter.ReadInt(&i) && iter.ReadInt(&i) &&
    433               iter.ReadInt64(&bucket_count) && iter.ReadUInt32(&ui32));
    434   EXPECT_EQ(3, bucket_count);
    435 
    436   int range;
    437   EXPECT_TRUE(iter.ReadInt(&range));
    438   EXPECT_EQ(10, range);
    439   EXPECT_TRUE(iter.ReadInt(&range));
    440   EXPECT_EQ(100, range);
    441 
    442   // No more data in the pickle.
    443   EXPECT_FALSE(iter.SkipBytes(1));
    444 }
    445 
    446 #if GTEST_HAS_DEATH_TEST
    447 // For Histogram, LinearHistogram and CustomHistogram, the minimum for a
    448 // declared range is 1, while the maximum is (HistogramBase::kSampleType_MAX -
    449 // 1). But we accept ranges exceeding those limits, and silently clamped to
    450 // those limits. This is for backwards compatibility.
    451 TEST(HistogramDeathTest, BadRangesTest) {
    452   HistogramBase* histogram = Histogram::FactoryGet(
    453       "BadRanges", 0, HistogramBase::kSampleType_MAX, 8,
    454       HistogramBase::kNoFlags);
    455   EXPECT_TRUE(
    456       histogram->HasConstructionArguments(
    457           1, HistogramBase::kSampleType_MAX - 1, 8));
    458 
    459   HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
    460       "BadRangesLinear", 0, HistogramBase::kSampleType_MAX, 8,
    461       HistogramBase::kNoFlags);
    462   EXPECT_TRUE(
    463       linear_histogram->HasConstructionArguments(
    464           1, HistogramBase::kSampleType_MAX - 1, 8));
    465 
    466   vector<int> custom_ranges;
    467   custom_ranges.push_back(0);
    468   custom_ranges.push_back(5);
    469   Histogram* custom_histogram = static_cast<Histogram*>(
    470       CustomHistogram::FactoryGet(
    471           "BadRangesCustom", custom_ranges, HistogramBase::kNoFlags));
    472   const BucketRanges* ranges = custom_histogram->bucket_ranges();
    473   ASSERT_EQ(3u, ranges->size());
    474   EXPECT_EQ(0, ranges->range(0));
    475   EXPECT_EQ(5, ranges->range(1));
    476   EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2));
    477 
    478   // CustomHistogram does not accepts kSampleType_MAX as range.
    479   custom_ranges.push_back(HistogramBase::kSampleType_MAX);
    480   EXPECT_DEATH(CustomHistogram::FactoryGet("BadRangesCustom2", custom_ranges,
    481                                            HistogramBase::kNoFlags),
    482                "");
    483 
    484   // CustomHistogram needs at least 1 valid range.
    485   custom_ranges.clear();
    486   custom_ranges.push_back(0);
    487   EXPECT_DEATH(CustomHistogram::FactoryGet("BadRangesCustom3", custom_ranges,
    488                                            HistogramBase::kNoFlags),
    489                "");
    490 }
    491 #endif
    492 
    493 }  // namespace base
    494