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