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 "components/metrics/content/subprocess_metrics_provider.h"
6
7 #include <memory>
8 #include <string>
9 #include <vector>
10
11 #include "base/metrics/histogram.h"
12 #include "base/metrics/histogram_flattener.h"
13 #include "base/metrics/histogram_snapshot_manager.h"
14 #include "base/metrics/persistent_histogram_allocator.h"
15 #include "base/metrics/persistent_memory_allocator.h"
16 #include "base/metrics/statistics_recorder.h"
17 #include "content/public/test/browser_task_environment.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 using ::testing::IsEmpty;
22 using ::testing::UnorderedElementsAre;
23
24 namespace metrics {
25 namespace {
26
27 const uint32_t TEST_MEMORY_SIZE = 64 << 10; // 64 KiB
28
29 class HistogramFlattenerDeltaRecorder : public base::HistogramFlattener {
30 public:
HistogramFlattenerDeltaRecorder()31 HistogramFlattenerDeltaRecorder() {}
32
33 HistogramFlattenerDeltaRecorder(const HistogramFlattenerDeltaRecorder&) =
34 delete;
35 HistogramFlattenerDeltaRecorder& operator=(
36 const HistogramFlattenerDeltaRecorder&) = delete;
37
RecordDelta(const base::HistogramBase & histogram,const base::HistogramSamples & snapshot)38 void RecordDelta(const base::HistogramBase& histogram,
39 const base::HistogramSamples& snapshot) override {
40 recorded_delta_histogram_names_.push_back(histogram.histogram_name());
41 }
42
GetRecordedDeltaHistogramNames()43 std::vector<std::string> GetRecordedDeltaHistogramNames() {
44 return recorded_delta_histogram_names_;
45 }
46
47 private:
48 std::vector<std::string> recorded_delta_histogram_names_;
49 };
50
51 } // namespace
52
53 class SubprocessMetricsProviderTest : public testing::Test {
54 public:
55 SubprocessMetricsProviderTest(const SubprocessMetricsProviderTest&) = delete;
56 SubprocessMetricsProviderTest& operator=(
57 const SubprocessMetricsProviderTest&) = delete;
58
59 protected:
SubprocessMetricsProviderTest()60 SubprocessMetricsProviderTest() {
61 // MergeHistogramDeltas needs to be called beause it uses a histogram
62 // macro which caches a pointer to a histogram. If not done before setting
63 // a persistent global allocator, then it would point into memory that
64 // will go away.
65 provider_.MergeHistogramDeltas();
66
67 // Create a dedicated StatisticsRecorder for this test.
68 test_recorder_ = base::StatisticsRecorder::CreateTemporaryForTesting();
69
70 // Create a global allocator using a block of memory from the heap.
71 base::GlobalHistogramAllocator::CreateWithLocalMemory(TEST_MEMORY_SIZE, 0,
72 "");
73 }
74
~SubprocessMetricsProviderTest()75 ~SubprocessMetricsProviderTest() override {
76 base::GlobalHistogramAllocator::ReleaseForTesting();
77 }
78
provider()79 SubprocessMetricsProvider* provider() { return &provider_; }
80
CreateDuplicateAllocator(base::PersistentHistogramAllocator * allocator)81 std::unique_ptr<base::PersistentHistogramAllocator> CreateDuplicateAllocator(
82 base::PersistentHistogramAllocator* allocator) {
83 // Just wrap around the data segment in-use by the passed allocator.
84 return std::make_unique<base::PersistentHistogramAllocator>(
85 std::make_unique<base::PersistentMemoryAllocator>(
86 const_cast<void*>(allocator->data()), allocator->length(), 0, 0,
87 std::string(), false));
88 }
89
GetSnapshotHistogramNames()90 std::vector<std::string> GetSnapshotHistogramNames() {
91 // Merge the data from the allocator into the StatisticsRecorder.
92 provider_.MergeHistogramDeltas();
93
94 // Flatten what is known to see what has changed since the last time.
95 HistogramFlattenerDeltaRecorder flattener;
96 base::HistogramSnapshotManager snapshot_manager(&flattener);
97 // "true" to the begin() includes histograms held in persistent storage.
98 base::StatisticsRecorder::PrepareDeltas(true, base::Histogram::kNoFlags,
99 base::Histogram::kNoFlags,
100 &snapshot_manager);
101 return flattener.GetRecordedDeltaHistogramNames();
102 }
103
EnableRecording()104 void EnableRecording() { provider_.OnRecordingEnabled(); }
DisableRecording()105 void DisableRecording() { provider_.OnRecordingDisabled(); }
106
RegisterSubprocessAllocator(int id,std::unique_ptr<base::PersistentHistogramAllocator> allocator)107 void RegisterSubprocessAllocator(
108 int id,
109 std::unique_ptr<base::PersistentHistogramAllocator> allocator) {
110 provider_.RegisterSubprocessAllocator(id, std::move(allocator));
111 }
112
DeregisterSubprocessAllocator(int id)113 void DeregisterSubprocessAllocator(int id) {
114 provider_.DeregisterSubprocessAllocator(id);
115 }
116
117 private:
118 // A thread-bundle makes the tests appear on the UI thread, something that is
119 // checked in methods called from the SubprocessMetricsProvider class under
120 // test. This must be constructed before the |provider_| field.
121 content::BrowserTaskEnvironment task_environment_;
122
123 SubprocessMetricsProvider provider_;
124 std::unique_ptr<base::StatisticsRecorder> test_recorder_;
125 };
126
TEST_F(SubprocessMetricsProviderTest,SnapshotMetrics)127 TEST_F(SubprocessMetricsProviderTest, SnapshotMetrics) {
128 base::HistogramBase* foo = base::Histogram::FactoryGet("foo", 1, 100, 10, 0);
129 base::HistogramBase* bar = base::Histogram::FactoryGet("bar", 1, 100, 10, 0);
130 base::HistogramBase* baz = base::Histogram::FactoryGet("baz", 1, 100, 10, 0);
131 foo->Add(42);
132 bar->Add(84);
133
134 // Detach the global allocator but keep it around until this method exits
135 // so that the memory holding histogram data doesn't get released. Register
136 // a new allocator that duplicates the global one.
137 std::unique_ptr<base::GlobalHistogramAllocator> global_allocator(
138 base::GlobalHistogramAllocator::ReleaseForTesting());
139 RegisterSubprocessAllocator(123,
140 CreateDuplicateAllocator(global_allocator.get()));
141
142 // Recording should find the two histograms created in persistent memory.
143 EXPECT_THAT(GetSnapshotHistogramNames(), UnorderedElementsAre("foo", "bar"));
144
145 // A second run should have nothing to produce.
146 EXPECT_THAT(GetSnapshotHistogramNames(), IsEmpty());
147
148 // Create a new histogram and update existing ones. Should now report 3 items.
149 baz->Add(1969);
150 foo->Add(10);
151 bar->Add(20);
152 EXPECT_THAT(GetSnapshotHistogramNames(),
153 UnorderedElementsAre("foo", "bar", "baz"));
154
155 // Ensure that deregistering does a final merge of the data.
156 foo->Add(10);
157 bar->Add(20);
158 DeregisterSubprocessAllocator(123);
159 EXPECT_THAT(GetSnapshotHistogramNames(), UnorderedElementsAre("foo", "bar"));
160
161 // Further snapshots should be empty even if things have changed.
162 foo->Add(10);
163 bar->Add(20);
164 EXPECT_THAT(GetSnapshotHistogramNames(), IsEmpty());
165 }
166
167 } // namespace metrics
168