|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/metrics/histogram.h" | 
|  |  | 
|  | #include <limits.h> | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <climits> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/metrics/bucket_ranges.h" | 
|  | #include "base/metrics/dummy_histogram.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/metrics/metrics_hashes.h" | 
|  | #include "base/metrics/persistent_histogram_allocator.h" | 
|  | #include "base/metrics/persistent_memory_allocator.h" | 
|  | #include "base/metrics/record_histogram_checker.h" | 
|  | #include "base/metrics/sample_vector.h" | 
|  | #include "base/metrics/statistics_recorder.h" | 
|  | #include "base/pickle.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/test/gtest_util.h" | 
|  | #include "base/time/time.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace { | 
|  |  | 
|  | const char kExpiredHistogramName[] = "ExpiredHistogram"; | 
|  |  | 
|  | // Test implementation of RecordHistogramChecker interface. | 
|  | class TestRecordHistogramChecker : public RecordHistogramChecker { | 
|  | public: | 
|  | ~TestRecordHistogramChecker() override = default; | 
|  |  | 
|  | // RecordHistogramChecker: | 
|  | bool ShouldRecord(uint64_t histogram_hash) const override { | 
|  | return histogram_hash != HashMetricName(kExpiredHistogramName); | 
|  | } | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Test parameter indicates if a persistent memory allocator should be used | 
|  | // for histogram allocation. False will allocate histograms from the process | 
|  | // heap. | 
|  | class HistogramTest : public testing::TestWithParam<bool> { | 
|  | protected: | 
|  | const int32_t kAllocatorMemorySize = 8 << 20;  // 8 MiB | 
|  |  | 
|  | HistogramTest() : use_persistent_histogram_allocator_(GetParam()) {} | 
|  |  | 
|  | void SetUp() override { | 
|  | if (use_persistent_histogram_allocator_) | 
|  | CreatePersistentHistogramAllocator(); | 
|  |  | 
|  | // Each test will have a clean state (no Histogram / BucketRanges | 
|  | // registered). | 
|  | InitializeStatisticsRecorder(); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | if (allocator_) { | 
|  | ASSERT_FALSE(allocator_->IsFull()); | 
|  | ASSERT_FALSE(allocator_->IsCorrupt()); | 
|  | } | 
|  | UninitializeStatisticsRecorder(); | 
|  | DestroyPersistentHistogramAllocator(); | 
|  | } | 
|  |  | 
|  | void InitializeStatisticsRecorder() { | 
|  | DCHECK(!statistics_recorder_); | 
|  | statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting(); | 
|  | auto record_checker = std::make_unique<TestRecordHistogramChecker>(); | 
|  | StatisticsRecorder::SetRecordChecker(std::move(record_checker)); | 
|  | } | 
|  |  | 
|  | void UninitializeStatisticsRecorder() { | 
|  | statistics_recorder_.reset(); | 
|  | } | 
|  |  | 
|  | void CreatePersistentHistogramAllocator() { | 
|  | GlobalHistogramAllocator::CreateWithLocalMemory( | 
|  | kAllocatorMemorySize, 0, "HistogramAllocatorTest"); | 
|  | allocator_ = GlobalHistogramAllocator::Get()->memory_allocator(); | 
|  | } | 
|  |  | 
|  | void DestroyPersistentHistogramAllocator() { | 
|  | allocator_ = nullptr; | 
|  | GlobalHistogramAllocator::ReleaseForTesting(); | 
|  | } | 
|  |  | 
|  | const bool use_persistent_histogram_allocator_; | 
|  |  | 
|  | std::unique_ptr<StatisticsRecorder> statistics_recorder_; | 
|  | std::unique_ptr<char[]> allocator_memory_; | 
|  | PersistentMemoryAllocator* allocator_ = nullptr; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(HistogramTest); | 
|  | }; | 
|  |  | 
|  | // Run all HistogramTest cases with both heap and persistent memory. | 
|  | INSTANTIATE_TEST_CASE_P(HeapAndPersistent, HistogramTest, testing::Bool()); | 
|  |  | 
|  |  | 
|  | // Check for basic syntax and use. | 
|  | TEST_P(HistogramTest, BasicTest) { | 
|  | // Try basic construction | 
|  | HistogramBase* histogram = Histogram::FactoryGet( | 
|  | "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags); | 
|  | EXPECT_TRUE(histogram); | 
|  |  | 
|  | HistogramBase* linear_histogram = LinearHistogram::FactoryGet( | 
|  | "TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags); | 
|  | EXPECT_TRUE(linear_histogram); | 
|  |  | 
|  | std::vector<int> custom_ranges; | 
|  | custom_ranges.push_back(1); | 
|  | custom_ranges.push_back(5); | 
|  | HistogramBase* custom_histogram = CustomHistogram::FactoryGet( | 
|  | "TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags); | 
|  | EXPECT_TRUE(custom_histogram); | 
|  |  | 
|  | // Macros that create histograms have an internal static variable which will | 
|  | // continue to point to those from the very first run of this method even | 
|  | // during subsequent runs. | 
|  | static bool already_run = false; | 
|  | if (already_run) | 
|  | return; | 
|  | already_run = true; | 
|  |  | 
|  | // Use standard macros (but with fixed samples) | 
|  | LOCAL_HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1)); | 
|  | LOCAL_HISTOGRAM_COUNTS("Test3Histogram", 30); | 
|  |  | 
|  | LOCAL_HISTOGRAM_ENUMERATION("Test6Histogram", 129, 130); | 
|  | } | 
|  |  | 
|  | // Check that the macro correctly matches histograms by name and records their | 
|  | // data together. | 
|  | TEST_P(HistogramTest, NameMatchTest) { | 
|  | // Macros that create histograms have an internal static variable which will | 
|  | // continue to point to those from the very first run of this method even | 
|  | // during subsequent runs. | 
|  | static bool already_run = false; | 
|  | if (already_run) | 
|  | return; | 
|  | already_run = true; | 
|  |  | 
|  | LOCAL_HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10); | 
|  | LOCAL_HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10); | 
|  | HistogramBase* histogram = LinearHistogram::FactoryGet( | 
|  | "DuplicatedHistogram", 1, 101, 102, HistogramBase::kNoFlags); | 
|  |  | 
|  | std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples(); | 
|  | EXPECT_EQ(2, samples->TotalCount()); | 
|  | EXPECT_EQ(2, samples->GetCount(10)); | 
|  | } | 
|  |  | 
|  | // Check that delta calculations work correctly. | 
|  | TEST_P(HistogramTest, DeltaTest) { | 
|  | HistogramBase* histogram = | 
|  | Histogram::FactoryGet("DeltaHistogram", 1, 64, 8, | 
|  | HistogramBase::kNoFlags); | 
|  | histogram->Add(1); | 
|  | histogram->Add(10); | 
|  | histogram->Add(50); | 
|  |  | 
|  | std::unique_ptr<HistogramSamples> samples = histogram->SnapshotDelta(); | 
|  | EXPECT_EQ(3, samples->TotalCount()); | 
|  | EXPECT_EQ(1, samples->GetCount(1)); | 
|  | EXPECT_EQ(1, samples->GetCount(10)); | 
|  | EXPECT_EQ(1, samples->GetCount(50)); | 
|  | EXPECT_EQ(samples->TotalCount(), samples->redundant_count()); | 
|  |  | 
|  | samples = histogram->SnapshotDelta(); | 
|  | EXPECT_EQ(0, samples->TotalCount()); | 
|  |  | 
|  | histogram->Add(10); | 
|  | histogram->Add(10); | 
|  | samples = histogram->SnapshotDelta(); | 
|  | EXPECT_EQ(2, samples->TotalCount()); | 
|  | EXPECT_EQ(2, samples->GetCount(10)); | 
|  |  | 
|  | samples = histogram->SnapshotDelta(); | 
|  | EXPECT_EQ(0, samples->TotalCount()); | 
|  | } | 
|  |  | 
|  | // Check that final-delta calculations work correctly. | 
|  | TEST_P(HistogramTest, FinalDeltaTest) { | 
|  | HistogramBase* histogram = | 
|  | Histogram::FactoryGet("FinalDeltaHistogram", 1, 64, 8, | 
|  | HistogramBase::kNoFlags); | 
|  | histogram->Add(1); | 
|  | histogram->Add(10); | 
|  | histogram->Add(50); | 
|  |  | 
|  | std::unique_ptr<HistogramSamples> samples = histogram->SnapshotDelta(); | 
|  | EXPECT_EQ(3, samples->TotalCount()); | 
|  | EXPECT_EQ(1, samples->GetCount(1)); | 
|  | EXPECT_EQ(1, samples->GetCount(10)); | 
|  | EXPECT_EQ(1, samples->GetCount(50)); | 
|  | EXPECT_EQ(samples->TotalCount(), samples->redundant_count()); | 
|  |  | 
|  | histogram->Add(2); | 
|  | histogram->Add(50); | 
|  |  | 
|  | samples = histogram->SnapshotFinalDelta(); | 
|  | EXPECT_EQ(2, samples->TotalCount()); | 
|  | EXPECT_EQ(1, samples->GetCount(2)); | 
|  | EXPECT_EQ(1, samples->GetCount(50)); | 
|  | EXPECT_EQ(samples->TotalCount(), samples->redundant_count()); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, ExponentialRangesTest) { | 
|  | // Check that we got a nice exponential when there was enough room. | 
|  | BucketRanges ranges(9); | 
|  | Histogram::InitializeBucketRanges(1, 64, &ranges); | 
|  | EXPECT_EQ(0, ranges.range(0)); | 
|  | int power_of_2 = 1; | 
|  | for (int i = 1; i < 8; i++) { | 
|  | EXPECT_EQ(power_of_2, ranges.range(i)); | 
|  | power_of_2 *= 2; | 
|  | } | 
|  | EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8)); | 
|  |  | 
|  | // Check the corresponding Histogram will use the correct ranges. | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags)); | 
|  | EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges())); | 
|  |  | 
|  | // When bucket count is limited, exponential ranges will partially look like | 
|  | // linear. | 
|  | BucketRanges ranges2(16); | 
|  | Histogram::InitializeBucketRanges(1, 32, &ranges2); | 
|  |  | 
|  | EXPECT_EQ(0, ranges2.range(0)); | 
|  | EXPECT_EQ(1, ranges2.range(1)); | 
|  | EXPECT_EQ(2, ranges2.range(2)); | 
|  | EXPECT_EQ(3, ranges2.range(3)); | 
|  | EXPECT_EQ(4, ranges2.range(4)); | 
|  | EXPECT_EQ(5, ranges2.range(5)); | 
|  | EXPECT_EQ(6, ranges2.range(6)); | 
|  | EXPECT_EQ(7, ranges2.range(7)); | 
|  | EXPECT_EQ(9, ranges2.range(8)); | 
|  | EXPECT_EQ(11, ranges2.range(9)); | 
|  | EXPECT_EQ(14, ranges2.range(10)); | 
|  | EXPECT_EQ(17, ranges2.range(11)); | 
|  | EXPECT_EQ(21, ranges2.range(12)); | 
|  | EXPECT_EQ(26, ranges2.range(13)); | 
|  | EXPECT_EQ(32, ranges2.range(14)); | 
|  | EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(15)); | 
|  |  | 
|  | // Check the corresponding Histogram will use the correct ranges. | 
|  | Histogram* histogram2 = static_cast<Histogram*>( | 
|  | Histogram::FactoryGet("Histogram2", 1, 32, 15, HistogramBase::kNoFlags)); | 
|  | EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges())); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, LinearRangesTest) { | 
|  | BucketRanges ranges(9); | 
|  | LinearHistogram::InitializeBucketRanges(1, 7, &ranges); | 
|  | // Gets a nice linear set of bucket ranges. | 
|  | for (int i = 0; i < 8; i++) | 
|  | EXPECT_EQ(i, ranges.range(i)); | 
|  | EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8)); | 
|  |  | 
|  | // The correspoding LinearHistogram should use the correct ranges. | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | LinearHistogram::FactoryGet("Linear", 1, 7, 8, HistogramBase::kNoFlags)); | 
|  | EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges())); | 
|  |  | 
|  | // Linear ranges are not divisible. | 
|  | BucketRanges ranges2(6); | 
|  | LinearHistogram::InitializeBucketRanges(1, 6, &ranges2); | 
|  | EXPECT_EQ(0, ranges2.range(0)); | 
|  | EXPECT_EQ(1, ranges2.range(1)); | 
|  | EXPECT_EQ(3, ranges2.range(2)); | 
|  | EXPECT_EQ(4, ranges2.range(3)); | 
|  | EXPECT_EQ(6, ranges2.range(4)); | 
|  | EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(5)); | 
|  | // The correspoding LinearHistogram should use the correct ranges. | 
|  | Histogram* histogram2 = static_cast<Histogram*>( | 
|  | LinearHistogram::FactoryGet("Linear2", 1, 6, 5, HistogramBase::kNoFlags)); | 
|  | EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges())); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, ArrayToCustomEnumRangesTest) { | 
|  | const HistogramBase::Sample ranges[3] = {5, 10, 20}; | 
|  | std::vector<HistogramBase::Sample> ranges_vec = | 
|  | CustomHistogram::ArrayToCustomEnumRanges(ranges); | 
|  | ASSERT_EQ(6u, ranges_vec.size()); | 
|  | EXPECT_EQ(5, ranges_vec[0]); | 
|  | EXPECT_EQ(6, ranges_vec[1]); | 
|  | EXPECT_EQ(10, ranges_vec[2]); | 
|  | EXPECT_EQ(11, ranges_vec[3]); | 
|  | EXPECT_EQ(20, ranges_vec[4]); | 
|  | EXPECT_EQ(21, ranges_vec[5]); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, CustomHistogramTest) { | 
|  | // A well prepared custom ranges. | 
|  | std::vector<HistogramBase::Sample> custom_ranges; | 
|  | custom_ranges.push_back(1); | 
|  | custom_ranges.push_back(2); | 
|  |  | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | CustomHistogram::FactoryGet("TestCustomHistogram1", custom_ranges, | 
|  | HistogramBase::kNoFlags)); | 
|  | const BucketRanges* ranges = histogram->bucket_ranges(); | 
|  | ASSERT_EQ(4u, ranges->size()); | 
|  | EXPECT_EQ(0, ranges->range(0));  // Auto added. | 
|  | EXPECT_EQ(1, ranges->range(1)); | 
|  | EXPECT_EQ(2, ranges->range(2)); | 
|  | EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3));  // Auto added. | 
|  |  | 
|  | // A unordered custom ranges. | 
|  | custom_ranges.clear(); | 
|  | custom_ranges.push_back(2); | 
|  | custom_ranges.push_back(1); | 
|  | histogram = static_cast<Histogram*>( | 
|  | CustomHistogram::FactoryGet("TestCustomHistogram2", custom_ranges, | 
|  | HistogramBase::kNoFlags)); | 
|  | ranges = histogram->bucket_ranges(); | 
|  | ASSERT_EQ(4u, ranges->size()); | 
|  | EXPECT_EQ(0, ranges->range(0)); | 
|  | EXPECT_EQ(1, ranges->range(1)); | 
|  | EXPECT_EQ(2, ranges->range(2)); | 
|  | EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); | 
|  |  | 
|  | // A custom ranges with duplicated values. | 
|  | custom_ranges.clear(); | 
|  | custom_ranges.push_back(4); | 
|  | custom_ranges.push_back(1); | 
|  | custom_ranges.push_back(4); | 
|  | histogram = static_cast<Histogram*>( | 
|  | CustomHistogram::FactoryGet("TestCustomHistogram3", custom_ranges, | 
|  | HistogramBase::kNoFlags)); | 
|  | ranges = histogram->bucket_ranges(); | 
|  | ASSERT_EQ(4u, ranges->size()); | 
|  | EXPECT_EQ(0, ranges->range(0)); | 
|  | EXPECT_EQ(1, ranges->range(1)); | 
|  | EXPECT_EQ(4, ranges->range(2)); | 
|  | EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, CustomHistogramWithOnly2Buckets) { | 
|  | // This test exploits the fact that the CustomHistogram can have 2 buckets, | 
|  | // while the base class Histogram is *supposed* to have at least 3 buckets. | 
|  | // We should probably change the restriction on the base class (or not inherit | 
|  | // the base class!). | 
|  |  | 
|  | std::vector<HistogramBase::Sample> custom_ranges; | 
|  | custom_ranges.push_back(4); | 
|  |  | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | CustomHistogram::FactoryGet("2BucketsCustomHistogram", custom_ranges, | 
|  | HistogramBase::kNoFlags)); | 
|  | const BucketRanges* ranges = histogram->bucket_ranges(); | 
|  | ASSERT_EQ(3u, ranges->size()); | 
|  | EXPECT_EQ(0, ranges->range(0)); | 
|  | EXPECT_EQ(4, ranges->range(1)); | 
|  | EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2)); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, AddCountTest) { | 
|  | const size_t kBucketCount = 50; | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | Histogram::FactoryGet("AddCountHistogram", 10, 100, kBucketCount, | 
|  | HistogramBase::kNoFlags)); | 
|  |  | 
|  | histogram->AddCount(20, 15); | 
|  | histogram->AddCount(30, 14); | 
|  |  | 
|  | std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples(); | 
|  | EXPECT_EQ(29, samples->TotalCount()); | 
|  | EXPECT_EQ(15, samples->GetCount(20)); | 
|  | EXPECT_EQ(14, samples->GetCount(30)); | 
|  |  | 
|  | histogram->AddCount(20, 25); | 
|  | histogram->AddCount(30, 24); | 
|  |  | 
|  | std::unique_ptr<HistogramSamples> samples2 = histogram->SnapshotSamples(); | 
|  | EXPECT_EQ(78, samples2->TotalCount()); | 
|  | EXPECT_EQ(40, samples2->GetCount(20)); | 
|  | EXPECT_EQ(38, samples2->GetCount(30)); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, AddCount_LargeValuesDontOverflow) { | 
|  | const size_t kBucketCount = 50; | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | Histogram::FactoryGet("AddCountHistogram", 10, 1000000000, kBucketCount, | 
|  | HistogramBase::kNoFlags)); | 
|  |  | 
|  | histogram->AddCount(200000000, 15); | 
|  | histogram->AddCount(300000000, 14); | 
|  |  | 
|  | std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples(); | 
|  | EXPECT_EQ(29, samples->TotalCount()); | 
|  | EXPECT_EQ(15, samples->GetCount(200000000)); | 
|  | EXPECT_EQ(14, samples->GetCount(300000000)); | 
|  |  | 
|  | histogram->AddCount(200000000, 25); | 
|  | histogram->AddCount(300000000, 24); | 
|  |  | 
|  | std::unique_ptr<HistogramSamples> samples2 = histogram->SnapshotSamples(); | 
|  | EXPECT_EQ(78, samples2->TotalCount()); | 
|  | EXPECT_EQ(40, samples2->GetCount(200000000)); | 
|  | EXPECT_EQ(38, samples2->GetCount(300000000)); | 
|  | EXPECT_EQ(19400000000LL, samples2->sum()); | 
|  | } | 
|  |  | 
|  | // Some metrics are designed so that they are guaranteed not to overflow between | 
|  | // snapshots, but could overflow over a long-running session. | 
|  | // Make sure that counts returned by Histogram::SnapshotDelta do not overflow | 
|  | // even when a total count (returned by Histogram::SnapshotSample) does. | 
|  | TEST_P(HistogramTest, AddCount_LargeCountsDontOverflow) { | 
|  | const size_t kBucketCount = 10; | 
|  | Histogram* histogram = static_cast<Histogram*>(Histogram::FactoryGet( | 
|  | "AddCountHistogram", 10, 50, kBucketCount, HistogramBase::kNoFlags)); | 
|  |  | 
|  | const int count = (1 << 30) - 1; | 
|  |  | 
|  | // Repeat N times to make sure that there is no internal value overflow. | 
|  | for (int i = 0; i < 10; ++i) { | 
|  | histogram->AddCount(42, count); | 
|  | std::unique_ptr<HistogramSamples> samples = histogram->SnapshotDelta(); | 
|  | EXPECT_EQ(count, samples->TotalCount()); | 
|  | EXPECT_EQ(count, samples->GetCount(42)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Make sure histogram handles out-of-bounds data gracefully. | 
|  | TEST_P(HistogramTest, BoundsTest) { | 
|  | const size_t kBucketCount = 50; | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | Histogram::FactoryGet("Bounded", 10, 100, kBucketCount, | 
|  | HistogramBase::kNoFlags)); | 
|  |  | 
|  | // Put two samples "out of bounds" above and below. | 
|  | histogram->Add(5); | 
|  | histogram->Add(-50); | 
|  |  | 
|  | histogram->Add(100); | 
|  | histogram->Add(10000); | 
|  |  | 
|  | // Verify they landed in the underflow, and overflow buckets. | 
|  | std::unique_ptr<SampleVector> samples = histogram->SnapshotAllSamples(); | 
|  | EXPECT_EQ(2, samples->GetCountAtIndex(0)); | 
|  | EXPECT_EQ(0, samples->GetCountAtIndex(1)); | 
|  | size_t array_size = histogram->bucket_count(); | 
|  | EXPECT_EQ(kBucketCount, array_size); | 
|  | EXPECT_EQ(0, samples->GetCountAtIndex(array_size - 2)); | 
|  | EXPECT_EQ(2, samples->GetCountAtIndex(array_size - 1)); | 
|  |  | 
|  | std::vector<int> custom_ranges; | 
|  | custom_ranges.push_back(10); | 
|  | custom_ranges.push_back(50); | 
|  | custom_ranges.push_back(100); | 
|  | Histogram* test_custom_histogram = static_cast<Histogram*>( | 
|  | CustomHistogram::FactoryGet("TestCustomRangeBoundedHistogram", | 
|  | custom_ranges, HistogramBase::kNoFlags)); | 
|  |  | 
|  | // Put two samples "out of bounds" above and below. | 
|  | test_custom_histogram->Add(5); | 
|  | test_custom_histogram->Add(-50); | 
|  | test_custom_histogram->Add(100); | 
|  | test_custom_histogram->Add(1000); | 
|  | test_custom_histogram->Add(INT_MAX); | 
|  |  | 
|  | // Verify they landed in the underflow, and overflow buckets. | 
|  | std::unique_ptr<SampleVector> custom_samples = | 
|  | test_custom_histogram->SnapshotAllSamples(); | 
|  | EXPECT_EQ(2, custom_samples->GetCountAtIndex(0)); | 
|  | EXPECT_EQ(0, custom_samples->GetCountAtIndex(1)); | 
|  | size_t bucket_count = test_custom_histogram->bucket_count(); | 
|  | EXPECT_EQ(0, custom_samples->GetCountAtIndex(bucket_count - 2)); | 
|  | EXPECT_EQ(3, custom_samples->GetCountAtIndex(bucket_count - 1)); | 
|  | } | 
|  |  | 
|  | // Check to be sure samples land as expected is "correct" buckets. | 
|  | TEST_P(HistogramTest, BucketPlacementTest) { | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags)); | 
|  |  | 
|  | // Add i+1 samples to the i'th bucket. | 
|  | histogram->Add(0); | 
|  | int power_of_2 = 1; | 
|  | for (int i = 1; i < 8; i++) { | 
|  | for (int j = 0; j <= i; j++) | 
|  | histogram->Add(power_of_2); | 
|  | power_of_2 *= 2; | 
|  | } | 
|  |  | 
|  | // Check to see that the bucket counts reflect our additions. | 
|  | std::unique_ptr<SampleVector> samples = histogram->SnapshotAllSamples(); | 
|  | for (int i = 0; i < 8; i++) | 
|  | EXPECT_EQ(i + 1, samples->GetCountAtIndex(i)); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, CorruptSampleCounts) { | 
|  | // The internal code creates histograms via macros and thus keeps static | 
|  | // pointers to them. If those pointers are to persistent memory which will | 
|  | // be free'd then any following calls to that code will crash with a | 
|  | // segmentation violation. | 
|  | if (use_persistent_histogram_allocator_) | 
|  | return; | 
|  |  | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags)); | 
|  |  | 
|  | // Add some samples. | 
|  | histogram->Add(20); | 
|  | histogram->Add(40); | 
|  |  | 
|  | std::unique_ptr<SampleVector> snapshot = histogram->SnapshotAllSamples(); | 
|  | EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES, | 
|  | histogram->FindCorruption(*snapshot)); | 
|  | EXPECT_EQ(2, snapshot->redundant_count()); | 
|  | EXPECT_EQ(2, snapshot->TotalCount()); | 
|  |  | 
|  | snapshot->counts()[3] += 100;  // Sample count won't match redundant count. | 
|  | EXPECT_EQ(HistogramBase::COUNT_LOW_ERROR, | 
|  | histogram->FindCorruption(*snapshot)); | 
|  | snapshot->counts()[2] -= 200; | 
|  | EXPECT_EQ(HistogramBase::COUNT_HIGH_ERROR, | 
|  | histogram->FindCorruption(*snapshot)); | 
|  |  | 
|  | // But we can't spot a corruption if it is compensated for. | 
|  | snapshot->counts()[1] += 100; | 
|  | EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES, | 
|  | histogram->FindCorruption(*snapshot)); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, CorruptBucketBounds) { | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags)); | 
|  |  | 
|  | std::unique_ptr<HistogramSamples> snapshot = histogram->SnapshotSamples(); | 
|  | EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES, | 
|  | histogram->FindCorruption(*snapshot)); | 
|  |  | 
|  | BucketRanges* bucket_ranges = | 
|  | const_cast<BucketRanges*>(histogram->bucket_ranges()); | 
|  | HistogramBase::Sample tmp = bucket_ranges->range(1); | 
|  | bucket_ranges->set_range(1, bucket_ranges->range(2)); | 
|  | bucket_ranges->set_range(2, tmp); | 
|  | EXPECT_EQ( | 
|  | HistogramBase::BUCKET_ORDER_ERROR | HistogramBase::RANGE_CHECKSUM_ERROR, | 
|  | histogram->FindCorruption(*snapshot)); | 
|  |  | 
|  | bucket_ranges->set_range(2, bucket_ranges->range(1)); | 
|  | bucket_ranges->set_range(1, tmp); | 
|  | EXPECT_EQ(0U, histogram->FindCorruption(*snapshot)); | 
|  |  | 
|  | // Show that two simple changes don't offset each other | 
|  | bucket_ranges->set_range(3, bucket_ranges->range(3) + 1); | 
|  | EXPECT_EQ(HistogramBase::RANGE_CHECKSUM_ERROR, | 
|  | histogram->FindCorruption(*snapshot)); | 
|  |  | 
|  | bucket_ranges->set_range(4, bucket_ranges->range(4) - 1); | 
|  | EXPECT_EQ(HistogramBase::RANGE_CHECKSUM_ERROR, | 
|  | histogram->FindCorruption(*snapshot)); | 
|  |  | 
|  | // Repair histogram so that destructor won't DCHECK(). | 
|  | bucket_ranges->set_range(3, bucket_ranges->range(3) - 1); | 
|  | bucket_ranges->set_range(4, bucket_ranges->range(4) + 1); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, HistogramSerializeInfo) { | 
|  | Histogram* histogram = static_cast<Histogram*>( | 
|  | Histogram::FactoryGet("Histogram", 1, 64, 8, | 
|  | HistogramBase::kIPCSerializationSourceFlag)); | 
|  | Pickle pickle; | 
|  | histogram->SerializeInfo(&pickle); | 
|  |  | 
|  | PickleIterator iter(pickle); | 
|  |  | 
|  | int type; | 
|  | EXPECT_TRUE(iter.ReadInt(&type)); | 
|  | EXPECT_EQ(HISTOGRAM, type); | 
|  |  | 
|  | std::string name; | 
|  | EXPECT_TRUE(iter.ReadString(&name)); | 
|  | EXPECT_EQ("Histogram", name); | 
|  |  | 
|  | int flag; | 
|  | EXPECT_TRUE(iter.ReadInt(&flag)); | 
|  | EXPECT_EQ(HistogramBase::kIPCSerializationSourceFlag, | 
|  | flag & ~HistogramBase::kIsPersistent); | 
|  |  | 
|  | int min; | 
|  | EXPECT_TRUE(iter.ReadInt(&min)); | 
|  | EXPECT_EQ(1, min); | 
|  |  | 
|  | int max; | 
|  | EXPECT_TRUE(iter.ReadInt(&max)); | 
|  | EXPECT_EQ(64, max); | 
|  |  | 
|  | uint32_t bucket_count; | 
|  | EXPECT_TRUE(iter.ReadUInt32(&bucket_count)); | 
|  | EXPECT_EQ(8u, bucket_count); | 
|  |  | 
|  | uint32_t checksum; | 
|  | EXPECT_TRUE(iter.ReadUInt32(&checksum)); | 
|  | EXPECT_EQ(histogram->bucket_ranges()->checksum(), checksum); | 
|  |  | 
|  | // No more data in the pickle. | 
|  | EXPECT_FALSE(iter.SkipBytes(1)); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, CustomHistogramSerializeInfo) { | 
|  | std::vector<int> custom_ranges; | 
|  | custom_ranges.push_back(10); | 
|  | custom_ranges.push_back(100); | 
|  |  | 
|  | HistogramBase* custom_histogram = CustomHistogram::FactoryGet( | 
|  | "TestCustomRangeBoundedHistogram", | 
|  | custom_ranges, | 
|  | HistogramBase::kNoFlags); | 
|  | Pickle pickle; | 
|  | custom_histogram->SerializeInfo(&pickle); | 
|  |  | 
|  | // Validate the pickle. | 
|  | PickleIterator iter(pickle); | 
|  |  | 
|  | int i; | 
|  | std::string s; | 
|  | uint32_t bucket_count; | 
|  | uint32_t ui32; | 
|  | EXPECT_TRUE(iter.ReadInt(&i) && iter.ReadString(&s) && iter.ReadInt(&i) && | 
|  | iter.ReadInt(&i) && iter.ReadInt(&i) && | 
|  | iter.ReadUInt32(&bucket_count) && iter.ReadUInt32(&ui32)); | 
|  | EXPECT_EQ(3u, bucket_count); | 
|  |  | 
|  | int range; | 
|  | EXPECT_TRUE(iter.ReadInt(&range)); | 
|  | EXPECT_EQ(10, range); | 
|  | EXPECT_TRUE(iter.ReadInt(&range)); | 
|  | EXPECT_EQ(100, range); | 
|  |  | 
|  | // No more data in the pickle. | 
|  | EXPECT_FALSE(iter.SkipBytes(1)); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, BadConstruction) { | 
|  | HistogramBase* histogram = Histogram::FactoryGet( | 
|  | "BadConstruction", 0, 100, 8, HistogramBase::kNoFlags); | 
|  | EXPECT_TRUE(histogram->HasConstructionArguments(1, 100, 8)); | 
|  |  | 
|  | // Try to get the same histogram name with different arguments. | 
|  | HistogramBase* bad_histogram = Histogram::FactoryGet( | 
|  | "BadConstruction", 0, 100, 7, HistogramBase::kNoFlags); | 
|  | EXPECT_EQ(DummyHistogram::GetInstance(), bad_histogram); | 
|  | bad_histogram = Histogram::FactoryGet( | 
|  | "BadConstruction", 0, 99, 8, HistogramBase::kNoFlags); | 
|  | EXPECT_EQ(DummyHistogram::GetInstance(), bad_histogram); | 
|  |  | 
|  | HistogramBase* linear_histogram = LinearHistogram::FactoryGet( | 
|  | "BadConstructionLinear", 0, 100, 8, HistogramBase::kNoFlags); | 
|  | EXPECT_TRUE(linear_histogram->HasConstructionArguments(1, 100, 8)); | 
|  |  | 
|  | // Try to get the same histogram name with different arguments. | 
|  | bad_histogram = LinearHistogram::FactoryGet( | 
|  | "BadConstructionLinear", 0, 100, 7, HistogramBase::kNoFlags); | 
|  | EXPECT_EQ(DummyHistogram::GetInstance(), bad_histogram); | 
|  | bad_histogram = LinearHistogram::FactoryGet( | 
|  | "BadConstructionLinear", 10, 100, 8, HistogramBase::kNoFlags); | 
|  | EXPECT_EQ(DummyHistogram::GetInstance(), bad_histogram); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, FactoryTime) { | 
|  | const int kTestCreateCount = 1 << 14;  // Must be power-of-2. | 
|  | const int kTestLookupCount = 100000; | 
|  | const int kTestAddCount = 1000000; | 
|  |  | 
|  | // Create all histogram names in advance for accurate timing below. | 
|  | std::vector<std::string> histogram_names; | 
|  | for (int i = 0; i < kTestCreateCount; ++i) { | 
|  | histogram_names.push_back( | 
|  | StringPrintf("TestHistogram.%d", i % kTestCreateCount)); | 
|  | } | 
|  |  | 
|  | // Calculate cost of creating histograms. | 
|  | TimeTicks create_start = TimeTicks::Now(); | 
|  | for (int i = 0; i < kTestCreateCount; ++i) { | 
|  | Histogram::FactoryGet(histogram_names[i], 1, 100, 10, | 
|  | HistogramBase::kNoFlags); | 
|  | } | 
|  | TimeDelta create_ticks = TimeTicks::Now() - create_start; | 
|  | int64_t create_ms = create_ticks.InMilliseconds(); | 
|  |  | 
|  | VLOG(1) << kTestCreateCount << " histogram creations took " << create_ms | 
|  | << "ms or about " | 
|  | << (create_ms * 1000000) / kTestCreateCount | 
|  | << "ns each."; | 
|  |  | 
|  | // Calculate cost of looking up existing histograms. | 
|  | TimeTicks lookup_start = TimeTicks::Now(); | 
|  | for (int i = 0; i < kTestLookupCount; ++i) { | 
|  | // 6007 is co-prime with kTestCreateCount and so will do lookups in an | 
|  | // order less likely to be cacheable (but still hit them all) should the | 
|  | // underlying storage use the exact histogram name as the key. | 
|  | const int i_mult = 6007; | 
|  | static_assert(i_mult < INT_MAX / kTestCreateCount, "Multiplier too big"); | 
|  | int index = (i * i_mult) & (kTestCreateCount - 1); | 
|  | Histogram::FactoryGet(histogram_names[index], 1, 100, 10, | 
|  | HistogramBase::kNoFlags); | 
|  | } | 
|  | TimeDelta lookup_ticks = TimeTicks::Now() - lookup_start; | 
|  | int64_t lookup_ms = lookup_ticks.InMilliseconds(); | 
|  |  | 
|  | VLOG(1) << kTestLookupCount << " histogram lookups took " << lookup_ms | 
|  | << "ms or about " | 
|  | << (lookup_ms * 1000000) / kTestLookupCount | 
|  | << "ns each."; | 
|  |  | 
|  | // Calculate cost of accessing histograms. | 
|  | HistogramBase* histogram = Histogram::FactoryGet( | 
|  | histogram_names[0], 1, 100, 10, HistogramBase::kNoFlags); | 
|  | ASSERT_TRUE(histogram); | 
|  | TimeTicks add_start = TimeTicks::Now(); | 
|  | for (int i = 0; i < kTestAddCount; ++i) | 
|  | histogram->Add(i & 127); | 
|  | TimeDelta add_ticks = TimeTicks::Now() - add_start; | 
|  | int64_t add_ms = add_ticks.InMilliseconds(); | 
|  |  | 
|  | VLOG(1) << kTestAddCount << " histogram adds took " << add_ms | 
|  | << "ms or about " | 
|  | << (add_ms * 1000000) / kTestAddCount | 
|  | << "ns each."; | 
|  | } | 
|  |  | 
|  | // For Histogram, LinearHistogram and CustomHistogram, the minimum for a | 
|  | // declared range is 1, while the maximum is (HistogramBase::kSampleType_MAX - | 
|  | // 1). But we accept ranges exceeding those limits, and silently clamped to | 
|  | // those limits. This is for backwards compatibility. | 
|  | TEST(HistogramDeathTest, BadRangesTest) { | 
|  | HistogramBase* histogram = Histogram::FactoryGet( | 
|  | "BadRanges", 0, HistogramBase::kSampleType_MAX, 8, | 
|  | HistogramBase::kNoFlags); | 
|  | EXPECT_TRUE( | 
|  | histogram->HasConstructionArguments( | 
|  | 1, HistogramBase::kSampleType_MAX - 1, 8)); | 
|  |  | 
|  | HistogramBase* linear_histogram = LinearHistogram::FactoryGet( | 
|  | "BadRangesLinear", 0, HistogramBase::kSampleType_MAX, 8, | 
|  | HistogramBase::kNoFlags); | 
|  | EXPECT_TRUE( | 
|  | linear_histogram->HasConstructionArguments( | 
|  | 1, HistogramBase::kSampleType_MAX - 1, 8)); | 
|  |  | 
|  | std::vector<int> custom_ranges; | 
|  | custom_ranges.push_back(0); | 
|  | custom_ranges.push_back(5); | 
|  | Histogram* custom_histogram = static_cast<Histogram*>( | 
|  | CustomHistogram::FactoryGet( | 
|  | "BadRangesCustom", custom_ranges, HistogramBase::kNoFlags)); | 
|  | const BucketRanges* ranges = custom_histogram->bucket_ranges(); | 
|  | ASSERT_EQ(3u, ranges->size()); | 
|  | EXPECT_EQ(0, ranges->range(0)); | 
|  | EXPECT_EQ(5, ranges->range(1)); | 
|  | EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2)); | 
|  |  | 
|  | // CustomHistogram does not accepts kSampleType_MAX as range. | 
|  | custom_ranges.push_back(HistogramBase::kSampleType_MAX); | 
|  | EXPECT_DEATH_IF_SUPPORTED( | 
|  | CustomHistogram::FactoryGet("BadRangesCustom2", custom_ranges, | 
|  | HistogramBase::kNoFlags), | 
|  | ""); | 
|  |  | 
|  | // CustomHistogram needs at least 1 valid range. | 
|  | custom_ranges.clear(); | 
|  | custom_ranges.push_back(0); | 
|  | EXPECT_DEATH_IF_SUPPORTED( | 
|  | CustomHistogram::FactoryGet("BadRangesCustom3", custom_ranges, | 
|  | HistogramBase::kNoFlags), | 
|  | ""); | 
|  | } | 
|  |  | 
|  | TEST_P(HistogramTest, ExpiredHistogramTest) { | 
|  | HistogramBase* expired = Histogram::FactoryGet(kExpiredHistogramName, 1, 1000, | 
|  | 10, HistogramBase::kNoFlags); | 
|  | ASSERT_TRUE(expired); | 
|  | expired->Add(5); | 
|  | expired->Add(500); | 
|  | auto samples = expired->SnapshotDelta(); | 
|  | EXPECT_EQ(0, samples->TotalCount()); | 
|  |  | 
|  | HistogramBase* linear_expired = LinearHistogram::FactoryGet( | 
|  | kExpiredHistogramName, 1, 1000, 10, HistogramBase::kNoFlags); | 
|  | ASSERT_TRUE(linear_expired); | 
|  | linear_expired->Add(5); | 
|  | linear_expired->Add(500); | 
|  | samples = linear_expired->SnapshotDelta(); | 
|  | EXPECT_EQ(0, samples->TotalCount()); | 
|  |  | 
|  | std::vector<int> custom_ranges; | 
|  | custom_ranges.push_back(1); | 
|  | custom_ranges.push_back(5); | 
|  | HistogramBase* custom_expired = CustomHistogram::FactoryGet( | 
|  | kExpiredHistogramName, custom_ranges, HistogramBase::kNoFlags); | 
|  | ASSERT_TRUE(custom_expired); | 
|  | custom_expired->Add(2); | 
|  | custom_expired->Add(4); | 
|  | samples = custom_expired->SnapshotDelta(); | 
|  | EXPECT_EQ(0, samples->TotalCount()); | 
|  |  | 
|  | HistogramBase* valid = Histogram::FactoryGet("ValidHistogram", 1, 1000, 10, | 
|  | HistogramBase::kNoFlags); | 
|  | ASSERT_TRUE(valid); | 
|  | valid->Add(5); | 
|  | valid->Add(500); | 
|  | samples = valid->SnapshotDelta(); | 
|  | EXPECT_EQ(2, samples->TotalCount()); | 
|  |  | 
|  | HistogramBase* linear_valid = LinearHistogram::FactoryGet( | 
|  | "LinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags); | 
|  | ASSERT_TRUE(linear_valid); | 
|  | linear_valid->Add(5); | 
|  | linear_valid->Add(500); | 
|  | samples = linear_valid->SnapshotDelta(); | 
|  | EXPECT_EQ(2, samples->TotalCount()); | 
|  |  | 
|  | HistogramBase* custom_valid = CustomHistogram::FactoryGet( | 
|  | "CustomHistogram", custom_ranges, HistogramBase::kNoFlags); | 
|  | ASSERT_TRUE(custom_valid); | 
|  | custom_valid->Add(2); | 
|  | custom_valid->Add(4); | 
|  | samples = custom_valid->SnapshotDelta(); | 
|  | EXPECT_EQ(2, samples->TotalCount()); | 
|  | } | 
|  |  | 
|  | }  // namespace base |