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