1 // Copyright 2017 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/test/task_environment.h"
6 #include "services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.h"
7
8 #include "base/bind.h"
9 #include "base/callback_helpers.h"
10 #include "base/memory/ref_counted_memory.h"
11 #include "base/run_loop.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/test/test_io_thread.h"
14 #include "base/test/trace_event_analyzer.h"
15 #include "base/threading/sequenced_task_runner_handle.h"
16 #include "base/trace_event/memory_dump_manager.h"
17 #include "base/trace_event/memory_dump_manager_test_utils.h"
18 #include "base/trace_event/memory_dump_scheduler.h"
19 #include "base/trace_event/memory_infra_background_allowlist.h"
20 #include "base/trace_event/trace_buffer.h"
21 #include "base/trace_event/trace_config.h"
22 #include "base/trace_event/trace_config_memory_test_util.h"
23 #include "base/trace_event/trace_log.h"
24 #include "mojo/public/cpp/bindings/pending_receiver.h"
25 #include "mojo/public/cpp/bindings/pending_remote.h"
26 #include "mojo/public/cpp/bindings/receiver_set.h"
27 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
28 #include "testing/gmock/include/gmock/gmock.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30
31 using testing::_;
32 using testing::AnyNumber;
33 using testing::Invoke;
34 using testing::Return;
35
36 using base::trace_event::MemoryAllocatorDump;
37 using base::trace_event::MemoryDumpArgs;
38 using base::trace_event::MemoryDumpDeterminism;
39 using base::trace_event::MemoryDumpLevelOfDetail;
40 using base::trace_event::MemoryDumpManager;
41 using base::trace_event::MemoryDumpProvider;
42 using base::trace_event::MemoryDumpRequestArgs;
43 using base::trace_event::MemoryDumpScheduler;
44 using base::trace_event::MemoryDumpType;
45 using base::trace_event::ProcessMemoryDump;
46 using base::trace_event::TraceConfig;
47 using base::trace_event::TraceLog;
48 using base::trace_event::TraceResultBuffer;
49
50 namespace memory_instrumentation {
51
52 namespace {
53
54 const char kMDPName[] = "TestDumpProvider";
55 const char* kWhitelistedMDPName = "WhitelistedTestDumpProvider";
56 const char* kBackgroundButNotSummaryWhitelistedMDPName =
57 "BackgroundButNotSummaryWhitelistedTestDumpProvider";
58 const char* const kTestMDPWhitelist[] = {
59 kWhitelistedMDPName, kBackgroundButNotSummaryWhitelistedMDPName, nullptr};
60
61 // GTest matchers for MemoryDumpRequestArgs arguments.
62 MATCHER(IsDetailedDump, "") {
63 return arg.level_of_detail == MemoryDumpLevelOfDetail::DETAILED;
64 }
65
66 MATCHER(IsLightDump, "") {
67 return arg.level_of_detail == MemoryDumpLevelOfDetail::LIGHT;
68 }
69
70 MATCHER(IsBackgroundDump, "") {
71 return arg.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND;
72 }
73
74 // TODO(ssid): This class is replicated in memory_dump_manager_unittest. Move
75 // this to memory_dump_manager_test_utils.h crbug.com/728199.
76 class MockMemoryDumpProvider : public MemoryDumpProvider {
77 public:
78 MOCK_METHOD0(Destructor, void());
79 MOCK_METHOD2(OnMemoryDump,
80 bool(const MemoryDumpArgs& args, ProcessMemoryDump* pmd));
81
MockMemoryDumpProvider()82 MockMemoryDumpProvider() : enable_mock_destructor(false) {
83 ON_CALL(*this, OnMemoryDump(_, _))
84 .WillByDefault(
85 Invoke([](const MemoryDumpArgs&, ProcessMemoryDump* pmd) -> bool {
86 return true;
87 }));
88 }
89
~MockMemoryDumpProvider()90 ~MockMemoryDumpProvider() override {
91 if (enable_mock_destructor)
92 Destructor();
93 }
94
95 bool enable_mock_destructor;
96 };
97
98 } // namespace
99
100 class MemoryTracingIntegrationTest;
101
102 class MockCoordinator : public mojom::Coordinator {
103 public:
MockCoordinator(MemoryTracingIntegrationTest * client)104 explicit MockCoordinator(MemoryTracingIntegrationTest* client)
105 : client_(client) {}
106
BindReceiver(mojo::PendingReceiver<mojom::Coordinator> receiver)107 void BindReceiver(mojo::PendingReceiver<mojom::Coordinator> receiver) {
108 receivers_.Add(this, std::move(receiver));
109 }
110
111 void RequestGlobalMemoryDump(
112 MemoryDumpType dump_type,
113 MemoryDumpLevelOfDetail level_of_detail,
114 MemoryDumpDeterminism determinism,
115 const std::vector<std::string>& allocator_dump_names,
116 RequestGlobalMemoryDumpCallback) override;
117
RequestGlobalMemoryDumpForPid(base::ProcessId pid,const std::vector<std::string> & allocator_dump_names,RequestGlobalMemoryDumpForPidCallback)118 void RequestGlobalMemoryDumpForPid(
119 base::ProcessId pid,
120 const std::vector<std::string>& allocator_dump_names,
121 RequestGlobalMemoryDumpForPidCallback) override {}
122
RequestPrivateMemoryFootprint(base::ProcessId pid,RequestPrivateMemoryFootprintCallback)123 void RequestPrivateMemoryFootprint(
124 base::ProcessId pid,
125 RequestPrivateMemoryFootprintCallback) override {}
126
127 void RequestGlobalMemoryDumpAndAppendToTrace(
128 MemoryDumpType dump_type,
129 MemoryDumpLevelOfDetail level_of_detail,
130 MemoryDumpDeterminism determinism,
131 RequestGlobalMemoryDumpAndAppendToTraceCallback) override;
132
133 private:
134 mojo::ReceiverSet<mojom::Coordinator> receivers_;
135 MemoryTracingIntegrationTest* client_;
136 };
137
138 class MemoryTracingIntegrationTest : public testing::Test {
139 public:
SetUp()140 void SetUp() override {
141 task_environment_ =
142 std::make_unique<base::test::SingleThreadTaskEnvironment>();
143 coordinator_ = std::make_unique<MockCoordinator>(this);
144 }
145
InitializeClientProcess(mojom::ProcessType process_type)146 void InitializeClientProcess(mojom::ProcessType process_type) {
147 mdm_ = MemoryDumpManager::CreateInstanceForTesting();
148 mdm_->set_dumper_registrations_ignored_for_testing(true);
149
150 mojo::PendingRemote<mojom::Coordinator> coordinator;
151 mojo::PendingRemote<mojom::ClientProcess> process;
152 auto process_receiver = process.InitWithNewPipeAndPassReceiver();
153 coordinator_->BindReceiver(coordinator.InitWithNewPipeAndPassReceiver());
154 client_process_.reset(new ClientProcessImpl(
155 std::move(process_receiver), std::move(coordinator),
156 process_type == mojom::ProcessType::BROWSER,
157 /*initialize_memory_instrumentation=*/false));
158 }
159
TearDown()160 void TearDown() override {
161 TraceLog::GetInstance()->SetDisabled();
162 mdm_.reset();
163 client_process_.reset();
164 coordinator_.reset();
165 task_environment_.reset();
166 TraceLog::ResetForTesting();
167 }
168
169 // Blocks the current thread (spinning a nested message loop) until the
170 // memory dump is complete. Returns:
171 // - return value: the |success| from the RequestChromeMemoryDump() callback.
RequestChromeDumpAndWait(MemoryDumpType dump_type,MemoryDumpLevelOfDetail level_of_detail,std::unique_ptr<base::trace_event::ProcessMemoryDump> * result=nullptr)172 bool RequestChromeDumpAndWait(
173 MemoryDumpType dump_type,
174 MemoryDumpLevelOfDetail level_of_detail,
175 std::unique_ptr<base::trace_event::ProcessMemoryDump>* result = nullptr) {
176 base::RunLoop run_loop;
177 bool success = false;
178 uint64_t req_guid = ++guid_counter_;
179 MemoryDumpRequestArgs request_args{req_guid, dump_type, level_of_detail};
180 ClientProcessImpl::RequestChromeMemoryDumpCallback callback =
181 base::BindOnce(
182 [](bool* curried_success, base::OnceClosure curried_quit_closure,
183 std::unique_ptr<base::trace_event::ProcessMemoryDump>*
184 curried_result,
185 uint64_t curried_expected_guid, bool success, uint64_t dump_guid,
186 std::unique_ptr<base::trace_event::ProcessMemoryDump> result) {
187 EXPECT_EQ(curried_expected_guid, dump_guid);
188 *curried_success = success;
189 if (curried_result)
190 *curried_result = std::move(result);
191 std::move(curried_quit_closure).Run();
192 },
193 &success, run_loop.QuitClosure(), result, req_guid);
194 client_process_->RequestChromeMemoryDump(request_args, std::move(callback));
195 run_loop.Run();
196 return success;
197 }
198
RequestChromeDump(MemoryDumpType dump_type,MemoryDumpLevelOfDetail level_of_detail,MemoryDumpDeterminism determinism)199 void RequestChromeDump(MemoryDumpType dump_type,
200 MemoryDumpLevelOfDetail level_of_detail,
201 MemoryDumpDeterminism determinism) {
202 uint64_t req_guid = ++guid_counter_;
203 MemoryDumpRequestArgs request_args{req_guid, dump_type, level_of_detail,
204 determinism};
205 ClientProcessImpl::RequestChromeMemoryDumpCallback callback =
206 base::BindOnce(
207 [](bool success, uint64_t dump_guid,
208 std::unique_ptr<base::trace_event::ProcessMemoryDump> result) {
209 });
210 client_process_->RequestChromeMemoryDump(request_args, std::move(callback));
211 }
212
213 protected:
EnableMemoryInfraTracing()214 void EnableMemoryInfraTracing() {
215 TraceLog::GetInstance()->SetEnabled(
216 TraceConfig(MemoryDumpManager::kTraceCategory, ""),
217 TraceLog::RECORDING_MODE);
218 }
219
EnableMemoryInfraTracingWithTraceConfig(const std::string & trace_config)220 void EnableMemoryInfraTracingWithTraceConfig(
221 const std::string& trace_config) {
222 TraceLog::GetInstance()->SetEnabled(TraceConfig(trace_config),
223 TraceLog::RECORDING_MODE);
224 }
225
DisableTracing()226 void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); }
227
RegisterDumpProvider(MemoryDumpProvider * mdp,scoped_refptr<base::SingleThreadTaskRunner> task_runner,const MemoryDumpProvider::Options & options,const char * name=kMDPName)228 void RegisterDumpProvider(
229 MemoryDumpProvider* mdp,
230 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
231 const MemoryDumpProvider::Options& options,
232 const char* name = kMDPName) {
233 mdm_->set_dumper_registrations_ignored_for_testing(false);
234 mdm_->RegisterDumpProvider(mdp, name, std::move(task_runner), options);
235 mdm_->set_dumper_registrations_ignored_for_testing(true);
236 }
237
RegisterDumpProvider(MemoryDumpProvider * mdp,scoped_refptr<base::SingleThreadTaskRunner> task_runner)238 void RegisterDumpProvider(
239 MemoryDumpProvider* mdp,
240 scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
241 RegisterDumpProvider(mdp, task_runner, MemoryDumpProvider::Options());
242 }
243
IsPeriodicDumpingEnabled() const244 bool IsPeriodicDumpingEnabled() const {
245 return MemoryDumpScheduler::GetInstance()->is_enabled_for_testing();
246 }
247
248 std::unique_ptr<MemoryDumpManager> mdm_;
249
250 private:
251 std::unique_ptr<base::test::SingleThreadTaskEnvironment> task_environment_;
252 std::unique_ptr<MockCoordinator> coordinator_;
253 std::unique_ptr<ClientProcessImpl> client_process_;
254 uint64_t guid_counter_ = 0;
255 };
256
RequestGlobalMemoryDump(MemoryDumpType dump_type,MemoryDumpLevelOfDetail level_of_detail,MemoryDumpDeterminism determinism,const std::vector<std::string> & allocator_dump_names,RequestGlobalMemoryDumpCallback callback)257 void MockCoordinator::RequestGlobalMemoryDump(
258 MemoryDumpType dump_type,
259 MemoryDumpLevelOfDetail level_of_detail,
260 MemoryDumpDeterminism determinism,
261 const std::vector<std::string>& allocator_dump_names,
262 RequestGlobalMemoryDumpCallback callback) {
263 client_->RequestChromeDump(dump_type, level_of_detail, determinism);
264 std::move(callback).Run(true, mojom::GlobalMemoryDumpPtr());
265 }
266
RequestGlobalMemoryDumpAndAppendToTrace(MemoryDumpType dump_type,MemoryDumpLevelOfDetail level_of_detail,MemoryDumpDeterminism determinism,RequestGlobalMemoryDumpAndAppendToTraceCallback callback)267 void MockCoordinator::RequestGlobalMemoryDumpAndAppendToTrace(
268 MemoryDumpType dump_type,
269 MemoryDumpLevelOfDetail level_of_detail,
270 MemoryDumpDeterminism determinism,
271 RequestGlobalMemoryDumpAndAppendToTraceCallback callback) {
272 client_->RequestChromeDump(dump_type, level_of_detail, determinism);
273 std::move(callback).Run(1, true);
274 }
275
276 // Checks that is the ClientProcessImpl is initialized after tracing already
277 // began, it will still late-join the party (real use case: startup tracing).
TEST_F(MemoryTracingIntegrationTest,InitializedAfterStartOfTracing)278 TEST_F(MemoryTracingIntegrationTest, InitializedAfterStartOfTracing) {
279 EnableMemoryInfraTracing();
280
281 // TODO(ssid): Add tests for
282 // MemoryInstrumentation::RequestGlobalDumpAndAppendToTrace to fail gracefully
283 // before creating ClientProcessImpl.
284
285 // Now late-initialize and check that the CreateProcessDump() completes
286 // successfully.
287 InitializeClientProcess(mojom::ProcessType::RENDERER);
288 MockMemoryDumpProvider mdp;
289 RegisterDumpProvider(&mdp, nullptr, MemoryDumpProvider::Options());
290 EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1);
291 EXPECT_TRUE(RequestChromeDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
292 MemoryDumpLevelOfDetail::DETAILED));
293 DisableTracing();
294 }
295
296 // Configures periodic dumps with MemoryDumpLevelOfDetail::BACKGROUND triggers
297 // and tests that only BACKGROUND are added to the trace, but not LIGHT or
298 // DETAILED, even if requested explicitly.
TEST_F(MemoryTracingIntegrationTest,TestBackgroundTracingSetup)299 TEST_F(MemoryTracingIntegrationTest, TestBackgroundTracingSetup) {
300 InitializeClientProcess(mojom::ProcessType::BROWSER);
301 base::trace_event::SetDumpProviderAllowlistForTesting(kTestMDPWhitelist);
302 auto mdp = std::make_unique<MockMemoryDumpProvider>();
303 RegisterDumpProvider(&*mdp, nullptr, MemoryDumpProvider::Options(),
304 kWhitelistedMDPName);
305
306 base::RunLoop run_loop;
307 auto test_task_runner = base::ThreadTaskRunnerHandle::Get();
308 auto quit_closure = run_loop.QuitClosure();
309
310 {
311 testing::InSequence sequence;
312 EXPECT_CALL(*mdp, OnMemoryDump(IsBackgroundDump(), _))
313 .Times(3)
314 .WillRepeatedly(Invoke(
315 [](const MemoryDumpArgs&, ProcessMemoryDump*) { return true; }));
316 EXPECT_CALL(*mdp, OnMemoryDump(IsBackgroundDump(), _))
317 .WillOnce(Invoke([test_task_runner, quit_closure](const MemoryDumpArgs&,
318 ProcessMemoryDump*) {
319 test_task_runner->PostTask(FROM_HERE, quit_closure);
320 return true;
321 }));
322 EXPECT_CALL(*mdp, OnMemoryDump(IsBackgroundDump(), _)).Times(AnyNumber());
323 }
324
325 EnableMemoryInfraTracingWithTraceConfig(
326 base::trace_event::TraceConfigMemoryTestUtil::
327 GetTraceConfig_BackgroundTrigger(1 /* period_ms */));
328
329 run_loop.Run();
330
331 // When requesting non-BACKGROUND dumps the MDP will be invoked.
332 EXPECT_CALL(*mdp, OnMemoryDump(IsLightDump(), _));
333 EXPECT_TRUE(RequestChromeDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
334 MemoryDumpLevelOfDetail::LIGHT));
335
336 EXPECT_CALL(*mdp, OnMemoryDump(IsDetailedDump(), _));
337 EXPECT_TRUE(RequestChromeDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
338 MemoryDumpLevelOfDetail::DETAILED));
339
340 ASSERT_TRUE(IsPeriodicDumpingEnabled());
341 DisableTracing();
342 mdm_->UnregisterAndDeleteDumpProviderSoon(std::move(mdp));
343 }
344
345 // This test (and the TraceConfigExpectationsWhenIsCoordinator below)
346 // crystallizes the expectations of the chrome://tracing UI and chrome telemetry
347 // w.r.t. periodic dumps in memory-infra, handling gracefully the transition
348 // between the legacy and the new-style (JSON-based) TraceConfig.
TEST_F(MemoryTracingIntegrationTest,TraceConfigExpectations)349 TEST_F(MemoryTracingIntegrationTest, TraceConfigExpectations) {
350 InitializeClientProcess(mojom::ProcessType::RENDERER);
351
352 // We don't need to create any dump in this test, only check whether the dumps
353 // are requested or not.
354
355 // Enabling memory-infra in a non-coordinator process should not trigger any
356 // periodic dumps.
357 EnableMemoryInfraTracing();
358 EXPECT_FALSE(IsPeriodicDumpingEnabled());
359 DisableTracing();
360
361 // Enabling memory-infra with the new (JSON) TraceConfig in a non-coordinator
362 // process with a fully defined trigger config should NOT enable any periodic
363 // dumps.
364 EnableMemoryInfraTracingWithTraceConfig(
365 base::trace_event::TraceConfigMemoryTestUtil::
366 GetTraceConfig_PeriodicTriggers(1, 5));
367 EXPECT_FALSE(IsPeriodicDumpingEnabled());
368 DisableTracing();
369 }
370
TEST_F(MemoryTracingIntegrationTest,TraceConfigExpectationsWhenIsCoordinator)371 TEST_F(MemoryTracingIntegrationTest, TraceConfigExpectationsWhenIsCoordinator) {
372 InitializeClientProcess(mojom::ProcessType::BROWSER);
373
374 // Enabling memory-infra with the legacy TraceConfig (category filter) in
375 // a coordinator process should not enable periodic dumps.
376 EnableMemoryInfraTracing();
377 EXPECT_FALSE(IsPeriodicDumpingEnabled());
378 DisableTracing();
379
380 // Enabling memory-infra with the new (JSON) TraceConfig in a coordinator
381 // process while specifying a "memory_dump_config" section should enable
382 // periodic dumps. This is to preserve the behavior chrome://tracing UI, that
383 // is: ticking memory-infra should dump periodically with an explicit config.
384 EnableMemoryInfraTracingWithTraceConfig(
385 base::trace_event::TraceConfigMemoryTestUtil::
386 GetTraceConfig_PeriodicTriggers(100, 5));
387
388 EXPECT_TRUE(IsPeriodicDumpingEnabled());
389 DisableTracing();
390
391 // Enabling memory-infra with the new (JSON) TraceConfig in a coordinator
392 // process with an empty "memory_dump_config" should NOT enable periodic
393 // dumps. This is the way telemetry is supposed to use memory-infra with
394 // only explicitly triggered dumps.
395 EnableMemoryInfraTracingWithTraceConfig(
396 base::trace_event::TraceConfigMemoryTestUtil::
397 GetTraceConfig_EmptyTriggers());
398 EXPECT_FALSE(IsPeriodicDumpingEnabled());
399 DisableTracing();
400 }
401
TEST_F(MemoryTracingIntegrationTest,PeriodicDumpingWithMultipleModes)402 TEST_F(MemoryTracingIntegrationTest, PeriodicDumpingWithMultipleModes) {
403 InitializeClientProcess(mojom::ProcessType::BROWSER);
404
405 // Enabling memory-infra with the new (JSON) TraceConfig in a coordinator
406 // process with a fully defined trigger config should cause periodic dumps to
407 // be performed in the correct order.
408 base::RunLoop run_loop;
409 auto test_task_runner = base::ThreadTaskRunnerHandle::Get();
410 auto quit_closure = run_loop.QuitClosure();
411
412 const int kHeavyDumpRate = 5;
413 const int kLightDumpPeriodMs = 1;
414 const int kHeavyDumpPeriodMs = kHeavyDumpRate * kLightDumpPeriodMs;
415
416 // The expected sequence with light=1ms, heavy=5ms is H,L,L,L,L,H,...
417 auto mdp = std::make_unique<MockMemoryDumpProvider>();
418 RegisterDumpProvider(&*mdp, nullptr, MemoryDumpProvider::Options(),
419 kWhitelistedMDPName);
420
421 testing::InSequence sequence;
422 EXPECT_CALL(*mdp, OnMemoryDump(IsDetailedDump(), _));
423 EXPECT_CALL(*mdp, OnMemoryDump(IsLightDump(), _)).Times(kHeavyDumpRate - 1);
424 EXPECT_CALL(*mdp, OnMemoryDump(IsDetailedDump(), _));
425 EXPECT_CALL(*mdp, OnMemoryDump(IsLightDump(), _)).Times(kHeavyDumpRate - 2);
426 EXPECT_CALL(*mdp, OnMemoryDump(IsLightDump(), _))
427 .WillOnce(Invoke([test_task_runner, quit_closure](const MemoryDumpArgs&,
428 ProcessMemoryDump*) {
429 test_task_runner->PostTask(FROM_HERE, quit_closure);
430 return true;
431 }));
432
433 // Swallow all the final spurious calls until tracing gets disabled.
434 EXPECT_CALL(*mdp, OnMemoryDump(_, _)).Times(AnyNumber());
435
436 EnableMemoryInfraTracingWithTraceConfig(
437 base::trace_event::TraceConfigMemoryTestUtil::
438 GetTraceConfig_PeriodicTriggers(kLightDumpPeriodMs,
439 kHeavyDumpPeriodMs));
440 run_loop.Run();
441 DisableTracing();
442 mdm_->UnregisterAndDeleteDumpProviderSoon(std::move(mdp));
443 }
444
TEST_F(MemoryTracingIntegrationTest,TestWhitelistingMDP)445 TEST_F(MemoryTracingIntegrationTest, TestWhitelistingMDP) {
446 InitializeClientProcess(mojom::ProcessType::RENDERER);
447 base::trace_event::SetDumpProviderAllowlistForTesting(kTestMDPWhitelist);
448 std::unique_ptr<MockMemoryDumpProvider> mdp1(new MockMemoryDumpProvider);
449 RegisterDumpProvider(mdp1.get(), nullptr);
450 std::unique_ptr<MockMemoryDumpProvider> mdp2(new MockMemoryDumpProvider);
451 RegisterDumpProvider(mdp2.get(), nullptr, MemoryDumpProvider::Options(),
452 kWhitelistedMDPName);
453
454 EXPECT_CALL(*mdp1, OnMemoryDump(_, _)).Times(0);
455 EXPECT_CALL(*mdp2, OnMemoryDump(_, _)).Times(1);
456
457 EnableMemoryInfraTracing();
458 EXPECT_FALSE(IsPeriodicDumpingEnabled());
459 EXPECT_TRUE(RequestChromeDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
460 MemoryDumpLevelOfDetail::BACKGROUND));
461 DisableTracing();
462 }
463
464 // Regression test for https://crbug.com/766274 .
TEST_F(MemoryTracingIntegrationTest,GenerationChangeDoesntReenterMDM)465 TEST_F(MemoryTracingIntegrationTest, GenerationChangeDoesntReenterMDM) {
466 InitializeClientProcess(mojom::ProcessType::RENDERER);
467
468 // We want the ThreadLocalEventBuffer MDPs to auto-register to repro this bug.
469 mdm_->set_dumper_registrations_ignored_for_testing(false);
470
471 // Disable any other tracing category, so we are likely to hit the
472 // ThreadLocalEventBuffer in MemoryDumpManager::InbokeOnMemoryDump() first.
473 const std::string kMemoryInfraTracingOnly =
474 std::string("-*,") + MemoryDumpManager::kTraceCategory;
475
476 auto thread =
477 std::make_unique<base::TestIOThread>(base::TestIOThread::kAutoStart);
478
479 TraceLog::GetInstance()->SetEnabled(
480 TraceConfig(kMemoryInfraTracingOnly,
481 base::trace_event::RECORD_UNTIL_FULL),
482 TraceLog::RECORDING_MODE);
483
484 // Creating a new thread after tracing has started causes the posted
485 // TRACE_EVENT0 to initialize and register a new ThreadLocalEventBuffer.
486 base::RunLoop run_loop;
487 thread->PostTask(
488 FROM_HERE,
489 base::BindOnce(
490 [](scoped_refptr<base::SequencedTaskRunner> main_task_runner,
491 base::OnceClosure quit_closure) {
492 TRACE_EVENT0(MemoryDumpManager::kTraceCategory, "foo");
493 main_task_runner->PostTask(FROM_HERE, std::move(quit_closure));
494 },
495 base::SequencedTaskRunnerHandle::Get(), run_loop.QuitClosure()));
496 run_loop.Run();
497
498 EXPECT_TRUE(RequestChromeDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
499 MemoryDumpLevelOfDetail::DETAILED));
500 DisableTracing();
501
502 // Now enable tracing again with a different RECORD_ mode. This will cause
503 // a TraceLog generation change. The generation change will be lazily detected
504 // in the |thread|'s ThreadLocalEventBuffer on its next TRACE_EVENT call (or
505 // whatever ends up calling InitializeThreadLocalEventBufferIfSupported()).
506 // The bug here conisted in MemoryDumpManager::InvokeOnMemoryDump() to hit
507 // that (which in turn causes an invalidation of the ThreadLocalEventBuffer)
508 // after having checked that the MDP is valid and having decided to invoke it.
509 TraceLog::GetInstance()->SetEnabled(
510 TraceConfig(kMemoryInfraTracingOnly,
511 base::trace_event::RECORD_CONTINUOUSLY),
512 TraceLog::RECORDING_MODE);
513 EXPECT_TRUE(RequestChromeDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
514 MemoryDumpLevelOfDetail::DETAILED));
515 DisableTracing();
516 }
517
518 } // namespace memory_instrumentation
519