123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.h"
- #include <inttypes.h>
- #include <utility>
- #include "base/bind.h"
- #include "base/command_line.h"
- #include "base/format_macros.h"
- #include "base/logging.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/strings/strcat.h"
- #include "base/strings/stringprintf.h"
- #include "base/trace_event/trace_event.h"
- #include "base/trace_event/traced_value.h"
- #include "build/build_config.h"
- #include "services/resource_coordinator/memory_instrumentation/aggregate_metrics_processor.h"
- #include "services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.h"
- #include "services/resource_coordinator/memory_instrumentation/switches.h"
- #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
- #include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer_proto.h"
- #include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer_traced_value.h"
- #include "third_party/perfetto/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h"
- #include "third_party/perfetto/protos/perfetto/trace/memory_graph.pbzero.h"
- #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
- #if BUILDFLAG(IS_MAC)
- #include "base/mac/mac_util.h"
- #endif
- using base::trace_event::TracedValue;
- using perfetto::trace_processor::GlobalNodeGraph;
- using perfetto::trace_processor::LevelOfDetail;
- using perfetto::trace_processor::RawMemoryGraphNode;
- using Node = perfetto::trace_processor::GlobalNodeGraph::Node;
- using perfetto::trace_processor::GraphProcessor;
- namespace memory_instrumentation {
- namespace {
- // Returns the private memory footprint calculated from given |os_dump|.
- //
- // See design docs linked in the bugs for the rationale of the computation:
- // - Linux/Android: https://crbug.com/707019 .
- // - Mac OS: https://crbug.com/707021 .
- // - Win: https://crbug.com/707022 .
- uint32_t CalculatePrivateFootprintKb(const mojom::RawOSMemDump& os_dump,
- uint32_t shared_resident_kb) {
- DCHECK(os_dump.platform_private_footprint);
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
- BUILDFLAG(IS_FUCHSIA)
- uint64_t rss_anon_bytes = os_dump.platform_private_footprint->rss_anon_bytes;
- uint64_t vm_swap_bytes = os_dump.platform_private_footprint->vm_swap_bytes;
- return (rss_anon_bytes + vm_swap_bytes) / 1024;
- #elif BUILDFLAG(IS_MAC)
- uint64_t phys_footprint_bytes =
- os_dump.platform_private_footprint->phys_footprint_bytes;
- return base::saturated_cast<uint32_t>(
- base::saturated_cast<int32_t>(phys_footprint_bytes / 1024) -
- base::saturated_cast<int32_t>(shared_resident_kb));
- #elif BUILDFLAG(IS_WIN)
- return base::saturated_cast<int32_t>(
- os_dump.platform_private_footprint->private_bytes / 1024);
- #else
- return 0;
- #endif
- }
- memory_instrumentation::mojom::OSMemDumpPtr CreatePublicOSDump(
- const mojom::RawOSMemDump& internal_os_dump,
- uint32_t shared_resident_kb) {
- mojom::OSMemDumpPtr os_dump = mojom::OSMemDump::New();
- os_dump->resident_set_kb = internal_os_dump.resident_set_kb;
- os_dump->peak_resident_set_kb = internal_os_dump.peak_resident_set_kb;
- os_dump->is_peak_rss_resettable = internal_os_dump.is_peak_rss_resettable;
- os_dump->private_footprint_kb =
- CalculatePrivateFootprintKb(internal_os_dump, shared_resident_kb);
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
- os_dump->private_footprint_swap_kb =
- internal_os_dump.platform_private_footprint->vm_swap_bytes / 1024;
- #endif
- return os_dump;
- }
- void NodeAsValueIntoRecursively(const GlobalNodeGraph::Node& node,
- TracedValue* value,
- std::vector<base::StringPiece>* path) {
- // Don't dump the root node.
- if (!path->empty()) {
- std::string string_conversion_buffer;
- std::string name = base::JoinString(*path, "/");
- value->BeginDictionaryWithCopiedName(name);
- if (!node.id().empty())
- value->SetString("id", node.id().ToString());
- value->BeginDictionary("attrs");
- for (const auto& name_to_entry : node.const_entries()) {
- const auto& entry = name_to_entry.second;
- value->BeginDictionaryWithCopiedName(name_to_entry.first);
- switch (entry.type) {
- case GlobalNodeGraph::Node::Entry::kUInt64:
- base::SStringPrintf(&string_conversion_buffer, "%" PRIx64,
- entry.value_uint64);
- value->SetString("type", RawMemoryGraphNode::kTypeScalar);
- value->SetString("value", string_conversion_buffer);
- break;
- case GlobalNodeGraph::Node::Entry::kString:
- value->SetString("type", RawMemoryGraphNode::kTypeString);
- value->SetString("value", entry.value_string);
- break;
- }
- switch (entry.units) {
- case GlobalNodeGraph::Node::Entry::ScalarUnits::kBytes:
- value->SetString("units", RawMemoryGraphNode::kUnitsBytes);
- break;
- case GlobalNodeGraph::Node::Entry::ScalarUnits::kObjects:
- value->SetString("units", RawMemoryGraphNode::kUnitsObjects);
- break;
- }
- value->EndDictionary();
- }
- value->EndDictionary(); // "attrs": { ... }
- if (node.is_weak())
- value->SetInteger("flags", RawMemoryGraphNode::Flags::kWeak);
- value->EndDictionary(); // "allocator_name/heap_subheap": { ... }
- }
- for (const auto& name_to_child : node.const_children()) {
- path->push_back(name_to_child.first);
- NodeAsValueIntoRecursively(*name_to_child.second, value, path);
- path->pop_back();
- }
- }
- std::unique_ptr<TracedValue> GetChromeDumpTracedValue(
- const GlobalNodeGraph::Process& process) {
- std::unique_ptr<TracedValue> traced_value = std::make_unique<TracedValue>();
- if (!process.root()->const_children().empty()) {
- traced_value->BeginDictionary("allocators");
- std::vector<base::StringPiece> path;
- NodeAsValueIntoRecursively(*process.root(), traced_value.get(), &path);
- traced_value->EndDictionary();
- }
- return traced_value;
- }
- std::unique_ptr<TracedValue> GetChromeDumpAndGlobalAndEdgesTracedValue(
- const GlobalNodeGraph::Process& process,
- const GlobalNodeGraph::Process& global_process,
- const std::forward_list<GlobalNodeGraph::Edge>& edges) {
- std::unique_ptr<TracedValue> traced_value = std::make_unique<TracedValue>();
- bool suppress_graphs = process.root()->const_children().empty() &&
- global_process.root()->const_children().empty();
- if (!suppress_graphs) {
- traced_value->BeginDictionary("allocators");
- std::vector<base::StringPiece> path;
- NodeAsValueIntoRecursively(*process.root(), traced_value.get(), &path);
- NodeAsValueIntoRecursively(*global_process.root(), traced_value.get(),
- &path);
- traced_value->EndDictionary();
- }
- traced_value->BeginArray("allocators_graph");
- for (const auto& edge : edges) {
- traced_value->BeginDictionary();
- traced_value->SetString("source", edge.source()->id().ToString());
- traced_value->SetString("target", edge.target()->id().ToString());
- traced_value->SetInteger("importance", edge.priority());
- traced_value->EndDictionary();
- }
- traced_value->EndArray();
- return traced_value;
- }
- mojom::AllocatorMemDumpPtr CreateAllocatorDumpForNode(const Node* node,
- bool recursive) {
- base::flat_map<std::string, uint64_t> numeric_entries;
- for (const auto& entry : node->const_entries()) {
- if (entry.second.type == Node::Entry::Type::kUInt64)
- numeric_entries.emplace(entry.first, entry.second.value_uint64);
- }
- base::flat_map<std::string, mojom::AllocatorMemDumpPtr> children;
- if (recursive) {
- for (const auto& child : node->const_children()) {
- children.emplace(child.first,
- CreateAllocatorDumpForNode(child.second, true));
- }
- }
- return mojom::AllocatorMemDump::New(std::move(numeric_entries),
- std::move(children));
- }
- } // namespace
- // static
- void QueuedRequestDispatcher::SetUpAndDispatch(
- QueuedRequest* request,
- const std::vector<ClientInfo>& clients,
- const ChromeCallback& chrome_callback,
- const OsCallback& os_callback) {
- using ResponseType = QueuedRequest::PendingResponse::Type;
- DCHECK(!request->dump_in_progress);
- request->dump_in_progress = true;
- request->start_time = base::TimeTicks::Now();
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(
- base::trace_event::MemoryDumpManager::kTraceCategory, "GlobalMemoryDump",
- TRACE_ID_LOCAL(request->dump_guid), "dump_type",
- base::trace_event::MemoryDumpTypeToString(request->args.dump_type),
- "level_of_detail",
- base::trace_event::MemoryDumpLevelOfDetailToString(
- request->args.level_of_detail));
- request->failed_memory_dump_count = 0;
- // Note: the service process itself is registered as a ClientProcess and
- // will be treated like any other process for the sake of memory dumps.
- request->pending_responses.clear();
- for (const auto& client_info : clients) {
- mojom::ClientProcess* client = client_info.client;
- // If we're only looking for a single pid process, then ignore clients
- // with different pid.
- if (request->args.pid != base::kNullProcessId &&
- request->args.pid != client_info.pid) {
- continue;
- }
- request->responses[client_info.pid].process_id = client_info.pid;
- request->responses[client_info.pid].process_type = client_info.process_type;
- request->responses[client_info.pid].service_name = client_info.service_name;
- // Don't request a chrome memory dump at all if the client only wants the
- // a memory footprint.
- //
- // This must occur before the call to RequestOSMemoryDump, as
- // ClientProcessImpl will [for macOS], delay the calculations for the
- // OSMemoryDump until the Chrome memory dump is finished. See
- // https://bugs.chromium.org/p/chromium/issues/detail?id=812346#c16 for more
- // details.
- if (!request->args.memory_footprint_only) {
- request->pending_responses.insert(
- {client_info.pid, ResponseType::kChromeDump});
- client->RequestChromeMemoryDump(
- request->GetRequestArgs(),
- base::BindOnce(chrome_callback, client_info.pid));
- }
- // On most platforms each process can dump data about their own process
- // so ask each process to do so Linux is special see below.
- #if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
- request->pending_responses.insert({client_info.pid, ResponseType::kOSDump});
- client->RequestOSMemoryDump(request->memory_map_option(),
- {base::kNullProcessId},
- base::BindOnce(os_callback, client_info.pid));
- #endif // !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS)
- // If we are in the single pid case, then we've already found the only
- // process we're looking for.
- if (request->args.pid != base::kNullProcessId)
- break;
- }
- // In some cases, OS stats can only be dumped from a privileged process to
- // get around to sandboxing/selinux restrictions (see crbug.com/461788).
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- std::vector<base::ProcessId> pids;
- mojom::ClientProcess* browser_client = nullptr;
- base::ProcessId browser_client_pid = base::kNullProcessId;
- pids.reserve(request->args.pid == base::kNullProcessId ? clients.size() : 1);
- for (const auto& client_info : clients) {
- if (request->args.pid == base::kNullProcessId ||
- client_info.pid == request->args.pid) {
- pids.push_back(client_info.pid);
- }
- if (client_info.process_type == mojom::ProcessType::BROWSER) {
- browser_client = client_info.client;
- browser_client_pid = client_info.pid;
- }
- }
- if (clients.size() > 0) {
- DCHECK(browser_client);
- }
- if (browser_client && pids.size() > 0) {
- request->pending_responses.insert(
- {browser_client_pid, ResponseType::kOSDump});
- auto callback = base::BindOnce(os_callback, browser_client_pid);
- browser_client->RequestOSMemoryDump(request->memory_map_option(), pids,
- std::move(callback));
- }
- #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- // In this case, we have not found the pid we are looking for so increment
- // the failed dump count and exit.
- if (request->args.pid != base::kNullProcessId &&
- request->pending_responses.empty()) {
- DLOG(ERROR) << "Memory dump request failed due to missing pid "
- << request->args.pid;
- request->failed_memory_dump_count++;
- return;
- }
- }
- // static
- void QueuedRequestDispatcher::SetUpAndDispatchVmRegionRequest(
- QueuedVmRegionRequest* request,
- const std::vector<ClientInfo>& clients,
- const std::vector<base::ProcessId>& desired_pids,
- const OsCallback& os_callback) {
- // On Linux, OS stats can only be dumped from a privileged process to
- // get around to sandboxing/selinux restrictions (see crbug.com/461788).
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- mojom::ClientProcess* browser_client = nullptr;
- base::ProcessId browser_client_pid = 0;
- for (const auto& client_info : clients) {
- if (client_info.process_type == mojom::ProcessType::BROWSER) {
- browser_client = client_info.client;
- browser_client_pid = client_info.pid;
- break;
- }
- }
- if (!browser_client) {
- DLOG(ERROR) << "Missing browser client.";
- return;
- }
- request->pending_responses.insert(browser_client_pid);
- request->responses[browser_client_pid].process_id = browser_client_pid;
- auto callback = base::BindOnce(os_callback, browser_client_pid);
- browser_client->RequestOSMemoryDump(mojom::MemoryMapOption::MODULES,
- desired_pids, std::move(callback));
- #else
- for (const auto& client_info : clients) {
- if (std::find(desired_pids.begin(), desired_pids.end(), client_info.pid) !=
- desired_pids.end()) {
- mojom::ClientProcess* client = client_info.client;
- request->pending_responses.insert(client_info.pid);
- request->responses[client_info.pid].process_id = client_info.pid;
- request->responses[client_info.pid].service_name =
- client_info.service_name;
- client->RequestOSMemoryDump(mojom::MemoryMapOption::MODULES,
- {base::kNullProcessId},
- base::BindOnce(os_callback, client_info.pid));
- }
- }
- #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- }
- // static
- QueuedRequestDispatcher::VmRegions
- QueuedRequestDispatcher::FinalizeVmRegionRequest(
- QueuedVmRegionRequest* request) {
- VmRegions results;
- for (auto& response : request->responses) {
- const base::ProcessId& original_pid = response.second.process_id;
- // |response| accumulates the replies received by each client process.
- // On Linux, the browser process will provide all OS dumps. On non-Linux,
- // each client process provides 1 OS dump, % the case where the client is
- // disconnected mid dump.
- OSMemDumpMap& extra_os_dumps = response.second.os_dumps;
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- for (auto& kv : extra_os_dumps) {
- auto pid = kv.first == base::kNullProcessId ? original_pid : kv.first;
- DCHECK(results.find(pid) == results.end());
- results.emplace(pid, std::move(kv.second->memory_maps));
- }
- #else
- // This can be empty if the client disconnects before providing both
- // dumps. See UnregisterClientProcess().
- DCHECK_LE(extra_os_dumps.size(), 1u);
- for (auto& kv : extra_os_dumps) {
- // When the OS dump comes from child processes, the pid is supposed to be
- // not used. We know the child process pid at the time of the request and
- // also wouldn't trust pids coming from child processes.
- DCHECK_EQ(base::kNullProcessId, kv.first);
- // Check we don't receive duplicate OS dumps for the same process.
- DCHECK(results.find(original_pid) == results.end());
- results.emplace(original_pid, std::move(kv.second->memory_maps));
- }
- #endif
- }
- return results;
- }
- void QueuedRequestDispatcher::Finalize(QueuedRequest* request,
- TracingObserver* tracing_observer,
- bool use_proto_writer) {
- DCHECK(request->dump_in_progress);
- DCHECK(request->pending_responses.empty());
- // Reconstruct a map of pid -> ProcessMemoryDump by reassembling the responses
- // received by the clients for this dump. In some cases the response coming
- // from one client can also provide the dump of OS counters for other
- // processes. A concrete case is Linux, where the browser process provides
- // details for the child processes to get around sandbox restrictions on
- // opening /proc pseudo files.
- // All the pointers in the maps will continue to be owned by |request|
- // which outlives these containers.
- std::map<base::ProcessId, mojom::ProcessType> pid_to_process_type;
- std::map<base::ProcessId, const base::trace_event::ProcessMemoryDump*>
- pid_to_pmd;
- std::map<base::ProcessId, mojom::RawOSMemDump*> pid_to_os_dump;
- for (auto& response : request->responses) {
- const base::ProcessId& original_pid = response.second.process_id;
- pid_to_process_type[original_pid] = response.second.process_type;
- // |chrome_dump| can be nullptr if this was a OS-counters only response.
- pid_to_pmd[original_pid] = response.second.chrome_dump.get();
- // |response| accumulates the replies received by each client process.
- // Depending on the OS each client process might return 1 chrome + 1 OS
- // dump each or, in the case of Linux, only 1 chrome dump each % the
- // browser process which will provide all the OS dumps.
- // In the former case (!OS_LINUX) we expect client processes to have
- // exactly one OS dump in their |response|, % the case when they
- // unexpectedly disconnect in the middle of a dump (e.g. because they
- // crash). In the latter case (OS_LINUX) we expect the full map to come
- // from the browser process response.
- OSMemDumpMap& extra_os_dumps = response.second.os_dumps;
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- for (const auto& kv : extra_os_dumps) {
- auto pid = kv.first == base::kNullProcessId ? original_pid : kv.first;
- DCHECK_EQ(pid_to_os_dump[pid], nullptr);
- pid_to_os_dump[pid] = kv.second.get();
- }
- #else
- // This can be empty if the client disconnects before providing both
- // dumps. See UnregisterClientProcess().
- DCHECK_LE(extra_os_dumps.size(), 1u);
- for (const auto& kv : extra_os_dumps) {
- // When the OS dump comes from child processes, the pid is supposed to be
- // not used. We know the child process pid at the time of the request and
- // also wouldn't trust pids coming from child processes.
- DCHECK_EQ(base::kNullProcessId, kv.first);
- // Check we don't receive duplicate OS dumps for the same process.
- DCHECK_EQ(pid_to_os_dump[original_pid], nullptr);
- pid_to_os_dump[original_pid] = kv.second.get();
- }
- #endif
- }
- MemoryDumpMapConverter converter;
- perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap perfettoNodeMap =
- converter.Convert(pid_to_pmd);
- // Generate the global memory graph from the map of pids to dumps, removing
- // weak nodes.
- // TODO (crbug.com/1112671): We should avoid graph processing once we moved
- // the shared footprint computation to perfetto.
- std::unique_ptr<GlobalNodeGraph> global_graph =
- GraphProcessor::CreateMemoryGraph(perfettoNodeMap);
- GraphProcessor::RemoveWeakNodesFromGraph(global_graph.get());
- // Compute the shared memory footprint for each process from the graph.
- auto original =
- GraphProcessor::ComputeSharedFootprintFromGraph(*global_graph);
- std::map<base::ProcessId, uint64_t> shared_footprints;
- for (const auto& item : original) {
- shared_footprints.emplace(static_cast<base::ProcessId>(item.first),
- item.second);
- }
- // Perform the rest of the computation on the graph.
- GraphProcessor::AddOverheadsAndPropagateEntries(global_graph.get());
- GraphProcessor::CalculateSizesForGraph(global_graph.get());
- // The same timestamp needs to be set for all dumps in the memory snapshot.
- base::TimeTicks timestamp = TRACE_TIME_TICKS_NOW();
- // Build up the global dump by iterating on the |valid| process dumps.
- mojom::GlobalMemoryDumpPtr global_dump(mojom::GlobalMemoryDump::New());
- global_dump->start_time = request->start_time;
- global_dump->process_dumps.reserve(request->responses.size());
- for (const auto& response : request->responses) {
- base::ProcessId pid = response.second.process_id;
- // On Linux, we may also have the browser process as a response.
- // Just ignore it if we are looking for the single pid case.
- if (request->args.pid != base::kNullProcessId && pid != request->args.pid)
- continue;
- // These pointers are owned by |request|.
- mojom::RawOSMemDump* raw_os_dump = pid_to_os_dump[pid];
- auto* raw_chrome_dump = pid_to_pmd[pid];
- // If we have the OS dump we should have the platform private footprint.
- DCHECK(!raw_os_dump || raw_os_dump->platform_private_footprint);
- // If the raw dump exists, create a summarised version of it.
- mojom::OSMemDumpPtr os_dump = nullptr;
- if (raw_os_dump) {
- uint64_t shared_resident_kb = 0;
- #if BUILDFLAG(IS_MAC)
- // The resident, anonymous shared memory for each process is only
- // relevant on macOS.
- const auto process_graph_it =
- global_graph->process_node_graphs().find(pid);
- if (process_graph_it != global_graph->process_node_graphs().end()) {
- const auto& process_graph = process_graph_it->second;
- auto* node = process_graph->FindNode("shared_memory");
- if (node) {
- const auto& entry =
- node->entries()->find(RawMemoryGraphNode::kNameSize);
- if (entry != node->entries()->end())
- shared_resident_kb = entry->second.value_uint64 / 1024;
- }
- }
- #endif
- os_dump = CreatePublicOSDump(
- *raw_os_dump, base::saturated_cast<uint32_t>(shared_resident_kb));
- os_dump->shared_footprint_kb =
- base::saturated_cast<uint32_t>(shared_footprints[pid] / 1024);
- }
- // Trace the OS and Chrome dumps if they exist.
- if (request->args.add_to_trace) {
- if (raw_os_dump) {
- bool trace_os_success = tracing_observer->AddOsDumpToTraceIfEnabled(
- request->GetRequestArgs(), pid, *os_dump, raw_os_dump->memory_maps,
- timestamp);
- if (!trace_os_success) {
- DLOG(ERROR) << "Tracing is disabled or not setup yet while receiving "
- "OS dump for pid "
- << pid;
- request->failed_memory_dump_count++;
- }
- }
- if (raw_chrome_dump) {
- bool trace_chrome_success = AddChromeMemoryDumpToTrace(
- request->GetRequestArgs(), pid, *raw_chrome_dump, *global_graph,
- pid_to_process_type, tracing_observer, use_proto_writer, timestamp);
- if (!trace_chrome_success) {
- DLOG(ERROR) << "Tracing is disabled or not setup yet while receiving "
- "Chrome dump for pid "
- << pid;
- request->failed_memory_dump_count++;
- }
- }
- }
- bool valid = false;
- if (request->args.memory_footprint_only) {
- valid = raw_os_dump;
- } else {
- // Ignore incomplete results (can happen if the client
- // crashes/disconnects).
- valid = raw_os_dump && raw_chrome_dump &&
- (request->memory_map_option() == mojom::MemoryMapOption::NONE ||
- (raw_os_dump && !raw_os_dump->memory_maps.empty()));
- }
- if (!valid)
- continue;
- mojom::ProcessMemoryDumpPtr pmd = mojom::ProcessMemoryDump::New();
- pmd->pid = pid;
- pmd->process_type = pid_to_process_type[pid];
- pmd->os_dump = std::move(os_dump);
- pmd->service_name = response.second.service_name;
- // If we have to return a summary, add all entries for the requested
- // allocator dumps.
- if (request->should_return_summaries() &&
- !request->args.memory_footprint_only) {
- const auto& process_graph =
- global_graph->process_node_graphs().find(pid)->second;
- for (const std::string& name : request->args.allocator_dump_names) {
- bool is_recursive = base::EndsWith(name, "/*");
- std::string node_name =
- (is_recursive ? name.substr(0, name.length() - 2) : name);
- Node* node = process_graph->FindNode(node_name);
- // Silently ignore any missing node in the process graph.
- if (!node)
- continue;
- pmd->chrome_allocator_dumps.emplace(
- node_name, CreateAllocatorDumpForNode(node, is_recursive));
- }
- }
- global_dump->process_dumps.push_back(std::move(pmd));
- }
- global_dump->aggregated_metrics =
- ComputeGlobalNativeCodeResidentMemoryKb(pid_to_os_dump);
- const bool global_success = request->failed_memory_dump_count == 0;
- // In the single process-case, we want to ensure that global_success
- // is true if and only if global_dump is not nullptr.
- if (request->args.pid != base::kNullProcessId && !global_success) {
- global_dump = nullptr;
- }
- auto& callback = request->callback;
- std::move(callback).Run(global_success, request->dump_guid,
- std::move(global_dump));
- UMA_HISTOGRAM_MEDIUM_TIMES("Memory.Experimental.Debug.GlobalDumpDuration",
- base::TimeTicks::Now() - request->start_time);
- UMA_HISTOGRAM_COUNTS_1000(
- "Memory.Experimental.Debug.FailedProcessDumpsPerGlobalDump",
- request->failed_memory_dump_count);
- char guid_str[20];
- snprintf(guid_str, sizeof(guid_str), "0x%" PRIx64, request->dump_guid);
- TRACE_EVENT_NESTABLE_ASYNC_END2(
- base::trace_event::MemoryDumpManager::kTraceCategory, "GlobalMemoryDump",
- TRACE_ID_LOCAL(request->dump_guid), "dump_guid", TRACE_STR_COPY(guid_str),
- "success", global_success);
- }
- bool QueuedRequestDispatcher::AddChromeMemoryDumpToTrace(
- const base::trace_event::MemoryDumpRequestArgs& args,
- base::ProcessId pid,
- const base::trace_event::ProcessMemoryDump& raw_chrome_dump,
- const GlobalNodeGraph& global_graph,
- const std::map<base::ProcessId, mojom::ProcessType>& pid_to_process_type,
- TracingObserver* tracing_observer,
- bool use_proto_writer,
- const base::TimeTicks& timestamp) {
- bool is_chrome_tracing_enabled =
- base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableChromeTracingComputation);
- if (!is_chrome_tracing_enabled) {
- return tracing_observer->AddChromeDumpToTraceIfEnabled(
- args, pid, &raw_chrome_dump, timestamp);
- }
- if (!tracing_observer->ShouldAddToTrace(args))
- return false;
- if (use_proto_writer) {
- return tracing_observer->AddChromeDumpToTraceIfEnabled(
- args, pid, &raw_chrome_dump, timestamp);
- }
- const GlobalNodeGraph::Process& process =
- *global_graph.process_node_graphs().find(pid)->second;
- std::unique_ptr<TracedValue> traced_value;
- if (pid_to_process_type.find(pid)->second == mojom::ProcessType::BROWSER) {
- traced_value = GetChromeDumpAndGlobalAndEdgesTracedValue(
- process, *global_graph.shared_memory_graph(), global_graph.edges());
- } else {
- traced_value = GetChromeDumpTracedValue(process);
- }
- TracingObserverTracedValue::AddToTrace(args, pid, std::move(traced_value));
- return true;
- }
- QueuedRequestDispatcher::ClientInfo::ClientInfo(
- mojom::ClientProcess* client,
- base::ProcessId pid,
- mojom::ProcessType process_type,
- absl::optional<std::string> service_name)
- : client(client),
- pid(pid),
- process_type(process_type),
- service_name(std::move(service_name)) {}
- QueuedRequestDispatcher::ClientInfo::ClientInfo(ClientInfo&& other) = default;
- QueuedRequestDispatcher::ClientInfo::~ClientInfo() = default;
- } // namespace memory_instrumentation
|