coordinator_impl_unittest.cc 46 KB

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