• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SparseHistogramTest()29   SparseHistogramTest() : use_persistent_histogram_allocator_(GetParam()) {}
30 
SetUp()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 
TearDown()40   void TearDown() override {
41     if (allocator_) {
42       ASSERT_FALSE(allocator_->IsFull());
43       ASSERT_FALSE(allocator_->IsCorrupt());
44     }
45     UninitializeStatisticsRecorder();
46     DestroyPersistentMemoryAllocator();
47   }
48 
InitializeStatisticsRecorder()49   void InitializeStatisticsRecorder() {
50     DCHECK(!statistics_recorder_);
51     statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting();
52   }
53 
UninitializeStatisticsRecorder()54   void UninitializeStatisticsRecorder() {
55     statistics_recorder_.reset();
56   }
57 
CreatePersistentMemoryAllocator()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 
DestroyPersistentMemoryAllocator()69   void DestroyPersistentMemoryAllocator() {
70     allocator_ = nullptr;
71     GlobalHistogramAllocator::ReleaseForTesting();
72   }
73 
NewSparseHistogram(const std::string & name)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 
TEST_P(SparseHistogramTest,BasicTest)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 
TEST_P(SparseHistogramTest,BasicTestAddCount)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 
TEST_P(SparseHistogramTest,AddCount_LargeValuesDontOverflow)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 
TEST_P(SparseHistogramTest,MacroBasicTest)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 
TEST_P(SparseHistogramTest,MacroInLoopTest)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 
TEST_P(SparseHistogramTest,Serialize)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.
TEST_P(SparseHistogramTest,DuplicationSafety)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 
TEST_P(SparseHistogramTest,FactoryTime)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