123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- // Copyright 2021 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 "fuchsia_web/webengine/browser/web_engine_memory_inspector.h"
- #include <lib/fpromise/promise.h>
- #include <lib/inspect/cpp/inspector.h>
- #include <sstream>
- #include "base/no_destructor.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/trace_event/memory_dump_request_args.h"
- #include "components/fuchsia_component_support/config_reader.h"
- #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
- namespace {
- std::vector<std::string> GetAllocatorDumpNamesFromConfig() {
- const absl::optional<base::Value>& config =
- fuchsia_component_support::LoadPackageConfig();
- if (!config)
- return {};
- const base::Value::List* names_list =
- config->GetDict().FindList("allocator-dump-names");
- if (!names_list)
- return {};
- std::vector<std::string> names;
- names.reserve(names_list->size());
- for (auto& name : *names_list) {
- names.push_back(name.GetString());
- }
- return names;
- }
- // List of allocator dump names to include.
- const std::vector<std::string>& AllocatorDumpNames() {
- static base::NoDestructor<std::vector<std::string>> allocator_dump_names(
- GetAllocatorDumpNamesFromConfig());
- return *allocator_dump_names;
- }
- // Returns true if every field in the supplied |dump|, and those of its
- // children, are zero. Generally parent nodes summarize the total usage across
- // all of their children, such that if the parent is all-zero then the children
- // must also be all-zero. This implementation is optimized for the case in
- // which that property holds, but also copes gracefully when it does not.
- bool AreAllDumpEntriesZero(
- const memory_instrumentation::mojom::AllocatorMemDump* dump) {
- for (auto& it : dump->numeric_entries) {
- if (it.second != 0u)
- return false;
- }
- for (auto& it : dump->children) {
- if (!AreAllDumpEntriesZero(it.second.get()))
- return false;
- }
- return true;
- }
- // Creates a node |name|, under |parent|, populated recursively with the
- // contents of |dump|. The returned tree of Nodes are emplace()d to be owned by
- // the specified |owner|.
- inspect::Node NodeFromAllocatorMemDump(
- inspect::Inspector* owner,
- inspect::Node* parent,
- const std::string& name,
- const memory_instrumentation::mojom::AllocatorMemDump* dump) {
- auto node = parent->CreateChild(name);
- // Add subordinate nodes for any children.
- std::vector<const memory_instrumentation::mojom::AllocatorMemDump*> children;
- children.reserve(dump->children.size());
- for (auto& it : dump->children) {
- // If a child contains no information then omit it.
- if (AreAllDumpEntriesZero(it.second.get()))
- continue;
- children.push_back(it.second.get());
- owner->emplace(
- NodeFromAllocatorMemDump(owner, &node, it.first, it.second.get()));
- }
- // Publish the allocator-provided fields into the node. Entries are not
- // published if there is a single child, with identical entries, to avoid
- // redundancy in the emitted output.
- bool same_as_child =
- (children.size() == 1u &&
- (*children.begin())->numeric_entries == dump->numeric_entries);
- if (!same_as_child) {
- for (auto& it : dump->numeric_entries) {
- node.CreateUint(it.first, it.second, owner);
- }
- }
- return node;
- }
- } // namespace
- WebEngineMemoryInspector::WebEngineMemoryInspector(inspect::Node& parent_node) {
- // Loading the allocator dump names from the config involves blocking I/O so
- // trigger it to be done during construction, before the prohibition on
- // blocking the main thread is applied.
- AllocatorDumpNames();
- node_ = parent_node.CreateLazyNode("memory", [this]() {
- return fpromise::make_promise(fit::bind_member(
- this, &WebEngineMemoryInspector::ResolveMemoryDumpPromise));
- });
- }
- WebEngineMemoryInspector::~WebEngineMemoryInspector() = default;
- fpromise::result<inspect::Inspector>
- WebEngineMemoryInspector::ResolveMemoryDumpPromise(fpromise::context& context) {
- // If there is a |dump_results_| then resolve the promise with it.
- if (dump_results_) {
- auto memory_dump = std::move(dump_results_);
- return fpromise::ok(*memory_dump);
- }
- // If MemoryInstrumentation is not initialized then resolve an error.
- auto* memory_instrumentation =
- memory_instrumentation::MemoryInstrumentation::GetInstance();
- if (!memory_instrumentation)
- return fpromise::error();
- // Request memory usage summaries for all processes, including details for
- // any configured allocator dumps.
- auto* coordinator = memory_instrumentation->GetCoordinator();
- DCHECK(coordinator);
- coordinator->RequestGlobalMemoryDump(
- base::trace_event::MemoryDumpType::SUMMARY_ONLY,
- base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND,
- base::trace_event::MemoryDumpDeterminism::NONE, AllocatorDumpNames(),
- base::BindOnce(&WebEngineMemoryInspector::OnMemoryDumpComplete,
- weak_this_.GetWeakPtr(), base::TimeTicks::Now(),
- context.suspend_task()));
- return fpromise::pending();
- }
- void WebEngineMemoryInspector::OnMemoryDumpComplete(
- base::TimeTicks requested_at,
- fpromise::suspended_task task,
- bool success,
- memory_instrumentation::mojom::GlobalMemoryDumpPtr raw_dump) {
- DCHECK(!dump_results_);
- dump_results_ = std::make_unique<inspect::Inspector>();
- // If capture failed then there is no data to report.
- if (!success || !raw_dump) {
- task.resume_task();
- return;
- }
- // Note the delay between requesting the dump, and it being started.
- dump_results_->GetRoot().CreateDouble(
- "dump_queued_duration_ms",
- (raw_dump->start_time - requested_at).InMillisecondsF(),
- dump_results_.get());
- // Note the delay between starting the dump, and it completing.
- dump_results_->GetRoot().CreateDouble(
- "dump_duration_ms",
- (base::TimeTicks::Now() - raw_dump->start_time).InMillisecondsF(),
- dump_results_.get());
- for (const auto& process_dump : raw_dump->process_dumps) {
- auto node = dump_results_->GetRoot().CreateChild(
- base::NumberToString(process_dump->pid));
- // Include details of each process' role in the web instance.
- std::ostringstream type;
- type << process_dump->process_type;
- static const inspect::StringReference kTypeNodeName("type");
- node.CreateString(kTypeNodeName, type.str(), dump_results_.get());
- const auto service_name = process_dump->service_name;
- if (service_name) {
- static const inspect::StringReference kServiceNodeName("service");
- node.CreateString(kServiceNodeName, *service_name, dump_results_.get());
- }
- // Include the summary of the process' memory usage.
- const auto& os_dump = process_dump->os_dump;
- static const inspect::StringReference kResidentKbNodeName("resident_kb");
- node.CreateUint(kResidentKbNodeName, os_dump->resident_set_kb,
- dump_results_.get());
- static const inspect::StringReference kPrivateKbNodeName("private_kb");
- node.CreateUint(kPrivateKbNodeName, os_dump->private_footprint_kb,
- dump_results_.get());
- static const inspect::StringReference kSharedKbNodeName("shared_kb");
- node.CreateUint(kSharedKbNodeName, os_dump->shared_footprint_kb,
- dump_results_.get());
- // If provided, include detail from individual allocators.
- if (!process_dump->chrome_allocator_dumps.empty()) {
- static const inspect::StringReference kAllocatorDumpNodeName(
- "allocator_dump");
- auto detail_node = node.CreateChild(kAllocatorDumpNodeName);
- for (auto& it : process_dump->chrome_allocator_dumps) {
- dump_results_->emplace(NodeFromAllocatorMemDump(
- dump_results_.get(), &detail_node, it.first, it.second.get()));
- }
- dump_results_->emplace(std::move(detail_node));
- }
- dump_results_->emplace(std::move(node));
- }
- task.resume_task();
- }
|