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