• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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