xref: /aosp_15_r20/external/cronet/base/metrics/persistent_histogram_allocator_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2016 The Chromium Authors
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/persistent_histogram_allocator.h"
6 
7 #include "base/files/file.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/memory/raw_ptr.h"
11 #include "base/metrics/bucket_ranges.h"
12 #include "base/metrics/histogram.h"
13 #include "base/metrics/histogram_functions.h"
14 #include "base/metrics/persistent_memory_allocator.h"
15 #include "base/metrics/sparse_histogram.h"
16 #include "base/metrics/statistics_recorder.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace base {
21 
22 class PersistentHistogramAllocatorTest : public testing::Test {
23  public:
24   PersistentHistogramAllocatorTest(const PersistentHistogramAllocatorTest&) =
25       delete;
26   PersistentHistogramAllocatorTest& operator=(
27       const PersistentHistogramAllocatorTest&) = delete;
28 
29  protected:
30   constexpr static int32_t kAllocatorMemorySize = 64 << 10;  // 64 KiB
31 
PersistentHistogramAllocatorTest()32   PersistentHistogramAllocatorTest()
33       : statistics_recorder_(StatisticsRecorder::CreateTemporaryForTesting()) {
34     CreatePersistentHistogramAllocator();
35   }
~PersistentHistogramAllocatorTest()36   ~PersistentHistogramAllocatorTest() override {
37     DestroyPersistentHistogramAllocator();
38   }
39 
CreatePersistentHistogramAllocator()40   void CreatePersistentHistogramAllocator() {
41     // GlobalHistogramAllocator is never deleted, hence intentionally leak
42     // allocated memory in this test.
43     allocator_memory_ = new char[kAllocatorMemorySize];
44     ANNOTATE_LEAKING_OBJECT_PTR(allocator_memory_);
45 
46     GlobalHistogramAllocator::ReleaseForTesting();
47     memset(allocator_memory_, 0, kAllocatorMemorySize);
48     GlobalHistogramAllocator::CreateWithPersistentMemory(
49         allocator_memory_, kAllocatorMemorySize, 0, 0,
50         "PersistentHistogramAllocatorTest");
51     allocator_ = GlobalHistogramAllocator::Get()->memory_allocator();
52   }
53 
DestroyPersistentHistogramAllocator()54   void DestroyPersistentHistogramAllocator() {
55     allocator_ = nullptr;
56     GlobalHistogramAllocator::ReleaseForTesting();
57   }
58 
59   std::unique_ptr<StatisticsRecorder> statistics_recorder_;
60   raw_ptr<char> allocator_memory_ = nullptr;
61   raw_ptr<PersistentMemoryAllocator> allocator_ = nullptr;
62 };
63 
TEST_F(PersistentHistogramAllocatorTest,CreateAndIterate)64 TEST_F(PersistentHistogramAllocatorTest, CreateAndIterate) {
65   PersistentMemoryAllocator::MemoryInfo meminfo0;
66   allocator_->GetMemoryInfo(&meminfo0);
67 
68   // Try basic construction
69   HistogramBase* histogram = Histogram::FactoryGet(
70       "TestHistogram", 1, 1000, 10, HistogramBase::kIsPersistent);
71   EXPECT_TRUE(histogram);
72   histogram->CheckName("TestHistogram");
73   PersistentMemoryAllocator::MemoryInfo meminfo1;
74   allocator_->GetMemoryInfo(&meminfo1);
75   EXPECT_GT(meminfo0.free, meminfo1.free);
76 
77   HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
78       "TestLinearHistogram", 1, 1000, 10, HistogramBase::kIsPersistent);
79   EXPECT_TRUE(linear_histogram);
80   linear_histogram->CheckName("TestLinearHistogram");
81   PersistentMemoryAllocator::MemoryInfo meminfo2;
82   allocator_->GetMemoryInfo(&meminfo2);
83   EXPECT_GT(meminfo1.free, meminfo2.free);
84 
85   HistogramBase* boolean_histogram = BooleanHistogram::FactoryGet(
86       "TestBooleanHistogram", HistogramBase::kIsPersistent);
87   EXPECT_TRUE(boolean_histogram);
88   boolean_histogram->CheckName("TestBooleanHistogram");
89   PersistentMemoryAllocator::MemoryInfo meminfo3;
90   allocator_->GetMemoryInfo(&meminfo3);
91   EXPECT_GT(meminfo2.free, meminfo3.free);
92 
93   std::vector<int> custom_ranges;
94   custom_ranges.push_back(1);
95   custom_ranges.push_back(5);
96   HistogramBase* custom_histogram = CustomHistogram::FactoryGet(
97       "TestCustomHistogram", custom_ranges, HistogramBase::kIsPersistent);
98   EXPECT_TRUE(custom_histogram);
99   custom_histogram->CheckName("TestCustomHistogram");
100   PersistentMemoryAllocator::MemoryInfo meminfo4;
101   allocator_->GetMemoryInfo(&meminfo4);
102   EXPECT_GT(meminfo3.free, meminfo4.free);
103 
104   PersistentMemoryAllocator::Iterator iter(allocator_);
105   uint32_t type;
106   EXPECT_NE(0U, iter.GetNext(&type));  // Histogram
107   EXPECT_NE(0U, iter.GetNext(&type));  // LinearHistogram
108   EXPECT_NE(0U, iter.GetNext(&type));  // BooleanHistogram
109   EXPECT_NE(0U, iter.GetNext(&type));  // CustomHistogram
110   EXPECT_EQ(0U, iter.GetNext(&type));
111 
112   // Create a second allocator and have it access the memory of the first.
113   std::unique_ptr<HistogramBase> recovered;
114   PersistentHistogramAllocator recovery(
115       std::make_unique<PersistentMemoryAllocator>(
116           allocator_memory_, kAllocatorMemorySize, 0, 0, "",
117           PersistentMemoryAllocator::kReadWrite));
118   PersistentHistogramAllocator::Iterator histogram_iter(&recovery);
119 
120   recovered = histogram_iter.GetNext();
121   ASSERT_TRUE(recovered);
122   recovered->CheckName("TestHistogram");
123 
124   recovered = histogram_iter.GetNext();
125   ASSERT_TRUE(recovered);
126   recovered->CheckName("TestLinearHistogram");
127 
128   recovered = histogram_iter.GetNext();
129   ASSERT_TRUE(recovered);
130   recovered->CheckName("TestBooleanHistogram");
131 
132   recovered = histogram_iter.GetNext();
133   ASSERT_TRUE(recovered);
134   recovered->CheckName("TestCustomHistogram");
135 
136   recovered = histogram_iter.GetNext();
137   EXPECT_FALSE(recovered);
138 }
139 
TEST_F(PersistentHistogramAllocatorTest,ConstructPaths)140 TEST_F(PersistentHistogramAllocatorTest, ConstructPaths) {
141   const FilePath dir_path(FILE_PATH_LITERAL("foo/"));
142   const std::string dir_string =
143       dir_path.NormalizePathSeparators().AsUTF8Unsafe();
144 
145   FilePath path = GlobalHistogramAllocator::ConstructFilePath(dir_path, "bar");
146   EXPECT_EQ(dir_string + "bar.pma", path.AsUTF8Unsafe());
147 
148   std::string name;
149   Time stamp;
150   ProcessId pid;
151   EXPECT_FALSE(
152       GlobalHistogramAllocator::ParseFilePath(path, &name, nullptr, nullptr));
153   EXPECT_FALSE(
154       GlobalHistogramAllocator::ParseFilePath(path, nullptr, &stamp, nullptr));
155   EXPECT_FALSE(
156       GlobalHistogramAllocator::ParseFilePath(path, nullptr, nullptr, &pid));
157 
158   path = GlobalHistogramAllocator::ConstructFilePathForUploadDir(
159       dir_path, "bar", Time::FromTimeT(12345), 6789);
160   EXPECT_EQ(dir_string + "bar-3039-1A85.pma", path.AsUTF8Unsafe());
161   ASSERT_TRUE(
162       GlobalHistogramAllocator::ParseFilePath(path, &name, &stamp, &pid));
163   EXPECT_EQ(name, "bar");
164   EXPECT_EQ(Time::FromTimeT(12345), stamp);
165   EXPECT_EQ(static_cast<ProcessId>(6789), pid);
166 }
167 
TEST_F(PersistentHistogramAllocatorTest,CreateWithFile)168 TEST_F(PersistentHistogramAllocatorTest, CreateWithFile) {
169   const char temp_name[] = "CreateWithFileTest";
170   ScopedTempDir temp_dir;
171   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
172   FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
173   const size_t temp_size = 64 << 10;  // 64 KiB
174 
175   // Test creation of a new file.
176   DestroyPersistentHistogramAllocator();
177   GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, temp_name);
178   EXPECT_EQ(std::string(temp_name),
179             GlobalHistogramAllocator::Get()->memory_allocator()->Name());
180 
181   // Test re-open of a possibly-existing file.
182   DestroyPersistentHistogramAllocator();
183   GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, "");
184   EXPECT_EQ(std::string(temp_name),
185             GlobalHistogramAllocator::Get()->memory_allocator()->Name());
186 
187   // Test re-open of an known-existing file.
188   DestroyPersistentHistogramAllocator();
189   GlobalHistogramAllocator::CreateWithFile(temp_file, 0, 0, "");
190   EXPECT_EQ(std::string(temp_name),
191             GlobalHistogramAllocator::Get()->memory_allocator()->Name());
192 
193   // Final release so file and temp-dir can be removed.
194   DestroyPersistentHistogramAllocator();
195 }
196 
TEST_F(PersistentHistogramAllocatorTest,CreateSpareFile)197 TEST_F(PersistentHistogramAllocatorTest, CreateSpareFile) {
198   const char temp_name[] = "CreateSpareFileTest.pma";
199   ScopedTempDir temp_dir;
200   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
201   FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
202   const size_t temp_size = 64 << 10;  // 64 KiB
203 
204   ASSERT_TRUE(GlobalHistogramAllocator::CreateSpareFile(temp_file, temp_size));
205 
206   File file(temp_file, File::FLAG_OPEN | File::FLAG_READ);
207   ASSERT_TRUE(file.IsValid());
208   EXPECT_EQ(static_cast<int64_t>(temp_size), file.GetLength());
209 
210   char buffer[256];
211   for (size_t pos = 0; pos < temp_size; pos += sizeof(buffer)) {
212     ASSERT_EQ(static_cast<int>(sizeof(buffer)),
213               file.ReadAtCurrentPos(buffer, sizeof(buffer)));
214     for (size_t i = 0; i < sizeof(buffer); ++i)
215       EXPECT_EQ(0, buffer[i]);
216   }
217 }
218 
TEST_F(PersistentHistogramAllocatorTest,StatisticsRecorderMerge)219 TEST_F(PersistentHistogramAllocatorTest, StatisticsRecorderMerge) {
220   const char LinearHistogramName[] = "SRTLinearHistogram";
221   const char SparseHistogramName[] = "SRTSparseHistogram";
222   const size_t global_sr_initial_histogram_count =
223       StatisticsRecorder::GetHistogramCount();
224   const size_t global_sr_initial_bucket_ranges_count =
225       StatisticsRecorder::GetBucketRanges().size();
226 
227   // Create a local StatisticsRecorder in which the newly created histogram
228   // will be recorded. The global allocator must be replaced after because the
229   // act of releasing will cause the active SR to forget about all histograms
230   // in the relased memory.
231   std::unique_ptr<StatisticsRecorder> local_sr =
232       StatisticsRecorder::CreateTemporaryForTesting();
233   EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount());
234   GlobalHistogramAllocator* old_allocator =
235       GlobalHistogramAllocator::ReleaseForTesting();
236   GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
237   ASSERT_TRUE(GlobalHistogramAllocator::Get());
238 
239   // Create a linear histogram for merge testing.
240   HistogramBase* histogram1 =
241       LinearHistogram::FactoryGet(LinearHistogramName, 1, 10, 10, 0);
242   ASSERT_TRUE(histogram1);
243   EXPECT_EQ(1U, StatisticsRecorder::GetHistogramCount());
244   histogram1->Add(3);
245   histogram1->Add(1);
246   histogram1->Add(4);
247   histogram1->AddCount(1, 4);
248   histogram1->Add(6);
249 
250   // Create a sparse histogram for merge testing.
251   HistogramBase* histogram2 =
252       SparseHistogram::FactoryGet(SparseHistogramName, 0);
253   ASSERT_TRUE(histogram2);
254   EXPECT_EQ(2U, StatisticsRecorder::GetHistogramCount());
255   histogram2->Add(3);
256   histogram2->Add(1);
257   histogram2->Add(4);
258   histogram2->AddCount(1, 4);
259   histogram2->Add(6);
260 
261   // Destroy the local SR and ensure that we're back to the initial state and
262   // restore the global allocator. Histograms created in the local SR will
263   // become unmanaged.
264   GlobalHistogramAllocator* new_allocator =
265       GlobalHistogramAllocator::ReleaseForTesting();
266   local_sr.reset();
267   EXPECT_EQ(global_sr_initial_histogram_count,
268             StatisticsRecorder::GetHistogramCount());
269   EXPECT_EQ(global_sr_initial_bucket_ranges_count,
270             StatisticsRecorder::GetBucketRanges().size());
271   GlobalHistogramAllocator::Set(old_allocator);
272 
273   // Create a "recovery" allocator using the same memory as the local one.
274   PersistentHistogramAllocator recovery1(
275       std::make_unique<PersistentMemoryAllocator>(
276           const_cast<void*>(new_allocator->memory_allocator()->data()),
277           new_allocator->memory_allocator()->size(), 0, 0, "",
278           PersistentMemoryAllocator::kReadWrite));
279   PersistentHistogramAllocator::Iterator histogram_iter1(&recovery1);
280 
281   // Get the histograms that were created locally (and forgotten) and merge
282   // them into the global SR. New objects will be created.
283   std::unique_ptr<HistogramBase> recovered;
284   while (true) {
285     recovered = histogram_iter1.GetNext();
286     if (!recovered)
287       break;
288 
289     recovery1.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
290     HistogramBase* found =
291         StatisticsRecorder::FindHistogram(recovered->histogram_name());
292     EXPECT_NE(recovered.get(), found);
293   }
294   EXPECT_EQ(global_sr_initial_histogram_count + 2,
295             StatisticsRecorder::GetHistogramCount());
296 
297   // Check the merged histograms for accuracy.
298   HistogramBase* found = StatisticsRecorder::FindHistogram(LinearHistogramName);
299   ASSERT_TRUE(found);
300   std::unique_ptr<HistogramSamples> snapshot = found->SnapshotSamples();
301   EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
302   EXPECT_EQ(1, snapshot->GetCount(3));
303   EXPECT_EQ(5, snapshot->GetCount(1));
304   EXPECT_EQ(1, snapshot->GetCount(4));
305   EXPECT_EQ(1, snapshot->GetCount(6));
306 
307   found = StatisticsRecorder::FindHistogram(SparseHistogramName);
308   ASSERT_TRUE(found);
309   snapshot = found->SnapshotSamples();
310   EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
311   EXPECT_EQ(1, snapshot->GetCount(3));
312   EXPECT_EQ(5, snapshot->GetCount(1));
313   EXPECT_EQ(1, snapshot->GetCount(4));
314   EXPECT_EQ(1, snapshot->GetCount(6));
315 
316   // Verify that the LinearHistogram's BucketRanges was registered with the
317   // global SR since the recovery allocator does not specify a custom
318   // RangesManager.
319   ASSERT_EQ(global_sr_initial_bucket_ranges_count + 1,
320             StatisticsRecorder::GetBucketRanges().size());
321 
322   // Perform additional histogram increments.
323   histogram1->AddCount(1, 3);
324   histogram1->Add(6);
325   histogram2->AddCount(1, 3);
326   histogram2->Add(7);
327 
328   // Do another merge.
329   PersistentHistogramAllocator recovery2(
330       std::make_unique<PersistentMemoryAllocator>(
331           const_cast<void*>(new_allocator->memory_allocator()->data()),
332           new_allocator->memory_allocator()->size(), 0, 0, "",
333           PersistentMemoryAllocator::kReadWrite));
334   PersistentHistogramAllocator::Iterator histogram_iter2(&recovery2);
335   while (true) {
336     recovered = histogram_iter2.GetNext();
337     if (!recovered)
338       break;
339     recovery2.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
340   }
341   EXPECT_EQ(global_sr_initial_histogram_count + 2,
342             StatisticsRecorder::GetHistogramCount());
343 
344   // And verify.
345   found = StatisticsRecorder::FindHistogram(LinearHistogramName);
346   snapshot = found->SnapshotSamples();
347   EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
348   EXPECT_EQ(1, snapshot->GetCount(3));
349   EXPECT_EQ(8, snapshot->GetCount(1));
350   EXPECT_EQ(1, snapshot->GetCount(4));
351   EXPECT_EQ(2, snapshot->GetCount(6));
352 
353   found = StatisticsRecorder::FindHistogram(SparseHistogramName);
354   snapshot = found->SnapshotSamples();
355   EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
356   EXPECT_EQ(1, snapshot->GetCount(3));
357   EXPECT_EQ(8, snapshot->GetCount(1));
358   EXPECT_EQ(1, snapshot->GetCount(4));
359   EXPECT_EQ(1, snapshot->GetCount(6));
360   EXPECT_EQ(1, snapshot->GetCount(7));
361 }
362 
363 // Verify that when merging histograms from an allocator with the global
364 // StatisticsRecorder, if the histogram has no samples to be merged, then it
365 // is skipped (no lookup/registration of the histogram with the SR).
TEST_F(PersistentHistogramAllocatorTest,StatisticsRecorderMerge_IsDefinitelyEmpty)366 TEST_F(PersistentHistogramAllocatorTest,
367        StatisticsRecorderMerge_IsDefinitelyEmpty) {
368   const size_t global_sr_initial_histogram_count =
369       StatisticsRecorder::GetHistogramCount();
370   const size_t global_sr_initial_bucket_ranges_count =
371       StatisticsRecorder::GetBucketRanges().size();
372 
373   // Create a local StatisticsRecorder in which the newly created histogram
374   // will be recorded. The global allocator must be replaced after because the
375   // act of releasing will cause the active SR to forget about all histograms
376   // in the released memory.
377   std::unique_ptr<StatisticsRecorder> local_sr =
378       StatisticsRecorder::CreateTemporaryForTesting();
379   EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount());
380   GlobalHistogramAllocator* old_allocator =
381       GlobalHistogramAllocator::ReleaseForTesting();
382   GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
383   ASSERT_TRUE(GlobalHistogramAllocator::Get());
384 
385   // Create a bunch of histograms, and call SnapshotDelta() on all of them so
386   // that their next SnapshotDelta() calls return an empty HistogramSamples.
387   LinearHistogram::FactoryGet("SRTLinearHistogram1", 1, 10, 10, 0);
388   HistogramBase* histogram2 =
389       LinearHistogram::FactoryGet("SRTLinearHistogram2", 1, 10, 10, 0);
390   histogram2->Add(3);
391   histogram2->SnapshotDelta();
392   HistogramBase* histogram3 =
393       LinearHistogram::FactoryGet("SRTLinearHistogram3", 1, 10, 10, 0);
394   histogram3->Add(1);
395   histogram3->Add(10);
396   histogram3->SnapshotDelta();
397   SparseHistogram::FactoryGet("SRTSparseHistogram1", 0);
398   HistogramBase* sparse_histogram2 =
399       SparseHistogram::FactoryGet("SRTSparseHistogram2", 0);
400   sparse_histogram2->Add(3);
401   sparse_histogram2->SnapshotDelta();
402   HistogramBase* sparse_histogram3 =
403       SparseHistogram::FactoryGet("SRTSparseHistogram3", 0);
404   sparse_histogram3->Add(1);
405   sparse_histogram3->Add(10);
406   sparse_histogram3->SnapshotDelta();
407 
408   EXPECT_EQ(6U, StatisticsRecorder::GetHistogramCount());
409 
410   // Destroy the local SR and ensure that we're back to the initial state and
411   // restore the global allocator. Histograms created in the local SR will
412   // become unmanaged.
413   GlobalHistogramAllocator* new_allocator =
414       GlobalHistogramAllocator::ReleaseForTesting();
415   local_sr.reset();
416   EXPECT_EQ(global_sr_initial_histogram_count,
417             StatisticsRecorder::GetHistogramCount());
418   EXPECT_EQ(global_sr_initial_bucket_ranges_count,
419             StatisticsRecorder::GetBucketRanges().size());
420   GlobalHistogramAllocator::Set(old_allocator);
421 
422   // Create a "recovery" allocator using the same memory as the local one.
423   PersistentHistogramAllocator recovery1(
424       std::make_unique<PersistentMemoryAllocator>(
425           const_cast<void*>(new_allocator->memory_allocator()->data()),
426           new_allocator->memory_allocator()->size(), 0, 0, "",
427           PersistentMemoryAllocator::kReadWrite));
428   PersistentHistogramAllocator::Iterator histogram_iter1(&recovery1);
429 
430   // Get the histograms that were created locally (and forgotten) and attempt
431   // to merge them into the global SR. Since their delta are all empty, nothing
432   // should end up being registered with the SR.
433   while (true) {
434     std::unique_ptr<HistogramBase> recovered = histogram_iter1.GetNext();
435     if (!recovered) {
436       break;
437     }
438 
439     recovery1.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
440     HistogramBase* found =
441         StatisticsRecorder::FindHistogram(recovered->histogram_name());
442     EXPECT_FALSE(found);
443   }
444   EXPECT_EQ(global_sr_initial_histogram_count,
445             StatisticsRecorder::GetHistogramCount());
446 
447   // Same as above, but with MergeHistogramFinalDeltaToStatisticsRecorder()
448   // instead of MergeHistogramDeltaToStatisticsRecorder().
449   PersistentHistogramAllocator recovery2(
450       std::make_unique<PersistentMemoryAllocator>(
451           const_cast<void*>(new_allocator->memory_allocator()->data()),
452           new_allocator->memory_allocator()->size(), 0, 0, "",
453           PersistentMemoryAllocator::kReadWrite));
454   PersistentHistogramAllocator::Iterator histogram_iter2(&recovery2);
455   while (true) {
456     std::unique_ptr<HistogramBase> recovered = histogram_iter2.GetNext();
457     if (!recovered) {
458       break;
459     }
460 
461     recovery2.MergeHistogramFinalDeltaToStatisticsRecorder(recovered.get());
462     HistogramBase* found =
463         StatisticsRecorder::FindHistogram(recovered->histogram_name());
464     EXPECT_FALSE(found);
465   }
466   EXPECT_EQ(global_sr_initial_histogram_count,
467             StatisticsRecorder::GetHistogramCount());
468 }
469 
TEST_F(PersistentHistogramAllocatorTest,MultipleSameSparseHistograms)470 TEST_F(PersistentHistogramAllocatorTest, MultipleSameSparseHistograms) {
471   const std::string kSparseHistogramName = "SRTSparseHistogram";
472 
473   // Create a temporary SR so that histograms created during this test aren't
474   // leaked to other tests.
475   std::unique_ptr<StatisticsRecorder> local_sr =
476       StatisticsRecorder::CreateTemporaryForTesting();
477 
478   // Create a sparse histogram.
479   HistogramBase* sparse = SparseHistogram::FactoryGet(kSparseHistogramName, 0);
480 
481   // Get the sparse histogram that was created above. We should have two
482   // distinct objects, but both representing and pointing to the same data.
483   PersistentHistogramAllocator::Iterator iter(GlobalHistogramAllocator::Get());
484   std::unique_ptr<HistogramBase> sparse2;
485   while (true) {
486     sparse2 = iter.GetNext();
487     if (!sparse2 || kSparseHistogramName == sparse2->histogram_name()) {
488       break;
489     }
490   }
491   ASSERT_TRUE(sparse2);
492   EXPECT_NE(sparse, sparse2.get());
493 
494   // Verify that both objects can coexist, i.e., samples emitted from one can be
495   // found by the other and vice versa.
496   sparse->AddCount(1, 3);
497   std::unique_ptr<HistogramSamples> snapshot =
498       sparse->SnapshotUnloggedSamples();
499   std::unique_ptr<HistogramSamples> snapshot2 =
500       sparse2->SnapshotUnloggedSamples();
501   EXPECT_EQ(snapshot->TotalCount(), 3);
502   EXPECT_EQ(snapshot2->TotalCount(), 3);
503   EXPECT_EQ(snapshot->GetCount(1), 3);
504   EXPECT_EQ(snapshot2->GetCount(1), 3);
505   snapshot = sparse->SnapshotDelta();
506   snapshot2 = sparse2->SnapshotDelta();
507   EXPECT_EQ(snapshot->TotalCount(), 3);
508   EXPECT_EQ(snapshot2->TotalCount(), 0);
509   EXPECT_EQ(snapshot->GetCount(1), 3);
510   EXPECT_EQ(snapshot2->GetCount(1), 0);
511 
512   sparse2->AddCount(2, 6);
513   snapshot = sparse->SnapshotUnloggedSamples();
514   snapshot2 = sparse2->SnapshotUnloggedSamples();
515   EXPECT_EQ(snapshot->TotalCount(), 6);
516   EXPECT_EQ(snapshot2->TotalCount(), 6);
517   EXPECT_EQ(snapshot->GetCount(2), 6);
518   EXPECT_EQ(snapshot2->GetCount(2), 6);
519   snapshot2 = sparse2->SnapshotDelta();
520   snapshot = sparse->SnapshotDelta();
521   EXPECT_EQ(snapshot->TotalCount(), 0);
522   EXPECT_EQ(snapshot2->TotalCount(), 6);
523   EXPECT_EQ(snapshot->GetCount(2), 0);
524   EXPECT_EQ(snapshot2->GetCount(2), 6);
525 }
526 
TEST_F(PersistentHistogramAllocatorTest,CustomRangesManager)527 TEST_F(PersistentHistogramAllocatorTest, CustomRangesManager) {
528   const char LinearHistogramName[] = "TestLinearHistogram";
529   const size_t global_sr_initial_bucket_ranges_count =
530       StatisticsRecorder::GetBucketRanges().size();
531 
532   // Create a local StatisticsRecorder in which the newly created histogram
533   // will be recorded. The global allocator must be replaced after because the
534   // act of releasing will cause the active SR to forget about all histograms
535   // in the released memory.
536   std::unique_ptr<StatisticsRecorder> local_sr =
537       StatisticsRecorder::CreateTemporaryForTesting();
538   EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount());
539   GlobalHistogramAllocator* old_allocator =
540       GlobalHistogramAllocator::ReleaseForTesting();
541   GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
542   ASSERT_TRUE(GlobalHistogramAllocator::Get());
543 
544   // Create a linear histogram and verify it is registered with the local SR.
545   HistogramBase* histogram = LinearHistogram::FactoryGet(
546       LinearHistogramName, /*minimum=*/1, /*maximum=*/10, /*bucket_count=*/10,
547       /*flags=*/0);
548   ASSERT_TRUE(histogram);
549   EXPECT_EQ(1U, StatisticsRecorder::GetHistogramCount());
550   histogram->Add(1);
551 
552   // Destroy the local SR and ensure that we're back to the initial state and
553   // restore the global allocator. The histogram created in the local SR will
554   // become unmanaged.
555   GlobalHistogramAllocator* new_allocator =
556       GlobalHistogramAllocator::ReleaseForTesting();
557   local_sr.reset();
558   EXPECT_EQ(global_sr_initial_bucket_ranges_count,
559             StatisticsRecorder::GetBucketRanges().size());
560   GlobalHistogramAllocator::Set(old_allocator);
561 
562   // Create a "recovery" allocator using the same memory as the local one.
563   PersistentHistogramAllocator recovery(
564       std::make_unique<PersistentMemoryAllocator>(
565           const_cast<void*>(new_allocator->memory_allocator()->data()),
566           new_allocator->memory_allocator()->size(), 0, 0, "",
567           PersistentMemoryAllocator::kReadWrite));
568 
569   // Set a custom RangesManager for the recovery allocator so that the
570   // BucketRanges are not registered with the global SR.
571   RangesManager* ranges_manager = new RangesManager();
572   recovery.SetRangesManager(ranges_manager);
573   EXPECT_EQ(0U, ranges_manager->GetBucketRanges().size());
574 
575   // Get the histogram that was created locally (and forgotten).
576   PersistentHistogramAllocator::Iterator histogram_iter1(&recovery);
577   std::unique_ptr<HistogramBase> recovered = histogram_iter1.GetNext();
578   ASSERT_TRUE(recovered);
579 
580   // Verify that there are no more histograms.
581   ASSERT_FALSE(histogram_iter1.GetNext());
582 
583   // Expect that the histogram's BucketRanges was not registered with the global
584   // statistics recorder since the recovery allocator specifies a custom
585   // RangesManager.
586   EXPECT_EQ(global_sr_initial_bucket_ranges_count,
587             StatisticsRecorder::GetBucketRanges().size());
588 
589   EXPECT_EQ(1U, ranges_manager->GetBucketRanges().size());
590 }
591 
TEST_F(PersistentHistogramAllocatorTest,RangesDeDuplication)592 TEST_F(PersistentHistogramAllocatorTest, RangesDeDuplication) {
593   // This corresponds to the "ranges_ref" field of the PersistentHistogramData
594   // structure defined (privately) inside persistent_histogram_allocator.cc.
595   const int kRangesRefIndex = 5;
596 
597   // Create two histograms with the same ranges.
598   HistogramBase* histogram1 =
599       Histogram::FactoryGet("TestHistogram1", 1, 1000, 10, 0);
600   HistogramBase* histogram2 =
601       Histogram::FactoryGet("TestHistogram2", 1, 1000, 10, 0);
602   const uint32_t ranges_ref = static_cast<Histogram*>(histogram1)
603                                   ->bucket_ranges()
604                                   ->persistent_reference();
605   ASSERT_NE(0U, ranges_ref);
606   EXPECT_EQ(ranges_ref, static_cast<Histogram*>(histogram2)
607                             ->bucket_ranges()
608                             ->persistent_reference());
609 
610   // Make sure that the persistent data record is also correct. Two histograms
611   // will be fetched; other allocations are not "iterable".
612   PersistentMemoryAllocator::Iterator iter(allocator_);
613   uint32_t type;
614   uint32_t ref1 = iter.GetNext(&type);
615   uint32_t ref2 = iter.GetNext(&type);
616   EXPECT_EQ(0U, iter.GetNext(&type));
617   EXPECT_NE(0U, ref1);
618   EXPECT_NE(0U, ref2);
619   EXPECT_NE(ref1, ref2);
620 
621   uint32_t* data1 =
622       allocator_->GetAsArray<uint32_t>(ref1, 0, kRangesRefIndex + 1);
623   uint32_t* data2 =
624       allocator_->GetAsArray<uint32_t>(ref2, 0, kRangesRefIndex + 1);
625   EXPECT_EQ(ranges_ref, data1[kRangesRefIndex]);
626   EXPECT_EQ(ranges_ref, data2[kRangesRefIndex]);
627 }
628 
TEST_F(PersistentHistogramAllocatorTest,MovePersistentFile)629 TEST_F(PersistentHistogramAllocatorTest, MovePersistentFile) {
630   const char temp_name[] = "MovePersistentFileTest.pma";
631   ScopedTempDir temp_dir;
632   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
633   FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
634   const size_t temp_size = 64 << 10;  // 64 KiB
635 
636   // Initialize persistent histogram system with a known file path.
637   DestroyPersistentHistogramAllocator();
638   GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, temp_name);
639   GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
640   ASSERT_TRUE(allocator->HasPersistentLocation());
641   EXPECT_EQ(allocator->GetPersistentLocation(), temp_file);
642   EXPECT_TRUE(base::PathExists(temp_file));
643 
644   // Move the persistent file to a new directory.
645   ScopedTempDir new_temp_dir;
646   ASSERT_TRUE(new_temp_dir.CreateUniqueTempDir());
647   EXPECT_TRUE(allocator->MovePersistentFile(new_temp_dir.GetPath()));
648 
649   // Verify that the persistent file was correctly moved |new_temp_dir|.
650   FilePath new_temp_file = new_temp_dir.GetPath().AppendASCII(temp_name);
651   ASSERT_TRUE(allocator->HasPersistentLocation());
652   EXPECT_EQ(allocator->GetPersistentLocation(), new_temp_file);
653   EXPECT_TRUE(base::PathExists(new_temp_file));
654   EXPECT_FALSE(base::PathExists(temp_file));
655 
656   // Emit a histogram after moving the file.
657   const char kHistogramName[] = "MovePersistentFile.Test";
658   base::UmaHistogramBoolean(kHistogramName, true);
659 
660   // Release the allocator.
661   DestroyPersistentHistogramAllocator();
662 
663   // Open and read the file in order to verify that |kHistogramName| was written
664   // to it even after being moved.
665   base::File file(new_temp_file, base::File::FLAG_OPEN | base::File::FLAG_READ);
666   std::unique_ptr<char[]> data = std::make_unique<char[]>(temp_size);
667   EXPECT_EQ(file.Read(/*offset=*/0, data.get(), temp_size),
668             static_cast<int>(temp_size));
669 
670   // Create an allocator and iterator using the file's data.
671   PersistentHistogramAllocator new_file_allocator(
672       std::make_unique<PersistentMemoryAllocator>(
673           data.get(), temp_size, 0, 0, "",
674           PersistentMemoryAllocator::kReadWrite));
675   PersistentHistogramAllocator::Iterator it(&new_file_allocator);
676 
677   // Verify that |kHistogramName| is in the file.
678   std::unique_ptr<HistogramBase> histogram;
679   bool found_histogram = false;
680   while ((histogram = it.GetNext()) != nullptr) {
681     if (strcmp(kHistogramName, histogram->histogram_name()) == 0) {
682       found_histogram = true;
683       break;
684     }
685   }
686   EXPECT_TRUE(found_histogram);
687 }
688 
689 }  // namespace base
690