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 #include "base/metrics/sparse_histogram.h"
      6 
      7 #include <memory>
      8 #include <string>
      9 
     10 #include "base/metrics/histogram_base.h"
     11 #include "base/metrics/histogram_samples.h"
     12 #include "base/metrics/persistent_histogram_allocator.h"
     13 #include "base/metrics/persistent_memory_allocator.h"
     14 #include "base/metrics/sample_map.h"
     15 #include "base/metrics/statistics_recorder.h"
     16 #include "base/pickle.h"
     17 #include "base/strings/stringprintf.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 namespace base {
     21 
     22 // Test parameter indicates if a persistent memory allocator should be used
     23 // for histogram allocation. False will allocate histograms from the process
     24 // heap.
     25 class SparseHistogramTest : public testing::TestWithParam<bool> {
     26  protected:
     27   const int32_t kAllocatorMemorySize = 8 << 20;  // 8 MiB
     28 
     29   SparseHistogramTest() : use_persistent_histogram_allocator_(GetParam()) {}
     30 
     31   void SetUp() override {
     32     if (use_persistent_histogram_allocator_)
     33       CreatePersistentMemoryAllocator();
     34 
     35     // Each test will have a clean state (no Histogram / BucketRanges
     36     // registered).
     37     InitializeStatisticsRecorder();
     38   }
     39 
     40   void TearDown() override {
     41     if (allocator_) {
     42       ASSERT_FALSE(allocator_->IsFull());
     43       ASSERT_FALSE(allocator_->IsCorrupt());
     44     }
     45     UninitializeStatisticsRecorder();
     46     DestroyPersistentMemoryAllocator();
     47   }
     48 
     49   void InitializeStatisticsRecorder() {
     50     DCHECK(!statistics_recorder_);
     51     statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting();
     52   }
     53 
     54   void UninitializeStatisticsRecorder() {
     55     statistics_recorder_.reset();
     56   }
     57 
     58   void CreatePersistentMemoryAllocator() {
     59     // By getting the results-histogram before any persistent allocator
     60     // is attached, that histogram is guaranteed not to be stored in
     61     // any persistent memory segment (which simplifies some tests).
     62     GlobalHistogramAllocator::GetCreateHistogramResultHistogram();
     63 
     64     GlobalHistogramAllocator::CreateWithLocalMemory(
     65         kAllocatorMemorySize, 0, "SparseHistogramAllocatorTest");
     66     allocator_ = GlobalHistogramAllocator::Get()->memory_allocator();
     67   }
     68 
     69   void DestroyPersistentMemoryAllocator() {
     70     allocator_ = nullptr;
     71     GlobalHistogramAllocator::ReleaseForTesting();
     72   }
     73 
     74   std::unique_ptr<SparseHistogram> NewSparseHistogram(const std::string& name) {
     75     return std::unique_ptr<SparseHistogram>(new SparseHistogram(name));
     76   }
     77 
     78   const bool use_persistent_histogram_allocator_;
     79 
     80   std::unique_ptr<StatisticsRecorder> statistics_recorder_;
     81   PersistentMemoryAllocator* allocator_ = nullptr;
     82 
     83  private:
     84   DISALLOW_COPY_AND_ASSIGN(SparseHistogramTest);
     85 };
     86 
     87 // Run all HistogramTest cases with both heap and persistent memory.
     88 INSTANTIATE_TEST_CASE_P(HeapAndPersistent,
     89                         SparseHistogramTest,
     90                         testing::Bool());
     91 
     92 
     93 TEST_P(SparseHistogramTest, BasicTest) {
     94   std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
     95   std::unique_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
     96   EXPECT_EQ(0, snapshot->TotalCount());
     97   EXPECT_EQ(0, snapshot->sum());
     98 
     99   histogram->Add(100);
    100   std::unique_ptr<HistogramSamples> snapshot1(histogram->SnapshotSamples());
    101   EXPECT_EQ(1, snapshot1->TotalCount());
    102   EXPECT_EQ(1, snapshot1->GetCount(100));
    103 
    104   histogram->Add(100);
    105   histogram->Add(101);
    106   std::unique_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
    107   EXPECT_EQ(3, snapshot2->TotalCount());
    108   EXPECT_EQ(2, snapshot2->GetCount(100));
    109   EXPECT_EQ(1, snapshot2->GetCount(101));
    110 }
    111 
    112 TEST_P(SparseHistogramTest, BasicTestAddCount) {
    113   std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
    114   std::unique_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
    115   EXPECT_EQ(0, snapshot->TotalCount());
    116   EXPECT_EQ(0, snapshot->sum());
    117 
    118   histogram->AddCount(100, 15);
    119   std::unique_ptr<HistogramSamples> snapshot1(histogram->SnapshotSamples());
    120   EXPECT_EQ(15, snapshot1->TotalCount());
    121   EXPECT_EQ(15, snapshot1->GetCount(100));
    122 
    123   histogram->AddCount(100, 15);
    124   histogram->AddCount(101, 25);
    125   std::unique_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
    126   EXPECT_EQ(55, snapshot2->TotalCount());
    127   EXPECT_EQ(30, snapshot2->GetCount(100));
    128   EXPECT_EQ(25, snapshot2->GetCount(101));
    129 }
    130 
    131 TEST_P(SparseHistogramTest, AddCount_LargeValuesDontOverflow) {
    132   std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
    133   std::unique_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
    134   EXPECT_EQ(0, snapshot->TotalCount());
    135   EXPECT_EQ(0, snapshot->sum());
    136 
    137   histogram->AddCount(1000000000, 15);
    138   std::unique_ptr<HistogramSamples> snapshot1(histogram->SnapshotSamples());
    139   EXPECT_EQ(15, snapshot1->TotalCount());
    140   EXPECT_EQ(15, snapshot1->GetCount(1000000000));
    141 
    142   histogram->AddCount(1000000000, 15);
    143   histogram->AddCount(1010000000, 25);
    144   std::unique_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
    145   EXPECT_EQ(55, snapshot2->TotalCount());
    146   EXPECT_EQ(30, snapshot2->GetCount(1000000000));
    147   EXPECT_EQ(25, snapshot2->GetCount(1010000000));
    148   EXPECT_EQ(55250000000LL, snapshot2->sum());
    149 }
    150 
    151 TEST_P(SparseHistogramTest, MacroBasicTest) {
    152   UMA_HISTOGRAM_SPARSE_SLOWLY("Sparse", 100);
    153   UMA_HISTOGRAM_SPARSE_SLOWLY("Sparse", 200);
    154   UMA_HISTOGRAM_SPARSE_SLOWLY("Sparse", 100);
    155 
    156   StatisticsRecorder::Histograms histograms;
    157   StatisticsRecorder::GetHistograms(&histograms);
    158 
    159   ASSERT_EQ(1U, histograms.size());
    160   HistogramBase* sparse_histogram = histograms[0];
    161 
    162   EXPECT_EQ(SPARSE_HISTOGRAM, sparse_histogram->GetHistogramType());
    163   EXPECT_EQ("Sparse", sparse_histogram->histogram_name());
    164   EXPECT_EQ(
    165       HistogramBase::kUmaTargetedHistogramFlag |
    166           (use_persistent_histogram_allocator_ ? HistogramBase::kIsPersistent
    167                                                : 0),
    168       sparse_histogram->flags());
    169 
    170   std::unique_ptr<HistogramSamples> samples =
    171       sparse_histogram->SnapshotSamples();
    172   EXPECT_EQ(3, samples->TotalCount());
    173   EXPECT_EQ(2, samples->GetCount(100));
    174   EXPECT_EQ(1, samples->GetCount(200));
    175 }
    176 
    177 TEST_P(SparseHistogramTest, MacroInLoopTest) {
    178   // Unlike the macros in histogram.h, SparseHistogram macros can have a
    179   // variable as histogram name.
    180   for (int i = 0; i < 2; i++) {
    181     std::string name = StringPrintf("Sparse%d", i + 1);
    182     UMA_HISTOGRAM_SPARSE_SLOWLY(name, 100);
    183   }
    184 
    185   StatisticsRecorder::Histograms histograms;
    186   StatisticsRecorder::GetHistograms(&histograms);
    187   ASSERT_EQ(2U, histograms.size());
    188 
    189   std::string name1 = histograms[0]->histogram_name();
    190   std::string name2 = histograms[1]->histogram_name();
    191   EXPECT_TRUE(("Sparse1" == name1 && "Sparse2" == name2) ||
    192               ("Sparse2" == name1 && "Sparse1" == name2));
    193 }
    194 
    195 TEST_P(SparseHistogramTest, Serialize) {
    196   std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
    197   histogram->SetFlags(HistogramBase::kIPCSerializationSourceFlag);
    198 
    199   Pickle pickle;
    200   histogram->SerializeInfo(&pickle);
    201 
    202   PickleIterator iter(pickle);
    203 
    204   int type;
    205   EXPECT_TRUE(iter.ReadInt(&type));
    206   EXPECT_EQ(SPARSE_HISTOGRAM, type);
    207 
    208   std::string name;
    209   EXPECT_TRUE(iter.ReadString(&name));
    210   EXPECT_EQ("Sparse", name);
    211 
    212   int flag;
    213   EXPECT_TRUE(iter.ReadInt(&flag));
    214   EXPECT_EQ(HistogramBase::kIPCSerializationSourceFlag, flag);
    215 
    216   // No more data in the pickle.
    217   EXPECT_FALSE(iter.SkipBytes(1));
    218 }
    219 
    220 // Ensure that race conditions that cause multiple, identical sparse histograms
    221 // to be created will safely resolve to a single one.
    222 TEST_P(SparseHistogramTest, DuplicationSafety) {
    223   const char histogram_name[] = "Duplicated";
    224   size_t histogram_count = StatisticsRecorder::GetHistogramCount();
    225 
    226   // Create a histogram that we will later duplicate.
    227   HistogramBase* original =
    228       SparseHistogram::FactoryGet(histogram_name, HistogramBase::kNoFlags);
    229   ++histogram_count;
    230   DCHECK_EQ(histogram_count, StatisticsRecorder::GetHistogramCount());
    231   original->Add(1);
    232 
    233   // Create a duplicate. This has to happen differently depending on where the
    234   // memory is taken from.
    235   if (use_persistent_histogram_allocator_) {
    236     // To allocate from persistent memory, clear the last_created reference in
    237     // the GlobalHistogramAllocator. This will cause an Import to recreate
    238     // the just-created histogram which will then be released as a duplicate.
    239     GlobalHistogramAllocator::Get()->ClearLastCreatedReferenceForTesting();
    240     // Creating a different histogram will first do an Import to ensure it
    241     // hasn't been created elsewhere, triggering the duplication and release.
    242     SparseHistogram::FactoryGet("something.new", HistogramBase::kNoFlags);
    243     ++histogram_count;
    244   } else {
    245     // To allocate from the heap, just call the (private) constructor directly.
    246     // Delete it immediately like would have happened within FactoryGet();
    247     std::unique_ptr<SparseHistogram> something =
    248         NewSparseHistogram(histogram_name);
    249     DCHECK_NE(original, something.get());
    250   }
    251   DCHECK_EQ(histogram_count, StatisticsRecorder::GetHistogramCount());
    252 
    253   // Re-creating the histogram via FactoryGet() will return the same one.
    254   HistogramBase* duplicate =
    255       SparseHistogram::FactoryGet(histogram_name, HistogramBase::kNoFlags);
    256   DCHECK_EQ(original, duplicate);
    257   DCHECK_EQ(histogram_count, StatisticsRecorder::GetHistogramCount());
    258   duplicate->Add(2);
    259 
    260   // Ensure that original histograms are still cross-functional.
    261   original->Add(2);
    262   duplicate->Add(1);
    263   std::unique_ptr<HistogramSamples> snapshot_orig = original->SnapshotSamples();
    264   std::unique_ptr<HistogramSamples> snapshot_dup = duplicate->SnapshotSamples();
    265   DCHECK_EQ(2, snapshot_orig->GetCount(2));
    266   DCHECK_EQ(2, snapshot_dup->GetCount(1));
    267 }
    268 
    269 TEST_P(SparseHistogramTest, FactoryTime) {
    270   const int kTestCreateCount = 1 << 10;  // Must be power-of-2.
    271   const int kTestLookupCount = 100000;
    272   const int kTestAddCount = 100000;
    273 
    274   // Create all histogram names in advance for accurate timing below.
    275   std::vector<std::string> histogram_names;
    276   for (int i = 0; i < kTestCreateCount; ++i) {
    277     histogram_names.push_back(
    278         StringPrintf("TestHistogram.%d", i % kTestCreateCount));
    279   }
    280 
    281   // Calculate cost of creating histograms.
    282   TimeTicks create_start = TimeTicks::Now();
    283   for (int i = 0; i < kTestCreateCount; ++i)
    284     SparseHistogram::FactoryGet(histogram_names[i], HistogramBase::kNoFlags);
    285   TimeDelta create_ticks = TimeTicks::Now() - create_start;
    286   int64_t create_ms = create_ticks.InMilliseconds();
    287 
    288   VLOG(1) << kTestCreateCount << " histogram creations took " << create_ms
    289           << "ms or about "
    290           << (create_ms * 1000000) / kTestCreateCount
    291           << "ns each.";
    292 
    293   // Calculate cost of looking up existing histograms.
    294   TimeTicks lookup_start = TimeTicks::Now();
    295   for (int i = 0; i < kTestLookupCount; ++i) {
    296     // 6007 is co-prime with kTestCreateCount and so will do lookups in an
    297     // order less likely to be cacheable (but still hit them all) should the
    298     // underlying storage use the exact histogram name as the key.
    299     const int i_mult = 6007;
    300     static_assert(i_mult < INT_MAX / kTestCreateCount, "Multiplier too big");
    301     int index = (i * i_mult) & (kTestCreateCount - 1);
    302     SparseHistogram::FactoryGet(histogram_names[index],
    303                                 HistogramBase::kNoFlags);
    304   }
    305   TimeDelta lookup_ticks = TimeTicks::Now() - lookup_start;
    306   int64_t lookup_ms = lookup_ticks.InMilliseconds();
    307 
    308   VLOG(1) << kTestLookupCount << " histogram lookups took " << lookup_ms
    309           << "ms or about "
    310           << (lookup_ms * 1000000) / kTestLookupCount
    311           << "ns each.";
    312 
    313   // Calculate cost of accessing histograms.
    314   HistogramBase* histogram =
    315       SparseHistogram::FactoryGet(histogram_names[0], HistogramBase::kNoFlags);
    316   ASSERT_TRUE(histogram);
    317   TimeTicks add_start = TimeTicks::Now();
    318   for (int i = 0; i < kTestAddCount; ++i)
    319     histogram->Add(i & 127);
    320   TimeDelta add_ticks = TimeTicks::Now() - add_start;
    321   int64_t add_ms = add_ticks.InMilliseconds();
    322 
    323   VLOG(1) << kTestAddCount << " histogram adds took " << add_ms
    324           << "ms or about "
    325           << (add_ms * 1000000) / kTestAddCount
    326           << "ns each.";
    327 }
    328 
    329 }  // namespace base
    330