1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
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/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/metrics/bucket_ranges.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/metrics/persistent_memory_allocator.h"
15 #include "base/metrics/statistics_recorder.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace base {
19 
20 class PersistentHistogramAllocatorTest : public testing::Test {
21  protected:
22   const int32_t kAllocatorMemorySize = 64 << 10;  // 64 KiB
23 
PersistentHistogramAllocatorTest()24   PersistentHistogramAllocatorTest()
25       : statistics_recorder_(StatisticsRecorder::CreateTemporaryForTesting()) {
26     CreatePersistentHistogramAllocator();
27   }
~PersistentHistogramAllocatorTest()28   ~PersistentHistogramAllocatorTest() override {
29     DestroyPersistentHistogramAllocator();
30   }
31 
CreatePersistentHistogramAllocator()32   void CreatePersistentHistogramAllocator() {
33     allocator_memory_.reset(new char[kAllocatorMemorySize]);
34 
35     GlobalHistogramAllocator::ReleaseForTesting();
36     memset(allocator_memory_.get(), 0, kAllocatorMemorySize);
37     GlobalHistogramAllocator::CreateWithPersistentMemory(
38         allocator_memory_.get(), kAllocatorMemorySize, 0, 0,
39         "PersistentHistogramAllocatorTest");
40     allocator_ = GlobalHistogramAllocator::Get()->memory_allocator();
41   }
42 
DestroyPersistentHistogramAllocator()43   void DestroyPersistentHistogramAllocator() {
44     allocator_ = nullptr;
45     GlobalHistogramAllocator::ReleaseForTesting();
46   }
47 
48   std::unique_ptr<StatisticsRecorder> statistics_recorder_;
49   std::unique_ptr<char[]> allocator_memory_;
50   PersistentMemoryAllocator* allocator_ = nullptr;
51 
52  private:
53   DISALLOW_COPY_AND_ASSIGN(PersistentHistogramAllocatorTest);
54 };
55 
TEST_F(PersistentHistogramAllocatorTest,CreateAndIterate)56 TEST_F(PersistentHistogramAllocatorTest, CreateAndIterate) {
57   PersistentMemoryAllocator::MemoryInfo meminfo0;
58   allocator_->GetMemoryInfo(&meminfo0);
59 
60   // Try basic construction
61   HistogramBase* histogram = Histogram::FactoryGet(
62       "TestHistogram", 1, 1000, 10, HistogramBase::kIsPersistent);
63   EXPECT_TRUE(histogram);
64   histogram->CheckName("TestHistogram");
65   PersistentMemoryAllocator::MemoryInfo meminfo1;
66   allocator_->GetMemoryInfo(&meminfo1);
67   EXPECT_GT(meminfo0.free, meminfo1.free);
68 
69   HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
70       "TestLinearHistogram", 1, 1000, 10, HistogramBase::kIsPersistent);
71   EXPECT_TRUE(linear_histogram);
72   linear_histogram->CheckName("TestLinearHistogram");
73   PersistentMemoryAllocator::MemoryInfo meminfo2;
74   allocator_->GetMemoryInfo(&meminfo2);
75   EXPECT_GT(meminfo1.free, meminfo2.free);
76 
77   HistogramBase* boolean_histogram = BooleanHistogram::FactoryGet(
78       "TestBooleanHistogram", HistogramBase::kIsPersistent);
79   EXPECT_TRUE(boolean_histogram);
80   boolean_histogram->CheckName("TestBooleanHistogram");
81   PersistentMemoryAllocator::MemoryInfo meminfo3;
82   allocator_->GetMemoryInfo(&meminfo3);
83   EXPECT_GT(meminfo2.free, meminfo3.free);
84 
85   std::vector<int> custom_ranges;
86   custom_ranges.push_back(1);
87   custom_ranges.push_back(5);
88   HistogramBase* custom_histogram = CustomHistogram::FactoryGet(
89       "TestCustomHistogram", custom_ranges, HistogramBase::kIsPersistent);
90   EXPECT_TRUE(custom_histogram);
91   custom_histogram->CheckName("TestCustomHistogram");
92   PersistentMemoryAllocator::MemoryInfo meminfo4;
93   allocator_->GetMemoryInfo(&meminfo4);
94   EXPECT_GT(meminfo3.free, meminfo4.free);
95 
96   PersistentMemoryAllocator::Iterator iter(allocator_);
97   uint32_t type;
98   EXPECT_NE(0U, iter.GetNext(&type));  // Histogram
99   EXPECT_NE(0U, iter.GetNext(&type));  // LinearHistogram
100   EXPECT_NE(0U, iter.GetNext(&type));  // BooleanHistogram
101   EXPECT_NE(0U, iter.GetNext(&type));  // CustomHistogram
102   EXPECT_EQ(0U, iter.GetNext(&type));
103 
104   // Create a second allocator and have it access the memory of the first.
105   std::unique_ptr<HistogramBase> recovered;
106   PersistentHistogramAllocator recovery(
107       std::make_unique<PersistentMemoryAllocator>(
108           allocator_memory_.get(), kAllocatorMemorySize, 0, 0, "", false));
109   PersistentHistogramAllocator::Iterator histogram_iter(&recovery);
110 
111   recovered = histogram_iter.GetNext();
112   ASSERT_TRUE(recovered);
113   recovered->CheckName("TestHistogram");
114 
115   recovered = histogram_iter.GetNext();
116   ASSERT_TRUE(recovered);
117   recovered->CheckName("TestLinearHistogram");
118 
119   recovered = histogram_iter.GetNext();
120   ASSERT_TRUE(recovered);
121   recovered->CheckName("TestBooleanHistogram");
122 
123   recovered = histogram_iter.GetNext();
124   ASSERT_TRUE(recovered);
125   recovered->CheckName("TestCustomHistogram");
126 
127   recovered = histogram_iter.GetNext();
128   EXPECT_FALSE(recovered);
129 }
130 
TEST_F(PersistentHistogramAllocatorTest,ConstructPaths)131 TEST_F(PersistentHistogramAllocatorTest, ConstructPaths) {
132   const FilePath dir_path(FILE_PATH_LITERAL("foo/"));
133   const std::string dir_string =
134       dir_path.NormalizePathSeparators().AsUTF8Unsafe();
135 
136   FilePath path = GlobalHistogramAllocator::ConstructFilePath(dir_path, "bar");
137   EXPECT_EQ(dir_string + "bar.pma", path.AsUTF8Unsafe());
138 
139   std::string name;
140   Time stamp;
141   ProcessId pid;
142   EXPECT_FALSE(
143       GlobalHistogramAllocator::ParseFilePath(path, &name, nullptr, nullptr));
144   EXPECT_FALSE(
145       GlobalHistogramAllocator::ParseFilePath(path, nullptr, &stamp, nullptr));
146   EXPECT_FALSE(
147       GlobalHistogramAllocator::ParseFilePath(path, nullptr, nullptr, &pid));
148 
149   path = GlobalHistogramAllocator::ConstructFilePathForUploadDir(
150       dir_path, "bar", Time::FromTimeT(12345), 6789);
151   EXPECT_EQ(dir_string + "bar-3039-1A85.pma", path.AsUTF8Unsafe());
152   ASSERT_TRUE(
153       GlobalHistogramAllocator::ParseFilePath(path, &name, &stamp, &pid));
154   EXPECT_EQ(name, "bar");
155   EXPECT_EQ(Time::FromTimeT(12345), stamp);
156   EXPECT_EQ(static_cast<ProcessId>(6789), pid);
157 }
158 
TEST_F(PersistentHistogramAllocatorTest,CreateWithFile)159 TEST_F(PersistentHistogramAllocatorTest, CreateWithFile) {
160   const char temp_name[] = "CreateWithFileTest";
161   ScopedTempDir temp_dir;
162   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
163   FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
164   const size_t temp_size = 64 << 10;  // 64 KiB
165 
166   // Test creation of a new file.
167   GlobalHistogramAllocator::ReleaseForTesting();
168   GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, temp_name);
169   EXPECT_EQ(std::string(temp_name),
170             GlobalHistogramAllocator::Get()->memory_allocator()->Name());
171 
172   // Test re-open of a possibly-existing file.
173   GlobalHistogramAllocator::ReleaseForTesting();
174   GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, "");
175   EXPECT_EQ(std::string(temp_name),
176             GlobalHistogramAllocator::Get()->memory_allocator()->Name());
177 
178   // Test re-open of an known-existing file.
179   GlobalHistogramAllocator::ReleaseForTesting();
180   GlobalHistogramAllocator::CreateWithFile(temp_file, 0, 0, "");
181   EXPECT_EQ(std::string(temp_name),
182             GlobalHistogramAllocator::Get()->memory_allocator()->Name());
183 
184   // Final release so file and temp-dir can be removed.
185   GlobalHistogramAllocator::ReleaseForTesting();
186 }
187 
TEST_F(PersistentHistogramAllocatorTest,CreateSpareFile)188 TEST_F(PersistentHistogramAllocatorTest, CreateSpareFile) {
189   const char temp_name[] = "CreateSpareFileTest.pma";
190   ScopedTempDir temp_dir;
191   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
192   FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
193   const size_t temp_size = 64 << 10;  // 64 KiB
194 
195   ASSERT_TRUE(GlobalHistogramAllocator::CreateSpareFile(temp_file, temp_size));
196 
197   File file(temp_file, File::FLAG_OPEN | File::FLAG_READ);
198   ASSERT_TRUE(file.IsValid());
199   EXPECT_EQ(static_cast<int64_t>(temp_size), file.GetLength());
200 
201   char buffer[256];
202   for (size_t pos = 0; pos < temp_size; pos += sizeof(buffer)) {
203     ASSERT_EQ(static_cast<int>(sizeof(buffer)),
204               file.ReadAtCurrentPos(buffer, sizeof(buffer)));
205     for (size_t i = 0; i < sizeof(buffer); ++i)
206       EXPECT_EQ(0, buffer[i]);
207   }
208 }
209 
TEST_F(PersistentHistogramAllocatorTest,StatisticsRecorderMerge)210 TEST_F(PersistentHistogramAllocatorTest, StatisticsRecorderMerge) {
211   const char LinearHistogramName[] = "SRTLinearHistogram";
212   const char SparseHistogramName[] = "SRTSparseHistogram";
213   const size_t starting_sr_count = StatisticsRecorder::GetHistogramCount();
214 
215   // Create a local StatisticsRecorder in which the newly created histogram
216   // will be recorded. The global allocator must be replaced after because the
217   // act of releasing will cause the active SR to forget about all histograms
218   // in the relased memory.
219   std::unique_ptr<StatisticsRecorder> local_sr =
220       StatisticsRecorder::CreateTemporaryForTesting();
221   EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount());
222   std::unique_ptr<GlobalHistogramAllocator> old_allocator =
223       GlobalHistogramAllocator::ReleaseForTesting();
224   GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
225   ASSERT_TRUE(GlobalHistogramAllocator::Get());
226 
227   // Create a linear histogram for merge testing.
228   HistogramBase* histogram1 =
229       LinearHistogram::FactoryGet(LinearHistogramName, 1, 10, 10, 0);
230   ASSERT_TRUE(histogram1);
231   EXPECT_EQ(1U, StatisticsRecorder::GetHistogramCount());
232   histogram1->Add(3);
233   histogram1->Add(1);
234   histogram1->Add(4);
235   histogram1->AddCount(1, 4);
236   histogram1->Add(6);
237 
238   // Create a sparse histogram for merge testing.
239   HistogramBase* histogram2 =
240       SparseHistogram::FactoryGet(SparseHistogramName, 0);
241   ASSERT_TRUE(histogram2);
242   EXPECT_EQ(2U, StatisticsRecorder::GetHistogramCount());
243   histogram2->Add(3);
244   histogram2->Add(1);
245   histogram2->Add(4);
246   histogram2->AddCount(1, 4);
247   histogram2->Add(6);
248 
249   // Destroy the local SR and ensure that we're back to the initial state and
250   // restore the global allocator. Histograms created in the local SR will
251   // become unmanaged.
252   std::unique_ptr<GlobalHistogramAllocator> new_allocator =
253       GlobalHistogramAllocator::ReleaseForTesting();
254   local_sr.reset();
255   EXPECT_EQ(starting_sr_count, StatisticsRecorder::GetHistogramCount());
256   GlobalHistogramAllocator::Set(std::move(old_allocator));
257 
258   // Create a "recovery" allocator using the same memory as the local one.
259   PersistentHistogramAllocator recovery1(
260       std::make_unique<PersistentMemoryAllocator>(
261           const_cast<void*>(new_allocator->memory_allocator()->data()),
262           new_allocator->memory_allocator()->size(), 0, 0, "", false));
263   PersistentHistogramAllocator::Iterator histogram_iter1(&recovery1);
264 
265   // Get the histograms that were created locally (and forgotten) and merge
266   // them into the global SR. New objects will be created.
267   std::unique_ptr<HistogramBase> recovered;
268   while (true) {
269     recovered = histogram_iter1.GetNext();
270     if (!recovered)
271       break;
272 
273     recovery1.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
274     HistogramBase* found =
275         StatisticsRecorder::FindHistogram(recovered->histogram_name());
276     EXPECT_NE(recovered.get(), found);
277   };
278   EXPECT_EQ(starting_sr_count + 2, StatisticsRecorder::GetHistogramCount());
279 
280   // Check the merged histograms for accuracy.
281   HistogramBase* found = StatisticsRecorder::FindHistogram(LinearHistogramName);
282   ASSERT_TRUE(found);
283   std::unique_ptr<HistogramSamples> snapshot = found->SnapshotSamples();
284   EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
285   EXPECT_EQ(1, snapshot->GetCount(3));
286   EXPECT_EQ(5, snapshot->GetCount(1));
287   EXPECT_EQ(1, snapshot->GetCount(4));
288   EXPECT_EQ(1, snapshot->GetCount(6));
289 
290   found = StatisticsRecorder::FindHistogram(SparseHistogramName);
291   ASSERT_TRUE(found);
292   snapshot = found->SnapshotSamples();
293   EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
294   EXPECT_EQ(1, snapshot->GetCount(3));
295   EXPECT_EQ(5, snapshot->GetCount(1));
296   EXPECT_EQ(1, snapshot->GetCount(4));
297   EXPECT_EQ(1, snapshot->GetCount(6));
298 
299   // Perform additional histogram increments.
300   histogram1->AddCount(1, 3);
301   histogram1->Add(6);
302   histogram2->AddCount(1, 3);
303   histogram2->Add(7);
304 
305   // Do another merge.
306   PersistentHistogramAllocator recovery2(
307       std::make_unique<PersistentMemoryAllocator>(
308           const_cast<void*>(new_allocator->memory_allocator()->data()),
309           new_allocator->memory_allocator()->size(), 0, 0, "", false));
310   PersistentHistogramAllocator::Iterator histogram_iter2(&recovery2);
311   while (true) {
312     recovered = histogram_iter2.GetNext();
313     if (!recovered)
314       break;
315     recovery2.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
316   };
317   EXPECT_EQ(starting_sr_count + 2, StatisticsRecorder::GetHistogramCount());
318 
319   // And verify.
320   found = StatisticsRecorder::FindHistogram(LinearHistogramName);
321   snapshot = found->SnapshotSamples();
322   EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
323   EXPECT_EQ(1, snapshot->GetCount(3));
324   EXPECT_EQ(8, snapshot->GetCount(1));
325   EXPECT_EQ(1, snapshot->GetCount(4));
326   EXPECT_EQ(2, snapshot->GetCount(6));
327 
328   found = StatisticsRecorder::FindHistogram(SparseHistogramName);
329   snapshot = found->SnapshotSamples();
330   EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
331   EXPECT_EQ(1, snapshot->GetCount(3));
332   EXPECT_EQ(8, snapshot->GetCount(1));
333   EXPECT_EQ(1, snapshot->GetCount(4));
334   EXPECT_EQ(1, snapshot->GetCount(6));
335   EXPECT_EQ(1, snapshot->GetCount(7));
336 }
337 
TEST_F(PersistentHistogramAllocatorTest,RangesDeDuplication)338 TEST_F(PersistentHistogramAllocatorTest, RangesDeDuplication) {
339   // This corresponds to the "ranges_ref" field of the PersistentHistogramData
340   // structure defined (privately) inside persistent_histogram_allocator.cc.
341   const int kRangesRefIndex = 5;
342 
343   // Create two histograms with the same ranges.
344   HistogramBase* histogram1 =
345       Histogram::FactoryGet("TestHistogram1", 1, 1000, 10, 0);
346   HistogramBase* histogram2 =
347       Histogram::FactoryGet("TestHistogram2", 1, 1000, 10, 0);
348   const uint32_t ranges_ref = static_cast<Histogram*>(histogram1)
349                                   ->bucket_ranges()
350                                   ->persistent_reference();
351   ASSERT_NE(0U, ranges_ref);
352   EXPECT_EQ(ranges_ref, static_cast<Histogram*>(histogram2)
353                             ->bucket_ranges()
354                             ->persistent_reference());
355 
356   // Make sure that the persistent data record is also correct. Two histograms
357   // will be fetched; other allocations are not "iterable".
358   PersistentMemoryAllocator::Iterator iter(allocator_);
359   uint32_t type;
360   uint32_t ref1 = iter.GetNext(&type);
361   uint32_t ref2 = iter.GetNext(&type);
362   EXPECT_EQ(0U, iter.GetNext(&type));
363   EXPECT_NE(0U, ref1);
364   EXPECT_NE(0U, ref2);
365   EXPECT_NE(ref1, ref2);
366 
367   uint32_t* data1 =
368       allocator_->GetAsArray<uint32_t>(ref1, 0, kRangesRefIndex + 1);
369   uint32_t* data2 =
370       allocator_->GetAsArray<uint32_t>(ref2, 0, kRangesRefIndex + 1);
371   EXPECT_EQ(ranges_ref, data1[kRangesRefIndex]);
372   EXPECT_EQ(ranges_ref, data2[kRangesRefIndex]);
373 }
374 
375 }  // namespace base
376