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/scoped_temp_dir.h"
8#include "base/logging.h"
9#include "base/memory/ptr_util.h"
10#include "base/metrics/bucket_ranges.h"
11#include "base/metrics/histogram_macros.h"
12#include "base/metrics/persistent_memory_allocator.h"
13#include "base/metrics/statistics_recorder.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace base {
17
18class PersistentHistogramAllocatorTest : public testing::Test {
19 protected:
20  const int32_t kAllocatorMemorySize = 64 << 10;  // 64 KiB
21
22  PersistentHistogramAllocatorTest()
23      : statistics_recorder_(StatisticsRecorder::CreateTemporaryForTesting()) {
24    CreatePersistentHistogramAllocator();
25  }
26  ~PersistentHistogramAllocatorTest() override {
27    DestroyPersistentHistogramAllocator();
28  }
29
30  void CreatePersistentHistogramAllocator() {
31    allocator_memory_.reset(new char[kAllocatorMemorySize]);
32
33    GlobalHistogramAllocator::ReleaseForTesting();
34    memset(allocator_memory_.get(), 0, kAllocatorMemorySize);
35    GlobalHistogramAllocator::GetCreateHistogramResultHistogram();
36    GlobalHistogramAllocator::CreateWithPersistentMemory(
37        allocator_memory_.get(), kAllocatorMemorySize, 0, 0,
38        "PersistentHistogramAllocatorTest");
39    allocator_ = GlobalHistogramAllocator::Get()->memory_allocator();
40  }
41
42  void DestroyPersistentHistogramAllocator() {
43    allocator_ = nullptr;
44    GlobalHistogramAllocator::ReleaseForTesting();
45  }
46
47  std::unique_ptr<StatisticsRecorder> statistics_recorder_;
48  std::unique_ptr<char[]> allocator_memory_;
49  PersistentMemoryAllocator* allocator_ = nullptr;
50
51 private:
52  DISALLOW_COPY_AND_ASSIGN(PersistentHistogramAllocatorTest);
53};
54
55TEST_F(PersistentHistogramAllocatorTest, CreateAndIterateTest) {
56  PersistentMemoryAllocator::MemoryInfo meminfo0;
57  allocator_->GetMemoryInfo(&meminfo0);
58
59  // Try basic construction
60  HistogramBase* histogram = Histogram::FactoryGet(
61      "TestHistogram", 1, 1000, 10, HistogramBase::kIsPersistent);
62  EXPECT_TRUE(histogram);
63  histogram->CheckName("TestHistogram");
64  PersistentMemoryAllocator::MemoryInfo meminfo1;
65  allocator_->GetMemoryInfo(&meminfo1);
66  EXPECT_GT(meminfo0.free, meminfo1.free);
67
68  HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
69      "TestLinearHistogram", 1, 1000, 10, HistogramBase::kIsPersistent);
70  EXPECT_TRUE(linear_histogram);
71  linear_histogram->CheckName("TestLinearHistogram");
72  PersistentMemoryAllocator::MemoryInfo meminfo2;
73  allocator_->GetMemoryInfo(&meminfo2);
74  EXPECT_GT(meminfo1.free, meminfo2.free);
75
76  HistogramBase* boolean_histogram = BooleanHistogram::FactoryGet(
77      "TestBooleanHistogram", HistogramBase::kIsPersistent);
78  EXPECT_TRUE(boolean_histogram);
79  boolean_histogram->CheckName("TestBooleanHistogram");
80  PersistentMemoryAllocator::MemoryInfo meminfo3;
81  allocator_->GetMemoryInfo(&meminfo3);
82  EXPECT_GT(meminfo2.free, meminfo3.free);
83
84  std::vector<int> custom_ranges;
85  custom_ranges.push_back(1);
86  custom_ranges.push_back(5);
87  HistogramBase* custom_histogram = CustomHistogram::FactoryGet(
88      "TestCustomHistogram", custom_ranges, HistogramBase::kIsPersistent);
89  EXPECT_TRUE(custom_histogram);
90  custom_histogram->CheckName("TestCustomHistogram");
91  PersistentMemoryAllocator::MemoryInfo meminfo4;
92  allocator_->GetMemoryInfo(&meminfo4);
93  EXPECT_GT(meminfo3.free, meminfo4.free);
94
95  PersistentMemoryAllocator::Iterator iter(allocator_);
96  uint32_t type;
97  EXPECT_NE(0U, iter.GetNext(&type));  // Histogram
98  EXPECT_NE(0U, iter.GetNext(&type));  // LinearHistogram
99  EXPECT_NE(0U, iter.GetNext(&type));  // BooleanHistogram
100  EXPECT_NE(0U, iter.GetNext(&type));  // CustomHistogram
101  EXPECT_EQ(0U, iter.GetNext(&type));
102
103  // Create a second allocator and have it access the memory of the first.
104  std::unique_ptr<HistogramBase> recovered;
105  PersistentHistogramAllocator recovery(MakeUnique<PersistentMemoryAllocator>(
106      allocator_memory_.get(), kAllocatorMemorySize, 0, 0, "", false));
107  PersistentHistogramAllocator::Iterator histogram_iter(&recovery);
108
109  recovered = histogram_iter.GetNext();
110  ASSERT_TRUE(recovered);
111  recovered->CheckName("TestHistogram");
112
113  recovered = histogram_iter.GetNext();
114  ASSERT_TRUE(recovered);
115  recovered->CheckName("TestLinearHistogram");
116
117  recovered = histogram_iter.GetNext();
118  ASSERT_TRUE(recovered);
119  recovered->CheckName("TestBooleanHistogram");
120
121  recovered = histogram_iter.GetNext();
122  ASSERT_TRUE(recovered);
123  recovered->CheckName("TestCustomHistogram");
124
125  recovered = histogram_iter.GetNext();
126  EXPECT_FALSE(recovered);
127}
128
129TEST_F(PersistentHistogramAllocatorTest, CreateWithFileTest) {
130  const char temp_name[] = "CreateWithFileTest";
131  ScopedTempDir temp_dir;
132  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
133  FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
134  const size_t temp_size = 64 << 10;  // 64 KiB
135
136  // Test creation of a new file.
137  GlobalHistogramAllocator::ReleaseForTesting();
138  GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, temp_name);
139  EXPECT_EQ(std::string(temp_name),
140            GlobalHistogramAllocator::Get()->memory_allocator()->Name());
141
142  // Test re-open of a possibly-existing file.
143  GlobalHistogramAllocator::ReleaseForTesting();
144  GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, "");
145  EXPECT_EQ(std::string(temp_name),
146            GlobalHistogramAllocator::Get()->memory_allocator()->Name());
147
148  // Test re-open of an known-existing file.
149  GlobalHistogramAllocator::ReleaseForTesting();
150  GlobalHistogramAllocator::CreateWithFile(temp_file, 0, 0, "");
151  EXPECT_EQ(std::string(temp_name),
152            GlobalHistogramAllocator::Get()->memory_allocator()->Name());
153
154  // Final release so file and temp-dir can be removed.
155  GlobalHistogramAllocator::ReleaseForTesting();
156}
157
158TEST_F(PersistentHistogramAllocatorTest, StatisticsRecorderMergeTest) {
159  const char LinearHistogramName[] = "SRTLinearHistogram";
160  const char SparseHistogramName[] = "SRTSparseHistogram";
161  const size_t starting_sr_count = StatisticsRecorder::GetHistogramCount();
162
163  // Create a local StatisticsRecorder in which the newly created histogram
164  // will be recorded. The global allocator must be replaced after because the
165  // act of releasing will cause the active SR to forget about all histograms
166  // in the relased memory.
167  std::unique_ptr<StatisticsRecorder> local_sr =
168      StatisticsRecorder::CreateTemporaryForTesting();
169  EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount());
170  std::unique_ptr<GlobalHistogramAllocator> old_allocator =
171      GlobalHistogramAllocator::ReleaseForTesting();
172  GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
173  ASSERT_TRUE(GlobalHistogramAllocator::Get());
174
175  // Create a linear histogram for merge testing.
176  HistogramBase* histogram1 =
177      LinearHistogram::FactoryGet(LinearHistogramName, 1, 10, 10, 0);
178  ASSERT_TRUE(histogram1);
179  EXPECT_EQ(1U, StatisticsRecorder::GetHistogramCount());
180  histogram1->Add(3);
181  histogram1->Add(1);
182  histogram1->Add(4);
183  histogram1->AddCount(1, 4);
184  histogram1->Add(6);
185
186  // Create a sparse histogram for merge testing.
187  HistogramBase* histogram2 =
188      SparseHistogram::FactoryGet(SparseHistogramName, 0);
189  ASSERT_TRUE(histogram2);
190  EXPECT_EQ(2U, StatisticsRecorder::GetHistogramCount());
191  histogram2->Add(3);
192  histogram2->Add(1);
193  histogram2->Add(4);
194  histogram2->AddCount(1, 4);
195  histogram2->Add(6);
196
197  // Destroy the local SR and ensure that we're back to the initial state and
198  // restore the global allocator. Histograms created in the local SR will
199  // become unmanaged.
200  std::unique_ptr<GlobalHistogramAllocator> new_allocator =
201      GlobalHistogramAllocator::ReleaseForTesting();
202  local_sr.reset();
203  EXPECT_EQ(starting_sr_count, StatisticsRecorder::GetHistogramCount());
204  GlobalHistogramAllocator::Set(std::move(old_allocator));
205
206  // Create a "recovery" allocator using the same memory as the local one.
207  PersistentHistogramAllocator recovery1(MakeUnique<PersistentMemoryAllocator>(
208      const_cast<void*>(new_allocator->memory_allocator()->data()),
209      new_allocator->memory_allocator()->size(), 0, 0, "", false));
210  PersistentHistogramAllocator::Iterator histogram_iter1(&recovery1);
211
212  // Get the histograms that were created locally (and forgotten) and merge
213  // them into the global SR. New objects will be created.
214  std::unique_ptr<HistogramBase> recovered;
215  while (true) {
216    recovered = histogram_iter1.GetNext();
217    if (!recovered)
218      break;
219
220    recovery1.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
221    HistogramBase* found =
222        StatisticsRecorder::FindHistogram(recovered->histogram_name());
223    EXPECT_NE(recovered.get(), found);
224  };
225  EXPECT_EQ(starting_sr_count + 2, StatisticsRecorder::GetHistogramCount());
226
227  // Check the merged histograms for accuracy.
228  HistogramBase* found = StatisticsRecorder::FindHistogram(LinearHistogramName);
229  ASSERT_TRUE(found);
230  std::unique_ptr<HistogramSamples> snapshot = found->SnapshotSamples();
231  EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
232  EXPECT_EQ(1, snapshot->GetCount(3));
233  EXPECT_EQ(5, snapshot->GetCount(1));
234  EXPECT_EQ(1, snapshot->GetCount(4));
235  EXPECT_EQ(1, snapshot->GetCount(6));
236
237  found = StatisticsRecorder::FindHistogram(SparseHistogramName);
238  ASSERT_TRUE(found);
239  snapshot = found->SnapshotSamples();
240  EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
241  EXPECT_EQ(1, snapshot->GetCount(3));
242  EXPECT_EQ(5, snapshot->GetCount(1));
243  EXPECT_EQ(1, snapshot->GetCount(4));
244  EXPECT_EQ(1, snapshot->GetCount(6));
245
246  // Perform additional histogram increments.
247  histogram1->AddCount(1, 3);
248  histogram1->Add(6);
249  histogram2->AddCount(1, 3);
250  histogram2->Add(7);
251
252  // Do another merge.
253  PersistentHistogramAllocator recovery2(MakeUnique<PersistentMemoryAllocator>(
254      const_cast<void*>(new_allocator->memory_allocator()->data()),
255      new_allocator->memory_allocator()->size(), 0, 0, "", false));
256  PersistentHistogramAllocator::Iterator histogram_iter2(&recovery2);
257  while (true) {
258    recovered = histogram_iter2.GetNext();
259    if (!recovered)
260      break;
261    recovery2.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
262  };
263  EXPECT_EQ(starting_sr_count + 2, StatisticsRecorder::GetHistogramCount());
264
265  // And verify.
266  found = StatisticsRecorder::FindHistogram(LinearHistogramName);
267  snapshot = found->SnapshotSamples();
268  EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
269  EXPECT_EQ(1, snapshot->GetCount(3));
270  EXPECT_EQ(8, snapshot->GetCount(1));
271  EXPECT_EQ(1, snapshot->GetCount(4));
272  EXPECT_EQ(2, snapshot->GetCount(6));
273
274  found = StatisticsRecorder::FindHistogram(SparseHistogramName);
275  snapshot = found->SnapshotSamples();
276  EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
277  EXPECT_EQ(1, snapshot->GetCount(3));
278  EXPECT_EQ(8, snapshot->GetCount(1));
279  EXPECT_EQ(1, snapshot->GetCount(4));
280  EXPECT_EQ(1, snapshot->GetCount(6));
281  EXPECT_EQ(1, snapshot->GetCount(7));
282}
283
284}  // namespace base
285