1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
// 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
|