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/public/cpp/memory_instrumentation/client_process_impl.h"
6 
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/containers/flat_map.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/strings/string_piece.h"
12 #include "base/synchronization/lock.h"
13 #include "base/trace_event/memory_dump_request_args.h"
14 #include "build/build_config.h"
15 #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
16 #include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
17 #include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.h"
18 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
19 
20 namespace memory_instrumentation {
21 
22 // static
CreateInstance(mojo::PendingReceiver<mojom::ClientProcess> receiver,mojo::PendingRemote<mojom::Coordinator> coordinator,bool is_browser_process)23 void ClientProcessImpl::CreateInstance(
24     mojo::PendingReceiver<mojom::ClientProcess> receiver,
25     mojo::PendingRemote<mojom::Coordinator> coordinator,
26     bool is_browser_process) {
27   static ClientProcessImpl* instance = nullptr;
28   if (!instance) {
29     instance = new ClientProcessImpl(
30         std::move(receiver), std::move(coordinator), is_browser_process,
31         /*initialize_memory_instrumentation=*/true);
32   } else {
33     NOTREACHED();
34   }
35 }
36 
ClientProcessImpl(mojo::PendingReceiver<mojom::ClientProcess> receiver,mojo::PendingRemote<mojom::Coordinator> coordinator,bool is_browser_process,bool initialize_memory_instrumentation)37 ClientProcessImpl::ClientProcessImpl(
38     mojo::PendingReceiver<mojom::ClientProcess> receiver,
39     mojo::PendingRemote<mojom::Coordinator> coordinator,
40     bool is_browser_process,
41     bool initialize_memory_instrumentation)
42     : receiver_(this, std::move(receiver)) {
43   if (initialize_memory_instrumentation) {
44     // Initialize the public-facing MemoryInstrumentation helper.
45     MemoryInstrumentation::CreateInstance(std::move(coordinator));
46   } else {
47     coordinator_.Bind(std::move(coordinator));
48   }
49 
50   task_runner_ = base::ThreadTaskRunnerHandle::Get();
51 
52   // TODO(primiano): this is a temporary workaround to tell the
53   // base::MemoryDumpManager that it is special and should coordinate periodic
54   // dumps for tracing. Remove this once the periodic dump scheduler is moved
55   // from base to the service. MDM should not care about being the coordinator.
56   base::trace_event::MemoryDumpManager::GetInstance()->Initialize(
57       base::BindRepeating(
58           &ClientProcessImpl::RequestGlobalMemoryDump_NoCallback,
59           base::Unretained(this)),
60       is_browser_process);
61 
62   tracing_observer_ = std::make_unique<TracingObserver>(
63       base::trace_event::TraceLog::GetInstance(),
64       base::trace_event::MemoryDumpManager::GetInstance());
65 }
66 
67 ClientProcessImpl::~ClientProcessImpl() = default;
68 
RequestChromeMemoryDump(const base::trace_event::MemoryDumpRequestArgs & args,RequestChromeMemoryDumpCallback callback)69 void ClientProcessImpl::RequestChromeMemoryDump(
70     const base::trace_event::MemoryDumpRequestArgs& args,
71     RequestChromeMemoryDumpCallback callback) {
72   DCHECK(!callback.is_null());
73   most_recent_chrome_memory_dump_guid_ = args.dump_guid;
74   auto it_and_inserted =
75       pending_chrome_callbacks_.emplace(args.dump_guid, std::move(callback));
76   DCHECK(it_and_inserted.second) << "Duplicated request id " << args.dump_guid;
77   base::trace_event::MemoryDumpManager::GetInstance()->CreateProcessDump(
78       args, base::BindOnce(&ClientProcessImpl::OnChromeMemoryDumpDone,
79                            base::Unretained(this)));
80 }
81 
OnChromeMemoryDumpDone(bool success,uint64_t dump_guid,std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump)82 void ClientProcessImpl::OnChromeMemoryDumpDone(
83     bool success,
84     uint64_t dump_guid,
85     std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump) {
86   DCHECK(success || !process_memory_dump);
87 
88   auto callback_it = pending_chrome_callbacks_.find(dump_guid);
89   DCHECK(callback_it != pending_chrome_callbacks_.end());
90 
91   auto callback = std::move(callback_it->second);
92   pending_chrome_callbacks_.erase(callback_it);
93 
94   auto it = delayed_os_memory_dump_callbacks_.find(dump_guid);
95   if (it != delayed_os_memory_dump_callbacks_.end()) {
96     for (auto& args : it->second) {
97       PerformOSMemoryDump(std::move(args));
98     }
99     delayed_os_memory_dump_callbacks_.erase(it);
100   }
101 
102   if (!process_memory_dump) {
103     std::move(callback).Run(false, dump_guid, nullptr);
104     return;
105   }
106   std::move(callback).Run(success, dump_guid, std::move(process_memory_dump));
107 }
108 
RequestGlobalMemoryDump_NoCallback(base::trace_event::MemoryDumpType dump_type,base::trace_event::MemoryDumpLevelOfDetail level_of_detail)109 void ClientProcessImpl::RequestGlobalMemoryDump_NoCallback(
110     base::trace_event::MemoryDumpType dump_type,
111     base::trace_event::MemoryDumpLevelOfDetail level_of_detail) {
112   if (!task_runner_->RunsTasksInCurrentSequence()) {
113     task_runner_->PostTask(
114         FROM_HERE,
115         base::BindOnce(&ClientProcessImpl::RequestGlobalMemoryDump_NoCallback,
116                        base::Unretained(this), dump_type, level_of_detail));
117     return;
118   }
119 
120   // NOTE: If this ClientProcessImpl was responsible for initializing the
121   // global MemoryInstrumentation object, its Coordinator pipe was passed along
122   // to that object, and |coordinator_| is unbound.
123   mojom::Coordinator* coordinator = nullptr;
124   if (coordinator_)
125     coordinator = coordinator_.get();
126   else
127     coordinator = MemoryInstrumentation::GetInstance()->GetCoordinator();
128   coordinator->RequestGlobalMemoryDumpAndAppendToTrace(
129       dump_type, level_of_detail,
130       base::trace_event::MemoryDumpDeterminism::NONE,
131       mojom::Coordinator::RequestGlobalMemoryDumpAndAppendToTraceCallback());
132 }
133 
RequestOSMemoryDump(mojom::MemoryMapOption mmap_option,const std::vector<base::ProcessId> & pids,RequestOSMemoryDumpCallback callback)134 void ClientProcessImpl::RequestOSMemoryDump(
135     mojom::MemoryMapOption mmap_option,
136     const std::vector<base::ProcessId>& pids,
137     RequestOSMemoryDumpCallback callback) {
138   OSMemoryDumpArgs args;
139   args.mmap_option = mmap_option;
140   args.pids = pids;
141   args.callback = std::move(callback);
142 
143 #if defined(OS_MAC)
144   // If the most recent chrome memory dump hasn't finished, wait for that to
145   // finish.
146   if (most_recent_chrome_memory_dump_guid_.has_value()) {
147     uint64_t guid = most_recent_chrome_memory_dump_guid_.value();
148     auto it = pending_chrome_callbacks_.find(guid);
149     if (it != pending_chrome_callbacks_.end()) {
150       delayed_os_memory_dump_callbacks_[guid].push_back(std::move(args));
151       return;
152     }
153   }
154 #endif
155   PerformOSMemoryDump(std::move(args));
156 }
157 
PerformOSMemoryDump(OSMemoryDumpArgs args)158 void ClientProcessImpl::PerformOSMemoryDump(OSMemoryDumpArgs args) {
159   bool global_success = true;
160   base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
161   for (const base::ProcessId& pid : args.pids) {
162     mojom::RawOSMemDumpPtr result = mojom::RawOSMemDump::New();
163     result->platform_private_footprint = mojom::PlatformPrivateFootprint::New();
164     bool success = OSMetrics::FillOSMemoryDump(pid, result.get());
165     if (args.mmap_option != mojom::MemoryMapOption::NONE) {
166       success = success && OSMetrics::FillProcessMemoryMaps(
167                                pid, args.mmap_option, result.get());
168     }
169     if (success)
170       results[pid] = std::move(result);
171     global_success = global_success && success;
172   }
173   std::move(args.callback).Run(global_success, std::move(results));
174 }
175 
176 ClientProcessImpl::OSMemoryDumpArgs::OSMemoryDumpArgs() = default;
177 ClientProcessImpl::OSMemoryDumpArgs::OSMemoryDumpArgs(OSMemoryDumpArgs&&) =
178     default;
179 ClientProcessImpl::OSMemoryDumpArgs::~OSMemoryDumpArgs() = default;
180 
181 }  // namespace memory_instrumentation
182