coordinator_impl.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  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 <inttypes.h>
  6. #include <stdio.h>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/callback_helpers.h"
  10. #include "base/command_line.h"
  11. #include "base/containers/contains.h"
  12. #include "base/location.h"
  13. #include "base/logging.h"
  14. #include "base/memory/ref_counted.h"
  15. #include "base/metrics/histogram_macros.h"
  16. #include "base/threading/sequenced_task_runner_handle.h"
  17. #include "base/trace_event/memory_dump_manager.h"
  18. #include "base/trace_event/memory_dump_request_args.h"
  19. #include "base/trace_event/trace_event.h"
  20. #include "build/build_config.h"
  21. #include "services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.h"
  22. #include "services/resource_coordinator/memory_instrumentation/switches.h"
  23. #include "services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.h"
  24. #include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer_proto.h"
  25. #include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer_traced_value.h"
  26. #include "services/resource_coordinator/public/mojom/memory_instrumentation/constants.mojom.h"
  27. #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
  28. #if BUILDFLAG(IS_MAC)
  29. #include "base/mac/mac_util.h"
  30. #endif
  31. using base::trace_event::MemoryDumpDeterminism;
  32. using base::trace_event::MemoryDumpLevelOfDetail;
  33. using base::trace_event::MemoryDumpType;
  34. namespace memory_instrumentation {
  35. namespace {
  36. memory_instrumentation::CoordinatorImpl* g_coordinator_impl;
  37. constexpr base::TimeDelta kHeapDumpTimeout = base::Seconds(60);
  38. // A wrapper classes that allows a string to be exported as JSON in a trace
  39. // event.
  40. class StringWrapper : public base::trace_event::ConvertableToTraceFormat {
  41. public:
  42. explicit StringWrapper(std::string&& json) : json_(std::move(json)) {}
  43. void AppendAsTraceFormat(std::string* out) const override {
  44. out->append(json_);
  45. }
  46. std::string json_;
  47. };
  48. } // namespace
  49. CoordinatorImpl::CoordinatorImpl()
  50. : next_dump_id_(0),
  51. client_process_timeout_(base::Seconds(15)),
  52. use_proto_writer_(!base::CommandLine::ForCurrentProcess()->HasSwitch(
  53. switches::kUseMemoryTrackingJsonWriter)),
  54. write_proto_heap_profile_(
  55. base::CommandLine::ForCurrentProcess()->HasSwitch(
  56. switches::kUseHeapProfilingProtoWriter)) {
  57. DCHECK(!g_coordinator_impl);
  58. g_coordinator_impl = this;
  59. base::trace_event::MemoryDumpManager::GetInstance()->set_tracing_process_id(
  60. mojom::kServiceTracingProcessId);
  61. if (use_proto_writer_) {
  62. tracing_observer_ = std::make_unique<TracingObserverProto>(
  63. base::trace_event::TraceLog::GetInstance(), nullptr);
  64. } else {
  65. tracing_observer_ = std::make_unique<TracingObserverTracedValue>(
  66. base::trace_event::TraceLog::GetInstance(), nullptr);
  67. }
  68. }
  69. CoordinatorImpl::~CoordinatorImpl() {
  70. g_coordinator_impl = nullptr;
  71. }
  72. // static
  73. CoordinatorImpl* CoordinatorImpl::GetInstance() {
  74. return g_coordinator_impl;
  75. }
  76. void CoordinatorImpl::RegisterHeapProfiler(
  77. mojo::PendingRemote<mojom::HeapProfiler> profiler,
  78. mojo::PendingReceiver<mojom::HeapProfilerHelper> helper_receiver) {
  79. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  80. heap_profiler_.Bind(std::move(profiler));
  81. heap_profiler_helper_receiver_.Bind(std::move(helper_receiver));
  82. }
  83. void CoordinatorImpl::RegisterClientProcess(
  84. mojo::PendingReceiver<mojom::Coordinator> receiver,
  85. mojo::PendingRemote<mojom::ClientProcess> client_process,
  86. mojom::ProcessType process_type,
  87. base::ProcessId process_id,
  88. const absl::optional<std::string>& service_name) {
  89. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  90. mojo::Remote<mojom::ClientProcess> process(std::move(client_process));
  91. if (receiver.is_valid())
  92. coordinator_receivers_.Add(this, std::move(receiver), process_id);
  93. process.set_disconnect_handler(
  94. base::BindOnce(&CoordinatorImpl::UnregisterClientProcess,
  95. base::Unretained(this), process_id));
  96. auto result = clients_.emplace(
  97. process_id, std::make_unique<ClientInfo>(std::move(process), process_type,
  98. service_name));
  99. DCHECK(result.second) << "Cannot register process " << process_id
  100. << " with type " << static_cast<int>(process_type)
  101. << ". Already registered for "
  102. << static_cast<int>(
  103. clients_.find(process_id)->second->process_type);
  104. }
  105. void CoordinatorImpl::RequestGlobalMemoryDump(
  106. MemoryDumpType dump_type,
  107. MemoryDumpLevelOfDetail level_of_detail,
  108. MemoryDumpDeterminism determinism,
  109. const std::vector<std::string>& allocator_dump_names,
  110. RequestGlobalMemoryDumpCallback callback) {
  111. // This merely strips out the |dump_guid| argument.
  112. auto adapter = [](RequestGlobalMemoryDumpCallback callback, bool success,
  113. uint64_t, mojom::GlobalMemoryDumpPtr global_memory_dump) {
  114. std::move(callback).Run(success, std::move(global_memory_dump));
  115. };
  116. QueuedRequest::Args args(dump_type, level_of_detail, determinism,
  117. allocator_dump_names, false /* add_to_trace */,
  118. base::kNullProcessId,
  119. /*memory_footprint_only=*/false);
  120. RequestGlobalMemoryDumpInternal(args,
  121. base::BindOnce(adapter, std::move(callback)));
  122. }
  123. void CoordinatorImpl::RequestGlobalMemoryDumpForPid(
  124. base::ProcessId pid,
  125. const std::vector<std::string>& allocator_dump_names,
  126. RequestGlobalMemoryDumpForPidCallback callback) {
  127. // Error out early if process id is null to avoid confusing with global
  128. // dump for all processes case when pid is kNullProcessId.
  129. if (pid == base::kNullProcessId) {
  130. std::move(callback).Run(false, nullptr);
  131. return;
  132. }
  133. // This merely strips out the |dump_guid| argument; this is not relevant
  134. // as we are not adding to trace.
  135. auto adapter = [](RequestGlobalMemoryDumpForPidCallback callback,
  136. bool success, uint64_t,
  137. mojom::GlobalMemoryDumpPtr global_memory_dump) {
  138. std::move(callback).Run(success, std::move(global_memory_dump));
  139. };
  140. QueuedRequest::Args args(
  141. base::trace_event::MemoryDumpType::SUMMARY_ONLY,
  142. base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND,
  143. base::trace_event::MemoryDumpDeterminism::NONE, allocator_dump_names,
  144. false /* add_to_trace */, pid,
  145. /*memory_footprint_only=*/false);
  146. RequestGlobalMemoryDumpInternal(args,
  147. base::BindOnce(adapter, std::move(callback)));
  148. }
  149. void CoordinatorImpl::RequestPrivateMemoryFootprint(
  150. base::ProcessId pid,
  151. RequestPrivateMemoryFootprintCallback callback) {
  152. // This merely strips out the |dump_guid| argument; this is not relevant
  153. // as we are not adding to trace.
  154. auto adapter = [](RequestPrivateMemoryFootprintCallback callback,
  155. bool success, uint64_t,
  156. mojom::GlobalMemoryDumpPtr global_memory_dump) {
  157. std::move(callback).Run(success, std::move(global_memory_dump));
  158. };
  159. QueuedRequest::Args args(
  160. base::trace_event::MemoryDumpType::SUMMARY_ONLY,
  161. base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND,
  162. base::trace_event::MemoryDumpDeterminism::NONE, {},
  163. false /* add_to_trace */, pid, /*memory_footprint_only=*/true);
  164. RequestGlobalMemoryDumpInternal(args,
  165. base::BindOnce(adapter, std::move(callback)));
  166. }
  167. void CoordinatorImpl::RequestGlobalMemoryDumpAndAppendToTrace(
  168. MemoryDumpType dump_type,
  169. MemoryDumpLevelOfDetail level_of_detail,
  170. MemoryDumpDeterminism determinism,
  171. RequestGlobalMemoryDumpAndAppendToTraceCallback callback) {
  172. // This merely strips out the |dump_ptr| argument.
  173. auto adapter = [](RequestGlobalMemoryDumpAndAppendToTraceCallback callback,
  174. bool success, uint64_t dump_guid,
  175. mojom::GlobalMemoryDumpPtr) {
  176. std::move(callback).Run(success, dump_guid);
  177. };
  178. QueuedRequest::Args args(dump_type, level_of_detail, determinism, {},
  179. true /* add_to_trace */, base::kNullProcessId,
  180. /*memory_footprint_only=*/false);
  181. RequestGlobalMemoryDumpInternal(args,
  182. base::BindOnce(adapter, std::move(callback)));
  183. }
  184. void CoordinatorImpl::GetVmRegionsForHeapProfiler(
  185. const std::vector<base::ProcessId>& pids,
  186. GetVmRegionsForHeapProfilerCallback callback) {
  187. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  188. uint64_t dump_guid = ++next_dump_id_;
  189. std::unique_ptr<QueuedVmRegionRequest> request =
  190. std::make_unique<QueuedVmRegionRequest>(dump_guid, std::move(callback));
  191. in_progress_vm_region_requests_[dump_guid] = std::move(request);
  192. std::vector<QueuedRequestDispatcher::ClientInfo> clients;
  193. for (const auto& entry : clients_) {
  194. const base::ProcessId pid = entry.first;
  195. clients.emplace_back(entry.second->client.get(), pid,
  196. entry.second->process_type,
  197. entry.second->service_name);
  198. }
  199. QueuedVmRegionRequest* request_ptr =
  200. in_progress_vm_region_requests_[dump_guid].get();
  201. auto os_callback =
  202. base::BindRepeating(&CoordinatorImpl::OnOSMemoryDumpForVMRegions,
  203. weak_ptr_factory_.GetWeakPtr(), dump_guid);
  204. QueuedRequestDispatcher::SetUpAndDispatchVmRegionRequest(request_ptr, clients,
  205. pids, os_callback);
  206. FinalizeVmRegionDumpIfAllManagersReplied(dump_guid);
  207. }
  208. void CoordinatorImpl::UnregisterClientProcess(base::ProcessId process_id) {
  209. QueuedRequest* request = GetCurrentRequest();
  210. if (request != nullptr) {
  211. // Check if we are waiting for an ack from this client process.
  212. auto it = request->pending_responses.begin();
  213. while (it != request->pending_responses.end()) {
  214. // The calls to On*MemoryDumpResponse below, if executed, will delete the
  215. // element under the iterator which invalidates it. To avoid this we
  216. // increment the iterator in advance while keeping a reference to the
  217. // current element.
  218. std::set<QueuedRequest::PendingResponse>::iterator current = it++;
  219. if (current->process_id != process_id)
  220. continue;
  221. RemovePendingResponse(process_id, current->type);
  222. DLOG(ERROR)
  223. << "Memory dump request failed due to disconnected child process "
  224. << process_id;
  225. request->failed_memory_dump_count++;
  226. }
  227. FinalizeGlobalMemoryDumpIfAllManagersReplied();
  228. }
  229. for (auto& pair : in_progress_vm_region_requests_) {
  230. QueuedVmRegionRequest* in_progress_request = pair.second.get();
  231. auto it = in_progress_request->pending_responses.begin();
  232. while (it != in_progress_request->pending_responses.end()) {
  233. auto current = it++;
  234. if (*current == process_id) {
  235. in_progress_request->pending_responses.erase(current);
  236. }
  237. }
  238. }
  239. // Try to finalize all outstanding vm region requests.
  240. for (auto& pair : in_progress_vm_region_requests_) {
  241. // PostTask to avoid re-entrancy or modification of data-structure during
  242. // iteration.
  243. base::SequencedTaskRunnerHandle::Get()->PostTask(
  244. FROM_HERE,
  245. base::BindOnce(
  246. &CoordinatorImpl::FinalizeVmRegionDumpIfAllManagersReplied,
  247. weak_ptr_factory_.GetWeakPtr(), pair.second->dump_guid));
  248. }
  249. size_t num_deleted = clients_.erase(process_id);
  250. DCHECK(num_deleted == 1);
  251. }
  252. void CoordinatorImpl::RequestGlobalMemoryDumpInternal(
  253. const QueuedRequest::Args& args,
  254. RequestGlobalMemoryDumpInternalCallback callback) {
  255. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  256. UMA_HISTOGRAM_COUNTS_1000(
  257. "Memory.Experimental.Debug.GlobalDumpQueueLength",
  258. base::saturated_cast<int32_t>(queued_memory_dump_requests_.size()));
  259. bool another_dump_is_queued = !queued_memory_dump_requests_.empty();
  260. // If this is a periodic or peak memory dump request and there already is
  261. // another request in the queue with the same level of detail, there's no
  262. // point in enqueuing this request.
  263. if (another_dump_is_queued &&
  264. args.dump_type == MemoryDumpType::PERIODIC_INTERVAL) {
  265. for (const auto& request : queued_memory_dump_requests_) {
  266. if (request.args.level_of_detail == args.level_of_detail) {
  267. VLOG(1) << "RequestGlobalMemoryDump("
  268. << base::trace_event::MemoryDumpTypeToString(args.dump_type)
  269. << ") skipped because another dump request with the same "
  270. "level of detail ("
  271. << base::trace_event::MemoryDumpLevelOfDetailToString(
  272. args.level_of_detail)
  273. << ") is already in the queue";
  274. std::move(callback).Run(false /* success */, 0 /* dump_guid */,
  275. nullptr /* global_memory_dump */);
  276. return;
  277. }
  278. }
  279. }
  280. queued_memory_dump_requests_.emplace_back(args, ++next_dump_id_,
  281. std::move(callback));
  282. // If another dump is already in queued, this dump will automatically be
  283. // scheduled when the other dump finishes.
  284. if (another_dump_is_queued)
  285. return;
  286. PerformNextQueuedGlobalMemoryDump();
  287. }
  288. void CoordinatorImpl::OnQueuedRequestTimedOut(uint64_t dump_guid) {
  289. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  290. QueuedRequest* request = GetCurrentRequest();
  291. // TODO(lalitm): add metrics for how often this happens.
  292. // Only consider the current request timed out if we fired off this
  293. // delayed callback in association with this request.
  294. if (!request || request->dump_guid != dump_guid)
  295. return;
  296. // Fail all remaining dumps being waited upon and clear the vector.
  297. if (request->pending_responses.size() > 0) {
  298. DLOG(ERROR) << "Global dump request timed out waiting for "
  299. << request->pending_responses.size() << " requests";
  300. }
  301. request->failed_memory_dump_count += request->pending_responses.size();
  302. request->pending_responses.clear();
  303. // Callback the consumer of the service.
  304. FinalizeGlobalMemoryDumpIfAllManagersReplied();
  305. }
  306. void CoordinatorImpl::OnHeapDumpTimeOut(uint64_t dump_guid) {
  307. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  308. QueuedRequest* request = GetCurrentRequest();
  309. // TODO(lalitm): add metrics for how often this happens.
  310. // Only consider the current request timed out if we fired off this
  311. // delayed callback in association with this request.
  312. if (!request || request->dump_guid != dump_guid)
  313. return;
  314. // Fail all remaining dumps being waited upon and clear the vector.
  315. if (request->heap_dump_in_progress) {
  316. request->heap_dump_in_progress = false;
  317. FinalizeGlobalMemoryDumpIfAllManagersReplied();
  318. }
  319. }
  320. void CoordinatorImpl::PerformNextQueuedGlobalMemoryDump() {
  321. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  322. QueuedRequest* request = GetCurrentRequest();
  323. if (request == nullptr)
  324. return;
  325. std::vector<QueuedRequestDispatcher::ClientInfo> clients;
  326. for (const auto& entry : clients_) {
  327. const base::ProcessId pid = entry.first;
  328. clients.emplace_back(entry.second->client.get(), pid,
  329. entry.second->process_type,
  330. entry.second->service_name);
  331. }
  332. auto chrome_callback =
  333. base::BindRepeating(&CoordinatorImpl::OnChromeMemoryDumpResponse,
  334. weak_ptr_factory_.GetWeakPtr());
  335. auto os_callback =
  336. base::BindRepeating(&CoordinatorImpl::OnOSMemoryDumpResponse,
  337. weak_ptr_factory_.GetWeakPtr(), request->dump_guid);
  338. QueuedRequestDispatcher::SetUpAndDispatch(request, clients, chrome_callback,
  339. os_callback);
  340. base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
  341. FROM_HERE,
  342. base::BindOnce(&CoordinatorImpl::OnQueuedRequestTimedOut,
  343. weak_ptr_factory_.GetWeakPtr(), request->dump_guid),
  344. client_process_timeout_);
  345. if (request->args.add_to_trace && heap_profiler_) {
  346. request->heap_dump_in_progress = true;
  347. // |IsArgumentFilterEnabled| is the round-about way of asking to anonymize
  348. // the trace. The only way that PII gets leaked is if the full path is
  349. // emitted for mapped files. Passing |strip_path_from_mapped_files|
  350. // is all that is necessary to anonymize the trace.
  351. bool strip_path_from_mapped_files =
  352. base::trace_event::TraceLog::GetInstance()
  353. ->GetCurrentTraceConfig()
  354. .IsArgumentFilterEnabled();
  355. heap_profiler_->DumpProcessesForTracing(
  356. strip_path_from_mapped_files, write_proto_heap_profile_,
  357. base::BindOnce(&CoordinatorImpl::OnDumpProcessesForTracing,
  358. weak_ptr_factory_.GetWeakPtr(), request->dump_guid));
  359. base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
  360. FROM_HERE,
  361. base::BindOnce(&CoordinatorImpl::OnHeapDumpTimeOut,
  362. weak_ptr_factory_.GetWeakPtr(), request->dump_guid),
  363. kHeapDumpTimeout);
  364. }
  365. // Run the callback in case there are no client processes registered.
  366. FinalizeGlobalMemoryDumpIfAllManagersReplied();
  367. }
  368. QueuedRequest* CoordinatorImpl::GetCurrentRequest() {
  369. if (queued_memory_dump_requests_.empty()) {
  370. return nullptr;
  371. }
  372. return &queued_memory_dump_requests_.front();
  373. }
  374. void CoordinatorImpl::OnChromeMemoryDumpResponse(
  375. base::ProcessId process_id,
  376. bool success,
  377. uint64_t dump_guid,
  378. std::unique_ptr<base::trace_event::ProcessMemoryDump> chrome_memory_dump) {
  379. using ResponseType = QueuedRequest::PendingResponse::Type;
  380. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  381. QueuedRequest* request = GetCurrentRequest();
  382. if (request == nullptr || request->dump_guid != dump_guid) {
  383. return;
  384. }
  385. RemovePendingResponse(process_id, ResponseType::kChromeDump);
  386. if (!base::Contains(clients_, process_id)) {
  387. VLOG(1) << "Received a memory dump response from an unregistered client";
  388. return;
  389. }
  390. auto* response = &request->responses[process_id];
  391. response->chrome_dump = std::move(chrome_memory_dump);
  392. if (!success) {
  393. DLOG(ERROR) << "Memory dump request failed: NACK from client process";
  394. request->failed_memory_dump_count++;
  395. }
  396. FinalizeGlobalMemoryDumpIfAllManagersReplied();
  397. }
  398. void CoordinatorImpl::OnOSMemoryDumpResponse(uint64_t dump_guid,
  399. base::ProcessId process_id,
  400. bool success,
  401. OSMemDumpMap os_dumps) {
  402. using ResponseType = QueuedRequest::PendingResponse::Type;
  403. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  404. QueuedRequest* request = GetCurrentRequest();
  405. if (request == nullptr || request->dump_guid != dump_guid) {
  406. return;
  407. }
  408. RemovePendingResponse(process_id, ResponseType::kOSDump);
  409. if (!base::Contains(clients_, process_id)) {
  410. VLOG(1) << "Received a memory dump response from an unregistered client";
  411. return;
  412. }
  413. request->responses[process_id].os_dumps = std::move(os_dumps);
  414. if (!success) {
  415. DLOG(ERROR) << "Memory dump request failed: NACK from client process";
  416. request->failed_memory_dump_count++;
  417. }
  418. FinalizeGlobalMemoryDumpIfAllManagersReplied();
  419. }
  420. void CoordinatorImpl::OnOSMemoryDumpForVMRegions(uint64_t dump_guid,
  421. base::ProcessId process_id,
  422. bool success,
  423. OSMemDumpMap os_dumps) {
  424. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  425. auto request_it = in_progress_vm_region_requests_.find(dump_guid);
  426. DCHECK(request_it != in_progress_vm_region_requests_.end());
  427. QueuedVmRegionRequest* request = request_it->second.get();
  428. auto it = request->pending_responses.find(process_id);
  429. DCHECK(it != request->pending_responses.end());
  430. request->pending_responses.erase(it);
  431. request->responses[process_id].os_dumps = std::move(os_dumps);
  432. FinalizeVmRegionDumpIfAllManagersReplied(request->dump_guid);
  433. }
  434. void CoordinatorImpl::FinalizeVmRegionDumpIfAllManagersReplied(
  435. uint64_t dump_guid) {
  436. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  437. auto it = in_progress_vm_region_requests_.find(dump_guid);
  438. if (it == in_progress_vm_region_requests_.end())
  439. return;
  440. if (!it->second->pending_responses.empty())
  441. return;
  442. QueuedRequestDispatcher::VmRegions results =
  443. QueuedRequestDispatcher::FinalizeVmRegionRequest(it->second.get());
  444. std::move(it->second->callback).Run(std::move(results));
  445. in_progress_vm_region_requests_.erase(it);
  446. }
  447. void CoordinatorImpl::OnDumpProcessesForTracing(
  448. uint64_t dump_guid,
  449. std::vector<mojom::HeapProfileResultPtr> heap_profile_results) {
  450. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  451. QueuedRequest* request = GetCurrentRequest();
  452. if (!request || request->dump_guid != dump_guid) {
  453. return;
  454. }
  455. request->heap_dump_in_progress = false;
  456. for (auto& result : heap_profile_results) {
  457. base::trace_event::TraceArguments args(
  458. "dumps", std::make_unique<StringWrapper>(std::move(result->json)));
  459. // Using the same id merges all of the heap dumps into a single detailed
  460. // dump node in the UI.
  461. TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_PROCESS_ID(
  462. TRACE_EVENT_PHASE_MEMORY_DUMP,
  463. base::trace_event::TraceLog::GetCategoryGroupEnabled(
  464. base::trace_event::MemoryDumpManager::kTraceCategory),
  465. "periodic_interval", trace_event_internal::kGlobalScope, dump_guid,
  466. result->pid, &args, TRACE_EVENT_FLAG_HAS_ID);
  467. }
  468. FinalizeGlobalMemoryDumpIfAllManagersReplied();
  469. }
  470. void CoordinatorImpl::RemovePendingResponse(
  471. base::ProcessId process_id,
  472. QueuedRequest::PendingResponse::Type type) {
  473. QueuedRequest* request = GetCurrentRequest();
  474. if (request == nullptr) {
  475. NOTREACHED() << "No current dump request.";
  476. return;
  477. }
  478. auto it = request->pending_responses.find({process_id, type});
  479. if (it == request->pending_responses.end()) {
  480. VLOG(1) << "Unexpected memory dump response";
  481. return;
  482. }
  483. request->pending_responses.erase(it);
  484. }
  485. void CoordinatorImpl::FinalizeGlobalMemoryDumpIfAllManagersReplied() {
  486. TRACE_EVENT0(base::trace_event::MemoryDumpManager::kTraceCategory,
  487. "GlobalMemoryDump.Computation");
  488. DCHECK(!queued_memory_dump_requests_.empty());
  489. QueuedRequest* request = &queued_memory_dump_requests_.front();
  490. if (!request->dump_in_progress || request->pending_responses.size() > 0 ||
  491. request->heap_dump_in_progress) {
  492. return;
  493. }
  494. QueuedRequestDispatcher::Finalize(request, tracing_observer_.get(),
  495. use_proto_writer_);
  496. queued_memory_dump_requests_.pop_front();
  497. request = nullptr;
  498. // Schedule the next queued dump (if applicable).
  499. if (!queued_memory_dump_requests_.empty()) {
  500. base::SequencedTaskRunnerHandle::Get()->PostTask(
  501. FROM_HERE,
  502. base::BindOnce(&CoordinatorImpl::PerformNextQueuedGlobalMemoryDump,
  503. weak_ptr_factory_.GetWeakPtr()));
  504. }
  505. }
  506. CoordinatorImpl::ClientInfo::ClientInfo(
  507. mojo::Remote<mojom::ClientProcess> client,
  508. mojom::ProcessType process_type,
  509. absl::optional<std::string> service_name)
  510. : client(std::move(client)),
  511. process_type(process_type),
  512. service_name(std::move(service_name)) {}
  513. CoordinatorImpl::ClientInfo::~ClientInfo() = default;
  514. } // namespace memory_instrumentation