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