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
20namespace 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.
25class 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.
88INSTANTIATE_TEST_CASE_P(HeapAndPersistent,
89                        SparseHistogramTest,
90                        testing::Bool());
91
92
93TEST_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
112TEST_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
131TEST_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
151TEST_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
177TEST_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
195TEST_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.
222TEST_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
269TEST_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