blob: 440ae0bd856dda10fda4187f5f4d81edcb818a2e [file] [log] [blame]
// 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/public/cpp/memory_instrumentation/client_process_impl.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/containers/flat_map.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/lock.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "build/build_config.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/coordinator.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.h"
#include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
#include "services/service_manager/public/cpp/bind_source_info.h"
#include "services/service_manager/public/cpp/connector.h"
namespace memory_instrumentation {
// static
void ClientProcessImpl::CreateInstance(const Config& config) {
static ClientProcessImpl* instance = nullptr;
if (!instance) {
instance = new ClientProcessImpl(config);
} else {
NOTREACHED();
}
}
ClientProcessImpl::ClientProcessImpl(const Config& config)
: binding_(this), process_type_(config.process_type), task_runner_() {
// |config.connector| can be null in tests.
if (config.connector) {
config.connector->BindInterface(config.service_name,
mojo::MakeRequest(&coordinator_));
mojom::ClientProcessPtr process;
binding_.Bind(mojo::MakeRequest(&process));
coordinator_->RegisterClientProcess(std::move(process),
config.process_type);
// Initialize the public-facing MemoryInstrumentation helper.
MemoryInstrumentation::CreateInstance(config.connector,
config.service_name);
} else {
config.coordinator_for_testing->BindCoordinatorReceiver(
mojo::MakeRequest(&coordinator_), service_manager::BindSourceInfo());
}
task_runner_ = base::ThreadTaskRunnerHandle::Get();
// TODO(primiano): this is a temporary workaround to tell the
// base::MemoryDumpManager that it is special and should coordinate periodic
// dumps for tracing. Remove this once the periodic dump scheduler is moved
// from base to the service. MDM should not care about being the coordinator.
bool is_coordinator_process =
config.process_type == mojom::ProcessType::BROWSER;
base::trace_event::MemoryDumpManager::GetInstance()->Initialize(
base::BindRepeating(
&ClientProcessImpl::RequestGlobalMemoryDump_NoCallback,
base::Unretained(this)),
is_coordinator_process);
tracing_observer_ = std::make_unique<TracingObserver>(
base::trace_event::TraceLog::GetInstance(),
base::trace_event::MemoryDumpManager::GetInstance());
}
ClientProcessImpl::~ClientProcessImpl() {}
void ClientProcessImpl::RequestChromeMemoryDump(
const base::trace_event::MemoryDumpRequestArgs& args,
RequestChromeMemoryDumpCallback callback) {
DCHECK(!callback.is_null());
most_recent_chrome_memory_dump_guid_ = args.dump_guid;
auto it_and_inserted =
pending_chrome_callbacks_.emplace(args.dump_guid, std::move(callback));
DCHECK(it_and_inserted.second) << "Duplicated request id " << args.dump_guid;
base::trace_event::MemoryDumpManager::GetInstance()->CreateProcessDump(
args, base::Bind(&ClientProcessImpl::OnChromeMemoryDumpDone,
base::Unretained(this)));
}
void ClientProcessImpl::OnChromeMemoryDumpDone(
bool success,
uint64_t dump_guid,
std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump) {
DCHECK(success || !process_memory_dump);
auto callback_it = pending_chrome_callbacks_.find(dump_guid);
DCHECK(callback_it != pending_chrome_callbacks_.end());
auto callback = std::move(callback_it->second);
pending_chrome_callbacks_.erase(callback_it);
auto it = delayed_os_memory_dump_callbacks_.find(dump_guid);
if (it != delayed_os_memory_dump_callbacks_.end()) {
for (auto& args : it->second) {
PerformOSMemoryDump(std::move(args));
}
delayed_os_memory_dump_callbacks_.erase(it);
}
if (!process_memory_dump) {
std::move(callback).Run(false, dump_guid, nullptr);
return;
}
std::move(callback).Run(success, dump_guid, std::move(process_memory_dump));
}
void ClientProcessImpl::RequestGlobalMemoryDump_NoCallback(
base::trace_event::MemoryDumpType dump_type,
base::trace_event::MemoryDumpLevelOfDetail level_of_detail) {
if (!task_runner_->RunsTasksInCurrentSequence()) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ClientProcessImpl::RequestGlobalMemoryDump_NoCallback,
base::Unretained(this), dump_type, level_of_detail));
return;
}
coordinator_->RequestGlobalMemoryDumpAndAppendToTrace(
dump_type, level_of_detail,
mojom::Coordinator::RequestGlobalMemoryDumpAndAppendToTraceCallback());
}
void ClientProcessImpl::RequestOSMemoryDump(
mojom::MemoryMapOption mmap_option,
const std::vector<base::ProcessId>& pids,
RequestOSMemoryDumpCallback callback) {
OSMemoryDumpArgs args;
args.mmap_option = mmap_option;
args.pids = pids;
args.callback = std::move(callback);
#if defined(OS_MACOSX)
// If the most recent chrome memory dump hasn't finished, wait for that to
// finish.
if (most_recent_chrome_memory_dump_guid_.has_value()) {
uint64_t guid = most_recent_chrome_memory_dump_guid_.value();
auto it = pending_chrome_callbacks_.find(guid);
if (it != pending_chrome_callbacks_.end()) {
delayed_os_memory_dump_callbacks_[guid].push_back(std::move(args));
return;
}
}
#endif
PerformOSMemoryDump(std::move(args));
}
void ClientProcessImpl::PerformOSMemoryDump(OSMemoryDumpArgs args) {
bool global_success = true;
base::flat_map<base::ProcessId, mojom::RawOSMemDumpPtr> results;
for (const base::ProcessId& pid : args.pids) {
mojom::RawOSMemDumpPtr result = mojom::RawOSMemDump::New();
result->platform_private_footprint = mojom::PlatformPrivateFootprint::New();
bool success = OSMetrics::FillOSMemoryDump(pid, result.get());
if (args.mmap_option != mojom::MemoryMapOption::NONE) {
success = success && OSMetrics::FillProcessMemoryMaps(
pid, args.mmap_option, result.get());
}
if (success)
results[pid] = std::move(result);
global_success = global_success && success;
}
std::move(args.callback).Run(global_success, std::move(results));
}
ClientProcessImpl::Config::Config(service_manager::Connector* connector,
const std::string& service_name,
mojom::ProcessType process_type)
: connector(connector),
service_name(service_name),
process_type(process_type) {}
ClientProcessImpl::Config::~Config() {}
ClientProcessImpl::OSMemoryDumpArgs::OSMemoryDumpArgs() = default;
ClientProcessImpl::OSMemoryDumpArgs::OSMemoryDumpArgs(OSMemoryDumpArgs&&) =
default;
ClientProcessImpl::OSMemoryDumpArgs::~OSMemoryDumpArgs() = default;
} // namespace memory_instrumentation