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