xref: /aosp_15_r20/external/cronet/components/metrics/content/subprocess_metrics_provider_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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/sparse_histogram.h"
17 #include "base/metrics/statistics_recorder.h"
18 #include "base/test/bind.h"
19 #include "content/public/test/browser_task_environment.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 using ::testing::IsEmpty;
24 using ::testing::UnorderedElementsAre;
25 
26 namespace metrics {
27 namespace {
28 
29 const uint32_t TEST_MEMORY_SIZE = 64 << 10;  // 64 KiB
30 
31 struct HistogramData {
32   const std::string histogram_name;
33   const base::HistogramBase::Count total_count;
34   const int64_t sum;
35 
operator ==metrics::__anon034290020111::HistogramData36   bool operator==(const HistogramData& other) const {
37     return histogram_name == other.histogram_name &&
38            total_count == other.total_count && sum == other.sum;
39   }
40 };
41 
42 class HistogramFlattenerDeltaRecorder : public base::HistogramFlattener {
43  public:
44   HistogramFlattenerDeltaRecorder() = default;
45 
46   HistogramFlattenerDeltaRecorder(const HistogramFlattenerDeltaRecorder&) =
47       delete;
48   HistogramFlattenerDeltaRecorder& operator=(
49       const HistogramFlattenerDeltaRecorder&) = delete;
50 
51   ~HistogramFlattenerDeltaRecorder() override = default;
52 
RecordDelta(const base::HistogramBase & histogram,const base::HistogramSamples & snapshot)53   void RecordDelta(const base::HistogramBase& histogram,
54                    const base::HistogramSamples& snapshot) override {
55     // Only record histograms that start with '_' (e.g., _foo, _bar, _baz) to
56     // not record histograms from outside what is being tested.
57     if (histogram.histogram_name()[0] == '_') {
58       recorded_delta_histograms_.emplace_back(
59           histogram.histogram_name(), snapshot.TotalCount(), snapshot.sum());
60     }
61   }
62 
GetRecordedDeltaHistograms()63   const std::vector<HistogramData>& GetRecordedDeltaHistograms() {
64     return recorded_delta_histograms_;
65   }
66 
67  private:
68   std::vector<HistogramData> recorded_delta_histograms_;
69 };
70 
71 // Same as PersistentHistogramAllocator, but will call a callback on being
72 // destroyed.
73 class TestPersistentHistogramAllocator
74     : public base::PersistentHistogramAllocator {
75  public:
76   using base::PersistentHistogramAllocator::PersistentHistogramAllocator;
77 
78   TestPersistentHistogramAllocator(const TestPersistentHistogramAllocator&) =
79       delete;
80   TestPersistentHistogramAllocator& operator=(
81       const TestPersistentHistogramAllocator&) = delete;
82 
~TestPersistentHistogramAllocator()83   ~TestPersistentHistogramAllocator() override {
84     if (!destroyed_callback_.is_null()) {
85       std::move(destroyed_callback_).Run();
86     }
87   }
88 
SetDestroyedCallback(base::OnceClosure destroyed_callback)89   void SetDestroyedCallback(base::OnceClosure destroyed_callback) {
90     destroyed_callback_ = std::move(destroyed_callback);
91   }
92 
93  private:
94   base::OnceClosure destroyed_callback_;
95 };
96 
97 }  // namespace
98 
99 class SubprocessMetricsProviderTest : public testing::Test {
100  public:
101   SubprocessMetricsProviderTest(const SubprocessMetricsProviderTest&) = delete;
102   SubprocessMetricsProviderTest& operator=(
103       const SubprocessMetricsProviderTest&) = delete;
104 
105  protected:
SubprocessMetricsProviderTest()106   SubprocessMetricsProviderTest()
107       : task_environment_(
108             // Use ThreadPoolExecutionMode::QUEUED so that tests can decide
109             // exactly when tasks posted to ThreadPool start running.
110             base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED) {
111     SubprocessMetricsProvider::CreateInstance();
112 
113     // Need to recreate a task runner, otherwise it may be a stale one from a
114     // previous test (it's possible the global instance is re-used across
115     // tests).
116     RecreateTaskRunnerForTesting();
117 
118     // MergeHistogramDeltas needs to be called because it uses a histogram
119     // macro which caches a pointer to a histogram. If not done before setting
120     // a persistent global allocator, then it would point into memory that
121     // will go away.
122     SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
123 
124     // Create a dedicated StatisticsRecorder for this test.
125     test_recorder_ = base::StatisticsRecorder::CreateTemporaryForTesting();
126 
127     // Create a global allocator using a block of memory from the heap.
128     base::GlobalHistogramAllocator::CreateWithLocalMemory(TEST_MEMORY_SIZE, 0,
129                                                           "");
130   }
131 
~SubprocessMetricsProviderTest()132   ~SubprocessMetricsProviderTest() override {
133     base::GlobalHistogramAllocator::ReleaseForTesting();
134   }
135 
CreateDuplicateAllocator(base::PersistentHistogramAllocator * allocator)136   std::unique_ptr<TestPersistentHistogramAllocator> CreateDuplicateAllocator(
137       base::PersistentHistogramAllocator* allocator) {
138     // Just wrap around the data segment in-use by the passed allocator.
139     return std::make_unique<TestPersistentHistogramAllocator>(
140         std::make_unique<base::PersistentMemoryAllocator>(
141             const_cast<void*>(allocator->data()), allocator->length(), 0, 0, "",
142             base::PersistentMemoryAllocator::kReadWrite));
143   }
144 
GetSnapshotHistograms()145   std::vector<HistogramData> GetSnapshotHistograms() {
146     // Flatten what is known to see what has changed since the last time.
147     HistogramFlattenerDeltaRecorder flattener;
148     base::HistogramSnapshotManager snapshot_manager(&flattener);
149     // "true" to the begin() includes histograms held in persistent storage.
150     base::StatisticsRecorder::PrepareDeltas(true, base::Histogram::kNoFlags,
151                                             base::Histogram::kNoFlags,
152                                             &snapshot_manager);
153     return flattener.GetRecordedDeltaHistograms();
154   }
155 
RegisterSubprocessAllocator(int id,std::unique_ptr<base::PersistentHistogramAllocator> allocator)156   void RegisterSubprocessAllocator(
157       int id,
158       std::unique_ptr<base::PersistentHistogramAllocator> allocator) {
159     SubprocessMetricsProvider::GetInstance()->RegisterSubprocessAllocator(
160         id, std::move(allocator));
161   }
162 
DeregisterSubprocessAllocator(int id)163   void DeregisterSubprocessAllocator(int id) {
164     SubprocessMetricsProvider::GetInstance()->DeregisterSubprocessAllocator(id);
165   }
166 
RecreateTaskRunnerForTesting()167   void RecreateTaskRunnerForTesting() {
168     SubprocessMetricsProvider::GetInstance()->RecreateTaskRunnerForTesting();
169   }
170 
task_environment()171   content::BrowserTaskEnvironment* task_environment() {
172     return &task_environment_;
173   }
174 
175  private:
176   // A thread-bundle makes the tests appear on the UI thread, something that is
177   // checked in methods called from the SubprocessMetricsProvider class under
178   // test. This must be constructed before the |provider_| field.
179   content::BrowserTaskEnvironment task_environment_;
180 
181   std::unique_ptr<base::StatisticsRecorder> test_recorder_;
182 };
183 
TEST_F(SubprocessMetricsProviderTest,SnapshotMetrics)184 TEST_F(SubprocessMetricsProviderTest, SnapshotMetrics) {
185   base::HistogramBase* foo = base::Histogram::FactoryGet("_foo", 1, 100, 10, 0);
186   base::HistogramBase* bar = base::Histogram::FactoryGet("_bar", 1, 100, 10, 0);
187   base::HistogramBase* baz = base::Histogram::FactoryGet("_baz", 1, 100, 10, 0);
188   base::HistogramBase* foobar = base::SparseHistogram::FactoryGet(
189       "_foobar", base::HistogramBase::kUmaTargetedHistogramFlag);
190   foo->Add(42);
191   bar->Add(84);
192   foobar->Add(1);
193   foobar->Add(2);
194   foobar->Add(3);
195 
196   // Register a new allocator that duplicates the global one.
197   base::GlobalHistogramAllocator* global_allocator(
198       base::GlobalHistogramAllocator::ReleaseForTesting());
199   auto duplicate_allocator = CreateDuplicateAllocator(global_allocator);
200   bool duplicate_allocator_destroyed = false;
201   duplicate_allocator->SetDestroyedCallback(base::BindLambdaForTesting(
202       [&] { duplicate_allocator_destroyed = true; }));
203   RegisterSubprocessAllocator(123, std::move(duplicate_allocator));
204 
205   // Recording should find the two histograms created in persistent memory.
206   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
207   EXPECT_THAT(GetSnapshotHistograms(),
208               UnorderedElementsAre(
209                   HistogramData{"_foo", /*total_count=*/1, /*sum=*/42},
210                   HistogramData{"_bar", /*total_count=*/1, /*sum=*/84},
211                   HistogramData{"_foobar", /*total_count=*/3, /*sum=*/6}));
212 
213   // A second run should have nothing to produce.
214   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
215   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
216 
217   // Create a new histogram and update existing ones. Should now report 3 items.
218   baz->Add(1969);
219   foo->Add(10);
220   bar->Add(20);
221   foobar->Add(4);
222   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
223   EXPECT_THAT(GetSnapshotHistograms(),
224               UnorderedElementsAre(
225                   HistogramData{"_foo", /*total_count=*/1, /*sum=*/10},
226                   HistogramData{"_bar", /*total_count=*/1, /*sum=*/20},
227                   HistogramData{"_baz", /*total_count=*/1, /*sum=*/1969},
228                   HistogramData{"_foobar", /*total_count=*/1, /*sum=*/4}));
229 
230   // Ensure that deregistering does a final merge of the data.
231   foo->Add(10);
232   bar->Add(20);
233   DeregisterSubprocessAllocator(123);
234   // Do not call MergeHistogramDeltas() here, because the call to
235   // DeregisterSubprocessAllocator() should have already scheduled a task to
236   // merge the histograms.
237   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
238   task_environment()->RunUntilIdle();
239   EXPECT_THAT(GetSnapshotHistograms(),
240               UnorderedElementsAre(
241                   HistogramData{"_foo", /*total_count=*/1, /*sum=*/10},
242                   HistogramData{"_bar", /*total_count=*/1, /*sum=*/20}));
243   // The allocator should have been released after deregistering.
244   EXPECT_TRUE(duplicate_allocator_destroyed);
245 
246   // Further snapshots should be empty even if things have changed.
247   foo->Add(10);
248   bar->Add(20);
249   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
250   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
251 }
252 
TEST_F(SubprocessMetricsProviderTest,SnapshotMetricsAsync)253 TEST_F(SubprocessMetricsProviderTest, SnapshotMetricsAsync) {
254   base::HistogramBase* foo = base::Histogram::FactoryGet("_foo", 1, 100, 10, 0);
255   base::HistogramBase* bar = base::Histogram::FactoryGet("_bar", 1, 100, 10, 0);
256   base::HistogramBase* baz = base::Histogram::FactoryGet("_baz", 1, 100, 10, 0);
257   base::HistogramBase* foobar = base::SparseHistogram::FactoryGet(
258       "_foobar", base::HistogramBase::kUmaTargetedHistogramFlag);
259   foo->Add(42);
260   bar->Add(84);
261   foobar->Add(1);
262   foobar->Add(2);
263   foobar->Add(3);
264 
265   // Register a new allocator that duplicates the global one.
266   base::GlobalHistogramAllocator* global_allocator(
267       base::GlobalHistogramAllocator::ReleaseForTesting());
268   RegisterSubprocessAllocator(123, CreateDuplicateAllocator(global_allocator));
269 
270   // Recording should find the two histograms created in persistent memory.
271   SubprocessMetricsProvider::MergeHistogramDeltasForTesting(
272       /*async=*/true, /*done_callback=*/task_environment()->QuitClosure());
273   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
274   task_environment()->RunUntilQuit();
275   EXPECT_THAT(GetSnapshotHistograms(),
276               UnorderedElementsAre(
277                   HistogramData{"_foo", /*total_count=*/1, /*sum=*/42},
278                   HistogramData{"_bar", /*total_count=*/1, /*sum=*/84},
279                   HistogramData{"_foobar", /*total_count=*/3, /*sum=*/6}));
280 
281   // A second run should have nothing to produce.
282   SubprocessMetricsProvider::MergeHistogramDeltasForTesting(
283       /*async=*/true, /*done_callback=*/task_environment()->QuitClosure());
284   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
285   task_environment()->RunUntilQuit();
286   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
287 
288   // Create a new histogram and update existing ones. Should now report 3 items.
289   baz->Add(1969);
290   foo->Add(10);
291   bar->Add(20);
292   foobar->Add(4);
293   SubprocessMetricsProvider::MergeHistogramDeltasForTesting(
294       /*async=*/true, /*done_callback=*/task_environment()->QuitClosure());
295   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
296   task_environment()->RunUntilQuit();
297   EXPECT_THAT(GetSnapshotHistograms(),
298               UnorderedElementsAre(
299                   HistogramData{"_foo", /*total_count=*/1, /*sum=*/10},
300                   HistogramData{"_bar", /*total_count=*/1, /*sum=*/20},
301                   HistogramData{"_baz", /*total_count=*/1, /*sum=*/1969},
302                   HistogramData{"_foobar", /*total_count=*/1, /*sum=*/4}));
303 
304   // Ensure that deregistering does a final merge of the data.
305   foo->Add(10);
306   bar->Add(20);
307   DeregisterSubprocessAllocator(123);
308   // Do not call MergeHistogramDeltas() here, because the call to
309   // DeregisterSubprocessAllocator() should have already scheduled a task to
310   // merge the histograms.
311   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
312   task_environment()->RunUntilIdle();
313   EXPECT_THAT(GetSnapshotHistograms(),
314               UnorderedElementsAre(
315                   HistogramData{"_foo", /*total_count=*/1, /*sum=*/10},
316                   HistogramData{"_bar", /*total_count=*/1, /*sum=*/20}));
317 
318   // Further snapshots should be empty even if things have changed.
319   foo->Add(10);
320   bar->Add(20);
321   SubprocessMetricsProvider::MergeHistogramDeltasForTesting(
322       /*async=*/true, /*done_callback=*/task_environment()->QuitClosure());
323   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
324   task_environment()->RunUntilQuit();
325   EXPECT_THAT(GetSnapshotHistograms(), IsEmpty());
326 }
327 
328 // Verifies that it is fine to deregister an allocator even if background tasks
329 // that access it are still pending/running.
TEST_F(SubprocessMetricsProviderTest,AllocatorRefCounted)330 TEST_F(SubprocessMetricsProviderTest, AllocatorRefCounted) {
331   base::HistogramBase* foo = base::Histogram::FactoryGet("_foo", 1, 100, 10, 0);
332   base::HistogramBase* bar = base::Histogram::FactoryGet("_bar", 1, 100, 10, 0);
333   base::HistogramBase* baz = base::SparseHistogram::FactoryGet(
334       "_baz", base::HistogramBase::kUmaTargetedHistogramFlag);
335   base::HistogramBase* foobar = base::SparseHistogram::FactoryGet(
336       "_foobar", base::HistogramBase::kUmaTargetedHistogramFlag);
337   foo->Add(42);
338   bar->Add(84);
339   baz->Add(1);
340   baz->Add(2);
341   baz->Add(3);
342   foobar->Add(4);
343   foobar->Add(5);
344   foobar->Add(6);
345 
346   // Register a new allocator that duplicates the global one.
347   base::GlobalHistogramAllocator* global_allocator(
348       base::GlobalHistogramAllocator::ReleaseForTesting());
349   auto duplicate_allocator = CreateDuplicateAllocator(global_allocator);
350   bool duplicate_allocator_destroyed = false;
351   duplicate_allocator->SetDestroyedCallback(base::BindLambdaForTesting(
352       [&] { duplicate_allocator_destroyed = true; }));
353   RegisterSubprocessAllocator(123, std::move(duplicate_allocator));
354 
355   // Merge histogram deltas. This will be done asynchronously.
356   SubprocessMetricsProvider::MergeHistogramDeltasForTesting(
357       /*async=*/true, /*done_callback=*/base::DoNothing());
358   // Deregister the allocator. This will be done asynchronously.
359   DeregisterSubprocessAllocator(123);
360 
361   // The call to DeregisterSubprocessAllocator() above will have removed the
362   // allocator from the internal map. However, the allocator should not have
363   // been freed yet as there are still background tasks pending/running
364   // that have a reference to it (i.e., the tasks from MergeHistogramDeltas()
365   // and DeregisterSubprocessAllocator()).
366   ASSERT_FALSE(duplicate_allocator_destroyed);
367 
368   // Run tasks.
369   task_environment()->RunUntilIdle();
370 
371   // After all the tasks have finished, the allocator should have been released.
372   EXPECT_TRUE(duplicate_allocator_destroyed);
373 
374   // Verify that the histograms were merged.
375   EXPECT_THAT(GetSnapshotHistograms(),
376               UnorderedElementsAre(
377                   HistogramData{"_foo", /*total_count=*/1, /*sum=*/42},
378                   HistogramData{"_bar", /*total_count=*/1, /*sum=*/84},
379                   HistogramData{"_baz", /*total_count=*/3, /*sum=*/6},
380                   HistogramData{"_foobar", /*total_count=*/3, /*sum=*/15}));
381 }
382 
383 }  // namespace metrics
384