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