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/histogram.h"
6
7 #include <limits.h>
8 #include <stddef.h>
9 #include <stdint.h>
10
11 #include <climits>
12 #include <memory>
13 #include <string>
14 #include <vector>
15
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/metrics/bucket_ranges.h"
19 #include "base/metrics/dummy_histogram.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/metrics/metrics_hashes.h"
22 #include "base/metrics/persistent_histogram_allocator.h"
23 #include "base/metrics/persistent_memory_allocator.h"
24 #include "base/metrics/record_histogram_checker.h"
25 #include "base/metrics/sample_vector.h"
26 #include "base/metrics/statistics_recorder.h"
27 #include "base/pickle.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/test/gtest_util.h"
30 #include "base/time/time.h"
31 #include "base/values.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34
35 namespace base {
36 namespace {
37
38 const char kExpiredHistogramName[] = "ExpiredHistogram";
39
40 // Test implementation of RecordHistogramChecker interface.
41 class TestRecordHistogramChecker : public RecordHistogramChecker {
42 public:
43 ~TestRecordHistogramChecker() override = default;
44
45 // RecordHistogramChecker:
ShouldRecord(uint64_t histogram_hash) const46 bool ShouldRecord(uint64_t histogram_hash) const override {
47 return histogram_hash != HashMetricName(kExpiredHistogramName);
48 }
49 };
50
51 } // namespace
52
53 // Test parameter indicates if a persistent memory allocator should be used
54 // for histogram allocation. False will allocate histograms from the process
55 // heap.
56 class HistogramTest : public testing::TestWithParam<bool> {
57 protected:
58 const int32_t kAllocatorMemorySize = 8 << 20; // 8 MiB
59
HistogramTest()60 HistogramTest() : use_persistent_histogram_allocator_(GetParam()) {}
61
SetUp()62 void SetUp() override {
63 if (use_persistent_histogram_allocator_)
64 CreatePersistentHistogramAllocator();
65
66 // Each test will have a clean state (no Histogram / BucketRanges
67 // registered).
68 InitializeStatisticsRecorder();
69 }
70
TearDown()71 void TearDown() override {
72 if (allocator_) {
73 ASSERT_FALSE(allocator_->IsFull());
74 ASSERT_FALSE(allocator_->IsCorrupt());
75 }
76 UninitializeStatisticsRecorder();
77 DestroyPersistentHistogramAllocator();
78 }
79
InitializeStatisticsRecorder()80 void InitializeStatisticsRecorder() {
81 DCHECK(!statistics_recorder_);
82 statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting();
83 }
84
UninitializeStatisticsRecorder()85 void UninitializeStatisticsRecorder() {
86 statistics_recorder_.reset();
87 }
88
CreatePersistentHistogramAllocator()89 void CreatePersistentHistogramAllocator() {
90 GlobalHistogramAllocator::CreateWithLocalMemory(
91 kAllocatorMemorySize, 0, "HistogramAllocatorTest");
92 allocator_ = GlobalHistogramAllocator::Get()->memory_allocator();
93 }
94
DestroyPersistentHistogramAllocator()95 void DestroyPersistentHistogramAllocator() {
96 allocator_ = nullptr;
97 GlobalHistogramAllocator::ReleaseForTesting();
98 }
99
SnapshotAllSamples(Histogram * h)100 std::unique_ptr<SampleVector> SnapshotAllSamples(Histogram* h) {
101 return h->SnapshotAllSamples();
102 }
103
GetCountAndBucketData(Histogram * histogram,base::Histogram::Count * count,int64_t * sum,base::ListValue * buckets)104 void GetCountAndBucketData(Histogram* histogram,
105 base::Histogram::Count* count,
106 int64_t* sum,
107 base::ListValue* buckets) {
108 // A simple wrapper around |GetCountAndBucketData| to make it visible for
109 // testing.
110 histogram->GetCountAndBucketData(count, sum, buckets);
111 }
112
113 const bool use_persistent_histogram_allocator_;
114
115 std::unique_ptr<StatisticsRecorder> statistics_recorder_;
116 std::unique_ptr<char[]> allocator_memory_;
117 PersistentMemoryAllocator* allocator_ = nullptr;
118
119 private:
120 DISALLOW_COPY_AND_ASSIGN(HistogramTest);
121 };
122
123 // Run all HistogramTest cases with both heap and persistent memory.
124 INSTANTIATE_TEST_SUITE_P(HeapAndPersistent, HistogramTest, testing::Bool());
125
126 // Check for basic syntax and use.
TEST_P(HistogramTest,BasicTest)127 TEST_P(HistogramTest, BasicTest) {
128 // Try basic construction
129 HistogramBase* histogram = Histogram::FactoryGet(
130 "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
131 EXPECT_TRUE(histogram);
132
133 HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
134 "TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
135 EXPECT_TRUE(linear_histogram);
136
137 std::vector<int> custom_ranges;
138 custom_ranges.push_back(1);
139 custom_ranges.push_back(5);
140 HistogramBase* custom_histogram = CustomHistogram::FactoryGet(
141 "TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags);
142 EXPECT_TRUE(custom_histogram);
143
144 // Macros that create histograms have an internal static variable which will
145 // continue to point to those from the very first run of this method even
146 // during subsequent runs.
147 static bool already_run = false;
148 if (already_run)
149 return;
150 already_run = true;
151
152 // Use standard macros (but with fixed samples)
153 LOCAL_HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1));
154 LOCAL_HISTOGRAM_COUNTS("Test3Histogram", 30);
155
156 LOCAL_HISTOGRAM_ENUMERATION("Test6Histogram", 129, 130);
157 }
158
159 // Check that the macro correctly matches histograms by name and records their
160 // data together.
TEST_P(HistogramTest,NameMatchTest)161 TEST_P(HistogramTest, NameMatchTest) {
162 // Macros that create histograms have an internal static variable which will
163 // continue to point to those from the very first run of this method even
164 // during subsequent runs.
165 static bool already_run = false;
166 if (already_run)
167 return;
168 already_run = true;
169
170 LOCAL_HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10);
171 LOCAL_HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10);
172 HistogramBase* histogram = LinearHistogram::FactoryGet(
173 "DuplicatedHistogram", 1, 101, 102, HistogramBase::kNoFlags);
174
175 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
176 EXPECT_EQ(2, samples->TotalCount());
177 EXPECT_EQ(2, samples->GetCount(10));
178 }
179
180 // Check that delta calculations work correctly.
TEST_P(HistogramTest,DeltaTest)181 TEST_P(HistogramTest, DeltaTest) {
182 HistogramBase* histogram =
183 Histogram::FactoryGet("DeltaHistogram", 1, 64, 8,
184 HistogramBase::kNoFlags);
185 histogram->Add(1);
186 histogram->Add(10);
187 histogram->Add(50);
188
189 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotDelta();
190 EXPECT_EQ(3, samples->TotalCount());
191 EXPECT_EQ(1, samples->GetCount(1));
192 EXPECT_EQ(1, samples->GetCount(10));
193 EXPECT_EQ(1, samples->GetCount(50));
194 EXPECT_EQ(samples->TotalCount(), samples->redundant_count());
195
196 samples = histogram->SnapshotDelta();
197 EXPECT_EQ(0, samples->TotalCount());
198
199 histogram->Add(10);
200 histogram->Add(10);
201 samples = histogram->SnapshotDelta();
202 EXPECT_EQ(2, samples->TotalCount());
203 EXPECT_EQ(2, samples->GetCount(10));
204
205 samples = histogram->SnapshotDelta();
206 EXPECT_EQ(0, samples->TotalCount());
207 }
208
209 // Check that final-delta calculations work correctly.
TEST_P(HistogramTest,FinalDeltaTest)210 TEST_P(HistogramTest, FinalDeltaTest) {
211 HistogramBase* histogram =
212 Histogram::FactoryGet("FinalDeltaHistogram", 1, 64, 8,
213 HistogramBase::kNoFlags);
214 histogram->Add(1);
215 histogram->Add(10);
216 histogram->Add(50);
217
218 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotDelta();
219 EXPECT_EQ(3, samples->TotalCount());
220 EXPECT_EQ(1, samples->GetCount(1));
221 EXPECT_EQ(1, samples->GetCount(10));
222 EXPECT_EQ(1, samples->GetCount(50));
223 EXPECT_EQ(samples->TotalCount(), samples->redundant_count());
224
225 histogram->Add(2);
226 histogram->Add(50);
227
228 samples = histogram->SnapshotFinalDelta();
229 EXPECT_EQ(2, samples->TotalCount());
230 EXPECT_EQ(1, samples->GetCount(2));
231 EXPECT_EQ(1, samples->GetCount(50));
232 EXPECT_EQ(samples->TotalCount(), samples->redundant_count());
233 }
234
TEST_P(HistogramTest,ExponentialRangesTest)235 TEST_P(HistogramTest, ExponentialRangesTest) {
236 // Check that we got a nice exponential when there was enough room.
237 BucketRanges ranges(9);
238 Histogram::InitializeBucketRanges(1, 64, &ranges);
239 EXPECT_EQ(0, ranges.range(0));
240 int power_of_2 = 1;
241 for (int i = 1; i < 8; i++) {
242 EXPECT_EQ(power_of_2, ranges.range(i));
243 power_of_2 *= 2;
244 }
245 EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8));
246
247 // Check the corresponding Histogram will use the correct ranges.
248 Histogram* histogram = static_cast<Histogram*>(
249 Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags));
250 EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges()));
251
252 // When bucket count is limited, exponential ranges will partially look like
253 // linear.
254 BucketRanges ranges2(16);
255 Histogram::InitializeBucketRanges(1, 32, &ranges2);
256
257 EXPECT_EQ(0, ranges2.range(0));
258 EXPECT_EQ(1, ranges2.range(1));
259 EXPECT_EQ(2, ranges2.range(2));
260 EXPECT_EQ(3, ranges2.range(3));
261 EXPECT_EQ(4, ranges2.range(4));
262 EXPECT_EQ(5, ranges2.range(5));
263 EXPECT_EQ(6, ranges2.range(6));
264 EXPECT_EQ(7, ranges2.range(7));
265 EXPECT_EQ(9, ranges2.range(8));
266 EXPECT_EQ(11, ranges2.range(9));
267 EXPECT_EQ(14, ranges2.range(10));
268 EXPECT_EQ(17, ranges2.range(11));
269 EXPECT_EQ(21, ranges2.range(12));
270 EXPECT_EQ(26, ranges2.range(13));
271 EXPECT_EQ(32, ranges2.range(14));
272 EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(15));
273
274 // Check the corresponding Histogram will use the correct ranges.
275 Histogram* histogram2 = static_cast<Histogram*>(
276 Histogram::FactoryGet("Histogram2", 1, 32, 15, HistogramBase::kNoFlags));
277 EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges()));
278 }
279
TEST_P(HistogramTest,LinearRangesTest)280 TEST_P(HistogramTest, LinearRangesTest) {
281 BucketRanges ranges(9);
282 LinearHistogram::InitializeBucketRanges(1, 7, &ranges);
283 // Gets a nice linear set of bucket ranges.
284 for (int i = 0; i < 8; i++)
285 EXPECT_EQ(i, ranges.range(i));
286 EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8));
287
288 // The correspoding LinearHistogram should use the correct ranges.
289 Histogram* histogram = static_cast<Histogram*>(
290 LinearHistogram::FactoryGet("Linear", 1, 7, 8, HistogramBase::kNoFlags));
291 EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges()));
292
293 // Linear ranges are not divisible.
294 BucketRanges ranges2(6);
295 LinearHistogram::InitializeBucketRanges(1, 6, &ranges2);
296 EXPECT_EQ(0, ranges2.range(0));
297 EXPECT_EQ(1, ranges2.range(1));
298 EXPECT_EQ(3, ranges2.range(2));
299 EXPECT_EQ(4, ranges2.range(3));
300 EXPECT_EQ(6, ranges2.range(4));
301 EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(5));
302 // The correspoding LinearHistogram should use the correct ranges.
303 Histogram* histogram2 = static_cast<Histogram*>(
304 LinearHistogram::FactoryGet("Linear2", 1, 6, 5, HistogramBase::kNoFlags));
305 EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges()));
306 }
307
TEST_P(HistogramTest,SingleValueEnumerationHistogram)308 TEST_P(HistogramTest, SingleValueEnumerationHistogram) {
309 // Make sure its possible to construct a linear histogram with only the two
310 // required outlier buckets (underflow and overflow).
311 HistogramBase* histogram = LinearHistogram::FactoryGet(
312 "SingleValueEnum", 1, 1, 2, HistogramBase::kNoFlags);
313 EXPECT_TRUE(histogram);
314
315 // Make sure the macros work properly. This can only be run when
316 // there is no persistent allocator which can be discarded and leave
317 // dangling pointers.
318 if (!use_persistent_histogram_allocator_) {
319 enum EnumWithMax {
320 kSomething = 0,
321 kMaxValue = kSomething,
322 };
323 UMA_HISTOGRAM_ENUMERATION("h1", kSomething);
324 }
325 }
326
TEST_P(HistogramTest,ArrayToCustomEnumRangesTest)327 TEST_P(HistogramTest, ArrayToCustomEnumRangesTest) {
328 const HistogramBase::Sample ranges[3] = {5, 10, 20};
329 std::vector<HistogramBase::Sample> ranges_vec =
330 CustomHistogram::ArrayToCustomEnumRanges(ranges);
331 ASSERT_EQ(6u, ranges_vec.size());
332 EXPECT_EQ(5, ranges_vec[0]);
333 EXPECT_EQ(6, ranges_vec[1]);
334 EXPECT_EQ(10, ranges_vec[2]);
335 EXPECT_EQ(11, ranges_vec[3]);
336 EXPECT_EQ(20, ranges_vec[4]);
337 EXPECT_EQ(21, ranges_vec[5]);
338 }
339
TEST_P(HistogramTest,CustomHistogramTest)340 TEST_P(HistogramTest, CustomHistogramTest) {
341 // A well prepared custom ranges.
342 std::vector<HistogramBase::Sample> custom_ranges;
343 custom_ranges.push_back(1);
344 custom_ranges.push_back(2);
345
346 Histogram* histogram = static_cast<Histogram*>(
347 CustomHistogram::FactoryGet("TestCustomHistogram1", custom_ranges,
348 HistogramBase::kNoFlags));
349 const BucketRanges* ranges = histogram->bucket_ranges();
350 ASSERT_EQ(4u, ranges->size());
351 EXPECT_EQ(0, ranges->range(0)); // Auto added.
352 EXPECT_EQ(1, ranges->range(1));
353 EXPECT_EQ(2, ranges->range(2));
354 EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); // Auto added.
355
356 // A unordered custom ranges.
357 custom_ranges.clear();
358 custom_ranges.push_back(2);
359 custom_ranges.push_back(1);
360 histogram = static_cast<Histogram*>(
361 CustomHistogram::FactoryGet("TestCustomHistogram2", custom_ranges,
362 HistogramBase::kNoFlags));
363 ranges = histogram->bucket_ranges();
364 ASSERT_EQ(4u, ranges->size());
365 EXPECT_EQ(0, ranges->range(0));
366 EXPECT_EQ(1, ranges->range(1));
367 EXPECT_EQ(2, ranges->range(2));
368 EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3));
369
370 // A custom ranges with duplicated values.
371 custom_ranges.clear();
372 custom_ranges.push_back(4);
373 custom_ranges.push_back(1);
374 custom_ranges.push_back(4);
375 histogram = static_cast<Histogram*>(
376 CustomHistogram::FactoryGet("TestCustomHistogram3", custom_ranges,
377 HistogramBase::kNoFlags));
378 ranges = histogram->bucket_ranges();
379 ASSERT_EQ(4u, ranges->size());
380 EXPECT_EQ(0, ranges->range(0));
381 EXPECT_EQ(1, ranges->range(1));
382 EXPECT_EQ(4, ranges->range(2));
383 EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3));
384 }
385
TEST_P(HistogramTest,CustomHistogramWithOnly2Buckets)386 TEST_P(HistogramTest, CustomHistogramWithOnly2Buckets) {
387 // This test exploits the fact that the CustomHistogram can have 2 buckets,
388 // while the base class Histogram is *supposed* to have at least 3 buckets.
389 // We should probably change the restriction on the base class (or not inherit
390 // the base class!).
391
392 std::vector<HistogramBase::Sample> custom_ranges;
393 custom_ranges.push_back(4);
394
395 Histogram* histogram = static_cast<Histogram*>(
396 CustomHistogram::FactoryGet("2BucketsCustomHistogram", custom_ranges,
397 HistogramBase::kNoFlags));
398 const BucketRanges* ranges = histogram->bucket_ranges();
399 ASSERT_EQ(3u, ranges->size());
400 EXPECT_EQ(0, ranges->range(0));
401 EXPECT_EQ(4, ranges->range(1));
402 EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2));
403 }
404
TEST_P(HistogramTest,AddCountTest)405 TEST_P(HistogramTest, AddCountTest) {
406 const size_t kBucketCount = 50;
407 Histogram* histogram = static_cast<Histogram*>(
408 Histogram::FactoryGet("AddCountHistogram", 10, 100, kBucketCount,
409 HistogramBase::kNoFlags));
410
411 histogram->AddCount(20, 15);
412 histogram->AddCount(30, 14);
413
414 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
415 EXPECT_EQ(29, samples->TotalCount());
416 EXPECT_EQ(15, samples->GetCount(20));
417 EXPECT_EQ(14, samples->GetCount(30));
418
419 histogram->AddCount(20, 25);
420 histogram->AddCount(30, 24);
421
422 std::unique_ptr<HistogramSamples> samples2 = histogram->SnapshotSamples();
423 EXPECT_EQ(78, samples2->TotalCount());
424 EXPECT_EQ(40, samples2->GetCount(20));
425 EXPECT_EQ(38, samples2->GetCount(30));
426 }
427
TEST_P(HistogramTest,AddCount_LargeValuesDontOverflow)428 TEST_P(HistogramTest, AddCount_LargeValuesDontOverflow) {
429 const size_t kBucketCount = 50;
430 Histogram* histogram = static_cast<Histogram*>(
431 Histogram::FactoryGet("AddCountHistogram", 10, 1000000000, kBucketCount,
432 HistogramBase::kNoFlags));
433
434 histogram->AddCount(200000000, 15);
435 histogram->AddCount(300000000, 14);
436
437 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
438 EXPECT_EQ(29, samples->TotalCount());
439 EXPECT_EQ(15, samples->GetCount(200000000));
440 EXPECT_EQ(14, samples->GetCount(300000000));
441
442 histogram->AddCount(200000000, 25);
443 histogram->AddCount(300000000, 24);
444
445 std::unique_ptr<HistogramSamples> samples2 = histogram->SnapshotSamples();
446 EXPECT_EQ(78, samples2->TotalCount());
447 EXPECT_EQ(40, samples2->GetCount(200000000));
448 EXPECT_EQ(38, samples2->GetCount(300000000));
449 EXPECT_EQ(19400000000LL, samples2->sum());
450 }
451
452 // Some metrics are designed so that they are guaranteed not to overflow between
453 // snapshots, but could overflow over a long-running session.
454 // Make sure that counts returned by Histogram::SnapshotDelta do not overflow
455 // even when a total count (returned by Histogram::SnapshotSample) does.
TEST_P(HistogramTest,AddCount_LargeCountsDontOverflow)456 TEST_P(HistogramTest, AddCount_LargeCountsDontOverflow) {
457 const size_t kBucketCount = 10;
458 Histogram* histogram = static_cast<Histogram*>(Histogram::FactoryGet(
459 "AddCountHistogram", 10, 50, kBucketCount, HistogramBase::kNoFlags));
460
461 const int count = (1 << 30) - 1;
462
463 // Repeat N times to make sure that there is no internal value overflow.
464 for (int i = 0; i < 10; ++i) {
465 histogram->AddCount(42, count);
466 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotDelta();
467 EXPECT_EQ(count, samples->TotalCount());
468 EXPECT_EQ(count, samples->GetCount(42));
469 }
470 }
471
472 // Make sure histogram handles out-of-bounds data gracefully.
TEST_P(HistogramTest,BoundsTest)473 TEST_P(HistogramTest, BoundsTest) {
474 const size_t kBucketCount = 50;
475 Histogram* histogram = static_cast<Histogram*>(
476 Histogram::FactoryGet("Bounded", 10, 100, kBucketCount,
477 HistogramBase::kNoFlags));
478
479 // Put two samples "out of bounds" above and below.
480 histogram->Add(5);
481 histogram->Add(-50);
482
483 histogram->Add(100);
484 histogram->Add(10000);
485
486 // Verify they landed in the underflow, and overflow buckets.
487 std::unique_ptr<SampleVector> samples = histogram->SnapshotAllSamples();
488 EXPECT_EQ(2, samples->GetCountAtIndex(0));
489 EXPECT_EQ(0, samples->GetCountAtIndex(1));
490 size_t array_size = histogram->bucket_count();
491 EXPECT_EQ(kBucketCount, array_size);
492 EXPECT_EQ(0, samples->GetCountAtIndex(array_size - 2));
493 EXPECT_EQ(2, samples->GetCountAtIndex(array_size - 1));
494
495 std::vector<int> custom_ranges;
496 custom_ranges.push_back(10);
497 custom_ranges.push_back(50);
498 custom_ranges.push_back(100);
499 Histogram* test_custom_histogram = static_cast<Histogram*>(
500 CustomHistogram::FactoryGet("TestCustomRangeBoundedHistogram",
501 custom_ranges, HistogramBase::kNoFlags));
502
503 // Put two samples "out of bounds" above and below.
504 test_custom_histogram->Add(5);
505 test_custom_histogram->Add(-50);
506 test_custom_histogram->Add(100);
507 test_custom_histogram->Add(1000);
508 test_custom_histogram->Add(INT_MAX);
509
510 // Verify they landed in the underflow, and overflow buckets.
511 std::unique_ptr<SampleVector> custom_samples =
512 test_custom_histogram->SnapshotAllSamples();
513 EXPECT_EQ(2, custom_samples->GetCountAtIndex(0));
514 EXPECT_EQ(0, custom_samples->GetCountAtIndex(1));
515 size_t bucket_count = test_custom_histogram->bucket_count();
516 EXPECT_EQ(0, custom_samples->GetCountAtIndex(bucket_count - 2));
517 EXPECT_EQ(3, custom_samples->GetCountAtIndex(bucket_count - 1));
518 }
519
520 // Check to be sure samples land as expected is "correct" buckets.
TEST_P(HistogramTest,BucketPlacementTest)521 TEST_P(HistogramTest, BucketPlacementTest) {
522 Histogram* histogram = static_cast<Histogram*>(
523 Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags));
524
525 // Add i+1 samples to the i'th bucket.
526 histogram->Add(0);
527 int power_of_2 = 1;
528 for (int i = 1; i < 8; i++) {
529 for (int j = 0; j <= i; j++)
530 histogram->Add(power_of_2);
531 power_of_2 *= 2;
532 }
533
534 // Check to see that the bucket counts reflect our additions.
535 std::unique_ptr<SampleVector> samples = histogram->SnapshotAllSamples();
536 for (int i = 0; i < 8; i++)
537 EXPECT_EQ(i + 1, samples->GetCountAtIndex(i));
538 }
539
TEST_P(HistogramTest,CorruptSampleCounts)540 TEST_P(HistogramTest, CorruptSampleCounts) {
541 // The internal code creates histograms via macros and thus keeps static
542 // pointers to them. If those pointers are to persistent memory which will
543 // be free'd then any following calls to that code will crash with a
544 // segmentation violation.
545 if (use_persistent_histogram_allocator_)
546 return;
547
548 Histogram* histogram = static_cast<Histogram*>(
549 Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags));
550
551 // Add some samples.
552 histogram->Add(20);
553 histogram->Add(40);
554
555 std::unique_ptr<SampleVector> snapshot = histogram->SnapshotAllSamples();
556 EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES,
557 histogram->FindCorruption(*snapshot));
558 EXPECT_EQ(2, snapshot->redundant_count());
559 EXPECT_EQ(2, snapshot->TotalCount());
560
561 snapshot->counts()[3] += 100; // Sample count won't match redundant count.
562 EXPECT_EQ(HistogramBase::COUNT_LOW_ERROR,
563 histogram->FindCorruption(*snapshot));
564 snapshot->counts()[2] -= 200;
565 EXPECT_EQ(HistogramBase::COUNT_HIGH_ERROR,
566 histogram->FindCorruption(*snapshot));
567
568 // But we can't spot a corruption if it is compensated for.
569 snapshot->counts()[1] += 100;
570 EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES,
571 histogram->FindCorruption(*snapshot));
572 }
573
TEST_P(HistogramTest,CorruptBucketBounds)574 TEST_P(HistogramTest, CorruptBucketBounds) {
575 Histogram* histogram = static_cast<Histogram*>(
576 Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags));
577
578 std::unique_ptr<HistogramSamples> snapshot = histogram->SnapshotSamples();
579 EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES,
580 histogram->FindCorruption(*snapshot));
581
582 BucketRanges* bucket_ranges =
583 const_cast<BucketRanges*>(histogram->bucket_ranges());
584 HistogramBase::Sample tmp = bucket_ranges->range(1);
585 bucket_ranges->set_range(1, bucket_ranges->range(2));
586 bucket_ranges->set_range(2, tmp);
587 EXPECT_EQ(
588 HistogramBase::BUCKET_ORDER_ERROR | HistogramBase::RANGE_CHECKSUM_ERROR,
589 histogram->FindCorruption(*snapshot));
590
591 bucket_ranges->set_range(2, bucket_ranges->range(1));
592 bucket_ranges->set_range(1, tmp);
593 EXPECT_EQ(0U, histogram->FindCorruption(*snapshot));
594
595 // Show that two simple changes don't offset each other
596 bucket_ranges->set_range(3, bucket_ranges->range(3) + 1);
597 EXPECT_EQ(HistogramBase::RANGE_CHECKSUM_ERROR,
598 histogram->FindCorruption(*snapshot));
599
600 bucket_ranges->set_range(4, bucket_ranges->range(4) - 1);
601 EXPECT_EQ(HistogramBase::RANGE_CHECKSUM_ERROR,
602 histogram->FindCorruption(*snapshot));
603
604 // Repair histogram so that destructor won't DCHECK().
605 bucket_ranges->set_range(3, bucket_ranges->range(3) - 1);
606 bucket_ranges->set_range(4, bucket_ranges->range(4) + 1);
607 }
608
TEST_P(HistogramTest,HistogramSerializeInfo)609 TEST_P(HistogramTest, HistogramSerializeInfo) {
610 Histogram* histogram = static_cast<Histogram*>(
611 Histogram::FactoryGet("Histogram", 1, 64, 8,
612 HistogramBase::kIPCSerializationSourceFlag));
613 Pickle pickle;
614 histogram->SerializeInfo(&pickle);
615
616 PickleIterator iter(pickle);
617
618 int type;
619 EXPECT_TRUE(iter.ReadInt(&type));
620 EXPECT_EQ(HISTOGRAM, type);
621
622 std::string name;
623 EXPECT_TRUE(iter.ReadString(&name));
624 EXPECT_EQ("Histogram", name);
625
626 int flag;
627 EXPECT_TRUE(iter.ReadInt(&flag));
628 EXPECT_EQ(HistogramBase::kIPCSerializationSourceFlag,
629 flag & ~HistogramBase::kIsPersistent);
630
631 int min;
632 EXPECT_TRUE(iter.ReadInt(&min));
633 EXPECT_EQ(1, min);
634
635 int max;
636 EXPECT_TRUE(iter.ReadInt(&max));
637 EXPECT_EQ(64, max);
638
639 uint32_t bucket_count;
640 EXPECT_TRUE(iter.ReadUInt32(&bucket_count));
641 EXPECT_EQ(8u, bucket_count);
642
643 uint32_t checksum;
644 EXPECT_TRUE(iter.ReadUInt32(&checksum));
645 EXPECT_EQ(histogram->bucket_ranges()->checksum(), checksum);
646
647 // No more data in the pickle.
648 EXPECT_FALSE(iter.SkipBytes(1));
649 }
650
TEST_P(HistogramTest,CustomHistogramSerializeInfo)651 TEST_P(HistogramTest, CustomHistogramSerializeInfo) {
652 std::vector<int> custom_ranges;
653 custom_ranges.push_back(10);
654 custom_ranges.push_back(100);
655
656 HistogramBase* custom_histogram = CustomHistogram::FactoryGet(
657 "TestCustomRangeBoundedHistogram",
658 custom_ranges,
659 HistogramBase::kNoFlags);
660 Pickle pickle;
661 custom_histogram->SerializeInfo(&pickle);
662
663 // Validate the pickle.
664 PickleIterator iter(pickle);
665
666 int i;
667 std::string s;
668 uint32_t bucket_count;
669 uint32_t ui32;
670 EXPECT_TRUE(iter.ReadInt(&i) && iter.ReadString(&s) && iter.ReadInt(&i) &&
671 iter.ReadInt(&i) && iter.ReadInt(&i) &&
672 iter.ReadUInt32(&bucket_count) && iter.ReadUInt32(&ui32));
673 EXPECT_EQ(3u, bucket_count);
674
675 int range;
676 EXPECT_TRUE(iter.ReadInt(&range));
677 EXPECT_EQ(10, range);
678 EXPECT_TRUE(iter.ReadInt(&range));
679 EXPECT_EQ(100, range);
680
681 // No more data in the pickle.
682 EXPECT_FALSE(iter.SkipBytes(1));
683 }
684
TEST_P(HistogramTest,BadConstruction)685 TEST_P(HistogramTest, BadConstruction) {
686 HistogramBase* histogram = Histogram::FactoryGet(
687 "BadConstruction", 0, 100, 8, HistogramBase::kNoFlags);
688 EXPECT_TRUE(histogram->HasConstructionArguments(1, 100, 8));
689
690 // Try to get the same histogram name with different arguments.
691 HistogramBase* bad_histogram = Histogram::FactoryGet(
692 "BadConstruction", 0, 100, 7, HistogramBase::kNoFlags);
693 EXPECT_EQ(DummyHistogram::GetInstance(), bad_histogram);
694 bad_histogram = Histogram::FactoryGet(
695 "BadConstruction", 0, 99, 8, HistogramBase::kNoFlags);
696 EXPECT_EQ(DummyHistogram::GetInstance(), bad_histogram);
697
698 HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
699 "BadConstructionLinear", 0, 100, 8, HistogramBase::kNoFlags);
700 EXPECT_TRUE(linear_histogram->HasConstructionArguments(1, 100, 8));
701
702 // Try to get the same histogram name with different arguments.
703 bad_histogram = LinearHistogram::FactoryGet(
704 "BadConstructionLinear", 0, 100, 7, HistogramBase::kNoFlags);
705 EXPECT_EQ(DummyHistogram::GetInstance(), bad_histogram);
706 bad_histogram = LinearHistogram::FactoryGet(
707 "BadConstructionLinear", 10, 100, 8, HistogramBase::kNoFlags);
708 EXPECT_EQ(DummyHistogram::GetInstance(), bad_histogram);
709 }
710
TEST_P(HistogramTest,FactoryTime)711 TEST_P(HistogramTest, FactoryTime) {
712 const int kTestCreateCount = 1 << 14; // Must be power-of-2.
713 const int kTestLookupCount = 100000;
714 const int kTestAddCount = 1000000;
715
716 // Create all histogram names in advance for accurate timing below.
717 std::vector<std::string> histogram_names;
718 for (int i = 0; i < kTestCreateCount; ++i) {
719 histogram_names.push_back(
720 StringPrintf("TestHistogram.%d", i % kTestCreateCount));
721 }
722
723 // Calculate cost of creating histograms.
724 TimeTicks create_start = TimeTicks::Now();
725 for (int i = 0; i < kTestCreateCount; ++i) {
726 Histogram::FactoryGet(histogram_names[i], 1, 100, 10,
727 HistogramBase::kNoFlags);
728 }
729 TimeDelta create_ticks = TimeTicks::Now() - create_start;
730 int64_t create_ms = create_ticks.InMilliseconds();
731
732 VLOG(1) << kTestCreateCount << " histogram creations took " << create_ms
733 << "ms or about "
734 << (create_ms * 1000000) / kTestCreateCount
735 << "ns each.";
736
737 // Calculate cost of looking up existing histograms.
738 TimeTicks lookup_start = TimeTicks::Now();
739 for (int i = 0; i < kTestLookupCount; ++i) {
740 // 6007 is co-prime with kTestCreateCount and so will do lookups in an
741 // order less likely to be cacheable (but still hit them all) should the
742 // underlying storage use the exact histogram name as the key.
743 const int i_mult = 6007;
744 static_assert(i_mult < INT_MAX / kTestCreateCount, "Multiplier too big");
745 int index = (i * i_mult) & (kTestCreateCount - 1);
746 Histogram::FactoryGet(histogram_names[index], 1, 100, 10,
747 HistogramBase::kNoFlags);
748 }
749 TimeDelta lookup_ticks = TimeTicks::Now() - lookup_start;
750 int64_t lookup_ms = lookup_ticks.InMilliseconds();
751
752 VLOG(1) << kTestLookupCount << " histogram lookups took " << lookup_ms
753 << "ms or about "
754 << (lookup_ms * 1000000) / kTestLookupCount
755 << "ns each.";
756
757 // Calculate cost of accessing histograms.
758 HistogramBase* histogram = Histogram::FactoryGet(
759 histogram_names[0], 1, 100, 10, HistogramBase::kNoFlags);
760 ASSERT_TRUE(histogram);
761 TimeTicks add_start = TimeTicks::Now();
762 for (int i = 0; i < kTestAddCount; ++i)
763 histogram->Add(i & 127);
764 TimeDelta add_ticks = TimeTicks::Now() - add_start;
765 int64_t add_ms = add_ticks.InMilliseconds();
766
767 VLOG(1) << kTestAddCount << " histogram adds took " << add_ms
768 << "ms or about "
769 << (add_ms * 1000000) / kTestAddCount
770 << "ns each.";
771 }
772
TEST_P(HistogramTest,ScaledLinearHistogram)773 TEST_P(HistogramTest, ScaledLinearHistogram) {
774 ScaledLinearHistogram scaled("SLH", 1, 5, 6, 100, HistogramBase::kNoFlags);
775
776 scaled.AddScaledCount(0, 1);
777 scaled.AddScaledCount(1, 49);
778 scaled.AddScaledCount(2, 50);
779 scaled.AddScaledCount(3, 101);
780 scaled.AddScaledCount(4, 160);
781 scaled.AddScaledCount(5, 130);
782 scaled.AddScaledCount(6, 140);
783
784 std::unique_ptr<SampleVector> samples =
785 SnapshotAllSamples(static_cast<Histogram*>(scaled.histogram()));
786 EXPECT_EQ(0, samples->GetCountAtIndex(0));
787 EXPECT_EQ(0, samples->GetCountAtIndex(1));
788 EXPECT_EQ(1, samples->GetCountAtIndex(2));
789 EXPECT_EQ(1, samples->GetCountAtIndex(3));
790 EXPECT_EQ(2, samples->GetCountAtIndex(4));
791 EXPECT_EQ(3, samples->GetCountAtIndex(5));
792
793 // Make sure the macros compile properly. This can only be run when
794 // there is no persistent allocator which can be discarded and leave
795 // dangling pointers.
796 if (!use_persistent_histogram_allocator_) {
797 enum EnumWithMax {
798 kA = 0,
799 kB = 1,
800 kC = 2,
801 kMaxValue = kC,
802 };
803 UMA_HISTOGRAM_SCALED_EXACT_LINEAR("h1", 1, 5000, 5, 100);
804 UMA_HISTOGRAM_SCALED_ENUMERATION("h2", kB, 5000, 100);
805 }
806 }
807
808 // For Histogram, LinearHistogram and CustomHistogram, the minimum for a
809 // declared range is 1, while the maximum is (HistogramBase::kSampleType_MAX -
810 // 1). But we accept ranges exceeding those limits, and silently clamped to
811 // those limits. This is for backwards compatibility.
TEST(HistogramDeathTest,BadRangesTest)812 TEST(HistogramDeathTest, BadRangesTest) {
813 HistogramBase* histogram = Histogram::FactoryGet(
814 "BadRanges", 0, HistogramBase::kSampleType_MAX, 8,
815 HistogramBase::kNoFlags);
816 EXPECT_TRUE(
817 histogram->HasConstructionArguments(
818 1, HistogramBase::kSampleType_MAX - 1, 8));
819
820 HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
821 "BadRangesLinear", 0, HistogramBase::kSampleType_MAX, 8,
822 HistogramBase::kNoFlags);
823 EXPECT_TRUE(
824 linear_histogram->HasConstructionArguments(
825 1, HistogramBase::kSampleType_MAX - 1, 8));
826
827 std::vector<int> custom_ranges;
828 custom_ranges.push_back(0);
829 custom_ranges.push_back(5);
830 Histogram* custom_histogram = static_cast<Histogram*>(
831 CustomHistogram::FactoryGet(
832 "BadRangesCustom", custom_ranges, HistogramBase::kNoFlags));
833 const BucketRanges* ranges = custom_histogram->bucket_ranges();
834 ASSERT_EQ(3u, ranges->size());
835 EXPECT_EQ(0, ranges->range(0));
836 EXPECT_EQ(5, ranges->range(1));
837 EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2));
838
839 // CustomHistogram does not accepts kSampleType_MAX as range.
840 custom_ranges.push_back(HistogramBase::kSampleType_MAX);
841 EXPECT_DEATH_IF_SUPPORTED(
842 CustomHistogram::FactoryGet("BadRangesCustom2", custom_ranges,
843 HistogramBase::kNoFlags),
844 "");
845
846 // CustomHistogram needs at least 1 valid range.
847 custom_ranges.clear();
848 custom_ranges.push_back(0);
849 EXPECT_DEATH_IF_SUPPORTED(
850 CustomHistogram::FactoryGet("BadRangesCustom3", custom_ranges,
851 HistogramBase::kNoFlags),
852 "");
853 }
854
TEST_P(HistogramTest,ExpiredHistogramTest)855 TEST_P(HistogramTest, ExpiredHistogramTest) {
856 auto record_checker = std::make_unique<TestRecordHistogramChecker>();
857 StatisticsRecorder::SetRecordChecker(std::move(record_checker));
858
859 HistogramBase* expired = Histogram::FactoryGet(kExpiredHistogramName, 1, 1000,
860 10, HistogramBase::kNoFlags);
861 ASSERT_TRUE(expired);
862 expired->Add(5);
863 expired->Add(500);
864 auto samples = expired->SnapshotDelta();
865 EXPECT_EQ(0, samples->TotalCount());
866
867 HistogramBase* linear_expired = LinearHistogram::FactoryGet(
868 kExpiredHistogramName, 1, 1000, 10, HistogramBase::kNoFlags);
869 ASSERT_TRUE(linear_expired);
870 linear_expired->Add(5);
871 linear_expired->Add(500);
872 samples = linear_expired->SnapshotDelta();
873 EXPECT_EQ(0, samples->TotalCount());
874
875 ScaledLinearHistogram scaled_linear_expired(kExpiredHistogramName, 1, 5, 6,
876 100, HistogramBase::kNoFlags);
877 scaled_linear_expired.AddScaledCount(0, 1);
878 scaled_linear_expired.AddScaledCount(1, 49);
879 samples = scaled_linear_expired.histogram()->SnapshotDelta();
880 EXPECT_EQ(0, samples->TotalCount());
881
882 std::vector<int> custom_ranges;
883 custom_ranges.push_back(1);
884 custom_ranges.push_back(5);
885 HistogramBase* custom_expired = CustomHistogram::FactoryGet(
886 kExpiredHistogramName, custom_ranges, HistogramBase::kNoFlags);
887 ASSERT_TRUE(custom_expired);
888 custom_expired->Add(2);
889 custom_expired->Add(4);
890 samples = custom_expired->SnapshotDelta();
891 EXPECT_EQ(0, samples->TotalCount());
892
893 HistogramBase* valid = Histogram::FactoryGet("ValidHistogram", 1, 1000, 10,
894 HistogramBase::kNoFlags);
895 ASSERT_TRUE(valid);
896 valid->Add(5);
897 valid->Add(500);
898 samples = valid->SnapshotDelta();
899 EXPECT_EQ(2, samples->TotalCount());
900
901 HistogramBase* linear_valid = LinearHistogram::FactoryGet(
902 "LinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
903 ASSERT_TRUE(linear_valid);
904 linear_valid->Add(5);
905 linear_valid->Add(500);
906 samples = linear_valid->SnapshotDelta();
907 EXPECT_EQ(2, samples->TotalCount());
908
909 HistogramBase* custom_valid = CustomHistogram::FactoryGet(
910 "CustomHistogram", custom_ranges, HistogramBase::kNoFlags);
911 ASSERT_TRUE(custom_valid);
912 custom_valid->Add(2);
913 custom_valid->Add(4);
914 samples = custom_valid->SnapshotDelta();
915 EXPECT_EQ(2, samples->TotalCount());
916 }
917
TEST_P(HistogramTest,CheckGetCountAndBucketData)918 TEST_P(HistogramTest, CheckGetCountAndBucketData) {
919 const size_t kBucketCount = 50;
920 Histogram* histogram = static_cast<Histogram*>(Histogram::FactoryGet(
921 "AddCountHistogram", 10, 100, kBucketCount, HistogramBase::kNoFlags));
922 // Add samples in reverse order and make sure the output is in correct order.
923 histogram->AddCount(/*sample=*/30, /*value=*/14);
924 histogram->AddCount(/*sample=*/20, /*value=*/15);
925 histogram->AddCount(/*sample=*/20, /*value=*/15);
926 histogram->AddCount(/*sample=*/30, /*value=*/14);
927
928 base::Histogram::Count total_count;
929 int64_t sum;
930 base::ListValue buckets;
931 GetCountAndBucketData(histogram, &total_count, &sum, &buckets);
932 EXPECT_EQ(58, total_count);
933 EXPECT_EQ(1440, sum);
934 EXPECT_EQ(2u, buckets.GetSize());
935
936 int low, high, count;
937 // Check the first bucket.
938 base::DictionaryValue* bucket1;
939 EXPECT_TRUE(buckets.GetDictionary(0, &bucket1));
940 EXPECT_TRUE(bucket1->GetInteger("low", &low));
941 EXPECT_TRUE(bucket1->GetInteger("high", &high));
942 EXPECT_TRUE(bucket1->GetInteger("count", &count));
943 EXPECT_EQ(20, low);
944 EXPECT_EQ(21, high);
945 EXPECT_EQ(30, count);
946
947 // Check the second bucket.
948 base::DictionaryValue* bucket2;
949 EXPECT_TRUE(buckets.GetDictionary(1, &bucket2));
950 EXPECT_TRUE(bucket2->GetInteger("low", &low));
951 EXPECT_TRUE(bucket2->GetInteger("high", &high));
952 EXPECT_TRUE(bucket2->GetInteger("count", &count));
953 EXPECT_EQ(30, low);
954 EXPECT_EQ(31, high);
955 EXPECT_EQ(28, count);
956 }
957
TEST_P(HistogramTest,WriteAscii)958 TEST_P(HistogramTest, WriteAscii) {
959 HistogramBase* histogram =
960 LinearHistogram::FactoryGet("AsciiOut", /*minimum=*/1, /*maximum=*/10,
961 /*bucket_count=*/5, HistogramBase::kNoFlags);
962 histogram->AddCount(/*sample=*/4, /*value=*/5);
963
964 std::string output;
965 histogram->WriteAscii(&output);
966
967 const char kOutputFormatRe[] =
968 R"(Histogram: AsciiOut recorded 5 samples, mean = 4\.0.*\n)"
969 R"(0 \.\.\. \n)"
970 R"(4 -+O \(5 = 100\.0%\) \{0\.0%\}\n)"
971 R"(7 \.\.\. \n)";
972
973 EXPECT_THAT(output, testing::MatchesRegex(kOutputFormatRe));
974 }
975
TEST_P(HistogramTest,ToGraphDict)976 TEST_P(HistogramTest, ToGraphDict) {
977 HistogramBase* histogram =
978 LinearHistogram::FactoryGet("HTMLOut", /*minimum=*/1, /*maximum=*/10,
979 /*bucket_count=*/5, HistogramBase::kNoFlags);
980 histogram->AddCount(/*sample=*/4, /*value=*/5);
981
982 base::DictionaryValue output = histogram->ToGraphDict();
983 std::string* header = output.FindStringKey("header");
984 std::string* body = output.FindStringKey("body");
985
986 const char kOutputHeaderFormatRe[] =
987 R"(Histogram: HTMLOut recorded 5 samples, mean = 4\.0.*)";
988 const char kOutputBodyFormatRe[] = R"(0 \.\.\. \n)"
989 R"(4 -+O \(5 = 100\.0%\) \{0\.0%\}\n)"
990 R"(7 \.\.\. \n)";
991
992 EXPECT_THAT(*header, testing::MatchesRegex(kOutputHeaderFormatRe));
993 EXPECT_THAT(*body, testing::MatchesRegex(kOutputBodyFormatRe));
994 }
995
996 } // namespace base
997