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