blob: e5e7aac229cbc376ed41599bb303811e9168bcd4 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/heap_profiling/in_process/browser_process_snapshot_controller.h"
#include <memory>
#include <utility>
#include "base/check.h"
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/rand_util.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "components/heap_profiling/in_process/heap_profiler_controller.h"
#include "components/heap_profiling/in_process/heap_profiler_parameters.h"
#include "components/heap_profiling/in_process/mojom/snapshot_controller.mojom.h"
#include "components/sampling_profiler/process_type.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote_set.h"
namespace heap_profiling {
// static
BrowserProcessSnapshotController*
BrowserProcessSnapshotController::GetInstance() {
const auto* controller = HeapProfilerController::GetInstance();
return controller ? controller->GetBrowserProcessSnapshotController()
: nullptr;
}
BrowserProcessSnapshotController::BrowserProcessSnapshotController(
scoped_refptr<base::SequencedTaskRunner> snapshot_task_runner)
: snapshot_task_runner_(std::move(snapshot_task_runner)) {
// Label this as the main sequence.
DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
// Initialize with all supported process types.
using RemoteSet = mojo::RemoteSet<mojom::SnapshotController>;
remotes_by_process_type_.emplace(sampling_profiler::ProfilerProcessType::kGpu,
std::make_unique<RemoteSet>());
remotes_by_process_type_.emplace(
sampling_profiler::ProfilerProcessType::kNetworkService,
std::make_unique<RemoteSet>());
remotes_by_process_type_.emplace(
sampling_profiler::ProfilerProcessType::kRenderer,
std::make_unique<RemoteSet>());
remotes_by_process_type_.emplace(
sampling_profiler::ProfilerProcessType::kUtility,
std::make_unique<RemoteSet>());
// Now that `remotes_by_process_type_` is initialized all further access
// should be on the snapshot sequence.
DETACH_FROM_SEQUENCE(snapshot_sequence_checker_);
}
BrowserProcessSnapshotController::~BrowserProcessSnapshotController() {
DCHECK_CALLED_ON_VALID_SEQUENCE(snapshot_sequence_checker_);
}
base::WeakPtr<BrowserProcessSnapshotController>
BrowserProcessSnapshotController::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void BrowserProcessSnapshotController::SetBindRemoteForChildProcessCallback(
BindRemoteCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
bind_remote_callback_ = std::move(callback);
}
void BrowserProcessSnapshotController::BindRemoteForChildProcess(
int child_process_id,
sampling_profiler::ProfilerProcessType child_process_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
mojo::PendingRemote<mojom::SnapshotController> remote;
bind_remote_callback_.Run(child_process_id,
remote.InitWithNewPipeAndPassReceiver());
snapshot_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&BrowserProcessSnapshotController::StoreRemoteOnSnapshotSequence,
GetWeakPtr(), std::move(remote), child_process_type));
}
void BrowserProcessSnapshotController::TakeSnapshotsOnSnapshotSequence() {
DCHECK_CALLED_ON_VALID_SEQUENCE(snapshot_sequence_checker_);
for (auto& [process_type, remote_set] : remotes_by_process_type_) {
CHECK(remote_set);
if (remote_set->empty()) {
// Avoid division by 0. This won't change any metrics since there are no
// processes to measure.
continue;
}
const int snapshot_probability_pct =
GetSnapshotProbabilityForProcess(process_type);
if (snapshot_probability_pct == 0) {
// No need to test each process since none will be chosen.
continue;
}
// Choose a random set of processes to snapshot. If randomness is
// suppressed, choose processes by their index in the set.
double prob_idx = 0; // Index for simulating probability.
size_t process_idx = 0; // Index of chosen process.
for (const auto& remote : *remote_set) {
const double prob = suppress_randomness_for_testing_
? (prob_idx++ / remote_set->size())
: base::RandDouble();
if (prob * 100.0 < snapshot_probability_pct) {
remote->TakeSnapshot(snapshot_probability_pct, process_idx++);
} else {
remote->LogMetricsWithoutSnapshot();
}
}
}
}
void BrowserProcessSnapshotController::SuppressRandomnessForTesting() {
suppress_randomness_for_testing_ = true;
}
void BrowserProcessSnapshotController::StoreRemoteOnSnapshotSequence(
mojo::PendingRemote<mojom::SnapshotController> remote,
sampling_profiler::ProfilerProcessType process_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(snapshot_sequence_checker_);
// at() will CHECK if `process_type` wasn't added in the constructor.
remotes_by_process_type_.at(process_type)
->Add(std::move(remote), snapshot_task_runner_);
}
} // namespace heap_profiling