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