queued_request_dispatcher.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  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. #include "services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.h"
  5. #include <inttypes.h>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/command_line.h"
  9. #include "base/format_macros.h"
  10. #include "base/logging.h"
  11. #include "base/metrics/histogram_macros.h"
  12. #include "base/strings/strcat.h"
  13. #include "base/strings/stringprintf.h"
  14. #include "base/trace_event/trace_event.h"
  15. #include "base/trace_event/traced_value.h"
  16. #include "build/build_config.h"
  17. #include "services/resource_coordinator/memory_instrumentation/aggregate_metrics_processor.h"
  18. #include "services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.h"
  19. #include "services/resource_coordinator/memory_instrumentation/switches.h"
  20. #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
  21. #include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer_proto.h"
  22. #include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer_traced_value.h"
  23. #include "third_party/perfetto/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h"
  24. #include "third_party/perfetto/protos/perfetto/trace/memory_graph.pbzero.h"
  25. #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
  26. #if BUILDFLAG(IS_MAC)
  27. #include "base/mac/mac_util.h"
  28. #endif
  29. using base::trace_event::TracedValue;
  30. using perfetto::trace_processor::GlobalNodeGraph;
  31. using perfetto::trace_processor::LevelOfDetail;
  32. using perfetto::trace_processor::RawMemoryGraphNode;
  33. using Node = perfetto::trace_processor::GlobalNodeGraph::Node;
  34. using perfetto::trace_processor::GraphProcessor;
  35. namespace memory_instrumentation {
  36. namespace {
  37. // Returns the private memory footprint calculated from given |os_dump|.
  38. //
  39. // See design docs linked in the bugs for the rationale of the computation:
  40. // - Linux/Android: https://crbug.com/707019 .
  41. // - Mac OS: https://crbug.com/707021 .
  42. // - Win: https://crbug.com/707022 .
  43. uint32_t CalculatePrivateFootprintKb(const mojom::RawOSMemDump& os_dump,
  44. uint32_t shared_resident_kb) {
  45. DCHECK(os_dump.platform_private_footprint);
  46. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
  47. BUILDFLAG(IS_FUCHSIA)
  48. uint64_t rss_anon_bytes = os_dump.platform_private_footprint->rss_anon_bytes;
  49. uint64_t vm_swap_bytes = os_dump.platform_private_footprint->vm_swap_bytes;
  50. return (rss_anon_bytes + vm_swap_bytes) / 1024;
  51. #elif BUILDFLAG(IS_MAC)
  52. uint64_t phys_footprint_bytes =
  53. os_dump.platform_private_footprint->phys_footprint_bytes;
  54. return base::saturated_cast<uint32_t>(
  55. base::saturated_cast<int32_t>(phys_footprint_bytes / 1024) -
  56. base::saturated_cast<int32_t>(shared_resident_kb));
  57. #elif BUILDFLAG(IS_WIN)
  58. return base::saturated_cast<int32_t>(
  59. os_dump.platform_private_footprint->private_bytes / 1024);
  60. #else
  61. return 0;
  62. #endif
  63. }
  64. memory_instrumentation::mojom::OSMemDumpPtr CreatePublicOSDump(
  65. const mojom::RawOSMemDump& internal_os_dump,
  66. uint32_t shared_resident_kb) {
  67. mojom::OSMemDumpPtr os_dump = mojom::OSMemDump::New();
  68. os_dump->resident_set_kb = internal_os_dump.resident_set_kb;
  69. os_dump->peak_resident_set_kb = internal_os_dump.peak_resident_set_kb;
  70. os_dump->is_peak_rss_resettable = internal_os_dump.is_peak_rss_resettable;
  71. os_dump->private_footprint_kb =
  72. CalculatePrivateFootprintKb(internal_os_dump, shared_resident_kb);
  73. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
  74. os_dump->private_footprint_swap_kb =
  75. internal_os_dump.platform_private_footprint->vm_swap_bytes / 1024;
  76. #endif
  77. return os_dump;
  78. }
  79. void NodeAsValueIntoRecursively(const GlobalNodeGraph::Node& node,
  80. TracedValue* value,
  81. std::vector<base::StringPiece>* path) {
  82. // Don't dump the root node.
  83. if (!path->empty()) {
  84. std::string string_conversion_buffer;
  85. std::string name = base::JoinString(*path, "/");
  86. value->BeginDictionaryWithCopiedName(name);
  87. if (!node.id().empty())
  88. value->SetString("id", node.id().ToString());
  89. value->BeginDictionary("attrs");
  90. for (const auto& name_to_entry : node.const_entries()) {
  91. const auto& entry = name_to_entry.second;
  92. value->BeginDictionaryWithCopiedName(name_to_entry.first);
  93. switch (entry.type) {
  94. case GlobalNodeGraph::Node::Entry::kUInt64:
  95. base::SStringPrintf(&string_conversion_buffer, "%" PRIx64,
  96. entry.value_uint64);
  97. value->SetString("type", RawMemoryGraphNode::kTypeScalar);
  98. value->SetString("value", string_conversion_buffer);
  99. break;
  100. case GlobalNodeGraph::Node::Entry::kString:
  101. value->SetString("type", RawMemoryGraphNode::kTypeString);
  102. value->SetString("value", entry.value_string);
  103. break;
  104. }
  105. switch (entry.units) {
  106. case GlobalNodeGraph::Node::Entry::ScalarUnits::kBytes:
  107. value->SetString("units", RawMemoryGraphNode::kUnitsBytes);
  108. break;
  109. case GlobalNodeGraph::Node::Entry::ScalarUnits::kObjects:
  110. value->SetString("units", RawMemoryGraphNode::kUnitsObjects);
  111. break;
  112. }
  113. value->EndDictionary();
  114. }
  115. value->EndDictionary(); // "attrs": { ... }
  116. if (node.is_weak())
  117. value->SetInteger("flags", RawMemoryGraphNode::Flags::kWeak);
  118. value->EndDictionary(); // "allocator_name/heap_subheap": { ... }
  119. }
  120. for (const auto& name_to_child : node.const_children()) {
  121. path->push_back(name_to_child.first);
  122. NodeAsValueIntoRecursively(*name_to_child.second, value, path);
  123. path->pop_back();
  124. }
  125. }
  126. std::unique_ptr<TracedValue> GetChromeDumpTracedValue(
  127. const GlobalNodeGraph::Process& process) {
  128. std::unique_ptr<TracedValue> traced_value = std::make_unique<TracedValue>();
  129. if (!process.root()->const_children().empty()) {
  130. traced_value->BeginDictionary("allocators");
  131. std::vector<base::StringPiece> path;
  132. NodeAsValueIntoRecursively(*process.root(), traced_value.get(), &path);
  133. traced_value->EndDictionary();
  134. }
  135. return traced_value;
  136. }
  137. std::unique_ptr<TracedValue> GetChromeDumpAndGlobalAndEdgesTracedValue(
  138. const GlobalNodeGraph::Process& process,
  139. const GlobalNodeGraph::Process& global_process,
  140. const std::forward_list<GlobalNodeGraph::Edge>& edges) {
  141. std::unique_ptr<TracedValue> traced_value = std::make_unique<TracedValue>();
  142. bool suppress_graphs = process.root()->const_children().empty() &&
  143. global_process.root()->const_children().empty();
  144. if (!suppress_graphs) {
  145. traced_value->BeginDictionary("allocators");
  146. std::vector<base::StringPiece> path;
  147. NodeAsValueIntoRecursively(*process.root(), traced_value.get(), &path);
  148. NodeAsValueIntoRecursively(*global_process.root(), traced_value.get(),
  149. &path);
  150. traced_value->EndDictionary();
  151. }
  152. traced_value->BeginArray("allocators_graph");
  153. for (const auto& edge : edges) {
  154. traced_value->BeginDictionary();
  155. traced_value->SetString("source", edge.source()->id().ToString());
  156. traced_value->SetString("target", edge.target()->id().ToString());
  157. traced_value->SetInteger("importance", edge.priority());
  158. traced_value->EndDictionary();
  159. }
  160. traced_value->EndArray();
  161. return traced_value;
  162. }
  163. mojom::AllocatorMemDumpPtr CreateAllocatorDumpForNode(const Node* node,
  164. bool recursive) {
  165. base::flat_map<std::string, uint64_t> numeric_entries;
  166. for (const auto& entry : node->const_entries()) {
  167. if (entry.second.type == Node::Entry::Type::kUInt64)
  168. numeric_entries.emplace(entry.first, entry.second.value_uint64);
  169. }
  170. base::flat_map<std::string, mojom::AllocatorMemDumpPtr> children;
  171. if (recursive) {
  172. for (const auto& child : node->const_children()) {
  173. children.emplace(child.first,
  174. CreateAllocatorDumpForNode(child.second, true));
  175. }
  176. }
  177. return mojom::AllocatorMemDump::New(std::move(numeric_entries),
  178. std::move(children));
  179. }
  180. } // namespace
  181. // static
  182. void QueuedRequestDispatcher::SetUpAndDispatch(
  183. QueuedRequest* request,
  184. const std::vector<ClientInfo>& clients,
  185. const ChromeCallback& chrome_callback,
  186. const OsCallback& os_callback) {
  187. using ResponseType = QueuedRequest::PendingResponse::Type;
  188. DCHECK(!request->dump_in_progress);
  189. request->dump_in_progress = true;
  190. request->start_time = base::TimeTicks::Now();
  191. TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(
  192. base::trace_event::MemoryDumpManager::kTraceCategory, "GlobalMemoryDump",
  193. TRACE_ID_LOCAL(request->dump_guid), "dump_type",
  194. base::trace_event::MemoryDumpTypeToString(request->args.dump_type),
  195. "level_of_detail",
  196. base::trace_event::MemoryDumpLevelOfDetailToString(
  197. request->args.level_of_detail));
  198. request->failed_memory_dump_count = 0;
  199. // Note: the service process itself is registered as a ClientProcess and
  200. // will be treated like any other process for the sake of memory dumps.
  201. request->pending_responses.clear();
  202. for (const auto& client_info : clients) {
  203. mojom::ClientProcess* client = client_info.client;
  204. // If we're only looking for a single pid process, then ignore clients
  205. // with different pid.
  206. if (request->args.pid != base::kNullProcessId &&
  207. request->args.pid != client_info.pid) {
  208. continue;
  209. }
  210. request->responses[client_info.pid].process_id = client_info.pid;
  211. request->responses[client_info.pid].process_type = client_info.process_type;
  212. request->responses[client_info.pid].service_name = client_info.service_name;
  213. // Don't request a chrome memory dump at all if the client only wants the
  214. // a memory footprint.
  215. //
  216. // This must occur before the call to RequestOSMemoryDump, as
  217. // ClientProcessImpl will [for macOS], delay the calculations for the
  218. // OSMemoryDump until the Chrome memory dump is finished. See
  219. // https://bugs.chromium.org/p/chromium/issues/detail?id=812346#c16 for more
  220. // details.
  221. if (!request->args.memory_footprint_only) {
  222. request->pending_responses.insert(
  223. {client_info.pid, ResponseType::kChromeDump});
  224. client->RequestChromeMemoryDump(
  225. request->GetRequestArgs(),
  226. base::BindOnce(chrome_callback, client_info.pid));
  227. }
  228. // On most platforms each process can dump data about their own process
  229. // so ask each process to do so Linux is special see below.
  230. #if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
  231. request->pending_responses.insert({client_info.pid, ResponseType::kOSDump});
  232. client->RequestOSMemoryDump(request->memory_map_option(),
  233. {base::kNullProcessId},
  234. base::BindOnce(os_callback, client_info.pid));
  235. #endif // !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
  236. // If we are in the single pid case, then we've already found the only
  237. // process we're looking for.
  238. if (request->args.pid != base::kNullProcessId)
  239. break;
  240. }
  241. // In some cases, OS stats can only be dumped from a privileged process to
  242. // get around to sandboxing/selinux restrictions (see crbug.com/461788).
  243. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  244. std::vector<base::ProcessId> pids;
  245. mojom::ClientProcess* browser_client = nullptr;
  246. base::ProcessId browser_client_pid = base::kNullProcessId;
  247. pids.reserve(request->args.pid == base::kNullProcessId ? clients.size() : 1);
  248. for (const auto& client_info : clients) {
  249. if (request->args.pid == base::kNullProcessId ||
  250. client_info.pid == request->args.pid) {
  251. pids.push_back(client_info.pid);
  252. }
  253. if (client_info.process_type == mojom::ProcessType::BROWSER) {
  254. browser_client = client_info.client;
  255. browser_client_pid = client_info.pid;
  256. }
  257. }
  258. if (clients.size() > 0) {
  259. DCHECK(browser_client);
  260. }
  261. if (browser_client && pids.size() > 0) {
  262. request->pending_responses.insert(
  263. {browser_client_pid, ResponseType::kOSDump});
  264. auto callback = base::BindOnce(os_callback, browser_client_pid);
  265. browser_client->RequestOSMemoryDump(request->memory_map_option(), pids,
  266. std::move(callback));
  267. }
  268. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  269. // In this case, we have not found the pid we are looking for so increment
  270. // the failed dump count and exit.
  271. if (request->args.pid != base::kNullProcessId &&
  272. request->pending_responses.empty()) {
  273. DLOG(ERROR) << "Memory dump request failed due to missing pid "
  274. << request->args.pid;
  275. request->failed_memory_dump_count++;
  276. return;
  277. }
  278. }
  279. // static
  280. void QueuedRequestDispatcher::SetUpAndDispatchVmRegionRequest(
  281. QueuedVmRegionRequest* request,
  282. const std::vector<ClientInfo>& clients,
  283. const std::vector<base::ProcessId>& desired_pids,
  284. const OsCallback& os_callback) {
  285. // On Linux, OS stats can only be dumped from a privileged process to
  286. // get around to sandboxing/selinux restrictions (see crbug.com/461788).
  287. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  288. mojom::ClientProcess* browser_client = nullptr;
  289. base::ProcessId browser_client_pid = 0;
  290. for (const auto& client_info : clients) {
  291. if (client_info.process_type == mojom::ProcessType::BROWSER) {
  292. browser_client = client_info.client;
  293. browser_client_pid = client_info.pid;
  294. break;
  295. }
  296. }
  297. if (!browser_client) {
  298. DLOG(ERROR) << "Missing browser client.";
  299. return;
  300. }
  301. request->pending_responses.insert(browser_client_pid);
  302. request->responses[browser_client_pid].process_id = browser_client_pid;
  303. auto callback = base::BindOnce(os_callback, browser_client_pid);
  304. browser_client->RequestOSMemoryDump(mojom::MemoryMapOption::MODULES,
  305. desired_pids, std::move(callback));
  306. #else
  307. for (const auto& client_info : clients) {
  308. if (std::find(desired_pids.begin(), desired_pids.end(), client_info.pid) !=
  309. desired_pids.end()) {
  310. mojom::ClientProcess* client = client_info.client;
  311. request->pending_responses.insert(client_info.pid);
  312. request->responses[client_info.pid].process_id = client_info.pid;
  313. request->responses[client_info.pid].service_name =
  314. client_info.service_name;
  315. client->RequestOSMemoryDump(mojom::MemoryMapOption::MODULES,
  316. {base::kNullProcessId},
  317. base::BindOnce(os_callback, client_info.pid));
  318. }
  319. }
  320. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  321. }
  322. // static
  323. QueuedRequestDispatcher::VmRegions
  324. QueuedRequestDispatcher::FinalizeVmRegionRequest(
  325. QueuedVmRegionRequest* request) {
  326. VmRegions results;
  327. for (auto& response : request->responses) {
  328. const base::ProcessId& original_pid = response.second.process_id;
  329. // |response| accumulates the replies received by each client process.
  330. // On Linux, the browser process will provide all OS dumps. On non-Linux,
  331. // each client process provides 1 OS dump, % the case where the client is
  332. // disconnected mid dump.
  333. OSMemDumpMap& extra_os_dumps = response.second.os_dumps;
  334. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  335. for (auto& kv : extra_os_dumps) {
  336. auto pid = kv.first == base::kNullProcessId ? original_pid : kv.first;
  337. DCHECK(results.find(pid) == results.end());
  338. results.emplace(pid, std::move(kv.second->memory_maps));
  339. }
  340. #else
  341. // This can be empty if the client disconnects before providing both
  342. // dumps. See UnregisterClientProcess().
  343. DCHECK_LE(extra_os_dumps.size(), 1u);
  344. for (auto& kv : extra_os_dumps) {
  345. // When the OS dump comes from child processes, the pid is supposed to be
  346. // not used. We know the child process pid at the time of the request and
  347. // also wouldn't trust pids coming from child processes.
  348. DCHECK_EQ(base::kNullProcessId, kv.first);
  349. // Check we don't receive duplicate OS dumps for the same process.
  350. DCHECK(results.find(original_pid) == results.end());
  351. results.emplace(original_pid, std::move(kv.second->memory_maps));
  352. }
  353. #endif
  354. }
  355. return results;
  356. }
  357. void QueuedRequestDispatcher::Finalize(QueuedRequest* request,
  358. TracingObserver* tracing_observer,
  359. bool use_proto_writer) {
  360. DCHECK(request->dump_in_progress);
  361. DCHECK(request->pending_responses.empty());
  362. // Reconstruct a map of pid -> ProcessMemoryDump by reassembling the responses
  363. // received by the clients for this dump. In some cases the response coming
  364. // from one client can also provide the dump of OS counters for other
  365. // processes. A concrete case is Linux, where the browser process provides
  366. // details for the child processes to get around sandbox restrictions on
  367. // opening /proc pseudo files.
  368. // All the pointers in the maps will continue to be owned by |request|
  369. // which outlives these containers.
  370. std::map<base::ProcessId, mojom::ProcessType> pid_to_process_type;
  371. std::map<base::ProcessId, const base::trace_event::ProcessMemoryDump*>
  372. pid_to_pmd;
  373. std::map<base::ProcessId, mojom::RawOSMemDump*> pid_to_os_dump;
  374. for (auto& response : request->responses) {
  375. const base::ProcessId& original_pid = response.second.process_id;
  376. pid_to_process_type[original_pid] = response.second.process_type;
  377. // |chrome_dump| can be nullptr if this was a OS-counters only response.
  378. pid_to_pmd[original_pid] = response.second.chrome_dump.get();
  379. // |response| accumulates the replies received by each client process.
  380. // Depending on the OS each client process might return 1 chrome + 1 OS
  381. // dump each or, in the case of Linux, only 1 chrome dump each % the
  382. // browser process which will provide all the OS dumps.
  383. // In the former case (!OS_LINUX) we expect client processes to have
  384. // exactly one OS dump in their |response|, % the case when they
  385. // unexpectedly disconnect in the middle of a dump (e.g. because they
  386. // crash). In the latter case (OS_LINUX) we expect the full map to come
  387. // from the browser process response.
  388. OSMemDumpMap& extra_os_dumps = response.second.os_dumps;
  389. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  390. for (const auto& kv : extra_os_dumps) {
  391. auto pid = kv.first == base::kNullProcessId ? original_pid : kv.first;
  392. DCHECK_EQ(pid_to_os_dump[pid], nullptr);
  393. pid_to_os_dump[pid] = kv.second.get();
  394. }
  395. #else
  396. // This can be empty if the client disconnects before providing both
  397. // dumps. See UnregisterClientProcess().
  398. DCHECK_LE(extra_os_dumps.size(), 1u);
  399. for (const auto& kv : extra_os_dumps) {
  400. // When the OS dump comes from child processes, the pid is supposed to be
  401. // not used. We know the child process pid at the time of the request and
  402. // also wouldn't trust pids coming from child processes.
  403. DCHECK_EQ(base::kNullProcessId, kv.first);
  404. // Check we don't receive duplicate OS dumps for the same process.
  405. DCHECK_EQ(pid_to_os_dump[original_pid], nullptr);
  406. pid_to_os_dump[original_pid] = kv.second.get();
  407. }
  408. #endif
  409. }
  410. MemoryDumpMapConverter converter;
  411. perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap perfettoNodeMap =
  412. converter.Convert(pid_to_pmd);
  413. // Generate the global memory graph from the map of pids to dumps, removing
  414. // weak nodes.
  415. // TODO (crbug.com/1112671): We should avoid graph processing once we moved
  416. // the shared footprint computation to perfetto.
  417. std::unique_ptr<GlobalNodeGraph> global_graph =
  418. GraphProcessor::CreateMemoryGraph(perfettoNodeMap);
  419. GraphProcessor::RemoveWeakNodesFromGraph(global_graph.get());
  420. // Compute the shared memory footprint for each process from the graph.
  421. auto original =
  422. GraphProcessor::ComputeSharedFootprintFromGraph(*global_graph);
  423. std::map<base::ProcessId, uint64_t> shared_footprints;
  424. for (const auto& item : original) {
  425. shared_footprints.emplace(static_cast<base::ProcessId>(item.first),
  426. item.second);
  427. }
  428. // Perform the rest of the computation on the graph.
  429. GraphProcessor::AddOverheadsAndPropagateEntries(global_graph.get());
  430. GraphProcessor::CalculateSizesForGraph(global_graph.get());
  431. // The same timestamp needs to be set for all dumps in the memory snapshot.
  432. base::TimeTicks timestamp = TRACE_TIME_TICKS_NOW();
  433. // Build up the global dump by iterating on the |valid| process dumps.
  434. mojom::GlobalMemoryDumpPtr global_dump(mojom::GlobalMemoryDump::New());
  435. global_dump->start_time = request->start_time;
  436. global_dump->process_dumps.reserve(request->responses.size());
  437. for (const auto& response : request->responses) {
  438. base::ProcessId pid = response.second.process_id;
  439. // On Linux, we may also have the browser process as a response.
  440. // Just ignore it if we are looking for the single pid case.
  441. if (request->args.pid != base::kNullProcessId && pid != request->args.pid)
  442. continue;
  443. // These pointers are owned by |request|.
  444. mojom::RawOSMemDump* raw_os_dump = pid_to_os_dump[pid];
  445. auto* raw_chrome_dump = pid_to_pmd[pid];
  446. // If we have the OS dump we should have the platform private footprint.
  447. DCHECK(!raw_os_dump || raw_os_dump->platform_private_footprint);
  448. // If the raw dump exists, create a summarised version of it.
  449. mojom::OSMemDumpPtr os_dump = nullptr;
  450. if (raw_os_dump) {
  451. uint64_t shared_resident_kb = 0;
  452. #if BUILDFLAG(IS_MAC)
  453. // The resident, anonymous shared memory for each process is only
  454. // relevant on macOS.
  455. const auto process_graph_it =
  456. global_graph->process_node_graphs().find(pid);
  457. if (process_graph_it != global_graph->process_node_graphs().end()) {
  458. const auto& process_graph = process_graph_it->second;
  459. auto* node = process_graph->FindNode("shared_memory");
  460. if (node) {
  461. const auto& entry =
  462. node->entries()->find(RawMemoryGraphNode::kNameSize);
  463. if (entry != node->entries()->end())
  464. shared_resident_kb = entry->second.value_uint64 / 1024;
  465. }
  466. }
  467. #endif
  468. os_dump = CreatePublicOSDump(
  469. *raw_os_dump, base::saturated_cast<uint32_t>(shared_resident_kb));
  470. os_dump->shared_footprint_kb =
  471. base::saturated_cast<uint32_t>(shared_footprints[pid] / 1024);
  472. }
  473. // Trace the OS and Chrome dumps if they exist.
  474. if (request->args.add_to_trace) {
  475. if (raw_os_dump) {
  476. bool trace_os_success = tracing_observer->AddOsDumpToTraceIfEnabled(
  477. request->GetRequestArgs(), pid, *os_dump, raw_os_dump->memory_maps,
  478. timestamp);
  479. if (!trace_os_success) {
  480. DLOG(ERROR) << "Tracing is disabled or not setup yet while receiving "
  481. "OS dump for pid "
  482. << pid;
  483. request->failed_memory_dump_count++;
  484. }
  485. }
  486. if (raw_chrome_dump) {
  487. bool trace_chrome_success = AddChromeMemoryDumpToTrace(
  488. request->GetRequestArgs(), pid, *raw_chrome_dump, *global_graph,
  489. pid_to_process_type, tracing_observer, use_proto_writer, timestamp);
  490. if (!trace_chrome_success) {
  491. DLOG(ERROR) << "Tracing is disabled or not setup yet while receiving "
  492. "Chrome dump for pid "
  493. << pid;
  494. request->failed_memory_dump_count++;
  495. }
  496. }
  497. }
  498. bool valid = false;
  499. if (request->args.memory_footprint_only) {
  500. valid = raw_os_dump;
  501. } else {
  502. // Ignore incomplete results (can happen if the client
  503. // crashes/disconnects).
  504. valid = raw_os_dump && raw_chrome_dump &&
  505. (request->memory_map_option() == mojom::MemoryMapOption::NONE ||
  506. (raw_os_dump && !raw_os_dump->memory_maps.empty()));
  507. }
  508. if (!valid)
  509. continue;
  510. mojom::ProcessMemoryDumpPtr pmd = mojom::ProcessMemoryDump::New();
  511. pmd->pid = pid;
  512. pmd->process_type = pid_to_process_type[pid];
  513. pmd->os_dump = std::move(os_dump);
  514. pmd->service_name = response.second.service_name;
  515. // If we have to return a summary, add all entries for the requested
  516. // allocator dumps.
  517. if (request->should_return_summaries() &&
  518. !request->args.memory_footprint_only) {
  519. const auto& process_graph =
  520. global_graph->process_node_graphs().find(pid)->second;
  521. for (const std::string& name : request->args.allocator_dump_names) {
  522. bool is_recursive = base::EndsWith(name, "/*");
  523. std::string node_name =
  524. (is_recursive ? name.substr(0, name.length() - 2) : name);
  525. Node* node = process_graph->FindNode(node_name);
  526. // Silently ignore any missing node in the process graph.
  527. if (!node)
  528. continue;
  529. pmd->chrome_allocator_dumps.emplace(
  530. node_name, CreateAllocatorDumpForNode(node, is_recursive));
  531. }
  532. }
  533. global_dump->process_dumps.push_back(std::move(pmd));
  534. }
  535. global_dump->aggregated_metrics =
  536. ComputeGlobalNativeCodeResidentMemoryKb(pid_to_os_dump);
  537. const bool global_success = request->failed_memory_dump_count == 0;
  538. // In the single process-case, we want to ensure that global_success
  539. // is true if and only if global_dump is not nullptr.
  540. if (request->args.pid != base::kNullProcessId && !global_success) {
  541. global_dump = nullptr;
  542. }
  543. auto& callback = request->callback;
  544. std::move(callback).Run(global_success, request->dump_guid,
  545. std::move(global_dump));
  546. UMA_HISTOGRAM_MEDIUM_TIMES("Memory.Experimental.Debug.GlobalDumpDuration",
  547. base::TimeTicks::Now() - request->start_time);
  548. UMA_HISTOGRAM_COUNTS_1000(
  549. "Memory.Experimental.Debug.FailedProcessDumpsPerGlobalDump",
  550. request->failed_memory_dump_count);
  551. char guid_str[20];
  552. snprintf(guid_str, sizeof(guid_str), "0x%" PRIx64, request->dump_guid);
  553. TRACE_EVENT_NESTABLE_ASYNC_END2(
  554. base::trace_event::MemoryDumpManager::kTraceCategory, "GlobalMemoryDump",
  555. TRACE_ID_LOCAL(request->dump_guid), "dump_guid", TRACE_STR_COPY(guid_str),
  556. "success", global_success);
  557. }
  558. bool QueuedRequestDispatcher::AddChromeMemoryDumpToTrace(
  559. const base::trace_event::MemoryDumpRequestArgs& args,
  560. base::ProcessId pid,
  561. const base::trace_event::ProcessMemoryDump& raw_chrome_dump,
  562. const GlobalNodeGraph& global_graph,
  563. const std::map<base::ProcessId, mojom::ProcessType>& pid_to_process_type,
  564. TracingObserver* tracing_observer,
  565. bool use_proto_writer,
  566. const base::TimeTicks& timestamp) {
  567. bool is_chrome_tracing_enabled =
  568. base::CommandLine::ForCurrentProcess()->HasSwitch(
  569. switches::kDisableChromeTracingComputation);
  570. if (!is_chrome_tracing_enabled) {
  571. return tracing_observer->AddChromeDumpToTraceIfEnabled(
  572. args, pid, &raw_chrome_dump, timestamp);
  573. }
  574. if (!tracing_observer->ShouldAddToTrace(args))
  575. return false;
  576. if (use_proto_writer) {
  577. return tracing_observer->AddChromeDumpToTraceIfEnabled(
  578. args, pid, &raw_chrome_dump, timestamp);
  579. }
  580. const GlobalNodeGraph::Process& process =
  581. *global_graph.process_node_graphs().find(pid)->second;
  582. std::unique_ptr<TracedValue> traced_value;
  583. if (pid_to_process_type.find(pid)->second == mojom::ProcessType::BROWSER) {
  584. traced_value = GetChromeDumpAndGlobalAndEdgesTracedValue(
  585. process, *global_graph.shared_memory_graph(), global_graph.edges());
  586. } else {
  587. traced_value = GetChromeDumpTracedValue(process);
  588. }
  589. TracingObserverTracedValue::AddToTrace(args, pid, std::move(traced_value));
  590. return true;
  591. }
  592. QueuedRequestDispatcher::ClientInfo::ClientInfo(
  593. mojom::ClientProcess* client,
  594. base::ProcessId pid,
  595. mojom::ProcessType process_type,
  596. absl::optional<std::string> service_name)
  597. : client(client),
  598. pid(pid),
  599. process_type(process_type),
  600. service_name(std::move(service_name)) {}
  601. QueuedRequestDispatcher::ClientInfo::ClientInfo(ClientInfo&& other) = default;
  602. QueuedRequestDispatcher::ClientInfo::~ClientInfo() = default;
  603. } // namespace memory_instrumentation