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