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