Revert "Convert HeapProfiler JSON params to regular FeatureParams"
This reverts commit 1c0d6593cb
.
Reason for revert: Caused crash on launch in Canary b/382002928
Original change's description:
> Convert HeapProfiler JSON params to regular FeatureParams
>
> Now that the heap profiler is controlled centrally from the browser
> process, many params are redundant. This simplifies the params that
> were in per-process JSON dicts:
>
> * `is_supported` is no longer needed. To disable in a subprocess, set
> its snapshot probability (eg. `gpu-prob-pct`, `renderer-proc-pct`)
> to 0. To disable in the browser process, disable the
> "HeapProfilerReporting" feature.
> * `stable_probability`, `nonstable_probability` and
> `collection_interval` now only affect the browser process, so make
> them global FeatureParams.
> * `sampling_rate_bytes` is split into separate FeatureParams for each
> process (eg. `gpu-sampling-rate-bytes`, `renderer-sampling-rate-
> bytes`).
>
> Also removes the test that enables profiler reports in a child process
> but not the browser process, since there's no longer any combination
> of feature params that can do this. (Since HeapProfilerCentralControl
> launched, the test only checked that the giving params that enabled a
> child process without enabling the browser did nothing.)
>
> Bug: 40840943
> Change-Id: I070d8ec1035ab6c21fe676e3a44c15e199508b9c
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6056270
> Commit-Queue: Joe Mason <joenotcharles@google.com>
> Reviewed-by: Sean Maher <spvw@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1390539}
Bug: 40840943, 382002928
Change-Id: I5a0bd185d48620983348a13944ff7cc212b29e13
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6062973
Reviewed-by: Ben Mason <benmason@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Daniel Yip <danielyip@google.com>
Commit-Queue: Daniel Yip <danielyip@google.com>
Owners-Override: Ben Mason <benmason@chromium.org>
Auto-Submit: Daniel Yip <danielyip@google.com>
Cr-Commit-Position: refs/heads/main@{#1390998}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
c389cdd416
commit
a453d5093f
@@ -55,7 +55,10 @@ source_set("unit_tests") {
|
||||
# HeapProfilerController's dependencies are not compiled on iOS unless
|
||||
# use_allocator_shim is true.
|
||||
if (!is_ios || use_allocator_shim) {
|
||||
sources = [ "heap_profiler_controller_unittest.cc" ]
|
||||
sources = [
|
||||
"heap_profiler_controller_unittest.cc",
|
||||
"heap_profiler_parameters_unittest.cc",
|
||||
]
|
||||
deps = [
|
||||
":in_process",
|
||||
":mojom",
|
||||
|
@@ -7,12 +7,14 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.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/metrics/field_trial_params.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
@@ -97,8 +99,25 @@ void BrowserProcessSnapshotController::TakeSnapshotsOnSnapshotSequence() {
|
||||
// processes to measure.
|
||||
continue;
|
||||
}
|
||||
const int snapshot_probability_pct =
|
||||
GetSnapshotProbabilityForProcess(process_type);
|
||||
int snapshot_probability_pct;
|
||||
switch (process_type) {
|
||||
case sampling_profiler::ProfilerProcessType::kGpu:
|
||||
snapshot_probability_pct = kGpuSnapshotProbability.Get();
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kNetworkService:
|
||||
snapshot_probability_pct = kNetworkSnapshotProbability.Get();
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kRenderer:
|
||||
snapshot_probability_pct = kRendererSnapshotProbability.Get();
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kUtility:
|
||||
snapshot_probability_pct = kUtilitySnapshotProbability.Get();
|
||||
break;
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
CHECK_GE(snapshot_probability_pct, 0);
|
||||
CHECK_LE(snapshot_probability_pct, 100);
|
||||
if (snapshot_probability_pct == 0) {
|
||||
// No need to test each process since none will be chosen.
|
||||
continue;
|
||||
|
@@ -17,12 +17,10 @@
|
||||
#include "base/allocator/dispatcher/reentry_guard.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/feature_list.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/metrics/field_trial_params.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/metrics/metrics_hashes.h"
|
||||
#include "base/notreached.h"
|
||||
@@ -45,7 +43,6 @@
|
||||
#include "components/metrics/call_stacks/call_stack_profile_builder.h"
|
||||
#include "components/sampling_profiler/process_type.h"
|
||||
#include "components/services/heap_profiling/public/cpp/merge_samples.h"
|
||||
#include "components/variations/variations_switches.h"
|
||||
#include "components/version_info/channel.h"
|
||||
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
|
||||
|
||||
@@ -110,18 +107,19 @@ std::string ProcessHistogramName(std::string_view base_name,
|
||||
}
|
||||
}
|
||||
|
||||
double GetChannelProbability(version_info::Channel channel) {
|
||||
double GetChannelProbability(version_info::Channel channel,
|
||||
const HeapProfilerParameters& params) {
|
||||
switch (channel) {
|
||||
case version_info::Channel::STABLE:
|
||||
case version_info::Channel::UNKNOWN:
|
||||
// If the channel can't be determined, treat it as `stable` for safety.
|
||||
// Don't disable heap profiling completely so that developers can still
|
||||
// enable it with --enable-feature flags.
|
||||
return kStableProbability.Get();
|
||||
return params.stable_probability;
|
||||
case version_info::Channel::BETA:
|
||||
case version_info::Channel::DEV:
|
||||
case version_info::Channel::CANARY:
|
||||
return kNonStableProbability.Get();
|
||||
return params.nonstable_probability;
|
||||
}
|
||||
NOTREACHED();
|
||||
}
|
||||
@@ -142,19 +140,15 @@ std::pair<bool, std::optional<std::string>> DecideIfCollectionIsEnabled(
|
||||
return {is_enabled, std::nullopt};
|
||||
}
|
||||
|
||||
// Never profile during benchmarking.
|
||||
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
variations::switches::kEnableBenchmarking)) {
|
||||
return {false, std::nullopt};
|
||||
}
|
||||
|
||||
if (!base::FeatureList::IsEnabled(kHeapProfilerReporting)) {
|
||||
return {false, std::nullopt};
|
||||
}
|
||||
|
||||
// Randomly determine whether profiling is enabled.
|
||||
HeapProfilerParameters params =
|
||||
GetHeapProfilerParametersForProcess(process_type);
|
||||
if (!params.is_supported) {
|
||||
return {false, std::nullopt};
|
||||
}
|
||||
|
||||
const double seed = base::RandDouble();
|
||||
const double probability = GetChannelProbability(channel);
|
||||
const double probability = GetChannelProbability(channel, params);
|
||||
if (seed < probability) {
|
||||
return {true, "Enabled"};
|
||||
}
|
||||
@@ -263,9 +257,13 @@ bool HeapProfilerController::StartIfEnabled() {
|
||||
if (!profiling_enabled_) {
|
||||
return false;
|
||||
}
|
||||
const size_t sampling_rate_bytes = GetSamplingRateForProcess(process_type_);
|
||||
if (sampling_rate_bytes > 0) {
|
||||
base::SamplingHeapProfiler::Get()->SetSamplingInterval(sampling_rate_bytes);
|
||||
HeapProfilerParameters profiler_params =
|
||||
GetHeapProfilerParametersForProcess(process_type_);
|
||||
// DecideIfCollectionIsEnabled() should return false if not supported.
|
||||
DCHECK(profiler_params.is_supported);
|
||||
if (profiler_params.sampling_rate_bytes > 0) {
|
||||
base::SamplingHeapProfiler::Get()->SetSamplingInterval(
|
||||
profiler_params.sampling_rate_bytes);
|
||||
}
|
||||
base::SamplingHeapProfiler::Get()->Start();
|
||||
|
||||
@@ -274,10 +272,9 @@ bool HeapProfilerController::StartIfEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
const base::TimeDelta collection_interval = kCollectionInterval.Get();
|
||||
CHECK(collection_interval.is_positive());
|
||||
DCHECK(profiler_params.collection_interval.is_positive());
|
||||
SnapshotParams params(
|
||||
collection_interval,
|
||||
profiler_params.collection_interval,
|
||||
/*use_random_interval=*/!suppress_randomness_for_testing_, stopped_,
|
||||
process_type_, creation_time_, std::move(on_first_snapshot_callback_));
|
||||
params.trigger_child_process_snapshot_closure = base::BindRepeating(
|
||||
@@ -371,7 +368,7 @@ void HeapProfilerController::AppendCommandLineSwitchInternal(
|
||||
BrowserProcessSnapshotController* snapshot_controller) {
|
||||
CHECK_NE(child_process_type, ProcessType::kBrowser);
|
||||
if (snapshot_controller &&
|
||||
GetSnapshotProbabilityForProcess(child_process_type) > 0) {
|
||||
GetHeapProfilerParametersForProcess(child_process_type).is_supported) {
|
||||
command_line->AppendSwitch(switches::kSubprocessHeapProfiling);
|
||||
snapshot_controller->BindRemoteForChildProcess(child_process_id,
|
||||
child_process_type);
|
||||
|
@@ -19,12 +19,16 @@
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/barrier_closure.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/containers/enum_set.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
#include "base/json/json_writer.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/metrics/field_trial_params.h"
|
||||
#include "base/metrics/metrics_hashes.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/process/launch.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
|
||||
@@ -42,6 +46,7 @@
|
||||
#include "base/test/test_timeouts.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/types/optional_util.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/heap_profiling/in_process/browser_process_snapshot_controller.h"
|
||||
#include "components/heap_profiling/in_process/child_process_snapshot_controller.h"
|
||||
@@ -113,12 +118,21 @@ namespace {
|
||||
#define ENABLE_MULTIPROCESS_TESTS 1
|
||||
#endif
|
||||
|
||||
using FeatureRef = base::test::FeatureRef;
|
||||
using FeatureRefAndParams = base::test::FeatureRefAndParams;
|
||||
using ProcessType = sampling_profiler::ProfilerProcessType;
|
||||
using ProcessTypeSet =
|
||||
base::EnumSet<ProcessType, ProcessType::kUnknown, ProcessType::kMax>;
|
||||
using ProfileCollectorCallback =
|
||||
base::RepeatingCallback<void(base::TimeTicks, metrics::SampledProfile)>;
|
||||
using base::allocator::dispatcher::AllocationNotificationData;
|
||||
using base::allocator::dispatcher::AllocationSubsystem;
|
||||
using base::allocator::dispatcher::FreeNotificationData;
|
||||
using base::test::FeatureRef;
|
||||
using base::test::FeatureRefAndParams;
|
||||
using sampling_profiler::ProfilerProcessType;
|
||||
using ScopedMuteHookedSamplesForTesting =
|
||||
base::PoissonAllocationSampler::ScopedMuteHookedSamplesForTesting;
|
||||
using ScopedSuppressRandomnessForTesting =
|
||||
base::PoissonAllocationSampler::ScopedSuppressRandomnessForTesting;
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AllOf;
|
||||
using ::testing::Conditional;
|
||||
@@ -131,13 +145,6 @@ using ::testing::Property;
|
||||
using ::testing::ResultOf;
|
||||
using ::testing::UnorderedElementsAreArray;
|
||||
|
||||
using ProfileCollectorCallback =
|
||||
base::RepeatingCallback<void(base::TimeTicks, metrics::SampledProfile)>;
|
||||
using ScopedMuteHookedSamplesForTesting =
|
||||
base::PoissonAllocationSampler::ScopedMuteHookedSamplesForTesting;
|
||||
using ScopedSuppressRandomnessForTesting =
|
||||
base::PoissonAllocationSampler::ScopedSuppressRandomnessForTesting;
|
||||
|
||||
constexpr size_t kSamplingRate = 1024;
|
||||
constexpr size_t kAllocationSize = 42 * kSamplingRate;
|
||||
|
||||
@@ -393,9 +400,8 @@ class MultiprocessTestChild final : public mojom::TestConnector,
|
||||
|
||||
// Start the heap profiler and wait for TakeSnapshot() messages from the
|
||||
// parent.
|
||||
HeapProfilerController controller(
|
||||
version_info::Channel::STABLE,
|
||||
static_cast<ProfilerProcessType>(process_type));
|
||||
HeapProfilerController controller(version_info::Channel::STABLE,
|
||||
static_cast<ProcessType>(process_type));
|
||||
controller.SuppressRandomnessForTesting();
|
||||
ASSERT_TRUE(controller.IsEnabled());
|
||||
controller.StartIfEnabled();
|
||||
@@ -517,7 +523,7 @@ class MultiprocessTestParent {
|
||||
// `should_profile` is false, simulate the embedder refusing to profile the
|
||||
// child process.
|
||||
void LaunchTestChild(HeapProfilerController* controller,
|
||||
ProfilerProcessType process_type,
|
||||
ProcessType process_type,
|
||||
int num_allocations,
|
||||
bool should_profile) {
|
||||
// `should_profile` will apply during next call to BindTestConnector().
|
||||
@@ -599,13 +605,14 @@ struct FeatureTestParams {
|
||||
};
|
||||
// Whether HeapProfilerReporting is enabled.
|
||||
bool feature_enabled = true;
|
||||
const ProcessTypeSet supported_processes;
|
||||
ChannelParams stable;
|
||||
ChannelParams nonstable;
|
||||
// Probabilities for snapshotting child processes.
|
||||
int gpu_snapshot_prob = 0;
|
||||
int network_snapshot_prob = 0;
|
||||
int renderer_snapshot_prob = 0;
|
||||
int utility_snapshot_prob = 0;
|
||||
int gpu_snapshot_prob = 100;
|
||||
int network_snapshot_prob = 100;
|
||||
int renderer_snapshot_prob = 100;
|
||||
int utility_snapshot_prob = 100;
|
||||
|
||||
base::FieldTrialParams ToFieldTrialParams() const;
|
||||
|
||||
@@ -616,32 +623,58 @@ struct FeatureTestParams {
|
||||
// Converts the test params to field trial parameters for the
|
||||
// HeapProfilerReporting feature.
|
||||
base::FieldTrialParams FeatureTestParams::ToFieldTrialParams() const {
|
||||
base::FieldTrialParams field_trial_params;
|
||||
base::FieldTrialParams field_trial_params{
|
||||
{"gpu-prob-pct", base::NumberToString(gpu_snapshot_prob)},
|
||||
{"network-prob-pct", base::NumberToString(network_snapshot_prob)},
|
||||
{"renderer-prob-pct", base::NumberToString(renderer_snapshot_prob)},
|
||||
{"utility-prob-pct", base::NumberToString(utility_snapshot_prob)},
|
||||
};
|
||||
|
||||
// Global parameters.
|
||||
field_trial_params["stable-probability"] =
|
||||
base::NumberToString(stable.probability);
|
||||
field_trial_params["nonstable-probability"] =
|
||||
base::NumberToString(nonstable.probability);
|
||||
// Add the default params.
|
||||
base::Value::Dict dict;
|
||||
if (!supported_processes.empty()) {
|
||||
// Explicitly disable profiling by default, so that only the processes
|
||||
// given in `supported_processes` will be enabled.
|
||||
dict.Set("is-supported", false);
|
||||
}
|
||||
dict.Set("stable-probability", stable.probability);
|
||||
dict.Set("nonstable-probability", nonstable.probability);
|
||||
dict.Set("sampling-rate-bytes", static_cast<int>(kSamplingRate));
|
||||
std::string param_string;
|
||||
base::JSONWriter::WriteWithOptions(
|
||||
dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, ¶m_string);
|
||||
field_trial_params["default-params"] = param_string;
|
||||
|
||||
// Per-process parameters.
|
||||
field_trial_params["browser-sampling-rate-bytes"] =
|
||||
base::NumberToString(kSamplingRate);
|
||||
field_trial_params["gpu-sampling-rate-bytes"] =
|
||||
base::NumberToString(kSamplingRate);
|
||||
field_trial_params["gpu-prob-pct"] = base::NumberToString(gpu_snapshot_prob);
|
||||
field_trial_params["network-sampling-rate-bytes"] =
|
||||
base::NumberToString(kSamplingRate);
|
||||
field_trial_params["network-prob-pct"] =
|
||||
base::NumberToString(network_snapshot_prob);
|
||||
field_trial_params["renderer-sampling-rate-bytes"] =
|
||||
base::NumberToString(kSamplingRate);
|
||||
field_trial_params["renderer-prob-pct"] =
|
||||
base::NumberToString(renderer_snapshot_prob);
|
||||
field_trial_params["utility-sampling-rate-bytes"] =
|
||||
base::NumberToString(kSamplingRate);
|
||||
field_trial_params["utility-prob-pct"] =
|
||||
base::NumberToString(utility_snapshot_prob);
|
||||
// Add a field trial param that enables each process type in
|
||||
// `supported_processes`.
|
||||
base::Value::Dict is_supported_dict;
|
||||
is_supported_dict.Set("is-supported", true);
|
||||
std::string is_supported_string;
|
||||
base::JSONWriter::WriteWithOptions(is_supported_dict,
|
||||
base::JSONWriter::OPTIONS_PRETTY_PRINT,
|
||||
&is_supported_string);
|
||||
|
||||
for (ProcessType process_type : supported_processes) {
|
||||
switch (process_type) {
|
||||
case ProcessType::kBrowser:
|
||||
field_trial_params["browser-process-params"] = is_supported_string;
|
||||
break;
|
||||
case ProcessType::kRenderer:
|
||||
field_trial_params["renderer-process-params"] = is_supported_string;
|
||||
break;
|
||||
case ProcessType::kGpu:
|
||||
field_trial_params["gpu-process-params"] = is_supported_string;
|
||||
break;
|
||||
case ProcessType::kUtility:
|
||||
field_trial_params["utility-process-params"] = is_supported_string;
|
||||
break;
|
||||
case ProcessType::kNetworkService:
|
||||
field_trial_params["network-process-params"] = is_supported_string;
|
||||
break;
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
return field_trial_params;
|
||||
}
|
||||
@@ -729,18 +762,18 @@ class HeapProfilerControllerTest
|
||||
// The test must call StartIfEnabled() after this to start profiling.
|
||||
void CreateHeapProfiler(
|
||||
version_info::Channel channel,
|
||||
ProfilerProcessType process_type,
|
||||
ProcessType process_type,
|
||||
bool expect_enabled,
|
||||
base::OnceClosure first_snapshot_callback = base::DoNothing(),
|
||||
ProfileCollectorCallback collector_callback = base::DoNothing()) {
|
||||
ASSERT_FALSE(controller_) << "CreateHeapProfiler called twice";
|
||||
switch (process_type) {
|
||||
case ProfilerProcessType::kBrowser:
|
||||
case ProcessType::kBrowser:
|
||||
expected_process_ = metrics::Process::BROWSER_PROCESS;
|
||||
metrics::CallStackProfileBuilder::SetBrowserProcessReceiverCallback(
|
||||
std::move(collector_callback));
|
||||
break;
|
||||
case ProfilerProcessType::kUtility:
|
||||
case ProcessType::kUtility:
|
||||
expected_process_ = metrics::Process::UTILITY_PROCESS;
|
||||
metrics::CallStackProfileBuilder::
|
||||
SetParentProfileCollectorForChildProcess(
|
||||
@@ -772,7 +805,7 @@ class HeapProfilerControllerTest
|
||||
// profiling.
|
||||
void StartHeapProfiling(
|
||||
version_info::Channel channel,
|
||||
ProfilerProcessType process_type,
|
||||
ProcessType process_type,
|
||||
bool expect_enabled,
|
||||
base::OnceClosure first_snapshot_callback = base::DoNothing(),
|
||||
ProfileCollectorCallback collector_callback = base::DoNothing()) {
|
||||
@@ -874,8 +907,7 @@ TEST_P(HeapProfilerControllerTest, ProfileCollectionsScheduler) {
|
||||
++profile_count;
|
||||
};
|
||||
|
||||
StartHeapProfiling(version_info::Channel::STABLE,
|
||||
ProfilerProcessType::kBrowser,
|
||||
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||
/*expect_enabled=*/true,
|
||||
/*first_snapshot_callback=*/base::DoNothing(),
|
||||
base::BindLambdaForTesting(check_profile));
|
||||
@@ -905,8 +937,7 @@ TEST_P(HeapProfilerControllerTest, ProfileCollectionsScheduler) {
|
||||
TEST_P(HeapProfilerControllerTest, UnhandledProcess) {
|
||||
// Starting the heap profiler in an unhandled process type should safely do
|
||||
// nothing.
|
||||
StartHeapProfiling(version_info::Channel::STABLE,
|
||||
ProfilerProcessType::kUnknown,
|
||||
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kUnknown,
|
||||
/*expect_enabled=*/false);
|
||||
// The Enabled summary histogram should not be logged for unsupported
|
||||
// processes, because they're not included in the per-process histograms that
|
||||
@@ -918,10 +949,10 @@ TEST_P(HeapProfilerControllerTest, EmptyProfile) {
|
||||
// Should save an empty profile even though no memory is allocated.
|
||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||
/*expect_take_snapshot=*/true, /*expect_sampled_profile=*/true);
|
||||
StartHeapProfiling(
|
||||
version_info::Channel::STABLE, ProfilerProcessType::kBrowser,
|
||||
/*expect_enabled=*/true, callbacks.first_snapshot_callback(),
|
||||
callbacks.collector_callback());
|
||||
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||
/*expect_enabled=*/true,
|
||||
callbacks.first_snapshot_callback(),
|
||||
callbacks.collector_callback());
|
||||
task_env().RunUntilQuit();
|
||||
EXPECT_TRUE(sample_received_);
|
||||
}
|
||||
@@ -972,9 +1003,8 @@ TEST_P(HeapProfilerControllerChannelTest, StableChannel) {
|
||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||
/*expect_take_snapshot=*/profiling_enabled,
|
||||
GetParam().stable.expect_browser_sample);
|
||||
StartHeapProfiling(version_info::Channel::STABLE,
|
||||
ProfilerProcessType::kBrowser, profiling_enabled,
|
||||
callbacks.first_snapshot_callback(),
|
||||
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||
callbacks.collector_callback());
|
||||
histogram_tester_.ExpectUniqueSample(
|
||||
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
||||
@@ -990,9 +1020,8 @@ TEST_P(HeapProfilerControllerChannelTest, CanaryChannel) {
|
||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||
/*expect_take_snapshot=*/profiling_enabled,
|
||||
GetParam().nonstable.expect_browser_sample);
|
||||
StartHeapProfiling(version_info::Channel::CANARY,
|
||||
ProfilerProcessType::kBrowser, profiling_enabled,
|
||||
callbacks.first_snapshot_callback(),
|
||||
StartHeapProfiling(version_info::Channel::CANARY, ProcessType::kBrowser,
|
||||
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||
callbacks.collector_callback());
|
||||
histogram_tester_.ExpectUniqueSample(
|
||||
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
||||
@@ -1010,9 +1039,8 @@ TEST_P(HeapProfilerControllerChannelTest, UnknownChannel) {
|
||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||
/*expect_take_snapshot=*/profiling_enabled,
|
||||
GetParam().stable.expect_browser_sample);
|
||||
StartHeapProfiling(version_info::Channel::UNKNOWN,
|
||||
ProfilerProcessType::kBrowser, profiling_enabled,
|
||||
callbacks.first_snapshot_callback(),
|
||||
StartHeapProfiling(version_info::Channel::UNKNOWN, ProcessType::kBrowser,
|
||||
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||
callbacks.collector_callback());
|
||||
histogram_tester_.ExpectUniqueSample(
|
||||
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
||||
@@ -1026,12 +1054,22 @@ TEST_P(HeapProfilerControllerChannelTest, UnknownChannel) {
|
||||
constexpr FeatureTestParams kProcessConfigs[] = {
|
||||
// Enabled in parent process only.
|
||||
{
|
||||
.supported_processes = {ProcessType::kBrowser},
|
||||
.stable = {.expect_browser_sample = true, .expect_child_sample = false},
|
||||
},
|
||||
// Enabled in child process only.
|
||||
// Central control only samples child processes when the browser process is
|
||||
// sampled, so no samples are expected even though sampling is supported in
|
||||
// the child process.
|
||||
{
|
||||
.supported_processes = {ProcessType::kUtility},
|
||||
.stable = {.expect_browser_sample = false,
|
||||
.expect_child_sample = false},
|
||||
},
|
||||
// Enabled in parent and child processes.
|
||||
{
|
||||
.supported_processes = {ProcessType::kBrowser, ProcessType::kUtility},
|
||||
.stable = {.expect_browser_sample = true, .expect_child_sample = true},
|
||||
.utility_snapshot_prob = 100,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1042,10 +1080,11 @@ INSTANTIATE_TEST_SUITE_P(All,
|
||||
::testing::ValuesIn(kProcessConfigs));
|
||||
|
||||
TEST_P(HeapProfilerControllerProcessTest, BrowserProcess) {
|
||||
// This test always enables profiling in the browser process. (Disabled is
|
||||
// tested in HeapProfilerControllerChannelTest.)
|
||||
const bool profiling_enabled =
|
||||
base::Contains(GetParam().supported_processes, ProcessType::kBrowser);
|
||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||
/*expect_take_snapshot=*/true, GetParam().stable.expect_browser_sample,
|
||||
/*expect_take_snapshot=*/profiling_enabled,
|
||||
GetParam().stable.expect_browser_sample,
|
||||
/*use_other_process_callback=*/GetParam().stable.expect_child_sample);
|
||||
|
||||
// Mock the child end of the SnapshotController mojo pipe. (Only used when
|
||||
@@ -1054,27 +1093,31 @@ TEST_P(HeapProfilerControllerProcessTest, BrowserProcess) {
|
||||
mojo::Receiver<mojom::SnapshotController> mock_receiver(
|
||||
&mock_child_snapshot_controller);
|
||||
|
||||
StartHeapProfiling(
|
||||
version_info::Channel::STABLE, ProfilerProcessType::kBrowser,
|
||||
/*expect_enabled=*/true, callbacks.first_snapshot_callback(),
|
||||
callbacks.collector_callback());
|
||||
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||
callbacks.collector_callback());
|
||||
histogram_tester_.ExpectUniqueSample(
|
||||
"HeapProfiling.InProcess.Enabled.Browser", true, 1);
|
||||
histogram_tester_.ExpectUniqueSample("HeapProfiling.InProcess.Enabled", true,
|
||||
1);
|
||||
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
||||
histogram_tester_.ExpectUniqueSample("HeapProfiling.InProcess.Enabled",
|
||||
profiling_enabled, 1);
|
||||
|
||||
constexpr int kTestChildProcessId = 1;
|
||||
ASSERT_TRUE(controller_->GetBrowserProcessSnapshotController());
|
||||
if (profiling_enabled) {
|
||||
ASSERT_TRUE(controller_->GetBrowserProcessSnapshotController());
|
||||
|
||||
// This callback should be invoked from AppendCommandLineSwitchForChildProcess
|
||||
// to bind the child end of the mojo pipe.
|
||||
controller_->GetBrowserProcessSnapshotController()
|
||||
->SetBindRemoteForChildProcessCallback(base::BindLambdaForTesting(
|
||||
[&](int child_process_id,
|
||||
mojo::PendingReceiver<mojom::SnapshotController> receiver) {
|
||||
EXPECT_EQ(child_process_id, kTestChildProcessId);
|
||||
mock_receiver.Bind(std::move(receiver));
|
||||
}));
|
||||
// This callback should be invoked from
|
||||
// AppendCommandLineSwitchForChildProcess to bind the child end of the mojo
|
||||
// pipe.
|
||||
controller_->GetBrowserProcessSnapshotController()
|
||||
->SetBindRemoteForChildProcessCallback(base::BindLambdaForTesting(
|
||||
[&](int child_process_id,
|
||||
mojo::PendingReceiver<mojom::SnapshotController> receiver) {
|
||||
EXPECT_EQ(child_process_id, kTestChildProcessId);
|
||||
mock_receiver.Bind(std::move(receiver));
|
||||
}));
|
||||
} else {
|
||||
EXPECT_FALSE(controller_->GetBrowserProcessSnapshotController());
|
||||
}
|
||||
|
||||
// Simulate a child process launch. If profiling is enabled in both browser
|
||||
// and child processes, this will bind the browser end of the mojo pipe to the
|
||||
@@ -1082,7 +1125,7 @@ TEST_P(HeapProfilerControllerProcessTest, BrowserProcess) {
|
||||
// child end to `mock_child_snapshot_controller`.
|
||||
base::CommandLine child_command_line(base::CommandLine::NO_PROGRAM);
|
||||
controller_->AppendCommandLineSwitchForChildProcess(
|
||||
&child_command_line, ProfilerProcessType::kUtility, kTestChildProcessId);
|
||||
&child_command_line, ProcessType::kUtility, kTestChildProcessId);
|
||||
|
||||
if (GetParam().stable.expect_child_sample) {
|
||||
EXPECT_CALL(mock_child_snapshot_controller, TakeSnapshot(100, 0))
|
||||
@@ -1100,12 +1143,12 @@ TEST_P(HeapProfilerControllerProcessTest, BrowserProcess) {
|
||||
}
|
||||
|
||||
TEST_P(HeapProfilerControllerProcessTest, ChildProcess) {
|
||||
// TakeSnapshot() is called in the child process when the browser process
|
||||
// triggers it, which always happens in this test when `utility_snapshot_prob`
|
||||
// > 0.
|
||||
const bool profiling_enabled = GetParam().utility_snapshot_prob > 0;
|
||||
const bool profiling_enabled =
|
||||
base::Contains(GetParam().supported_processes, ProcessType::kUtility);
|
||||
// TakeSnapshot() is only called in the child process when the browser process
|
||||
// triggers it.
|
||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||
/*expect_take_snapshot=*/profiling_enabled,
|
||||
/*expect_take_snapshot=*/GetParam().stable.expect_child_sample,
|
||||
/*expect_sampled_profile=*/GetParam().stable.expect_child_sample,
|
||||
/*use_other_process_callback=*/true);
|
||||
|
||||
@@ -1130,22 +1173,25 @@ TEST_P(HeapProfilerControllerProcessTest, ChildProcess) {
|
||||
|
||||
base::test::ScopedCommandLine scoped_command_line;
|
||||
HeapProfilerController::AppendCommandLineSwitchForTesting(
|
||||
scoped_command_line.GetProcessCommandLine(),
|
||||
ProfilerProcessType::kUtility, kTestChildProcessId,
|
||||
fake_browser_snapshot_controller.get());
|
||||
scoped_command_line.GetProcessCommandLine(), ProcessType::kUtility,
|
||||
kTestChildProcessId, fake_browser_snapshot_controller.get());
|
||||
|
||||
// Simulate the browser process taking a sample after a delay.
|
||||
// Simulate the browser process taking a sample after a delay. If profiling
|
||||
// isn't enabled in the browser process, just quit waiting after the delay.
|
||||
base::OnceClosure browser_snapshot_callback = base::DoNothing();
|
||||
if (base::Contains(GetParam().supported_processes, ProcessType::kBrowser)) {
|
||||
browser_snapshot_callback = base::BindOnce(
|
||||
&BrowserProcessSnapshotController::TakeSnapshotsOnSnapshotSequence,
|
||||
std::move(fake_browser_snapshot_controller));
|
||||
}
|
||||
snapshot_task_runner->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
&BrowserProcessSnapshotController::TakeSnapshotsOnSnapshotSequence,
|
||||
std::move(fake_browser_snapshot_controller))
|
||||
std::move(browser_snapshot_callback)
|
||||
.Then(callbacks.other_process_callback()),
|
||||
TestTimeouts::action_timeout());
|
||||
|
||||
StartHeapProfiling(version_info::Channel::STABLE,
|
||||
ProfilerProcessType::kUtility, profiling_enabled,
|
||||
callbacks.first_snapshot_callback(),
|
||||
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kUtility,
|
||||
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||
callbacks.collector_callback());
|
||||
histogram_tester_.ExpectUniqueSample(
|
||||
"HeapProfiling.InProcess.Enabled.Utility", profiling_enabled, 1);
|
||||
@@ -1189,8 +1235,8 @@ auto GetProfileMetadataFunc(std::string_view name) {
|
||||
// End-to-end test with multiple child processes.
|
||||
constexpr FeatureTestParams kMultipleChildConfigs[] = {
|
||||
{
|
||||
.gpu_snapshot_prob = 100,
|
||||
.network_snapshot_prob = 100,
|
||||
.supported_processes = {ProcessType::kBrowser, ProcessType::kGpu,
|
||||
ProcessType::kUtility, ProcessType::kRenderer},
|
||||
.renderer_snapshot_prob = 66,
|
||||
.utility_snapshot_prob = 50,
|
||||
},
|
||||
@@ -1217,19 +1263,19 @@ TEST_P(HeapProfilerControllerMultipleChildTest, EndToEnd) {
|
||||
|
||||
// Process types to test. Each will make a different
|
||||
// number of memory allocations so their reports are all different.
|
||||
const std::vector<std::pair<ProfilerProcessType, size_t>> kProcessesToTest{
|
||||
{ProfilerProcessType::kBrowser, 0},
|
||||
{ProfilerProcessType::kGpu, 1},
|
||||
const std::vector<std::pair<ProcessType, size_t>> kProcessesToTest{
|
||||
{ProcessType::kBrowser, 0},
|
||||
{ProcessType::kGpu, 1},
|
||||
// 2 utility processes.
|
||||
{ProfilerProcessType::kUtility, 2},
|
||||
{ProfilerProcessType::kUtility, 3},
|
||||
{ProcessType::kUtility, 2},
|
||||
{ProcessType::kUtility, 3},
|
||||
// 5 renderer processes including one with no samples. The first one will
|
||||
// be ignored to simulate the embedder refusing to profile it.
|
||||
{ProfilerProcessType::kRenderer, 10},
|
||||
{ProfilerProcessType::kRenderer, 0},
|
||||
{ProfilerProcessType::kRenderer, 4},
|
||||
{ProfilerProcessType::kRenderer, 5},
|
||||
{ProfilerProcessType::kRenderer, 6},
|
||||
{ProcessType::kRenderer, 10},
|
||||
{ProcessType::kRenderer, 0},
|
||||
{ProcessType::kRenderer, 4},
|
||||
{ProcessType::kRenderer, 5},
|
||||
{ProcessType::kRenderer, 6},
|
||||
};
|
||||
|
||||
// Expect only 1 utility process and 3 renderer processes to be sampled due
|
||||
@@ -1257,10 +1303,10 @@ TEST_P(HeapProfilerControllerMultipleChildTest, EndToEnd) {
|
||||
task_runner->DeleteSoon(FROM_HERE, controller_.release());
|
||||
}));
|
||||
|
||||
CreateHeapProfiler(
|
||||
version_info::Channel::STABLE, ProfilerProcessType::kBrowser,
|
||||
/*expect_enabled=*/true, std::move(stop_after_first_snapshot_callback),
|
||||
callbacks.collector_callback());
|
||||
CreateHeapProfiler(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||
/*expect_enabled=*/true,
|
||||
std::move(stop_after_first_snapshot_callback),
|
||||
callbacks.collector_callback());
|
||||
ASSERT_TRUE(controller_);
|
||||
|
||||
// Start all processes in `kProcessesToTest` except the browser.
|
||||
@@ -1286,11 +1332,10 @@ TEST_P(HeapProfilerControllerMultipleChildTest, EndToEnd) {
|
||||
|
||||
bool renderer_was_skipped = false;
|
||||
for (const auto [process_type, num_allocations] : kProcessesToTest) {
|
||||
if (process_type != ProfilerProcessType::kBrowser) {
|
||||
if (process_type != ProcessType::kBrowser) {
|
||||
// Skip the first renderer.
|
||||
bool should_profile = true;
|
||||
if (process_type == ProfilerProcessType::kRenderer &&
|
||||
!renderer_was_skipped) {
|
||||
if (process_type == ProcessType::kRenderer && !renderer_was_skipped) {
|
||||
should_profile = false;
|
||||
renderer_was_skipped = true;
|
||||
}
|
||||
|
@@ -4,14 +4,19 @@
|
||||
|
||||
#include "components/heap_profiling/in_process/heap_profiler_parameters.h"
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/json/json_value_converter.h"
|
||||
#include "base/metrics/field_trial_params.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/sampling_profiler/process_type.h"
|
||||
#include "components/variations/variations_switches.h"
|
||||
|
||||
namespace heap_profiling {
|
||||
|
||||
@@ -49,60 +54,65 @@ constexpr double kDefaultStableProbability = 0.01;
|
||||
// provider if it's on a non-stable channel.
|
||||
constexpr double kDefaultNonStableProbability = 0.5;
|
||||
|
||||
// The probability of including a child process in each snapshot that's taken,
|
||||
// as a percentage from 0 to 100. Defaults to 100, but can be set lower to
|
||||
// sub-sample process types that are very common (mainly renderers) to keep data
|
||||
// volume low. Samples from child processes are weighted in inverse proportion
|
||||
// to the snapshot probability to normalize the aggregated results. Set to 0 to
|
||||
// disable sampling a process completely.
|
||||
constexpr HeapProfilerParameters kDefaultHeapProfilerParameters{
|
||||
.is_supported = false,
|
||||
// If a process overrides `is_supported`, use the following defaults.
|
||||
.stable_probability = kDefaultStableProbability,
|
||||
.nonstable_probability = kDefaultNonStableProbability,
|
||||
.sampling_rate_bytes = kDefaultSamplingRateBytes,
|
||||
.collection_interval = kDefaultCollectionInterval,
|
||||
};
|
||||
|
||||
constexpr base::FeatureParam<int> kGpuSnapshotProbability{
|
||||
&kHeapProfilerReporting, "gpu-prob-pct", 100};
|
||||
// Feature parameters.
|
||||
|
||||
constexpr base::FeatureParam<int> kNetworkSnapshotProbability{
|
||||
&kHeapProfilerReporting, "network-prob-pct", 100};
|
||||
// JSON-encoded parameter map that will set the default parameters for the
|
||||
// heap profiler unless overridden by the process-specific parameters below.
|
||||
constexpr base::FeatureParam<std::string> kDefaultParameters{
|
||||
&kHeapProfilerReporting, "default-params", ""};
|
||||
|
||||
// Sample 10% of renderer processes by default, because last time this was
|
||||
// evaluated (2024-08) the 50th %ile of renderer process count
|
||||
// (Memory.RenderProcessHost.Count.All) ranged from 8 on Windows to 18 on Mac.
|
||||
// 10% is an easy default between 1/18 and 1/8.
|
||||
constexpr base::FeatureParam<int> kRendererSnapshotProbability{
|
||||
&kHeapProfilerReporting, "renderer-prob-pct", 10};
|
||||
// JSON-encoded parameter map that will override the default parameters for the
|
||||
// browser process.
|
||||
constexpr base::FeatureParam<std::string> kBrowserProcessParameters{
|
||||
&kHeapProfilerReporting, "browser-process-params", ""};
|
||||
|
||||
// Sample 50% of utility processes by default, because last time this was
|
||||
// evaluated (2024-08) the profiler collected 1.8x as many snapshots on Mac and
|
||||
// 2.4x as many snapshots on Windows for each browser process snapshot.
|
||||
constexpr base::FeatureParam<int> kUtilitySnapshotProbability{
|
||||
&kHeapProfilerReporting, "utility-prob-pct", 50};
|
||||
// JSON-encoded parameter map that will override the default parameters for
|
||||
// renderer processes.
|
||||
constexpr base::FeatureParam<std::string> kRendererProcessParameters{
|
||||
&kHeapProfilerReporting, "renderer-process-params", ""};
|
||||
|
||||
// The sampling rates of each process type, in bytes.
|
||||
// JSON-encoded parameter map that will override the default parameters for the
|
||||
// GPU process.
|
||||
constexpr base::FeatureParam<std::string> kGPUProcessParameters{
|
||||
&kHeapProfilerReporting, "gpu-process-params", ""};
|
||||
|
||||
constexpr base::FeatureParam<int> kBrowserSamplingRateBytes{
|
||||
&kHeapProfilerReporting, "browser-sampling-rate-bytes",
|
||||
kDefaultSamplingRateBytes};
|
||||
// JSON-encoded parameter map that will override the default parameters for
|
||||
// utility processes.
|
||||
constexpr base::FeatureParam<std::string> kUtilityProcessParameters{
|
||||
&kHeapProfilerReporting, "utility-process-params", ""};
|
||||
|
||||
// Use half the threshold used in the browser process, because last time it was
|
||||
// validated the GPU process allocated a bit over half as much memory at the
|
||||
// median.
|
||||
constexpr base::FeatureParam<int> kGpuSamplingRateBytes{
|
||||
&kHeapProfilerReporting, "gpu-sampling-rate-bytes",
|
||||
kDefaultSamplingRateBytes / 2};
|
||||
// JSON-encoded parameter map that will override the default parameters for the
|
||||
// network process.
|
||||
constexpr base::FeatureParam<std::string> kNetworkProcessParameters{
|
||||
&kHeapProfilerReporting, "network-process-params", ""};
|
||||
|
||||
constexpr base::FeatureParam<int> kNetworkSamplingRateBytes{
|
||||
&kHeapProfilerReporting, "network-sampling-rate-bytes",
|
||||
kDefaultSamplingRateBytes};
|
||||
|
||||
constexpr base::FeatureParam<int> kRendererSamplingRateBytes{
|
||||
&kHeapProfilerReporting, "renderer-sampling-rate-bytes",
|
||||
kDefaultSamplingRateBytes};
|
||||
|
||||
// Use 1/10th the threshold used in the browser process, because last time it
|
||||
// was validated with the default sampling rate (2024-08) the sampler collected
|
||||
// 6% to 11% as many samples per snapshot in the utility process, depending on
|
||||
// platform.
|
||||
constexpr base::FeatureParam<int> kUtilitySamplingRateBytes{
|
||||
&kHeapProfilerReporting, "utility-sampling-rate-bytes",
|
||||
kDefaultSamplingRateBytes / 10};
|
||||
// Interprets `value` as a positive number of minutes, and writes the converted
|
||||
// value to `result`. If `value` contains anything other than a positive
|
||||
// integer, returns false to indicate a conversion failure.
|
||||
bool ConvertCollectionInterval(const base::Value* value,
|
||||
base::TimeDelta* result) {
|
||||
if (!value) {
|
||||
// Missing values are ok, so report success without updating `result`.
|
||||
return true;
|
||||
}
|
||||
if (value->is_int()) {
|
||||
const int minutes = value->GetInt();
|
||||
if (minutes > 0) {
|
||||
*result = base::Minutes(minutes);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -110,65 +120,141 @@ BASE_FEATURE(kHeapProfilerReporting,
|
||||
"HeapProfilerReporting",
|
||||
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||
|
||||
const base::FeatureParam<double> kStableProbability{
|
||||
&kHeapProfilerReporting, "stable-probability", kDefaultStableProbability};
|
||||
// TODO(crbug.com/40840943): The process sampling probabilities are separate
|
||||
// FeatureParams, but other per-process parameters are parsed from a JSON string
|
||||
// in a single FeatureParam. The JSON parameters are too complicated: split them
|
||||
// up into separate FeatureParams.
|
||||
|
||||
const base::FeatureParam<double> kNonStableProbability{
|
||||
&kHeapProfilerReporting, "nonstable-probability",
|
||||
kDefaultNonStableProbability};
|
||||
const base::FeatureParam<int> kGpuSnapshotProbability{&kHeapProfilerReporting,
|
||||
"gpu-prob-pct", 100};
|
||||
|
||||
const base::FeatureParam<base::TimeDelta> kCollectionInterval{
|
||||
&kHeapProfilerReporting, "collection-interval", kDefaultCollectionInterval};
|
||||
const base::FeatureParam<int> kNetworkSnapshotProbability{
|
||||
&kHeapProfilerReporting, "network-prob-pct", 100};
|
||||
|
||||
size_t GetSamplingRateForProcess(
|
||||
sampling_profiler::ProfilerProcessType process_type) {
|
||||
int sampling_rate_bytes;
|
||||
switch (process_type) {
|
||||
case sampling_profiler::ProfilerProcessType::kBrowser:
|
||||
sampling_rate_bytes = kBrowserSamplingRateBytes.Get();
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kRenderer:
|
||||
sampling_rate_bytes = kRendererSamplingRateBytes.Get();
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kGpu:
|
||||
sampling_rate_bytes = kGpuSamplingRateBytes.Get();
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kUtility:
|
||||
sampling_rate_bytes = kUtilitySamplingRateBytes.Get();
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kNetworkService:
|
||||
sampling_rate_bytes = kNetworkSamplingRateBytes.Get();
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kUnknown:
|
||||
default:
|
||||
// Profiler should not be enabled for these process types.
|
||||
NOTREACHED();
|
||||
}
|
||||
return base::saturated_cast<size_t>(sampling_rate_bytes);
|
||||
// Sample 10% of renderer processes by default, because last time this was
|
||||
// evaluated (2024-08) the 50th %ile of renderer process count
|
||||
// (Memory.RenderProcessHost.Count.All) ranged from 8 on Windows to 18 on Mac.
|
||||
// 10% is an easy default between 1/18 and 1/8.
|
||||
const base::FeatureParam<int> kRendererSnapshotProbability{
|
||||
&kHeapProfilerReporting, "renderer-prob-pct", 10};
|
||||
|
||||
// Sample 50% of utility processes by default, because last time this was
|
||||
// evaluated (2024-08) the profiler collected 1.8x as many snapshots on Mac and
|
||||
// 2.4x as many snapshots on Windows for each browser process snapshot.
|
||||
const base::FeatureParam<int> kUtilitySnapshotProbability{
|
||||
&kHeapProfilerReporting, "utility-prob-pct", 50};
|
||||
|
||||
// static
|
||||
void HeapProfilerParameters::RegisterJSONConverter(
|
||||
base::JSONValueConverter<HeapProfilerParameters>* converter) {
|
||||
converter->RegisterBoolField("is-supported",
|
||||
&HeapProfilerParameters::is_supported);
|
||||
converter->RegisterDoubleField("stable-probability",
|
||||
&HeapProfilerParameters::stable_probability);
|
||||
converter->RegisterDoubleField(
|
||||
"nonstable-probability", &HeapProfilerParameters::nonstable_probability);
|
||||
converter->RegisterIntField("sampling-rate-bytes",
|
||||
&HeapProfilerParameters::sampling_rate_bytes);
|
||||
converter->RegisterCustomValueField(
|
||||
"collection-interval-minutes",
|
||||
&HeapProfilerParameters::collection_interval, &ConvertCollectionInterval);
|
||||
}
|
||||
|
||||
int GetSnapshotProbabilityForProcess(
|
||||
bool HeapProfilerParameters::UpdateFromJSON(std::string_view json_string) {
|
||||
if (json_string.empty())
|
||||
return true;
|
||||
|
||||
base::JSONValueConverter<HeapProfilerParameters> converter;
|
||||
std::optional<base::Value> value =
|
||||
base::JSONReader::Read(json_string, base::JSON_ALLOW_TRAILING_COMMAS |
|
||||
base::JSON_ALLOW_COMMENTS);
|
||||
if (value && converter.Convert(*value, this))
|
||||
return true;
|
||||
|
||||
// Error reading JSON params. Disable the heap sampler. This will be reported
|
||||
// when HeapProfilerController logs HeapProfiling.InProcess.Enabled.
|
||||
is_supported = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
HeapProfilerParameters GetDefaultHeapProfilerParameters() {
|
||||
HeapProfilerParameters params = kDefaultHeapProfilerParameters;
|
||||
params.UpdateFromJSON(kDefaultParameters.Get());
|
||||
return params;
|
||||
}
|
||||
|
||||
HeapProfilerParameters GetHeapProfilerParametersForProcess(
|
||||
sampling_profiler::ProfilerProcessType process_type) {
|
||||
int snapshot_probability_pct;
|
||||
using Process = sampling_profiler::ProfilerProcessType;
|
||||
|
||||
HeapProfilerParameters params = kDefaultHeapProfilerParameters;
|
||||
|
||||
// Apply per-process defaults.
|
||||
switch (process_type) {
|
||||
case sampling_profiler::ProfilerProcessType::kGpu:
|
||||
snapshot_probability_pct = kGpuSnapshotProbability.Get();
|
||||
case Process::kBrowser:
|
||||
params.is_supported = true;
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kNetworkService:
|
||||
snapshot_probability_pct = kNetworkSnapshotProbability.Get();
|
||||
case Process::kNetworkService:
|
||||
params.is_supported = true;
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kRenderer:
|
||||
snapshot_probability_pct = kRendererSnapshotProbability.Get();
|
||||
case Process::kGpu:
|
||||
params.is_supported = true;
|
||||
// Use half the threshold used in the browser process, because last time
|
||||
// it was validated the GPU process allocated a bit over half as much
|
||||
// memory at the median.
|
||||
params.sampling_rate_bytes = params.sampling_rate_bytes / 2;
|
||||
break;
|
||||
case sampling_profiler::ProfilerProcessType::kUtility:
|
||||
snapshot_probability_pct = kUtilitySnapshotProbability.Get();
|
||||
case Process::kRenderer:
|
||||
params.is_supported = true;
|
||||
break;
|
||||
case Process::kUtility:
|
||||
params.is_supported = true;
|
||||
// Use 1/10th the threshold used in the browser process, because last time
|
||||
// it was validated with the default sampling rate (2024-08) the sampler
|
||||
// collected 6% to 11% as many samples per snapshot in the utility
|
||||
// process, depending on platform.
|
||||
params.sampling_rate_bytes = params.sampling_rate_bytes / 10;
|
||||
break;
|
||||
case Process::kUnknown:
|
||||
default:
|
||||
NOTREACHED();
|
||||
// Do nothing. Profiler hasn't been tested in these process types.
|
||||
break;
|
||||
}
|
||||
CHECK_GE(snapshot_probability_pct, 0);
|
||||
CHECK_LE(snapshot_probability_pct, 100);
|
||||
return snapshot_probability_pct;
|
||||
|
||||
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
variations::switches::kEnableBenchmarking) ||
|
||||
!base::FeatureList::IsEnabled(kHeapProfilerReporting)) {
|
||||
params.is_supported = false;
|
||||
return params;
|
||||
}
|
||||
|
||||
// Override with field trial parameters if any are set.
|
||||
if (!params.UpdateFromJSON(kDefaultParameters.Get())) {
|
||||
// After an error is detected don't alter `params` further.
|
||||
return params;
|
||||
}
|
||||
switch (process_type) {
|
||||
case Process::kBrowser:
|
||||
params.UpdateFromJSON(kBrowserProcessParameters.Get());
|
||||
break;
|
||||
case Process::kRenderer:
|
||||
params.UpdateFromJSON(kRendererProcessParameters.Get());
|
||||
break;
|
||||
case Process::kGpu:
|
||||
params.UpdateFromJSON(kGPUProcessParameters.Get());
|
||||
break;
|
||||
case Process::kUtility:
|
||||
params.UpdateFromJSON(kUtilityProcessParameters.Get());
|
||||
break;
|
||||
case Process::kNetworkService:
|
||||
params.UpdateFromJSON(kNetworkProcessParameters.Get());
|
||||
break;
|
||||
case Process::kUnknown:
|
||||
default:
|
||||
// Do nothing. Profiler hasn't been tested in these process types.
|
||||
break;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
} // namespace heap_profiling
|
||||
|
@@ -5,7 +5,10 @@
|
||||
#ifndef COMPONENTS_HEAP_PROFILING_IN_PROCESS_HEAP_PROFILER_PARAMETERS_H_
|
||||
#define COMPONENTS_HEAP_PROFILING_IN_PROCESS_HEAP_PROFILER_PARAMETERS_H_
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "base/feature_list.h"
|
||||
#include "base/json/json_value_converter.h"
|
||||
#include "base/metrics/field_trial_params.h"
|
||||
#include "base/time/time.h"
|
||||
#include "components/sampling_profiler/process_type.h"
|
||||
@@ -20,24 +23,54 @@ namespace heap_profiling {
|
||||
// reporting is enabled.
|
||||
BASE_DECLARE_FEATURE(kHeapProfilerReporting);
|
||||
|
||||
// Chance that this client will report heap samples through a metrics
|
||||
// provider if it's on the stable channel.
|
||||
extern const base::FeatureParam<double> kStableProbability;
|
||||
// The probability of including a child process in each snapshot that's taken,
|
||||
// as a percentage from 0 to 100. Defaults to 100, but can be set lower to
|
||||
// sub-sample process types that are very common (mainly renderers) to keep data
|
||||
// volume low. Samples from child processes are weighted in inverse proportion
|
||||
// to the snapshot probability to normalize the aggregated results.
|
||||
extern const base::FeatureParam<int> kGpuSnapshotProbability;
|
||||
extern const base::FeatureParam<int> kNetworkSnapshotProbability;
|
||||
extern const base::FeatureParam<int> kRendererSnapshotProbability;
|
||||
extern const base::FeatureParam<int> kUtilitySnapshotProbability;
|
||||
|
||||
// Chance that this client will report heap samples through a metrics
|
||||
// provider if it's on a non-stable channel.
|
||||
extern const base::FeatureParam<double> kNonStableProbability;
|
||||
// Parameters to control the heap profiler.
|
||||
struct HeapProfilerParameters {
|
||||
// True if heap profiling is supported, false otherwise.
|
||||
bool is_supported = false;
|
||||
|
||||
// Mean time between snapshots.
|
||||
extern const base::FeatureParam<base::TimeDelta> kCollectionInterval;
|
||||
// Chance that this client will report heap samples through a metrics
|
||||
// provider if it's on the stable channel.
|
||||
double stable_probability = 0.0;
|
||||
|
||||
// Returns the sampling rate in bytes to use for `process_type`.
|
||||
size_t GetSamplingRateForProcess(
|
||||
sampling_profiler::ProfilerProcessType process_type);
|
||||
// Chance that this client will report heap samples through a metrics
|
||||
// provider if it's on a non-stable channel.
|
||||
double nonstable_probability = 0.0;
|
||||
|
||||
// Returns the probability of sampling a `process_type` process in each
|
||||
// snapshot, from 0 to 100.
|
||||
int GetSnapshotProbabilityForProcess(
|
||||
// Mean heap sampling interval in bytes.
|
||||
int sampling_rate_bytes = 0;
|
||||
|
||||
// Mean time between snapshots.
|
||||
base::TimeDelta collection_interval;
|
||||
|
||||
// Invoked by JSONValueConverter to parse parameters from JSON.
|
||||
static void RegisterJSONConverter(
|
||||
base::JSONValueConverter<HeapProfilerParameters>* converter);
|
||||
|
||||
// Overwrites this object's fields with parameters parsed from `json_string`.
|
||||
// Missing parameters will not be touched. If parsing fails, returns false and
|
||||
// sets `is_supported` to false to ensure heap profiling doesn't run with
|
||||
// invalid parameters.
|
||||
bool UpdateFromJSON(std::string_view json_string);
|
||||
};
|
||||
|
||||
// Returns a default set of parameters to use if not overridden for a
|
||||
// specific process.
|
||||
HeapProfilerParameters GetDefaultHeapProfilerParameters();
|
||||
|
||||
// Returns the set of process parameters to use for `process_type`. This will be
|
||||
// identical to the result of GetDefaultHeapProfilerParameters() unless
|
||||
// overridden by a field trial.
|
||||
HeapProfilerParameters GetHeapProfilerParametersForProcess(
|
||||
sampling_profiler::ProfilerProcessType process_type);
|
||||
|
||||
} // namespace heap_profiling
|
||||
|
@@ -0,0 +1,233 @@
|
||||
// Copyright 2022 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/heap_profiler_parameters.h"
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/time/time.h"
|
||||
#include "components/sampling_profiler/process_type.h"
|
||||
#include "components/variations/variations_switches.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace heap_profiling {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::testing::AllOf;
|
||||
using ::testing::Field;
|
||||
|
||||
// Can't define operator== because gmock has a conflicting operator== in an
|
||||
// internal namespace.
|
||||
auto MatchesParameters(const HeapProfilerParameters& expected) {
|
||||
return AllOf(
|
||||
Field("is_supported", &HeapProfilerParameters::is_supported,
|
||||
expected.is_supported),
|
||||
Field("stable_probability", &HeapProfilerParameters::stable_probability,
|
||||
expected.stable_probability),
|
||||
Field("nonstable_probability",
|
||||
&HeapProfilerParameters::nonstable_probability,
|
||||
expected.nonstable_probability),
|
||||
Field("sampling_rate_bytes", &HeapProfilerParameters::sampling_rate_bytes,
|
||||
expected.sampling_rate_bytes),
|
||||
Field("collection_interval", &HeapProfilerParameters::collection_interval,
|
||||
expected.collection_interval));
|
||||
}
|
||||
|
||||
TEST(HeapProfilerParametersTest, ParseEmptyParameters) {
|
||||
constexpr char kJSONParams[] = R"({})";
|
||||
HeapProfilerParameters params;
|
||||
EXPECT_TRUE(params.UpdateFromJSON(kJSONParams));
|
||||
EXPECT_THAT(params, MatchesParameters({}));
|
||||
}
|
||||
|
||||
TEST(HeapProfilerParametersTest, ParseParameters) {
|
||||
constexpr char kJSONParams[] = R"({
|
||||
"is-supported": true,
|
||||
"stable-probability": 0.1,
|
||||
// Comments should be allowed.
|
||||
// Double parameters should convert from integers.
|
||||
"nonstable-probability": 1,
|
||||
"sampling-rate-bytes": 1000,
|
||||
"collection-interval-minutes": 30,
|
||||
})";
|
||||
HeapProfilerParameters params;
|
||||
EXPECT_TRUE(params.UpdateFromJSON(kJSONParams));
|
||||
EXPECT_THAT(params, MatchesParameters({
|
||||
.is_supported = true,
|
||||
.stable_probability = 0.1,
|
||||
.nonstable_probability = 1.0,
|
||||
.sampling_rate_bytes = 1000,
|
||||
.collection_interval = base::Minutes(30),
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(HeapProfilerParametersTest, ParsePartialParameters) {
|
||||
constexpr char kJSONParams[] = R"({
|
||||
"is-supported": false,
|
||||
"stable-probability": 0.5,
|
||||
"collection-interval-minutes": 60,
|
||||
})";
|
||||
// Only the parameters that are included in the JSON should be overwritten.
|
||||
HeapProfilerParameters params{
|
||||
.is_supported = true,
|
||||
.stable_probability = 0.1,
|
||||
.nonstable_probability = 0.2,
|
||||
.sampling_rate_bytes = 1000,
|
||||
.collection_interval = base::Minutes(30),
|
||||
};
|
||||
EXPECT_TRUE(params.UpdateFromJSON(kJSONParams));
|
||||
EXPECT_THAT(params, MatchesParameters({
|
||||
.is_supported = false,
|
||||
.stable_probability = 0.5,
|
||||
.nonstable_probability = 0.2,
|
||||
.sampling_rate_bytes = 1000,
|
||||
.collection_interval = base::Minutes(60),
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(HeapProfilerParametersTest, ParseInvalidParameters) {
|
||||
constexpr char kJSONParams[] = R"({
|
||||
"collection-interval-minutes": -1,
|
||||
})";
|
||||
HeapProfilerParameters params;
|
||||
EXPECT_FALSE(params.UpdateFromJSON(kJSONParams));
|
||||
EXPECT_FALSE(params.is_supported);
|
||||
}
|
||||
|
||||
// Test that heap profiling is not supported for any process type when
|
||||
// --enable-benchmarking is specified on the command line.
|
||||
TEST(HeapProfilerParametersTest, EnableBenchmarking) {
|
||||
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
||||
variations::switches::kEnableBenchmarking);
|
||||
|
||||
using Process = sampling_profiler::ProfilerProcessType;
|
||||
EXPECT_FALSE(GetDefaultHeapProfilerParameters().is_supported);
|
||||
EXPECT_FALSE(
|
||||
GetHeapProfilerParametersForProcess(Process::kBrowser).is_supported);
|
||||
EXPECT_FALSE(GetHeapProfilerParametersForProcess(Process::kGpu).is_supported);
|
||||
EXPECT_FALSE(
|
||||
GetHeapProfilerParametersForProcess(Process::kRenderer).is_supported);
|
||||
EXPECT_FALSE(
|
||||
GetHeapProfilerParametersForProcess(Process::kUtility).is_supported);
|
||||
EXPECT_FALSE(GetHeapProfilerParametersForProcess(Process::kNetworkService)
|
||||
.is_supported);
|
||||
}
|
||||
|
||||
TEST(HeapProfilerParametersTest, ApplyParameters) {
|
||||
constexpr char kDefaultParams[] = R"({
|
||||
"is-supported": false,
|
||||
"stable-probability": 0.1,
|
||||
"nonstable-probability": 0.2,
|
||||
"sampling-rate-bytes": 1000,
|
||||
"collection-interval-minutes": 15,
|
||||
})";
|
||||
constexpr char kBrowserParams[] = R"({
|
||||
"sampling-rate-bytes": 1001,
|
||||
})";
|
||||
constexpr char kGPUParams[] = R"({
|
||||
"is-supported": true,
|
||||
"sampling-rate-bytes": 1002,
|
||||
"collection-interval-minutes": 60,
|
||||
})";
|
||||
constexpr char kRendererParams[] = R"({
|
||||
"is-supported": false,
|
||||
"sampling-rate-bytes": 1003,
|
||||
})";
|
||||
|
||||
base::test::ScopedFeatureList feature_list;
|
||||
feature_list.InitAndEnableFeatureWithParameters(
|
||||
kHeapProfilerReporting, {
|
||||
{"default-params", kDefaultParams},
|
||||
{"browser-process-params", kBrowserParams},
|
||||
{"gpu-process-params", kGPUParams},
|
||||
{"renderer-process-params", kRendererParams},
|
||||
{"utility-process-params", "{}"},
|
||||
});
|
||||
|
||||
EXPECT_THAT(GetDefaultHeapProfilerParameters(),
|
||||
MatchesParameters({
|
||||
.is_supported = false,
|
||||
.stable_probability = 0.1,
|
||||
.nonstable_probability = 0.2,
|
||||
.sampling_rate_bytes = 1000,
|
||||
.collection_interval = base::Minutes(15),
|
||||
}));
|
||||
|
||||
using Process = sampling_profiler::ProfilerProcessType;
|
||||
EXPECT_THAT(GetHeapProfilerParametersForProcess(Process::kBrowser),
|
||||
MatchesParameters({
|
||||
.is_supported = false,
|
||||
.stable_probability = 0.1,
|
||||
.nonstable_probability = 0.2,
|
||||
.sampling_rate_bytes = 1001,
|
||||
.collection_interval = base::Minutes(15),
|
||||
}));
|
||||
|
||||
EXPECT_THAT(GetHeapProfilerParametersForProcess(Process::kGpu),
|
||||
MatchesParameters({
|
||||
.is_supported = true,
|
||||
.stable_probability = 0.1,
|
||||
.nonstable_probability = 0.2,
|
||||
.sampling_rate_bytes = 1002,
|
||||
.collection_interval = base::Minutes(60),
|
||||
}));
|
||||
|
||||
EXPECT_THAT(GetHeapProfilerParametersForProcess(Process::kRenderer),
|
||||
MatchesParameters({
|
||||
.is_supported = false,
|
||||
.stable_probability = 0.1,
|
||||
.nonstable_probability = 0.2,
|
||||
.sampling_rate_bytes = 1003,
|
||||
.collection_interval = base::Minutes(15),
|
||||
}));
|
||||
|
||||
EXPECT_THAT(GetHeapProfilerParametersForProcess(Process::kUtility),
|
||||
MatchesParameters({
|
||||
.is_supported = false,
|
||||
.stable_probability = 0.1,
|
||||
.nonstable_probability = 0.2,
|
||||
.sampling_rate_bytes = 1000,
|
||||
.collection_interval = base::Minutes(15),
|
||||
}));
|
||||
|
||||
EXPECT_THAT(GetHeapProfilerParametersForProcess(Process::kNetworkService),
|
||||
MatchesParameters({
|
||||
.is_supported = false,
|
||||
.stable_probability = 0.1,
|
||||
.nonstable_probability = 0.2,
|
||||
.sampling_rate_bytes = 1000,
|
||||
.collection_interval = base::Minutes(15),
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(HeapProfilerParametersTest, ApplyInvalidParameters) {
|
||||
constexpr char kDefaultParams[] = R"({
|
||||
"is-supported": true,
|
||||
"collection-interval-minutes": "unexpected string",
|
||||
})";
|
||||
// Ensure that valid per-process params don't overwrite invalid default
|
||||
// params.
|
||||
constexpr char kBrowserParams[] = R"({
|
||||
"is-supported": true,
|
||||
"collection-interval-minutes": 1,
|
||||
})";
|
||||
|
||||
base::test::ScopedFeatureList feature_list;
|
||||
feature_list.InitAndEnableFeatureWithParameters(
|
||||
kHeapProfilerReporting, {
|
||||
{"default-params", kDefaultParams},
|
||||
{"browser-process-params", kBrowserParams},
|
||||
});
|
||||
|
||||
EXPECT_FALSE(GetDefaultHeapProfilerParameters().is_supported);
|
||||
EXPECT_FALSE(GetHeapProfilerParametersForProcess(
|
||||
sampling_profiler::ProfilerProcessType::kBrowser)
|
||||
.is_supported);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace heap_profiling
|
Reference in New Issue
Block a user