sparse_histogram_unittest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  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. #include "base/metrics/sparse_histogram.h"
  5. #include <memory>
  6. #include <string>
  7. #include <vector>
  8. #include "base/logging.h"
  9. #include "base/memory/raw_ptr.h"
  10. #include "base/metrics/histogram_base.h"
  11. #include "base/metrics/histogram_functions.h"
  12. #include "base/metrics/histogram_samples.h"
  13. #include "base/metrics/metrics_hashes.h"
  14. #include "base/metrics/persistent_histogram_allocator.h"
  15. #include "base/metrics/persistent_memory_allocator.h"
  16. #include "base/metrics/sample_map.h"
  17. #include "base/metrics/statistics_recorder.h"
  18. #include "base/pickle.h"
  19. #include "base/strings/stringprintf.h"
  20. #include "base/values.h"
  21. #include "testing/gmock/include/gmock/gmock.h"
  22. #include "testing/gtest/include/gtest/gtest.h"
  23. namespace base {
  24. // Test parameter indicates if a persistent memory allocator should be used
  25. // for histogram allocation. False will allocate histograms from the process
  26. // heap.
  27. class SparseHistogramTest : public testing::TestWithParam<bool> {
  28. public:
  29. SparseHistogramTest() : use_persistent_histogram_allocator_(GetParam()) {}
  30. SparseHistogramTest(const SparseHistogramTest&) = delete;
  31. SparseHistogramTest& operator=(const SparseHistogramTest&) = delete;
  32. protected:
  33. const int32_t kAllocatorMemorySize = 8 << 20; // 8 MiB
  34. using CountAndBucketData = base::SparseHistogram::CountAndBucketData;
  35. void SetUp() override {
  36. if (use_persistent_histogram_allocator_)
  37. CreatePersistentMemoryAllocator();
  38. // Each test will have a clean state (no Histogram / BucketRanges
  39. // registered).
  40. InitializeStatisticsRecorder();
  41. }
  42. void TearDown() override {
  43. if (allocator_) {
  44. ASSERT_FALSE(allocator_->IsFull());
  45. ASSERT_FALSE(allocator_->IsCorrupt());
  46. }
  47. UninitializeStatisticsRecorder();
  48. DestroyPersistentMemoryAllocator();
  49. }
  50. void InitializeStatisticsRecorder() {
  51. DCHECK(!statistics_recorder_);
  52. statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting();
  53. }
  54. void UninitializeStatisticsRecorder() { statistics_recorder_.reset(); }
  55. void CreatePersistentMemoryAllocator() {
  56. GlobalHistogramAllocator::CreateWithLocalMemory(
  57. kAllocatorMemorySize, 0, "SparseHistogramAllocatorTest");
  58. allocator_ = GlobalHistogramAllocator::Get()->memory_allocator();
  59. }
  60. void DestroyPersistentMemoryAllocator() {
  61. allocator_ = nullptr;
  62. GlobalHistogramAllocator::ReleaseForTesting();
  63. }
  64. std::unique_ptr<SparseHistogram> NewSparseHistogram(const char* name) {
  65. // std::make_unique can't access protected ctor so do it manually. This
  66. // test class is a friend so can access it.
  67. return std::unique_ptr<SparseHistogram>(new SparseHistogram(name));
  68. }
  69. CountAndBucketData GetCountAndBucketData(SparseHistogram* histogram) {
  70. // A simple wrapper around |GetCountAndBucketData| to make it visible for
  71. // testing.
  72. return histogram->GetCountAndBucketData();
  73. }
  74. const bool use_persistent_histogram_allocator_;
  75. std::unique_ptr<StatisticsRecorder> statistics_recorder_;
  76. raw_ptr<PersistentMemoryAllocator> allocator_ = nullptr;
  77. };
  78. // Run all HistogramTest cases with both heap and persistent memory.
  79. INSTANTIATE_TEST_SUITE_P(HeapAndPersistent,
  80. SparseHistogramTest,
  81. testing::Bool());
  82. TEST_P(SparseHistogramTest, BasicTest) {
  83. std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
  84. std::unique_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
  85. EXPECT_EQ(0, snapshot->TotalCount());
  86. EXPECT_EQ(0, snapshot->sum());
  87. histogram->Add(100);
  88. std::unique_ptr<HistogramSamples> snapshot1(histogram->SnapshotSamples());
  89. EXPECT_EQ(1, snapshot1->TotalCount());
  90. EXPECT_EQ(1, snapshot1->GetCount(100));
  91. histogram->Add(100);
  92. histogram->Add(101);
  93. std::unique_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
  94. EXPECT_EQ(3, snapshot2->TotalCount());
  95. EXPECT_EQ(2, snapshot2->GetCount(100));
  96. EXPECT_EQ(1, snapshot2->GetCount(101));
  97. }
  98. TEST_P(SparseHistogramTest, BasicTestAddCount) {
  99. std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
  100. std::unique_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
  101. EXPECT_EQ(0, snapshot->TotalCount());
  102. EXPECT_EQ(0, snapshot->sum());
  103. histogram->AddCount(100, 15);
  104. std::unique_ptr<HistogramSamples> snapshot1(histogram->SnapshotSamples());
  105. EXPECT_EQ(15, snapshot1->TotalCount());
  106. EXPECT_EQ(15, snapshot1->GetCount(100));
  107. histogram->AddCount(100, 15);
  108. histogram->AddCount(101, 25);
  109. std::unique_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
  110. EXPECT_EQ(55, snapshot2->TotalCount());
  111. EXPECT_EQ(30, snapshot2->GetCount(100));
  112. EXPECT_EQ(25, snapshot2->GetCount(101));
  113. }
  114. TEST_P(SparseHistogramTest, AddCount_LargeValuesDontOverflow) {
  115. std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
  116. std::unique_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
  117. EXPECT_EQ(0, snapshot->TotalCount());
  118. EXPECT_EQ(0, snapshot->sum());
  119. histogram->AddCount(1000000000, 15);
  120. std::unique_ptr<HistogramSamples> snapshot1(histogram->SnapshotSamples());
  121. EXPECT_EQ(15, snapshot1->TotalCount());
  122. EXPECT_EQ(15, snapshot1->GetCount(1000000000));
  123. histogram->AddCount(1000000000, 15);
  124. histogram->AddCount(1010000000, 25);
  125. std::unique_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
  126. EXPECT_EQ(55, snapshot2->TotalCount());
  127. EXPECT_EQ(30, snapshot2->GetCount(1000000000));
  128. EXPECT_EQ(25, snapshot2->GetCount(1010000000));
  129. EXPECT_EQ(55250000000LL, snapshot2->sum());
  130. }
  131. // Make sure that counts returned by Histogram::SnapshotDelta do not overflow
  132. // even when a total count (returned by Histogram::SnapshotSample) does.
  133. TEST_P(SparseHistogramTest, AddCount_LargeCountsDontOverflow) {
  134. std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
  135. std::unique_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
  136. EXPECT_EQ(0, snapshot->TotalCount());
  137. EXPECT_EQ(0, snapshot->sum());
  138. const int count = (1 << 30) - 1;
  139. // Repeat N times to make sure that there is no internal value overflow.
  140. for (int i = 0; i < 10; ++i) {
  141. histogram->AddCount(42, count);
  142. std::unique_ptr<HistogramSamples> samples = histogram->SnapshotDelta();
  143. EXPECT_EQ(count, samples->TotalCount());
  144. EXPECT_EQ(count, samples->GetCount(42));
  145. }
  146. }
  147. TEST_P(SparseHistogramTest, MacroBasicTest) {
  148. UmaHistogramSparse("Sparse", 100);
  149. UmaHistogramSparse("Sparse", 200);
  150. UmaHistogramSparse("Sparse", 100);
  151. const StatisticsRecorder::Histograms histograms =
  152. StatisticsRecorder::GetHistograms();
  153. ASSERT_THAT(histograms, testing::SizeIs(1));
  154. const HistogramBase* const sparse_histogram = histograms[0];
  155. EXPECT_EQ(SPARSE_HISTOGRAM, sparse_histogram->GetHistogramType());
  156. EXPECT_EQ("Sparse", StringPiece(sparse_histogram->histogram_name()));
  157. EXPECT_EQ(
  158. HistogramBase::kUmaTargetedHistogramFlag |
  159. (use_persistent_histogram_allocator_ ? HistogramBase::kIsPersistent
  160. : 0),
  161. sparse_histogram->flags());
  162. std::unique_ptr<HistogramSamples> samples =
  163. sparse_histogram->SnapshotSamples();
  164. EXPECT_EQ(3, samples->TotalCount());
  165. EXPECT_EQ(2, samples->GetCount(100));
  166. EXPECT_EQ(1, samples->GetCount(200));
  167. }
  168. TEST_P(SparseHistogramTest, MacroInLoopTest) {
  169. // Unlike the macros in histogram.h, SparseHistogram macros can have a
  170. // variable as histogram name.
  171. for (int i = 0; i < 2; i++) {
  172. UmaHistogramSparse(StringPrintf("Sparse%d", i), 100);
  173. }
  174. const StatisticsRecorder::Histograms histograms =
  175. StatisticsRecorder::Sort(StatisticsRecorder::GetHistograms());
  176. ASSERT_THAT(histograms, testing::SizeIs(2));
  177. EXPECT_STREQ(histograms[0]->histogram_name(), "Sparse0");
  178. EXPECT_STREQ(histograms[1]->histogram_name(), "Sparse1");
  179. }
  180. TEST_P(SparseHistogramTest, Serialize) {
  181. std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
  182. histogram->SetFlags(HistogramBase::kIPCSerializationSourceFlag);
  183. Pickle pickle;
  184. histogram->SerializeInfo(&pickle);
  185. PickleIterator iter(pickle);
  186. int type;
  187. EXPECT_TRUE(iter.ReadInt(&type));
  188. EXPECT_EQ(SPARSE_HISTOGRAM, type);
  189. std::string name;
  190. EXPECT_TRUE(iter.ReadString(&name));
  191. EXPECT_EQ("Sparse", name);
  192. int flag;
  193. EXPECT_TRUE(iter.ReadInt(&flag));
  194. EXPECT_EQ(HistogramBase::kIPCSerializationSourceFlag, flag);
  195. // No more data in the pickle.
  196. EXPECT_FALSE(iter.SkipBytes(1));
  197. }
  198. // Ensure that race conditions that cause multiple, identical sparse histograms
  199. // to be created will safely resolve to a single one.
  200. TEST_P(SparseHistogramTest, DuplicationSafety) {
  201. const char histogram_name[] = "Duplicated";
  202. size_t histogram_count = StatisticsRecorder::GetHistogramCount();
  203. // Create a histogram that we will later duplicate.
  204. HistogramBase* original =
  205. SparseHistogram::FactoryGet(histogram_name, HistogramBase::kNoFlags);
  206. ++histogram_count;
  207. DCHECK_EQ(histogram_count, StatisticsRecorder::GetHistogramCount());
  208. original->Add(1);
  209. // Create a duplicate. This has to happen differently depending on where the
  210. // memory is taken from.
  211. if (use_persistent_histogram_allocator_) {
  212. // To allocate from persistent memory, clear the last_created reference in
  213. // the GlobalHistogramAllocator. This will cause an Import to recreate
  214. // the just-created histogram which will then be released as a duplicate.
  215. GlobalHistogramAllocator::Get()->ClearLastCreatedReferenceForTesting();
  216. // Creating a different histogram will first do an Import to ensure it
  217. // hasn't been created elsewhere, triggering the duplication and release.
  218. SparseHistogram::FactoryGet("something.new", HistogramBase::kNoFlags);
  219. ++histogram_count;
  220. } else {
  221. // To allocate from the heap, just call the (private) constructor directly.
  222. // Delete it immediately like would have happened within FactoryGet();
  223. std::unique_ptr<SparseHistogram> something =
  224. NewSparseHistogram(histogram_name);
  225. DCHECK_NE(original, something.get());
  226. }
  227. DCHECK_EQ(histogram_count, StatisticsRecorder::GetHistogramCount());
  228. // Re-creating the histogram via FactoryGet() will return the same one.
  229. HistogramBase* duplicate =
  230. SparseHistogram::FactoryGet(histogram_name, HistogramBase::kNoFlags);
  231. DCHECK_EQ(original, duplicate);
  232. DCHECK_EQ(histogram_count, StatisticsRecorder::GetHistogramCount());
  233. duplicate->Add(2);
  234. // Ensure that original histograms are still cross-functional.
  235. original->Add(2);
  236. duplicate->Add(1);
  237. std::unique_ptr<HistogramSamples> snapshot_orig = original->SnapshotSamples();
  238. std::unique_ptr<HistogramSamples> snapshot_dup = duplicate->SnapshotSamples();
  239. DCHECK_EQ(2, snapshot_orig->GetCount(2));
  240. DCHECK_EQ(2, snapshot_dup->GetCount(1));
  241. }
  242. TEST_P(SparseHistogramTest, FactoryTime) {
  243. const int kTestCreateCount = 1 << 10; // Must be power-of-2.
  244. const int kTestLookupCount = 100000;
  245. const int kTestAddCount = 100000;
  246. // Create all histogram names in advance for accurate timing below.
  247. std::vector<std::string> histogram_names;
  248. for (int i = 0; i < kTestCreateCount; ++i) {
  249. histogram_names.push_back(
  250. StringPrintf("TestHistogram.%d", i % kTestCreateCount));
  251. }
  252. // Calculate cost of creating histograms.
  253. TimeTicks create_start = TimeTicks::Now();
  254. for (int i = 0; i < kTestCreateCount; ++i)
  255. SparseHistogram::FactoryGet(histogram_names[i], HistogramBase::kNoFlags);
  256. TimeDelta create_ticks = TimeTicks::Now() - create_start;
  257. int64_t create_ms = create_ticks.InMilliseconds();
  258. VLOG(1) << kTestCreateCount << " histogram creations took " << create_ms
  259. << "ms or about " << (create_ms * 1000000) / kTestCreateCount
  260. << "ns each.";
  261. // Calculate cost of looking up existing histograms.
  262. TimeTicks lookup_start = TimeTicks::Now();
  263. for (int i = 0; i < kTestLookupCount; ++i) {
  264. // 6007 is co-prime with kTestCreateCount and so will do lookups in an
  265. // order less likely to be cacheable (but still hit them all) should the
  266. // underlying storage use the exact histogram name as the key.
  267. const int i_mult = 6007;
  268. static_assert(i_mult < INT_MAX / kTestCreateCount, "Multiplier too big");
  269. int index = (i * i_mult) & (kTestCreateCount - 1);
  270. SparseHistogram::FactoryGet(histogram_names[index],
  271. HistogramBase::kNoFlags);
  272. }
  273. TimeDelta lookup_ticks = TimeTicks::Now() - lookup_start;
  274. int64_t lookup_ms = lookup_ticks.InMilliseconds();
  275. VLOG(1) << kTestLookupCount << " histogram lookups took " << lookup_ms
  276. << "ms or about " << (lookup_ms * 1000000) / kTestLookupCount
  277. << "ns each.";
  278. // Calculate cost of accessing histograms.
  279. HistogramBase* histogram =
  280. SparseHistogram::FactoryGet(histogram_names[0], HistogramBase::kNoFlags);
  281. ASSERT_TRUE(histogram);
  282. TimeTicks add_start = TimeTicks::Now();
  283. for (int i = 0; i < kTestAddCount; ++i)
  284. histogram->Add(i & 127);
  285. TimeDelta add_ticks = TimeTicks::Now() - add_start;
  286. int64_t add_ms = add_ticks.InMilliseconds();
  287. VLOG(1) << kTestAddCount << " histogram adds took " << add_ms
  288. << "ms or about " << (add_ms * 1000000) / kTestAddCount << "ns each.";
  289. }
  290. TEST_P(SparseHistogramTest, ExtremeValues) {
  291. static const struct {
  292. Histogram::Sample sample;
  293. int64_t expected_max;
  294. } cases[] = {
  295. // Note: We use -2147483647 - 1 rather than -2147483648 because the later
  296. // is interpreted as - operator applied to 2147483648 and the latter can't
  297. // be represented as an int32 and causes a warning.
  298. {-2147483647 - 1, -2147483647LL},
  299. {0, 1},
  300. {2147483647, 2147483648LL},
  301. };
  302. for (size_t i = 0; i < std::size(cases); ++i) {
  303. HistogramBase* histogram =
  304. SparseHistogram::FactoryGet(StringPrintf("ExtremeValues_%zu", i),
  305. HistogramBase::kUmaTargetedHistogramFlag);
  306. histogram->Add(cases[i].sample);
  307. std::unique_ptr<HistogramSamples> snapshot = histogram->SnapshotSamples();
  308. std::unique_ptr<SampleCountIterator> it = snapshot->Iterator();
  309. ASSERT_FALSE(it->Done());
  310. base::Histogram::Sample min;
  311. int64_t max;
  312. base::Histogram::Count count;
  313. it->Get(&min, &max, &count);
  314. EXPECT_EQ(1, count);
  315. EXPECT_EQ(cases[i].sample, min);
  316. EXPECT_EQ(cases[i].expected_max, max);
  317. it->Next();
  318. EXPECT_TRUE(it->Done());
  319. }
  320. }
  321. TEST_P(SparseHistogramTest, HistogramNameHash) {
  322. const char kName[] = "TestName";
  323. HistogramBase* histogram = SparseHistogram::FactoryGet(
  324. kName, HistogramBase::kUmaTargetedHistogramFlag);
  325. EXPECT_EQ(histogram->name_hash(), HashMetricName(kName));
  326. }
  327. TEST_P(SparseHistogramTest, CheckGetCountAndBucketData) {
  328. std::unique_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse"));
  329. // Add samples in reverse order and make sure the output is in correct order.
  330. histogram->AddCount(/*sample=*/200, /*count=*/15);
  331. histogram->AddCount(/*sample=*/100, /*count=*/5);
  332. // Add samples to the same bucket and make sure they'll be aggregated.
  333. histogram->AddCount(/*sample=*/100, /*count=*/5);
  334. const CountAndBucketData count_and_data_bucket =
  335. GetCountAndBucketData(histogram.get());
  336. EXPECT_EQ(25, count_and_data_bucket.count);
  337. EXPECT_EQ(4000, count_and_data_bucket.sum);
  338. const base::Value::List& buckets_list = count_and_data_bucket.buckets;
  339. ASSERT_EQ(2u, buckets_list.size());
  340. // Check the first bucket.
  341. const base::Value::Dict* bucket1 = buckets_list[0].GetIfDict();
  342. ASSERT_TRUE(bucket1 != nullptr);
  343. EXPECT_EQ(bucket1->FindInt("low"), absl::optional<int>(100));
  344. EXPECT_EQ(bucket1->FindInt("high"), absl::optional<int>(101));
  345. EXPECT_EQ(bucket1->FindInt("count"), absl::optional<int>(10));
  346. // Check the second bucket.
  347. const base::Value::Dict* bucket2 = buckets_list[1].GetIfDict();
  348. ASSERT_TRUE(bucket2 != nullptr);
  349. EXPECT_EQ(bucket2->FindInt("low"), absl::optional<int>(200));
  350. EXPECT_EQ(bucket2->FindInt("high"), absl::optional<int>(201));
  351. EXPECT_EQ(bucket2->FindInt("count"), absl::optional<int>(15));
  352. }
  353. TEST_P(SparseHistogramTest, WriteAscii) {
  354. HistogramBase* histogram =
  355. SparseHistogram::FactoryGet("AsciiOut", HistogramBase::kNoFlags);
  356. histogram->AddCount(/*sample=*/4, /*count=*/5);
  357. histogram->AddCount(/*sample=*/10, /*count=*/15);
  358. std::string output;
  359. histogram->WriteAscii(&output);
  360. const char kOutputFormatRe[] =
  361. R"(Histogram: AsciiOut recorded 20 samples.*\n)"
  362. R"(4 -+O +\(5 = 25.0%\)\n)"
  363. R"(10 -+O +\(15 = 75.0%\)\n)";
  364. EXPECT_THAT(output, testing::MatchesRegex(kOutputFormatRe));
  365. }
  366. TEST_P(SparseHistogramTest, ToGraphDict) {
  367. HistogramBase* histogram =
  368. SparseHistogram::FactoryGet("HTMLOut", HistogramBase::kNoFlags);
  369. histogram->AddCount(/*sample=*/4, /*count=*/5);
  370. histogram->AddCount(/*sample=*/10, /*count=*/15);
  371. base::Value::Dict output = histogram->ToGraphDict();
  372. std::string* header = output.FindString("header");
  373. std::string* body = output.FindString("body");
  374. const char kOutputHeaderFormatRe[] =
  375. R"(Histogram: HTMLOut recorded 20 samples.*)";
  376. const char kOutputBodyFormatRe[] = R"(4 -+O +\(5 = 25.0%\)\n)"
  377. R"(10 -+O +\(15 = 75.0%\)\n)";
  378. EXPECT_THAT(*header, testing::MatchesRegex(kOutputHeaderFormatRe));
  379. EXPECT_THAT(*body, testing::MatchesRegex(kOutputBodyFormatRe));
  380. }
  381. } // namespace base