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 "services/resource_coordinator/memory_instrumentation/coordinator_impl.h"
6
7 #include "base/bind.h"
8 #include "base/run_loop.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/test/gmock_callback_support.h"
11 #include "base/test/task_environment.h"
12 #include "base/test/trace_event_analyzer.h"
13 #include "base/threading/platform_thread.h"
14 #include "base/time/time.h"
15 #include "base/trace_event/memory_dump_request_args.h"
16 #include "build/build_config.h"
17 #include "mojo/public/cpp/bindings/interface_request.h"
18 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 using base::test::RunOnceClosure;
23 using ::testing::_;
24 using ::testing::AllOf;
25 using ::testing::Contains;
26 using ::testing::Eq;
27 using ::testing::Field;
28 using ::testing::Invoke;
29 using ::testing::IsEmpty;
30 using ::testing::Ne;
31 using ::testing::NiceMock;
32 using ::testing::NotNull;
33 using ::testing::Pointee;
34 using ::testing::Property;
35 using ::testing::Return;
36 using ::testing::UnorderedElementsAre;
37
38 using GetVmRegionsForHeapProfilerCallback = memory_instrumentation::
39 CoordinatorImpl::GetVmRegionsForHeapProfilerCallback;
40 using RequestGlobalMemoryDumpAndAppendToTraceCallback = memory_instrumentation::
41 CoordinatorImpl::RequestGlobalMemoryDumpAndAppendToTraceCallback;
42 using RequestGlobalMemoryDumpCallback =
43 memory_instrumentation::CoordinatorImpl::RequestGlobalMemoryDumpCallback;
44 using RequestGlobalMemoryDumpForPidCallback = memory_instrumentation::
45 CoordinatorImpl::RequestGlobalMemoryDumpForPidCallback;
46 using base::trace_event::MemoryAllocatorDump;
47 using base::trace_event::MemoryDumpArgs;
48 using base::trace_event::MemoryDumpLevelOfDetail;
49 using base::trace_event::MemoryDumpManager;
50 using base::trace_event::MemoryDumpRequestArgs;
51 using base::trace_event::MemoryDumpType;
52 using base::trace_event::ProcessMemoryDump;
53 using base::trace_event::TraceLog;
54 using memory_instrumentation::mojom::GlobalMemoryDump;
55 using memory_instrumentation::mojom::GlobalMemoryDumpPtr;
56
57 namespace memory_instrumentation {
58
59 class FakeCoordinatorImpl : public CoordinatorImpl {
60 public:
61 FakeCoordinatorImpl() = default;
62 ~FakeCoordinatorImpl() override = default;
63
64 MOCK_CONST_METHOD0(ComputePidToServiceNamesMap,
65 std::map<base::ProcessId, std::vector<std::string>>());
66 };
67
68 class CoordinatorImplTest : public testing::Test {
69 public:
70 CoordinatorImplTest() = default;
71
SetUp()72 void SetUp() override {
73 coordinator_.reset(new NiceMock<FakeCoordinatorImpl>);
74 }
75
TearDown()76 void TearDown() override { coordinator_.reset(); }
77
RegisterClientProcess(mojo::PendingReceiver<mojom::Coordinator> receiver,mojo::PendingRemote<mojom::ClientProcess> client_process,mojom::ProcessType process_type,base::ProcessId pid)78 void RegisterClientProcess(
79 mojo::PendingReceiver<mojom::Coordinator> receiver,
80 mojo::PendingRemote<mojom::ClientProcess> client_process,
81 mojom::ProcessType process_type,
82 base::ProcessId pid) {
83 coordinator_->RegisterClientProcess(
84 std::move(receiver), std::move(client_process), process_type, pid,
85 /*service_name=*/base::nullopt);
86 }
87
RequestGlobalMemoryDump(RequestGlobalMemoryDumpCallback callback)88 void RequestGlobalMemoryDump(RequestGlobalMemoryDumpCallback callback) {
89 RequestGlobalMemoryDump(
90 MemoryDumpType::SUMMARY_ONLY, MemoryDumpLevelOfDetail::BACKGROUND,
91 MemoryDumpDeterminism::NONE, {}, std::move(callback));
92 }
93
RequestGlobalMemoryDump(MemoryDumpType dump_type,MemoryDumpLevelOfDetail level_of_detail,MemoryDumpDeterminism determinism,const std::vector<std::string> & allocator_dump_names,RequestGlobalMemoryDumpCallback callback)94 void RequestGlobalMemoryDump(
95 MemoryDumpType dump_type,
96 MemoryDumpLevelOfDetail level_of_detail,
97 MemoryDumpDeterminism determinism,
98 const std::vector<std::string>& allocator_dump_names,
99 RequestGlobalMemoryDumpCallback callback) {
100 coordinator_->RequestGlobalMemoryDump(dump_type, level_of_detail,
101 determinism, allocator_dump_names,
102 std::move(callback));
103 }
104
RequestGlobalMemoryDumpForPid(base::ProcessId pid,const std::vector<std::string> & allocator_dump_names,RequestGlobalMemoryDumpForPidCallback callback)105 void RequestGlobalMemoryDumpForPid(
106 base::ProcessId pid,
107 const std::vector<std::string>& allocator_dump_names,
108 RequestGlobalMemoryDumpForPidCallback callback) {
109 coordinator_->RequestGlobalMemoryDumpForPid(pid, allocator_dump_names,
110 std::move(callback));
111 }
112
RequestGlobalMemoryDumpAndAppendToTrace(RequestGlobalMemoryDumpAndAppendToTraceCallback callback)113 void RequestGlobalMemoryDumpAndAppendToTrace(
114 RequestGlobalMemoryDumpAndAppendToTraceCallback callback) {
115 coordinator_->RequestGlobalMemoryDumpAndAppendToTrace(
116 MemoryDumpType::EXPLICITLY_TRIGGERED, MemoryDumpLevelOfDetail::DETAILED,
117 MemoryDumpDeterminism::NONE, std::move(callback));
118 }
119
GetVmRegionsForHeapProfiler(const std::vector<base::ProcessId> & pids,GetVmRegionsForHeapProfilerCallback callback)120 void GetVmRegionsForHeapProfiler(
121 const std::vector<base::ProcessId>& pids,
122 GetVmRegionsForHeapProfilerCallback callback) {
123 coordinator_->GetVmRegionsForHeapProfiler(pids, std::move(callback));
124 }
125
ReduceCoordinatorClientProcessTimeout()126 void ReduceCoordinatorClientProcessTimeout() {
127 coordinator_->set_client_process_timeout(
128 base::TimeDelta::FromMilliseconds(5));
129 }
130
131 protected:
132 std::unique_ptr<NiceMock<FakeCoordinatorImpl>> coordinator_;
133
134 base::test::SingleThreadTaskEnvironment task_environment_{
135 base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME};
136 };
137
138 class MockClientProcess : public mojom::ClientProcess {
139 public:
MockClientProcess(CoordinatorImplTest * test_coordinator)140 MockClientProcess(CoordinatorImplTest* test_coordinator)
141 : MockClientProcess(test_coordinator,
142 base::GetCurrentProcId(),
143 mojom::ProcessType::OTHER) {}
144
MockClientProcess(CoordinatorImplTest * test_coordinator,base::ProcessId pid,mojom::ProcessType process_type)145 MockClientProcess(CoordinatorImplTest* test_coordinator,
146 base::ProcessId pid,
147 mojom::ProcessType process_type) {
148 // Register to the coordinator.
149 mojo::Remote<mojom::Coordinator> remote_coordinator;
150 mojo::PendingRemote<mojom::ClientProcess> client_process;
151 receiver_.Bind(client_process.InitWithNewPipeAndPassReceiver());
152 test_coordinator->RegisterClientProcess(
153 remote_coordinator.BindNewPipeAndPassReceiver(),
154 std::move(client_process), process_type, pid);
155
156 ON_CALL(*this, RequestChromeMemoryDumpMock(_, _))
157 .WillByDefault(Invoke([pid](const MemoryDumpRequestArgs& args,
158 RequestChromeMemoryDumpCallback& callback) {
159 MemoryDumpArgs dump_args{MemoryDumpLevelOfDetail::DETAILED};
160 auto pmd = std::make_unique<ProcessMemoryDump>(dump_args);
161 auto* mad = pmd->CreateAllocatorDump(
162 "malloc", base::trace_event::MemoryAllocatorDumpGuid(pid));
163 mad->AddScalar(MemoryAllocatorDump::kNameSize,
164 MemoryAllocatorDump::kUnitsBytes, 1024);
165
166 std::move(callback).Run(true, args.dump_guid, std::move(pmd));
167 }));
168
169 ON_CALL(*this, RequestOSMemoryDumpMock(_, _, _))
170 .WillByDefault(Invoke([](mojom::MemoryMapOption,
171 const std::vector<base::ProcessId> pids,
172 RequestOSMemoryDumpCallback& callback) {
173 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
174 std::move(callback).Run(true, std::move(results));
175 }));
176 }
177
178 ~MockClientProcess() override = default;
179
180 // TODO(crbug.com/729950): Remove non const reference here once GMock is
181 // updated to support move-only types.
182 MOCK_METHOD2(RequestChromeMemoryDumpMock,
183 void(const MemoryDumpRequestArgs& args,
184 RequestChromeMemoryDumpCallback& callback));
185 MOCK_METHOD3(RequestOSMemoryDumpMock,
186 void(mojom::MemoryMapOption option,
187 const std::vector<base::ProcessId>& args,
188 RequestOSMemoryDumpCallback& callback));
189
RequestChromeMemoryDump(const MemoryDumpRequestArgs & args,RequestChromeMemoryDumpCallback callback)190 void RequestChromeMemoryDump(
191 const MemoryDumpRequestArgs& args,
192 RequestChromeMemoryDumpCallback callback) override {
193 RequestChromeMemoryDumpMock(args, callback);
194 }
RequestOSMemoryDump(mojom::MemoryMapOption option,const std::vector<base::ProcessId> & args,RequestOSMemoryDumpCallback callback)195 void RequestOSMemoryDump(mojom::MemoryMapOption option,
196 const std::vector<base::ProcessId>& args,
197 RequestOSMemoryDumpCallback callback) override {
198 RequestOSMemoryDumpMock(option, args, callback);
199 }
200
201 private:
202 mojo::Receiver<mojom::ClientProcess> receiver_{this};
203 };
204
205 class MockGlobalMemoryDumpCallback {
206 public:
207 MockGlobalMemoryDumpCallback() = default;
208 MOCK_METHOD2(OnCall, void(bool, GlobalMemoryDump*));
209
Run(bool success,GlobalMemoryDumpPtr ptr)210 void Run(bool success, GlobalMemoryDumpPtr ptr) {
211 OnCall(success, ptr.get());
212 }
213
Get()214 RequestGlobalMemoryDumpCallback Get() {
215 return base::BindRepeating(&MockGlobalMemoryDumpCallback::Run,
216 base::Unretained(this));
217 }
218 };
219
220 class MockGlobalMemoryDumpAndAppendToTraceCallback {
221 public:
222 MockGlobalMemoryDumpAndAppendToTraceCallback() = default;
223 MOCK_METHOD2(OnCall, void(bool, uint64_t));
224
Run(bool success,uint64_t dump_guid)225 void Run(bool success, uint64_t dump_guid) { OnCall(success, dump_guid); }
226
Get()227 RequestGlobalMemoryDumpAndAppendToTraceCallback Get() {
228 return base::BindRepeating(
229 &MockGlobalMemoryDumpAndAppendToTraceCallback::Run,
230 base::Unretained(this));
231 }
232 };
233
234 class MockGetVmRegionsForHeapProfilerCallback {
235 public:
236 MockGetVmRegionsForHeapProfilerCallback() = default;
237 MOCK_METHOD1(OnCall,
238 void(const base::flat_map<base::ProcessId,
239 std::vector<mojom::VmRegionPtr>>&));
240
Run(base::flat_map<base::ProcessId,std::vector<mojom::VmRegionPtr>> results)241 void Run(base::flat_map<base::ProcessId, std::vector<mojom::VmRegionPtr>>
242 results) {
243 OnCall(results);
244 }
245
Get()246 GetVmRegionsForHeapProfilerCallback Get() {
247 return base::BindRepeating(&MockGetVmRegionsForHeapProfilerCallback::Run,
248 base::Unretained(this));
249 }
250 };
251
GetFakeAddrForVmRegion(int pid,int region_index)252 uint64_t GetFakeAddrForVmRegion(int pid, int region_index) {
253 return 0x100000ul * pid * (region_index + 1);
254 }
255
GetFakeSizeForVmRegion(int pid,int region_index)256 uint64_t GetFakeSizeForVmRegion(int pid, int region_index) {
257 return 4096 * pid * (region_index + 1);
258 }
259
FillRawOSDump(int pid)260 mojom::RawOSMemDumpPtr FillRawOSDump(int pid) {
261 mojom::RawOSMemDumpPtr raw_os_dump = mojom::RawOSMemDump::New();
262 raw_os_dump->platform_private_footprint =
263 mojom::PlatformPrivateFootprint::New();
264 raw_os_dump->resident_set_kb = pid;
265 for (int i = 0; i < 3; i++) {
266 mojom::VmRegionPtr vm_region = mojom::VmRegion::New();
267 vm_region->start_address = GetFakeAddrForVmRegion(pid, i);
268 vm_region->size_in_bytes = GetFakeSizeForVmRegion(pid, i);
269 raw_os_dump->memory_maps.push_back(std::move(vm_region));
270 }
271 return raw_os_dump;
272 }
273
274 // Tests that the global dump is acked even in absence of clients.
TEST_F(CoordinatorImplTest,NoClients)275 TEST_F(CoordinatorImplTest, NoClients) {
276 MockGlobalMemoryDumpCallback callback;
277 EXPECT_CALL(callback, OnCall(true, NotNull()));
278 RequestGlobalMemoryDump(callback.Get());
279 }
280
281 // Nominal behavior: several clients contributing to the global dump.
TEST_F(CoordinatorImplTest,SeveralClients)282 TEST_F(CoordinatorImplTest, SeveralClients) {
283 base::RunLoop run_loop;
284
285 NiceMock<MockClientProcess> client_process_1(this, 1,
286 mojom::ProcessType::BROWSER);
287 NiceMock<MockClientProcess> client_process_2(this);
288
289 EXPECT_CALL(client_process_1, RequestChromeMemoryDumpMock(_, _)).Times(1);
290 EXPECT_CALL(client_process_2, RequestChromeMemoryDumpMock(_, _)).Times(1);
291
292 MockGlobalMemoryDumpCallback callback;
293 EXPECT_CALL(callback, OnCall(true, NotNull()))
294 .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
295 RequestGlobalMemoryDump(callback.Get());
296 run_loop.Run();
297 }
298
299 // Issuing two requests will cause the second one to be queued.
TEST_F(CoordinatorImplTest,QueuedRequest)300 TEST_F(CoordinatorImplTest, QueuedRequest) {
301 base::RunLoop run_loop;
302
303 // This variable to be static as the lambda below has to convert to a function
304 // pointer rather than a functor.
305 static base::test::SingleThreadTaskEnvironment* task_environment = nullptr;
306 task_environment = &task_environment_;
307
308 NiceMock<MockClientProcess> client_process_1(this, 1,
309 mojom::ProcessType::BROWSER);
310 NiceMock<MockClientProcess> client_process_2(this);
311
312 // Each request will invoke on both processes.
313 EXPECT_CALL(client_process_1, RequestChromeMemoryDumpMock(_, _)).Times(2);
314 EXPECT_CALL(client_process_2, RequestChromeMemoryDumpMock(_, _))
315 .Times(2)
316 .WillRepeatedly(Invoke(
317 [](const MemoryDumpRequestArgs& args,
318 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
319 // Skip the wall clock time-ticks forward to make sure start_time
320 // is strictly increasing.
321 task_environment->FastForwardBy(
322 base::TimeDelta::FromMilliseconds(10));
323 MemoryDumpArgs dump_args{MemoryDumpLevelOfDetail::DETAILED};
324 auto pmd = std::make_unique<ProcessMemoryDump>(dump_args);
325 std::move(callback).Run(true, args.dump_guid, std::move(pmd));
326 }));
327
328 MockGlobalMemoryDumpCallback callback1;
329 MockGlobalMemoryDumpCallback callback2;
330
331 // Verify that the start time of subsequent dumps is monotonically
332 // increasing.
333 base::TimeTicks before = base::TimeTicks::Now();
334 base::TimeTicks first_dump_time;
335 EXPECT_CALL(callback1, OnCall(true, NotNull()))
336 .WillOnce(Invoke([&](bool success, GlobalMemoryDump* global_dump) {
337 EXPECT_LE(before, global_dump->start_time);
338 first_dump_time = global_dump->start_time;
339 }));
340 EXPECT_CALL(callback2, OnCall(true, NotNull()))
341 .WillOnce(Invoke([&](bool success, GlobalMemoryDump* global_dump) {
342 EXPECT_LT(before, global_dump->start_time);
343 EXPECT_LT(first_dump_time, global_dump->start_time);
344 run_loop.Quit();
345 }));
346 RequestGlobalMemoryDump(callback1.Get());
347 RequestGlobalMemoryDump(callback2.Get());
348 run_loop.Run();
349 }
350
TEST_F(CoordinatorImplTest,MissingChromeDump)351 TEST_F(CoordinatorImplTest, MissingChromeDump) {
352 base::RunLoop run_loop;
353
354 NiceMock<MockClientProcess> client_process(this, 1,
355 mojom::ProcessType::BROWSER);
356
357 EXPECT_CALL(client_process, RequestChromeMemoryDumpMock(_, _))
358 .WillOnce(Invoke(
359 [](const MemoryDumpRequestArgs& args,
360 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
361 MemoryDumpArgs dump_args{MemoryDumpLevelOfDetail::DETAILED};
362 auto pmd = std::make_unique<ProcessMemoryDump>(dump_args);
363 std::move(callback).Run(true, args.dump_guid, std::move(pmd));
364 }));
365
366 MockGlobalMemoryDumpCallback callback;
367 EXPECT_CALL(
368 callback,
369 OnCall(true, Pointee(Field(&mojom::GlobalMemoryDump::process_dumps,
370 IsEmpty()))))
371 .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
372 RequestGlobalMemoryDump(callback.Get());
373 run_loop.Run();
374 }
375
TEST_F(CoordinatorImplTest,MissingOsDump)376 TEST_F(CoordinatorImplTest, MissingOsDump) {
377 base::RunLoop run_loop;
378
379 NiceMock<MockClientProcess> client_process(this, 1,
380 mojom::ProcessType::BROWSER);
381
382 EXPECT_CALL(client_process, RequestOSMemoryDumpMock(_, _, _))
383 .WillOnce(Invoke(
384 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
385 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
386 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
387 std::move(callback).Run(true, std::move(results));
388 }));
389
390 MockGlobalMemoryDumpCallback callback;
391 EXPECT_CALL(
392 callback,
393 OnCall(true, Pointee(Field(&mojom::GlobalMemoryDump::process_dumps,
394 IsEmpty()))))
395 .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
396 RequestGlobalMemoryDump(callback.Get());
397 run_loop.Run();
398 }
399
TEST_F(CoordinatorImplTest,TimeOutStuckChild)400 TEST_F(CoordinatorImplTest, TimeOutStuckChild) {
401 base::RunLoop run_loop;
402
403 // |stuck_callback| should be destroyed after |client_process| or mojo
404 // will complain about the callback being destoyed before the binding.
405 MockClientProcess::RequestChromeMemoryDumpCallback stuck_callback;
406 NiceMock<MockClientProcess> client_process(this, 1,
407 mojom::ProcessType::BROWSER);
408
409 // Store a reference to the callback passed to RequestChromeMemoryDump
410 // to emulate "stuck" behaviour.
411 EXPECT_CALL(client_process, RequestChromeMemoryDumpMock(_, _))
412 .WillOnce(Invoke(
413 [&stuck_callback](
414 const MemoryDumpRequestArgs&,
415 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
416 stuck_callback = std::move(callback);
417 }));
418
419 MockGlobalMemoryDumpCallback callback;
420 EXPECT_CALL(
421 callback,
422 OnCall(false, Pointee(Field(&mojom::GlobalMemoryDump::process_dumps,
423 IsEmpty()))))
424 .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
425 ReduceCoordinatorClientProcessTimeout();
426 RequestGlobalMemoryDump(callback.Get());
427 run_loop.Run();
428 }
429
TEST_F(CoordinatorImplTest,TimeOutStuckChildMultiProcess)430 TEST_F(CoordinatorImplTest, TimeOutStuckChildMultiProcess) {
431 base::RunLoop run_loop;
432
433 static constexpr base::ProcessId kBrowserPid = 1;
434 static constexpr base::ProcessId kRendererPid = 2;
435
436 // |stuck_callback| should be destroyed after |renderer_client| or mojo
437 // will complain about the callback being destoyed before the binding.
438 MockClientProcess::RequestChromeMemoryDumpCallback stuck_callback;
439 MockClientProcess browser_client(this, kBrowserPid,
440 mojom::ProcessType::BROWSER);
441 MockClientProcess renderer_client(this, kRendererPid,
442 mojom::ProcessType::RENDERER);
443
444 // This ifdef is here to match the sandboxing behavior of the client.
445 // On Linux, all memory dumps come from the browser client. On all other
446 // platforms, they are expected to come from each individual client.
447 #if defined(OS_LINUX)
448 EXPECT_CALL(browser_client,
449 RequestOSMemoryDumpMock(
450 _, AllOf(Contains(kBrowserPid), Contains(kRendererPid)), _))
451 .WillOnce(Invoke(
452 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
453 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
454 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
455 results[kBrowserPid] = FillRawOSDump(kBrowserPid);
456 results[kRendererPid] = FillRawOSDump(kRendererPid);
457 std::move(callback).Run(true, std::move(results));
458 }));
459 EXPECT_CALL(renderer_client, RequestOSMemoryDumpMock(_, _, _)).Times(0);
460 #else
461 EXPECT_CALL(browser_client, RequestOSMemoryDumpMock(_, Contains(0), _))
462 .WillOnce(Invoke(
463 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
464 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
465 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
466 results[0] = FillRawOSDump(kBrowserPid);
467 std::move(callback).Run(true, std::move(results));
468 }));
469 EXPECT_CALL(renderer_client, RequestOSMemoryDumpMock(_, Contains(0), _))
470 .WillOnce(Invoke(
471 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
472 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
473 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
474 results[0] = FillRawOSDump(kRendererPid);
475 std::move(callback).Run(true, std::move(results));
476 }));
477 #endif // defined(OS_LINUX)
478
479 // Make the browser respond correctly but pretend the renderer is "stuck"
480 // by storing a callback.
481 EXPECT_CALL(renderer_client, RequestChromeMemoryDumpMock(_, _))
482 .WillOnce(Invoke(
483 [&stuck_callback](
484 const MemoryDumpRequestArgs&,
485 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
486 stuck_callback = std::move(callback);
487 }));
488
489 MockGlobalMemoryDumpCallback callback;
490 EXPECT_CALL(callback, OnCall(false, _))
491 .WillOnce(
492 Invoke([&run_loop](bool success, GlobalMemoryDump* global_dump) {
493 EXPECT_EQ(1U, global_dump->process_dumps.size());
494 run_loop.Quit();
495 }));
496 ReduceCoordinatorClientProcessTimeout();
497 RequestGlobalMemoryDump(callback.Get());
498 run_loop.Run();
499 }
500
501 // Tests that a global dump is completed even if a client disconnects (e.g. due
502 // to a crash) while a global dump is happening.
TEST_F(CoordinatorImplTest,ClientCrashDuringGlobalDump)503 TEST_F(CoordinatorImplTest, ClientCrashDuringGlobalDump) {
504 base::RunLoop run_loop;
505
506 auto client_process_1 = std::make_unique<NiceMock<MockClientProcess>>(
507 this, 1, mojom::ProcessType::BROWSER);
508 auto client_process_2 = std::make_unique<NiceMock<MockClientProcess>>(this);
509
510 // One of the client processes dies after a global dump is requested and
511 // before it receives the corresponding process dump request. The coordinator
512 // should detect that one of its clients is disconnected and claim the global
513 // dump attempt has failed.
514
515 // Whichever client is called first destroys the other client.
516 ON_CALL(*client_process_1, RequestChromeMemoryDumpMock(_, _))
517 .WillByDefault(Invoke(
518 [&client_process_2](
519 const MemoryDumpRequestArgs& args,
520 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
521 client_process_2.reset();
522 std::move(callback).Run(true, args.dump_guid, nullptr);
523 }));
524 ON_CALL(*client_process_2, RequestChromeMemoryDumpMock(_, _))
525 .WillByDefault(Invoke(
526 [&client_process_1](
527 const MemoryDumpRequestArgs& args,
528 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
529 client_process_1.reset();
530 std::move(callback).Run(true, args.dump_guid, nullptr);
531 }));
532
533 MockGlobalMemoryDumpCallback callback;
534 EXPECT_CALL(callback, OnCall(false, NotNull()))
535 .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
536 RequestGlobalMemoryDump(callback.Get());
537 run_loop.Run();
538 }
539
540 // Like ClientCrashDuringGlobalDump but covers the case of having only one
541 // client. Regression testing for crbug.com/742265.
TEST_F(CoordinatorImplTest,SingleClientCrashDuringGlobalDump)542 TEST_F(CoordinatorImplTest, SingleClientCrashDuringGlobalDump) {
543 base::RunLoop run_loop;
544
545 auto client_process = std::make_unique<NiceMock<MockClientProcess>>(
546 this, 1, mojom::ProcessType::BROWSER);
547
548 ON_CALL(*client_process, RequestChromeMemoryDumpMock(_, _))
549 .WillByDefault(Invoke(
550 [&client_process](
551 const MemoryDumpRequestArgs& args,
552 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
553 // The dtor here will cause mojo to post an UnregisterClient call to
554 // the coordinator.
555 client_process.reset();
556 std::move(callback).Run(true, args.dump_guid, nullptr);
557 }));
558
559 MockGlobalMemoryDumpCallback callback;
560 EXPECT_CALL(callback, OnCall(false, NotNull()))
561 .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
562 RequestGlobalMemoryDump(callback.Get());
563 run_loop.Run();
564 }
565
TEST_F(CoordinatorImplTest,GlobalMemoryDumpStruct)566 TEST_F(CoordinatorImplTest, GlobalMemoryDumpStruct) {
567 base::RunLoop run_loop;
568
569 MockClientProcess browser_client(this, 1, mojom::ProcessType::BROWSER);
570 MockClientProcess renderer_client(this, 2, mojom::ProcessType::RENDERER);
571
572 EXPECT_CALL(browser_client, RequestChromeMemoryDumpMock(_, _))
573 .WillOnce(Invoke([](const MemoryDumpRequestArgs& args,
574 MockClientProcess::RequestChromeMemoryDumpCallback&
575 callback) {
576 MemoryDumpArgs dump_args{MemoryDumpLevelOfDetail::DETAILED};
577 auto pmd = std::make_unique<ProcessMemoryDump>(dump_args);
578 auto* size = MemoryAllocatorDump::kNameSize;
579 auto* bytes = MemoryAllocatorDump::kUnitsBytes;
580 const uint32_t kB = 1024;
581
582 pmd->CreateAllocatorDump("malloc",
583 base::trace_event::MemoryAllocatorDumpGuid(1))
584 ->AddScalar(size, bytes, 1 * kB);
585 pmd->CreateAllocatorDump("malloc/ignored")
586 ->AddScalar(size, bytes, 99 * kB);
587
588 pmd->CreateAllocatorDump("blink_gc")->AddScalar(size, bytes, 2 * kB);
589 pmd->CreateAllocatorDump("blink_gc/ignored")
590 ->AddScalar(size, bytes, 99 * kB);
591
592 pmd->CreateAllocatorDump("v8/foo")->AddScalar(size, bytes, 1 * kB);
593 pmd->CreateAllocatorDump("v8/bar")->AddScalar(size, bytes, 2 * kB);
594 pmd->CreateAllocatorDump("v8")->AddScalar(size, bytes, 99 * kB);
595
596 // All the 99 KB values here are expected to be ignored.
597 pmd->CreateAllocatorDump("partition_alloc")
598 ->AddScalar(size, bytes, 99 * kB);
599 pmd->CreateAllocatorDump("partition_alloc/allocated_objects")
600 ->AddScalar(size, bytes, 99 * kB);
601 pmd->CreateAllocatorDump("partition_alloc/allocated_objects/ignored")
602 ->AddScalar(size, bytes, 99 * kB);
603 pmd->CreateAllocatorDump("partition_alloc/partitions")
604 ->AddScalar(size, bytes, 99 * kB);
605 pmd->CreateAllocatorDump("partition_alloc/partitions/not_ignored_1")
606 ->AddScalar(size, bytes, 2 * kB);
607 pmd->CreateAllocatorDump("partition_alloc/partitions/not_ignored_2")
608 ->AddScalar(size, bytes, 2 * kB);
609
610 std::move(callback).Run(true, args.dump_guid, std::move(pmd));
611 }));
612 EXPECT_CALL(renderer_client, RequestChromeMemoryDumpMock(_, _))
613 .WillOnce(Invoke(
614 [](const MemoryDumpRequestArgs& args,
615 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
616 MemoryDumpArgs dump_args{MemoryDumpLevelOfDetail::DETAILED};
617 auto pmd = std::make_unique<ProcessMemoryDump>(dump_args);
618 auto* mad = pmd->CreateAllocatorDump(
619 "malloc", base::trace_event::MemoryAllocatorDumpGuid(2));
620 mad->AddScalar(MemoryAllocatorDump::kNameSize,
621 MemoryAllocatorDump::kUnitsBytes, 1024 * 2);
622 std::move(callback).Run(true, args.dump_guid, std::move(pmd));
623 }));
624 #if defined(OS_LINUX)
625 EXPECT_CALL(browser_client,
626 RequestOSMemoryDumpMock(_, AllOf(Contains(1), Contains(2)), _))
627 .WillOnce(Invoke(
628 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
629 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
630 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
631 results[1] = mojom::RawOSMemDump::New();
632 results[1]->resident_set_kb = 1;
633 results[1]->platform_private_footprint =
634 mojom::PlatformPrivateFootprint::New();
635 results[2] = mojom::RawOSMemDump::New();
636 results[2]->platform_private_footprint =
637 mojom::PlatformPrivateFootprint::New();
638 results[2]->resident_set_kb = 2;
639 std::move(callback).Run(true, std::move(results));
640 }));
641 EXPECT_CALL(renderer_client, RequestOSMemoryDumpMock(_, _, _)).Times(0);
642 #else
643 EXPECT_CALL(browser_client, RequestOSMemoryDumpMock(_, Contains(0), _))
644 .WillOnce(Invoke(
645 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
646 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
647 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
648 results[0] = mojom::RawOSMemDump::New();
649 results[0]->platform_private_footprint =
650 mojom::PlatformPrivateFootprint::New();
651 results[0]->resident_set_kb = 1;
652 std::move(callback).Run(true, std::move(results));
653 }));
654 EXPECT_CALL(renderer_client, RequestOSMemoryDumpMock(_, Contains(0), _))
655 .WillOnce(Invoke(
656 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
657 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
658 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
659 results[0] = mojom::RawOSMemDump::New();
660 results[0]->platform_private_footprint =
661 mojom::PlatformPrivateFootprint::New();
662 results[0]->resident_set_kb = 2;
663 std::move(callback).Run(true, std::move(results));
664 }));
665 #endif // defined(OS_LINUX)
666
667 MockGlobalMemoryDumpCallback callback;
668 EXPECT_CALL(callback, OnCall(true, NotNull()))
669 .WillOnce(Invoke([&run_loop](bool success,
670 GlobalMemoryDump* global_dump) {
671 EXPECT_TRUE(success);
672 EXPECT_EQ(2U, global_dump->process_dumps.size());
673 mojom::ProcessMemoryDumpPtr browser_dump = nullptr;
674 mojom::ProcessMemoryDumpPtr renderer_dump = nullptr;
675 for (mojom::ProcessMemoryDumpPtr& dump : global_dump->process_dumps) {
676 if (dump->process_type == mojom::ProcessType::BROWSER) {
677 browser_dump = std::move(dump);
678 } else if (dump->process_type == mojom::ProcessType::RENDERER) {
679 renderer_dump = std::move(dump);
680 }
681 }
682
683 EXPECT_EQ(browser_dump->os_dump->resident_set_kb, 1u);
684 EXPECT_EQ(renderer_dump->os_dump->resident_set_kb, 2u);
685 run_loop.Quit();
686 }));
687
688 RequestGlobalMemoryDump(callback.Get());
689 run_loop.Run();
690 }
691
TEST_F(CoordinatorImplTest,VmRegionsForHeapProfiler)692 TEST_F(CoordinatorImplTest, VmRegionsForHeapProfiler) {
693 base::RunLoop run_loop;
694 // Not using a constexpr base::ProcessId because std:unordered_map<>
695 // and friends makes it too easy to accidentally odr-use this variable
696 // causing all sorts of compiler-toolchain divergent fun when trying
697 // to decide of the lambda capture is necessary.
698 static constexpr base::ProcessId kBrowserPid = 1;
699 static constexpr base::ProcessId kRendererPid = 2;
700
701 MockClientProcess browser_client(this, kBrowserPid,
702 mojom::ProcessType::BROWSER);
703 MockClientProcess renderer_client(this, kRendererPid,
704 mojom::ProcessType::RENDERER);
705
706 // This ifdef is here to match the sandboxing behavior of the client.
707 // On Linux, all memory dumps come from the browser client. On all other
708 // platforms, they are expected to come from each individual client.
709 #if defined(OS_LINUX)
710 EXPECT_CALL(browser_client,
711 RequestOSMemoryDumpMock(
712 _, AllOf(Contains(kBrowserPid), Contains(kRendererPid)), _))
713 .WillOnce(Invoke(
714 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
715 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
716 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
717 results[kBrowserPid] = FillRawOSDump(kBrowserPid);
718 results[kRendererPid] = FillRawOSDump(kRendererPid);
719 std::move(callback).Run(true, std::move(results));
720 }));
721 EXPECT_CALL(renderer_client, RequestOSMemoryDumpMock(_, _, _)).Times(0);
722 #else
723 EXPECT_CALL(browser_client, RequestOSMemoryDumpMock(_, Contains(0), _))
724 .WillOnce(Invoke(
725 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
726 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
727 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
728 results[0] = FillRawOSDump(kBrowserPid);
729 std::move(callback).Run(true, std::move(results));
730 }));
731 EXPECT_CALL(renderer_client, RequestOSMemoryDumpMock(_, Contains(0), _))
732 .WillOnce(Invoke(
733 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
734 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
735 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
736 results[0] = FillRawOSDump(kRendererPid);
737 std::move(callback).Run(true, std::move(results));
738 }));
739 #endif // defined(OS_LINUX)
740
741 MockGetVmRegionsForHeapProfilerCallback callback;
742 EXPECT_CALL(callback, OnCall(_))
743 .WillOnce(Invoke(
744 [&run_loop](
745 const base::flat_map<base::ProcessId,
746 std::vector<mojom::VmRegionPtr>>& results) {
747 ASSERT_EQ(2U, results.size());
748
749 auto browser_it = results.find(kBrowserPid);
750 ASSERT_TRUE(browser_it != results.end());
751 auto renderer_it = results.find(kRendererPid);
752 ASSERT_TRUE(renderer_it != results.end());
753
754 const std::vector<mojom::VmRegionPtr>& browser_mmaps =
755 browser_it->second;
756 ASSERT_EQ(3u, browser_mmaps.size());
757 for (int i = 0; i < 3; i++) {
758 EXPECT_EQ(GetFakeAddrForVmRegion(kBrowserPid, i),
759 browser_mmaps[i]->start_address);
760 }
761
762 const std::vector<mojom::VmRegionPtr>& renderer_mmaps =
763 renderer_it->second;
764 ASSERT_EQ(3u, renderer_mmaps.size());
765 for (int i = 0; i < 3; i++) {
766 EXPECT_EQ(GetFakeAddrForVmRegion(kRendererPid, i),
767 renderer_mmaps[i]->start_address);
768 }
769 run_loop.Quit();
770 }));
771
772 std::vector<base::ProcessId> pids;
773 pids.push_back(kBrowserPid);
774 pids.push_back(kRendererPid);
775 GetVmRegionsForHeapProfiler(pids, callback.Get());
776 run_loop.Run();
777 }
778
779 // RequestGlobalMemoryDump, as opposite to RequestGlobalMemoryDumpAndAddToTrace,
780 // shouldn't add anything into the trace
TEST_F(CoordinatorImplTest,DumpsArentAddedToTraceUnlessRequested)781 TEST_F(CoordinatorImplTest, DumpsArentAddedToTraceUnlessRequested) {
782 using trace_analyzer::Query;
783
784 base::RunLoop run_loop;
785
786 NiceMock<MockClientProcess> client_process(this, 1,
787 mojom::ProcessType::BROWSER);
788
789 EXPECT_CALL(client_process, RequestChromeMemoryDumpMock(_, _))
790 .WillOnce(Invoke(
791 [](const MemoryDumpRequestArgs& args,
792 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
793 MemoryDumpArgs dump_args{MemoryDumpLevelOfDetail::DETAILED};
794 auto pmd = std::make_unique<ProcessMemoryDump>(dump_args);
795 std::move(callback).Run(true, args.dump_guid, std::move(pmd));
796 }));
797
798 MockGlobalMemoryDumpCallback callback;
799 EXPECT_CALL(
800 callback,
801 OnCall(true, Pointee(Field(&mojom::GlobalMemoryDump::process_dumps,
802 IsEmpty()))))
803 .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
804
805 trace_analyzer::Start(MemoryDumpManager::kTraceCategory);
806 RequestGlobalMemoryDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
807 MemoryDumpLevelOfDetail::DETAILED,
808 MemoryDumpDeterminism::NONE, {}, callback.Get());
809 run_loop.Run();
810 auto analyzer = trace_analyzer::Stop();
811
812 trace_analyzer::TraceEventVector events;
813 analyzer->FindEvents(
814 trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_MEMORY_DUMP),
815 &events);
816
817 ASSERT_EQ(0u, events.size());
818 }
819
TEST_F(CoordinatorImplTest,DumpsAreAddedToTraceWhenRequested)820 TEST_F(CoordinatorImplTest, DumpsAreAddedToTraceWhenRequested) {
821 using trace_analyzer::Query;
822
823 base::RunLoop run_loop;
824
825 NiceMock<MockClientProcess> client_process(this, 1,
826 mojom::ProcessType::BROWSER);
827 EXPECT_CALL(client_process, RequestChromeMemoryDumpMock(_, _))
828 .WillOnce(Invoke(
829 [](const MemoryDumpRequestArgs& args,
830 MockClientProcess::RequestChromeMemoryDumpCallback& callback) {
831 MemoryDumpArgs dump_args{MemoryDumpLevelOfDetail::DETAILED};
832 auto pmd = std::make_unique<ProcessMemoryDump>(dump_args);
833 std::move(callback).Run(true, args.dump_guid, std::move(pmd));
834 }));
835
836 MockGlobalMemoryDumpAndAppendToTraceCallback callback;
837 EXPECT_CALL(callback, OnCall(true, Ne(0ul)))
838 .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
839
840 trace_analyzer::Start(MemoryDumpManager::kTraceCategory);
841 RequestGlobalMemoryDumpAndAppendToTrace(callback.Get());
842 run_loop.Run();
843 auto analyzer = trace_analyzer::Stop();
844
845 trace_analyzer::TraceEventVector events;
846 analyzer->FindEvents(
847 trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_MEMORY_DUMP),
848 &events);
849
850 ASSERT_EQ(1u, events.size());
851 ASSERT_TRUE(trace_analyzer::CountMatches(
852 events, trace_analyzer::Query::EventNameIs(MemoryDumpTypeToString(
853 MemoryDumpType::EXPLICITLY_TRIGGERED))));
854 }
855
TEST_F(CoordinatorImplTest,DumpByPidSuccess)856 TEST_F(CoordinatorImplTest, DumpByPidSuccess) {
857 static constexpr base::ProcessId kBrowserPid = 1;
858 static constexpr base::ProcessId kRendererPid = 2;
859 static constexpr base::ProcessId kGpuPid = 3;
860
861 NiceMock<MockClientProcess> client_process_1(this, kBrowserPid,
862 mojom::ProcessType::BROWSER);
863 NiceMock<MockClientProcess> client_process_2(this, kRendererPid,
864 mojom::ProcessType::RENDERER);
865 NiceMock<MockClientProcess> client_process_3(this, kGpuPid,
866 mojom::ProcessType::GPU);
867
868 // This ifdef is here to match the sandboxing behavior of the client.
869 // On Linux, all memory dumps come from the browser client. On all other
870 // platforms, they are expected to come from each individual client.
871 #if defined(OS_LINUX)
872 EXPECT_CALL(client_process_1, RequestOSMemoryDumpMock(_, _, _))
873 .WillOnce(Invoke(
874 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
875 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
876 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
877 results[kBrowserPid] = FillRawOSDump(kBrowserPid);
878 std::move(callback).Run(true, std::move(results));
879 }))
880 .WillOnce(Invoke(
881 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
882 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
883 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
884 results[kRendererPid] = FillRawOSDump(kRendererPid);
885 std::move(callback).Run(true, std::move(results));
886 }))
887 .WillOnce(Invoke(
888 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
889 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
890 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
891 results[kGpuPid] = FillRawOSDump(kGpuPid);
892 std::move(callback).Run(true, std::move(results));
893 }));
894 #else
895 EXPECT_CALL(client_process_1, RequestOSMemoryDumpMock(_, Contains(0), _))
896 .WillOnce(Invoke(
897 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
898 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
899 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
900 results[0] = FillRawOSDump(kBrowserPid);
901 std::move(callback).Run(true, std::move(results));
902 }));
903 EXPECT_CALL(client_process_2, RequestOSMemoryDumpMock(_, Contains(0), _))
904 .WillOnce(Invoke(
905 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
906 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
907 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
908 results[0] = FillRawOSDump(kRendererPid);
909 std::move(callback).Run(true, std::move(results));
910 }));
911 EXPECT_CALL(client_process_3, RequestOSMemoryDumpMock(_, Contains(0), _))
912 .WillOnce(Invoke(
913 [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids,
914 MockClientProcess::RequestOSMemoryDumpCallback& callback) {
915 base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
916 results[0] = FillRawOSDump(kGpuPid);
917 std::move(callback).Run(true, std::move(results));
918 }));
919 #endif // defined(OS_LINUX)
920
921 base::RunLoop run_loop;
922
923 MockGlobalMemoryDumpCallback callback;
924 EXPECT_CALL(callback, OnCall(true, Ne(nullptr)))
925 .WillOnce(Invoke([](bool success, GlobalMemoryDump* global_dump) {
926 EXPECT_EQ(1U, global_dump->process_dumps.size());
927 EXPECT_EQ(global_dump->process_dumps[0]->pid, kBrowserPid);
928 }))
929 .WillOnce(Invoke([](bool success, GlobalMemoryDump* global_dump) {
930 EXPECT_EQ(1U, global_dump->process_dumps.size());
931 EXPECT_EQ(global_dump->process_dumps[0]->pid, kRendererPid);
932 }))
933 .WillOnce(
934 Invoke([&run_loop](bool success, GlobalMemoryDump* global_dump) {
935 EXPECT_EQ(1U, global_dump->process_dumps.size());
936 EXPECT_EQ(global_dump->process_dumps[0]->pid, kGpuPid);
937 run_loop.Quit();
938 }));
939
940 RequestGlobalMemoryDumpForPid(kBrowserPid, {}, callback.Get());
941 RequestGlobalMemoryDumpForPid(kRendererPid, {}, callback.Get());
942 RequestGlobalMemoryDumpForPid(kGpuPid, {}, callback.Get());
943 run_loop.Run();
944 }
945
TEST_F(CoordinatorImplTest,DumpByPidFailure)946 TEST_F(CoordinatorImplTest, DumpByPidFailure) {
947 NiceMock<MockClientProcess> client_process_1(this, 1,
948 mojom::ProcessType::BROWSER);
949
950 base::RunLoop run_loop;
951
952 MockGlobalMemoryDumpCallback callback;
953 EXPECT_CALL(callback, OnCall(false, nullptr))
954 .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
955
956 RequestGlobalMemoryDumpForPid(2, {}, callback.Get());
957 run_loop.Run();
958 }
959
960 } // namespace memory_instrumentation
961