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
|
# HeapProfilerController's dependencies are not compiled on iOS unless
|
||||||
# use_allocator_shim is true.
|
# use_allocator_shim is true.
|
||||||
if (!is_ios || use_allocator_shim) {
|
if (!is_ios || use_allocator_shim) {
|
||||||
sources = [ "heap_profiler_controller_unittest.cc" ]
|
sources = [
|
||||||
|
"heap_profiler_controller_unittest.cc",
|
||||||
|
"heap_profiler_parameters_unittest.cc",
|
||||||
|
]
|
||||||
deps = [
|
deps = [
|
||||||
":in_process",
|
":in_process",
|
||||||
":mojom",
|
":mojom",
|
||||||
|
@@ -7,12 +7,14 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/check.h"
|
#include "base/check_op.h"
|
||||||
#include "base/containers/flat_map.h"
|
#include "base/containers/flat_map.h"
|
||||||
#include "base/functional/bind.h"
|
#include "base/functional/bind.h"
|
||||||
#include "base/functional/callback.h"
|
#include "base/functional/callback.h"
|
||||||
#include "base/memory/scoped_refptr.h"
|
#include "base/memory/scoped_refptr.h"
|
||||||
#include "base/memory/weak_ptr.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/rand_util.h"
|
||||||
#include "base/sequence_checker.h"
|
#include "base/sequence_checker.h"
|
||||||
#include "base/task/sequenced_task_runner.h"
|
#include "base/task/sequenced_task_runner.h"
|
||||||
@@ -97,8 +99,25 @@ void BrowserProcessSnapshotController::TakeSnapshotsOnSnapshotSequence() {
|
|||||||
// processes to measure.
|
// processes to measure.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const int snapshot_probability_pct =
|
int snapshot_probability_pct;
|
||||||
GetSnapshotProbabilityForProcess(process_type);
|
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) {
|
if (snapshot_probability_pct == 0) {
|
||||||
// No need to test each process since none will be chosen.
|
// No need to test each process since none will be chosen.
|
||||||
continue;
|
continue;
|
||||||
|
@@ -17,12 +17,10 @@
|
|||||||
#include "base/allocator/dispatcher/reentry_guard.h"
|
#include "base/allocator/dispatcher/reentry_guard.h"
|
||||||
#include "base/check_op.h"
|
#include "base/check_op.h"
|
||||||
#include "base/command_line.h"
|
#include "base/command_line.h"
|
||||||
#include "base/feature_list.h"
|
|
||||||
#include "base/functional/bind.h"
|
#include "base/functional/bind.h"
|
||||||
#include "base/functional/callback.h"
|
#include "base/functional/callback.h"
|
||||||
#include "base/memory/scoped_refptr.h"
|
#include "base/memory/scoped_refptr.h"
|
||||||
#include "base/memory/weak_ptr.h"
|
#include "base/memory/weak_ptr.h"
|
||||||
#include "base/metrics/field_trial_params.h"
|
|
||||||
#include "base/metrics/histogram_functions.h"
|
#include "base/metrics/histogram_functions.h"
|
||||||
#include "base/metrics/metrics_hashes.h"
|
#include "base/metrics/metrics_hashes.h"
|
||||||
#include "base/notreached.h"
|
#include "base/notreached.h"
|
||||||
@@ -45,7 +43,6 @@
|
|||||||
#include "components/metrics/call_stacks/call_stack_profile_builder.h"
|
#include "components/metrics/call_stacks/call_stack_profile_builder.h"
|
||||||
#include "components/sampling_profiler/process_type.h"
|
#include "components/sampling_profiler/process_type.h"
|
||||||
#include "components/services/heap_profiling/public/cpp/merge_samples.h"
|
#include "components/services/heap_profiling/public/cpp/merge_samples.h"
|
||||||
#include "components/variations/variations_switches.h"
|
|
||||||
#include "components/version_info/channel.h"
|
#include "components/version_info/channel.h"
|
||||||
#include "third_party/abseil-cpp/absl/cleanup/cleanup.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) {
|
switch (channel) {
|
||||||
case version_info::Channel::STABLE:
|
case version_info::Channel::STABLE:
|
||||||
case version_info::Channel::UNKNOWN:
|
case version_info::Channel::UNKNOWN:
|
||||||
// If the channel can't be determined, treat it as `stable` for safety.
|
// If the channel can't be determined, treat it as `stable` for safety.
|
||||||
// Don't disable heap profiling completely so that developers can still
|
// Don't disable heap profiling completely so that developers can still
|
||||||
// enable it with --enable-feature flags.
|
// enable it with --enable-feature flags.
|
||||||
return kStableProbability.Get();
|
return params.stable_probability;
|
||||||
case version_info::Channel::BETA:
|
case version_info::Channel::BETA:
|
||||||
case version_info::Channel::DEV:
|
case version_info::Channel::DEV:
|
||||||
case version_info::Channel::CANARY:
|
case version_info::Channel::CANARY:
|
||||||
return kNonStableProbability.Get();
|
return params.nonstable_probability;
|
||||||
}
|
}
|
||||||
NOTREACHED();
|
NOTREACHED();
|
||||||
}
|
}
|
||||||
@@ -142,19 +140,15 @@ std::pair<bool, std::optional<std::string>> DecideIfCollectionIsEnabled(
|
|||||||
return {is_enabled, std::nullopt};
|
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.
|
// 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 seed = base::RandDouble();
|
||||||
const double probability = GetChannelProbability(channel);
|
const double probability = GetChannelProbability(channel, params);
|
||||||
if (seed < probability) {
|
if (seed < probability) {
|
||||||
return {true, "Enabled"};
|
return {true, "Enabled"};
|
||||||
}
|
}
|
||||||
@@ -263,9 +257,13 @@ bool HeapProfilerController::StartIfEnabled() {
|
|||||||
if (!profiling_enabled_) {
|
if (!profiling_enabled_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const size_t sampling_rate_bytes = GetSamplingRateForProcess(process_type_);
|
HeapProfilerParameters profiler_params =
|
||||||
if (sampling_rate_bytes > 0) {
|
GetHeapProfilerParametersForProcess(process_type_);
|
||||||
base::SamplingHeapProfiler::Get()->SetSamplingInterval(sampling_rate_bytes);
|
// 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();
|
base::SamplingHeapProfiler::Get()->Start();
|
||||||
|
|
||||||
@@ -274,10 +272,9 @@ bool HeapProfilerController::StartIfEnabled() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const base::TimeDelta collection_interval = kCollectionInterval.Get();
|
DCHECK(profiler_params.collection_interval.is_positive());
|
||||||
CHECK(collection_interval.is_positive());
|
|
||||||
SnapshotParams params(
|
SnapshotParams params(
|
||||||
collection_interval,
|
profiler_params.collection_interval,
|
||||||
/*use_random_interval=*/!suppress_randomness_for_testing_, stopped_,
|
/*use_random_interval=*/!suppress_randomness_for_testing_, stopped_,
|
||||||
process_type_, creation_time_, std::move(on_first_snapshot_callback_));
|
process_type_, creation_time_, std::move(on_first_snapshot_callback_));
|
||||||
params.trigger_child_process_snapshot_closure = base::BindRepeating(
|
params.trigger_child_process_snapshot_closure = base::BindRepeating(
|
||||||
@@ -371,7 +368,7 @@ void HeapProfilerController::AppendCommandLineSwitchInternal(
|
|||||||
BrowserProcessSnapshotController* snapshot_controller) {
|
BrowserProcessSnapshotController* snapshot_controller) {
|
||||||
CHECK_NE(child_process_type, ProcessType::kBrowser);
|
CHECK_NE(child_process_type, ProcessType::kBrowser);
|
||||||
if (snapshot_controller &&
|
if (snapshot_controller &&
|
||||||
GetSnapshotProbabilityForProcess(child_process_type) > 0) {
|
GetHeapProfilerParametersForProcess(child_process_type).is_supported) {
|
||||||
command_line->AppendSwitch(switches::kSubprocessHeapProfiling);
|
command_line->AppendSwitch(switches::kSubprocessHeapProfiling);
|
||||||
snapshot_controller->BindRemoteForChildProcess(child_process_id,
|
snapshot_controller->BindRemoteForChildProcess(child_process_id,
|
||||||
child_process_type);
|
child_process_type);
|
||||||
|
@@ -19,12 +19,16 @@
|
|||||||
#include "base/auto_reset.h"
|
#include "base/auto_reset.h"
|
||||||
#include "base/barrier_closure.h"
|
#include "base/barrier_closure.h"
|
||||||
#include "base/command_line.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/bind.h"
|
||||||
#include "base/functional/callback.h"
|
#include "base/functional/callback.h"
|
||||||
#include "base/functional/callback_helpers.h"
|
#include "base/functional/callback_helpers.h"
|
||||||
|
#include "base/json/json_writer.h"
|
||||||
#include "base/memory/scoped_refptr.h"
|
#include "base/memory/scoped_refptr.h"
|
||||||
#include "base/metrics/field_trial_params.h"
|
#include "base/metrics/field_trial_params.h"
|
||||||
#include "base/metrics/metrics_hashes.h"
|
#include "base/metrics/metrics_hashes.h"
|
||||||
|
#include "base/notreached.h"
|
||||||
#include "base/process/launch.h"
|
#include "base/process/launch.h"
|
||||||
#include "base/process/process.h"
|
#include "base/process/process.h"
|
||||||
#include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
|
#include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
|
||||||
@@ -42,6 +46,7 @@
|
|||||||
#include "base/test/test_timeouts.h"
|
#include "base/test/test_timeouts.h"
|
||||||
#include "base/time/time.h"
|
#include "base/time/time.h"
|
||||||
#include "base/types/optional_util.h"
|
#include "base/types/optional_util.h"
|
||||||
|
#include "base/values.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "components/heap_profiling/in_process/browser_process_snapshot_controller.h"
|
#include "components/heap_profiling/in_process/browser_process_snapshot_controller.h"
|
||||||
#include "components/heap_profiling/in_process/child_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
|
#define ENABLE_MULTIPROCESS_TESTS 1
|
||||||
#endif
|
#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::AllocationNotificationData;
|
||||||
using base::allocator::dispatcher::AllocationSubsystem;
|
using base::allocator::dispatcher::AllocationSubsystem;
|
||||||
using base::allocator::dispatcher::FreeNotificationData;
|
using base::allocator::dispatcher::FreeNotificationData;
|
||||||
using base::test::FeatureRef;
|
using ScopedMuteHookedSamplesForTesting =
|
||||||
using base::test::FeatureRefAndParams;
|
base::PoissonAllocationSampler::ScopedMuteHookedSamplesForTesting;
|
||||||
using sampling_profiler::ProfilerProcessType;
|
using ScopedSuppressRandomnessForTesting =
|
||||||
|
base::PoissonAllocationSampler::ScopedSuppressRandomnessForTesting;
|
||||||
|
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
using ::testing::AllOf;
|
using ::testing::AllOf;
|
||||||
using ::testing::Conditional;
|
using ::testing::Conditional;
|
||||||
@@ -131,13 +145,6 @@ using ::testing::Property;
|
|||||||
using ::testing::ResultOf;
|
using ::testing::ResultOf;
|
||||||
using ::testing::UnorderedElementsAreArray;
|
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 kSamplingRate = 1024;
|
||||||
constexpr size_t kAllocationSize = 42 * kSamplingRate;
|
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
|
// Start the heap profiler and wait for TakeSnapshot() messages from the
|
||||||
// parent.
|
// parent.
|
||||||
HeapProfilerController controller(
|
HeapProfilerController controller(version_info::Channel::STABLE,
|
||||||
version_info::Channel::STABLE,
|
static_cast<ProcessType>(process_type));
|
||||||
static_cast<ProfilerProcessType>(process_type));
|
|
||||||
controller.SuppressRandomnessForTesting();
|
controller.SuppressRandomnessForTesting();
|
||||||
ASSERT_TRUE(controller.IsEnabled());
|
ASSERT_TRUE(controller.IsEnabled());
|
||||||
controller.StartIfEnabled();
|
controller.StartIfEnabled();
|
||||||
@@ -517,7 +523,7 @@ class MultiprocessTestParent {
|
|||||||
// `should_profile` is false, simulate the embedder refusing to profile the
|
// `should_profile` is false, simulate the embedder refusing to profile the
|
||||||
// child process.
|
// child process.
|
||||||
void LaunchTestChild(HeapProfilerController* controller,
|
void LaunchTestChild(HeapProfilerController* controller,
|
||||||
ProfilerProcessType process_type,
|
ProcessType process_type,
|
||||||
int num_allocations,
|
int num_allocations,
|
||||||
bool should_profile) {
|
bool should_profile) {
|
||||||
// `should_profile` will apply during next call to BindTestConnector().
|
// `should_profile` will apply during next call to BindTestConnector().
|
||||||
@@ -599,13 +605,14 @@ struct FeatureTestParams {
|
|||||||
};
|
};
|
||||||
// Whether HeapProfilerReporting is enabled.
|
// Whether HeapProfilerReporting is enabled.
|
||||||
bool feature_enabled = true;
|
bool feature_enabled = true;
|
||||||
|
const ProcessTypeSet supported_processes;
|
||||||
ChannelParams stable;
|
ChannelParams stable;
|
||||||
ChannelParams nonstable;
|
ChannelParams nonstable;
|
||||||
// Probabilities for snapshotting child processes.
|
// Probabilities for snapshotting child processes.
|
||||||
int gpu_snapshot_prob = 0;
|
int gpu_snapshot_prob = 100;
|
||||||
int network_snapshot_prob = 0;
|
int network_snapshot_prob = 100;
|
||||||
int renderer_snapshot_prob = 0;
|
int renderer_snapshot_prob = 100;
|
||||||
int utility_snapshot_prob = 0;
|
int utility_snapshot_prob = 100;
|
||||||
|
|
||||||
base::FieldTrialParams ToFieldTrialParams() const;
|
base::FieldTrialParams ToFieldTrialParams() const;
|
||||||
|
|
||||||
@@ -616,32 +623,58 @@ struct FeatureTestParams {
|
|||||||
// Converts the test params to field trial parameters for the
|
// Converts the test params to field trial parameters for the
|
||||||
// HeapProfilerReporting feature.
|
// HeapProfilerReporting feature.
|
||||||
base::FieldTrialParams FeatureTestParams::ToFieldTrialParams() const {
|
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.
|
// Add the default params.
|
||||||
field_trial_params["stable-probability"] =
|
base::Value::Dict dict;
|
||||||
base::NumberToString(stable.probability);
|
if (!supported_processes.empty()) {
|
||||||
field_trial_params["nonstable-probability"] =
|
// Explicitly disable profiling by default, so that only the processes
|
||||||
base::NumberToString(nonstable.probability);
|
// 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.
|
// Add a field trial param that enables each process type in
|
||||||
field_trial_params["browser-sampling-rate-bytes"] =
|
// `supported_processes`.
|
||||||
base::NumberToString(kSamplingRate);
|
base::Value::Dict is_supported_dict;
|
||||||
field_trial_params["gpu-sampling-rate-bytes"] =
|
is_supported_dict.Set("is-supported", true);
|
||||||
base::NumberToString(kSamplingRate);
|
std::string is_supported_string;
|
||||||
field_trial_params["gpu-prob-pct"] = base::NumberToString(gpu_snapshot_prob);
|
base::JSONWriter::WriteWithOptions(is_supported_dict,
|
||||||
field_trial_params["network-sampling-rate-bytes"] =
|
base::JSONWriter::OPTIONS_PRETTY_PRINT,
|
||||||
base::NumberToString(kSamplingRate);
|
&is_supported_string);
|
||||||
field_trial_params["network-prob-pct"] =
|
|
||||||
base::NumberToString(network_snapshot_prob);
|
for (ProcessType process_type : supported_processes) {
|
||||||
field_trial_params["renderer-sampling-rate-bytes"] =
|
switch (process_type) {
|
||||||
base::NumberToString(kSamplingRate);
|
case ProcessType::kBrowser:
|
||||||
field_trial_params["renderer-prob-pct"] =
|
field_trial_params["browser-process-params"] = is_supported_string;
|
||||||
base::NumberToString(renderer_snapshot_prob);
|
break;
|
||||||
field_trial_params["utility-sampling-rate-bytes"] =
|
case ProcessType::kRenderer:
|
||||||
base::NumberToString(kSamplingRate);
|
field_trial_params["renderer-process-params"] = is_supported_string;
|
||||||
field_trial_params["utility-prob-pct"] =
|
break;
|
||||||
base::NumberToString(utility_snapshot_prob);
|
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;
|
return field_trial_params;
|
||||||
}
|
}
|
||||||
@@ -729,18 +762,18 @@ class HeapProfilerControllerTest
|
|||||||
// The test must call StartIfEnabled() after this to start profiling.
|
// The test must call StartIfEnabled() after this to start profiling.
|
||||||
void CreateHeapProfiler(
|
void CreateHeapProfiler(
|
||||||
version_info::Channel channel,
|
version_info::Channel channel,
|
||||||
ProfilerProcessType process_type,
|
ProcessType process_type,
|
||||||
bool expect_enabled,
|
bool expect_enabled,
|
||||||
base::OnceClosure first_snapshot_callback = base::DoNothing(),
|
base::OnceClosure first_snapshot_callback = base::DoNothing(),
|
||||||
ProfileCollectorCallback collector_callback = base::DoNothing()) {
|
ProfileCollectorCallback collector_callback = base::DoNothing()) {
|
||||||
ASSERT_FALSE(controller_) << "CreateHeapProfiler called twice";
|
ASSERT_FALSE(controller_) << "CreateHeapProfiler called twice";
|
||||||
switch (process_type) {
|
switch (process_type) {
|
||||||
case ProfilerProcessType::kBrowser:
|
case ProcessType::kBrowser:
|
||||||
expected_process_ = metrics::Process::BROWSER_PROCESS;
|
expected_process_ = metrics::Process::BROWSER_PROCESS;
|
||||||
metrics::CallStackProfileBuilder::SetBrowserProcessReceiverCallback(
|
metrics::CallStackProfileBuilder::SetBrowserProcessReceiverCallback(
|
||||||
std::move(collector_callback));
|
std::move(collector_callback));
|
||||||
break;
|
break;
|
||||||
case ProfilerProcessType::kUtility:
|
case ProcessType::kUtility:
|
||||||
expected_process_ = metrics::Process::UTILITY_PROCESS;
|
expected_process_ = metrics::Process::UTILITY_PROCESS;
|
||||||
metrics::CallStackProfileBuilder::
|
metrics::CallStackProfileBuilder::
|
||||||
SetParentProfileCollectorForChildProcess(
|
SetParentProfileCollectorForChildProcess(
|
||||||
@@ -772,7 +805,7 @@ class HeapProfilerControllerTest
|
|||||||
// profiling.
|
// profiling.
|
||||||
void StartHeapProfiling(
|
void StartHeapProfiling(
|
||||||
version_info::Channel channel,
|
version_info::Channel channel,
|
||||||
ProfilerProcessType process_type,
|
ProcessType process_type,
|
||||||
bool expect_enabled,
|
bool expect_enabled,
|
||||||
base::OnceClosure first_snapshot_callback = base::DoNothing(),
|
base::OnceClosure first_snapshot_callback = base::DoNothing(),
|
||||||
ProfileCollectorCallback collector_callback = base::DoNothing()) {
|
ProfileCollectorCallback collector_callback = base::DoNothing()) {
|
||||||
@@ -874,8 +907,7 @@ TEST_P(HeapProfilerControllerTest, ProfileCollectionsScheduler) {
|
|||||||
++profile_count;
|
++profile_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
StartHeapProfiling(version_info::Channel::STABLE,
|
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||||
ProfilerProcessType::kBrowser,
|
|
||||||
/*expect_enabled=*/true,
|
/*expect_enabled=*/true,
|
||||||
/*first_snapshot_callback=*/base::DoNothing(),
|
/*first_snapshot_callback=*/base::DoNothing(),
|
||||||
base::BindLambdaForTesting(check_profile));
|
base::BindLambdaForTesting(check_profile));
|
||||||
@@ -905,8 +937,7 @@ TEST_P(HeapProfilerControllerTest, ProfileCollectionsScheduler) {
|
|||||||
TEST_P(HeapProfilerControllerTest, UnhandledProcess) {
|
TEST_P(HeapProfilerControllerTest, UnhandledProcess) {
|
||||||
// Starting the heap profiler in an unhandled process type should safely do
|
// Starting the heap profiler in an unhandled process type should safely do
|
||||||
// nothing.
|
// nothing.
|
||||||
StartHeapProfiling(version_info::Channel::STABLE,
|
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kUnknown,
|
||||||
ProfilerProcessType::kUnknown,
|
|
||||||
/*expect_enabled=*/false);
|
/*expect_enabled=*/false);
|
||||||
// The Enabled summary histogram should not be logged for unsupported
|
// The Enabled summary histogram should not be logged for unsupported
|
||||||
// processes, because they're not included in the per-process histograms that
|
// 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.
|
// Should save an empty profile even though no memory is allocated.
|
||||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||||
/*expect_take_snapshot=*/true, /*expect_sampled_profile=*/true);
|
/*expect_take_snapshot=*/true, /*expect_sampled_profile=*/true);
|
||||||
StartHeapProfiling(
|
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||||
version_info::Channel::STABLE, ProfilerProcessType::kBrowser,
|
/*expect_enabled=*/true,
|
||||||
/*expect_enabled=*/true, callbacks.first_snapshot_callback(),
|
callbacks.first_snapshot_callback(),
|
||||||
callbacks.collector_callback());
|
callbacks.collector_callback());
|
||||||
task_env().RunUntilQuit();
|
task_env().RunUntilQuit();
|
||||||
EXPECT_TRUE(sample_received_);
|
EXPECT_TRUE(sample_received_);
|
||||||
}
|
}
|
||||||
@@ -972,9 +1003,8 @@ TEST_P(HeapProfilerControllerChannelTest, StableChannel) {
|
|||||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||||
/*expect_take_snapshot=*/profiling_enabled,
|
/*expect_take_snapshot=*/profiling_enabled,
|
||||||
GetParam().stable.expect_browser_sample);
|
GetParam().stable.expect_browser_sample);
|
||||||
StartHeapProfiling(version_info::Channel::STABLE,
|
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||||
ProfilerProcessType::kBrowser, profiling_enabled,
|
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||||
callbacks.first_snapshot_callback(),
|
|
||||||
callbacks.collector_callback());
|
callbacks.collector_callback());
|
||||||
histogram_tester_.ExpectUniqueSample(
|
histogram_tester_.ExpectUniqueSample(
|
||||||
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
||||||
@@ -990,9 +1020,8 @@ TEST_P(HeapProfilerControllerChannelTest, CanaryChannel) {
|
|||||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||||
/*expect_take_snapshot=*/profiling_enabled,
|
/*expect_take_snapshot=*/profiling_enabled,
|
||||||
GetParam().nonstable.expect_browser_sample);
|
GetParam().nonstable.expect_browser_sample);
|
||||||
StartHeapProfiling(version_info::Channel::CANARY,
|
StartHeapProfiling(version_info::Channel::CANARY, ProcessType::kBrowser,
|
||||||
ProfilerProcessType::kBrowser, profiling_enabled,
|
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||||
callbacks.first_snapshot_callback(),
|
|
||||||
callbacks.collector_callback());
|
callbacks.collector_callback());
|
||||||
histogram_tester_.ExpectUniqueSample(
|
histogram_tester_.ExpectUniqueSample(
|
||||||
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
||||||
@@ -1010,9 +1039,8 @@ TEST_P(HeapProfilerControllerChannelTest, UnknownChannel) {
|
|||||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||||
/*expect_take_snapshot=*/profiling_enabled,
|
/*expect_take_snapshot=*/profiling_enabled,
|
||||||
GetParam().stable.expect_browser_sample);
|
GetParam().stable.expect_browser_sample);
|
||||||
StartHeapProfiling(version_info::Channel::UNKNOWN,
|
StartHeapProfiling(version_info::Channel::UNKNOWN, ProcessType::kBrowser,
|
||||||
ProfilerProcessType::kBrowser, profiling_enabled,
|
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||||
callbacks.first_snapshot_callback(),
|
|
||||||
callbacks.collector_callback());
|
callbacks.collector_callback());
|
||||||
histogram_tester_.ExpectUniqueSample(
|
histogram_tester_.ExpectUniqueSample(
|
||||||
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
||||||
@@ -1026,12 +1054,22 @@ TEST_P(HeapProfilerControllerChannelTest, UnknownChannel) {
|
|||||||
constexpr FeatureTestParams kProcessConfigs[] = {
|
constexpr FeatureTestParams kProcessConfigs[] = {
|
||||||
// Enabled in parent process only.
|
// Enabled in parent process only.
|
||||||
{
|
{
|
||||||
|
.supported_processes = {ProcessType::kBrowser},
|
||||||
.stable = {.expect_browser_sample = true, .expect_child_sample = false},
|
.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.
|
// Enabled in parent and child processes.
|
||||||
{
|
{
|
||||||
|
.supported_processes = {ProcessType::kBrowser, ProcessType::kUtility},
|
||||||
.stable = {.expect_browser_sample = true, .expect_child_sample = true},
|
.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));
|
::testing::ValuesIn(kProcessConfigs));
|
||||||
|
|
||||||
TEST_P(HeapProfilerControllerProcessTest, BrowserProcess) {
|
TEST_P(HeapProfilerControllerProcessTest, BrowserProcess) {
|
||||||
// This test always enables profiling in the browser process. (Disabled is
|
const bool profiling_enabled =
|
||||||
// tested in HeapProfilerControllerChannelTest.)
|
base::Contains(GetParam().supported_processes, ProcessType::kBrowser);
|
||||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
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);
|
/*use_other_process_callback=*/GetParam().stable.expect_child_sample);
|
||||||
|
|
||||||
// Mock the child end of the SnapshotController mojo pipe. (Only used when
|
// 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(
|
mojo::Receiver<mojom::SnapshotController> mock_receiver(
|
||||||
&mock_child_snapshot_controller);
|
&mock_child_snapshot_controller);
|
||||||
|
|
||||||
StartHeapProfiling(
|
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||||
version_info::Channel::STABLE, ProfilerProcessType::kBrowser,
|
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||||
/*expect_enabled=*/true, callbacks.first_snapshot_callback(),
|
callbacks.collector_callback());
|
||||||
callbacks.collector_callback());
|
|
||||||
histogram_tester_.ExpectUniqueSample(
|
histogram_tester_.ExpectUniqueSample(
|
||||||
"HeapProfiling.InProcess.Enabled.Browser", true, 1);
|
"HeapProfiling.InProcess.Enabled.Browser", profiling_enabled, 1);
|
||||||
histogram_tester_.ExpectUniqueSample("HeapProfiling.InProcess.Enabled", true,
|
histogram_tester_.ExpectUniqueSample("HeapProfiling.InProcess.Enabled",
|
||||||
1);
|
profiling_enabled, 1);
|
||||||
|
|
||||||
constexpr int kTestChildProcessId = 1;
|
constexpr int kTestChildProcessId = 1;
|
||||||
ASSERT_TRUE(controller_->GetBrowserProcessSnapshotController());
|
if (profiling_enabled) {
|
||||||
|
ASSERT_TRUE(controller_->GetBrowserProcessSnapshotController());
|
||||||
|
|
||||||
// This callback should be invoked from AppendCommandLineSwitchForChildProcess
|
// This callback should be invoked from
|
||||||
// to bind the child end of the mojo pipe.
|
// AppendCommandLineSwitchForChildProcess to bind the child end of the mojo
|
||||||
controller_->GetBrowserProcessSnapshotController()
|
// pipe.
|
||||||
->SetBindRemoteForChildProcessCallback(base::BindLambdaForTesting(
|
controller_->GetBrowserProcessSnapshotController()
|
||||||
[&](int child_process_id,
|
->SetBindRemoteForChildProcessCallback(base::BindLambdaForTesting(
|
||||||
mojo::PendingReceiver<mojom::SnapshotController> receiver) {
|
[&](int child_process_id,
|
||||||
EXPECT_EQ(child_process_id, kTestChildProcessId);
|
mojo::PendingReceiver<mojom::SnapshotController> receiver) {
|
||||||
mock_receiver.Bind(std::move(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
|
// 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
|
// 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`.
|
// child end to `mock_child_snapshot_controller`.
|
||||||
base::CommandLine child_command_line(base::CommandLine::NO_PROGRAM);
|
base::CommandLine child_command_line(base::CommandLine::NO_PROGRAM);
|
||||||
controller_->AppendCommandLineSwitchForChildProcess(
|
controller_->AppendCommandLineSwitchForChildProcess(
|
||||||
&child_command_line, ProfilerProcessType::kUtility, kTestChildProcessId);
|
&child_command_line, ProcessType::kUtility, kTestChildProcessId);
|
||||||
|
|
||||||
if (GetParam().stable.expect_child_sample) {
|
if (GetParam().stable.expect_child_sample) {
|
||||||
EXPECT_CALL(mock_child_snapshot_controller, TakeSnapshot(100, 0))
|
EXPECT_CALL(mock_child_snapshot_controller, TakeSnapshot(100, 0))
|
||||||
@@ -1100,12 +1143,12 @@ TEST_P(HeapProfilerControllerProcessTest, BrowserProcess) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(HeapProfilerControllerProcessTest, ChildProcess) {
|
TEST_P(HeapProfilerControllerProcessTest, ChildProcess) {
|
||||||
// TakeSnapshot() is called in the child process when the browser process
|
const bool profiling_enabled =
|
||||||
// triggers it, which always happens in this test when `utility_snapshot_prob`
|
base::Contains(GetParam().supported_processes, ProcessType::kUtility);
|
||||||
// > 0.
|
// TakeSnapshot() is only called in the child process when the browser process
|
||||||
const bool profiling_enabled = GetParam().utility_snapshot_prob > 0;
|
// triggers it.
|
||||||
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
ScopedCallbacks callbacks = CreateScopedCallbacks(
|
||||||
/*expect_take_snapshot=*/profiling_enabled,
|
/*expect_take_snapshot=*/GetParam().stable.expect_child_sample,
|
||||||
/*expect_sampled_profile=*/GetParam().stable.expect_child_sample,
|
/*expect_sampled_profile=*/GetParam().stable.expect_child_sample,
|
||||||
/*use_other_process_callback=*/true);
|
/*use_other_process_callback=*/true);
|
||||||
|
|
||||||
@@ -1130,22 +1173,25 @@ TEST_P(HeapProfilerControllerProcessTest, ChildProcess) {
|
|||||||
|
|
||||||
base::test::ScopedCommandLine scoped_command_line;
|
base::test::ScopedCommandLine scoped_command_line;
|
||||||
HeapProfilerController::AppendCommandLineSwitchForTesting(
|
HeapProfilerController::AppendCommandLineSwitchForTesting(
|
||||||
scoped_command_line.GetProcessCommandLine(),
|
scoped_command_line.GetProcessCommandLine(), ProcessType::kUtility,
|
||||||
ProfilerProcessType::kUtility, kTestChildProcessId,
|
kTestChildProcessId, fake_browser_snapshot_controller.get());
|
||||||
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(
|
snapshot_task_runner->PostDelayedTask(
|
||||||
FROM_HERE,
|
FROM_HERE,
|
||||||
base::BindOnce(
|
std::move(browser_snapshot_callback)
|
||||||
&BrowserProcessSnapshotController::TakeSnapshotsOnSnapshotSequence,
|
|
||||||
std::move(fake_browser_snapshot_controller))
|
|
||||||
.Then(callbacks.other_process_callback()),
|
.Then(callbacks.other_process_callback()),
|
||||||
TestTimeouts::action_timeout());
|
TestTimeouts::action_timeout());
|
||||||
|
|
||||||
StartHeapProfiling(version_info::Channel::STABLE,
|
StartHeapProfiling(version_info::Channel::STABLE, ProcessType::kUtility,
|
||||||
ProfilerProcessType::kUtility, profiling_enabled,
|
profiling_enabled, callbacks.first_snapshot_callback(),
|
||||||
callbacks.first_snapshot_callback(),
|
|
||||||
callbacks.collector_callback());
|
callbacks.collector_callback());
|
||||||
histogram_tester_.ExpectUniqueSample(
|
histogram_tester_.ExpectUniqueSample(
|
||||||
"HeapProfiling.InProcess.Enabled.Utility", profiling_enabled, 1);
|
"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.
|
// End-to-end test with multiple child processes.
|
||||||
constexpr FeatureTestParams kMultipleChildConfigs[] = {
|
constexpr FeatureTestParams kMultipleChildConfigs[] = {
|
||||||
{
|
{
|
||||||
.gpu_snapshot_prob = 100,
|
.supported_processes = {ProcessType::kBrowser, ProcessType::kGpu,
|
||||||
.network_snapshot_prob = 100,
|
ProcessType::kUtility, ProcessType::kRenderer},
|
||||||
.renderer_snapshot_prob = 66,
|
.renderer_snapshot_prob = 66,
|
||||||
.utility_snapshot_prob = 50,
|
.utility_snapshot_prob = 50,
|
||||||
},
|
},
|
||||||
@@ -1217,19 +1263,19 @@ TEST_P(HeapProfilerControllerMultipleChildTest, EndToEnd) {
|
|||||||
|
|
||||||
// Process types to test. Each will make a different
|
// Process types to test. Each will make a different
|
||||||
// number of memory allocations so their reports are all different.
|
// number of memory allocations so their reports are all different.
|
||||||
const std::vector<std::pair<ProfilerProcessType, size_t>> kProcessesToTest{
|
const std::vector<std::pair<ProcessType, size_t>> kProcessesToTest{
|
||||||
{ProfilerProcessType::kBrowser, 0},
|
{ProcessType::kBrowser, 0},
|
||||||
{ProfilerProcessType::kGpu, 1},
|
{ProcessType::kGpu, 1},
|
||||||
// 2 utility processes.
|
// 2 utility processes.
|
||||||
{ProfilerProcessType::kUtility, 2},
|
{ProcessType::kUtility, 2},
|
||||||
{ProfilerProcessType::kUtility, 3},
|
{ProcessType::kUtility, 3},
|
||||||
// 5 renderer processes including one with no samples. The first one will
|
// 5 renderer processes including one with no samples. The first one will
|
||||||
// be ignored to simulate the embedder refusing to profile it.
|
// be ignored to simulate the embedder refusing to profile it.
|
||||||
{ProfilerProcessType::kRenderer, 10},
|
{ProcessType::kRenderer, 10},
|
||||||
{ProfilerProcessType::kRenderer, 0},
|
{ProcessType::kRenderer, 0},
|
||||||
{ProfilerProcessType::kRenderer, 4},
|
{ProcessType::kRenderer, 4},
|
||||||
{ProfilerProcessType::kRenderer, 5},
|
{ProcessType::kRenderer, 5},
|
||||||
{ProfilerProcessType::kRenderer, 6},
|
{ProcessType::kRenderer, 6},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Expect only 1 utility process and 3 renderer processes to be sampled due
|
// 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());
|
task_runner->DeleteSoon(FROM_HERE, controller_.release());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
CreateHeapProfiler(
|
CreateHeapProfiler(version_info::Channel::STABLE, ProcessType::kBrowser,
|
||||||
version_info::Channel::STABLE, ProfilerProcessType::kBrowser,
|
/*expect_enabled=*/true,
|
||||||
/*expect_enabled=*/true, std::move(stop_after_first_snapshot_callback),
|
std::move(stop_after_first_snapshot_callback),
|
||||||
callbacks.collector_callback());
|
callbacks.collector_callback());
|
||||||
ASSERT_TRUE(controller_);
|
ASSERT_TRUE(controller_);
|
||||||
|
|
||||||
// Start all processes in `kProcessesToTest` except the browser.
|
// Start all processes in `kProcessesToTest` except the browser.
|
||||||
@@ -1286,11 +1332,10 @@ TEST_P(HeapProfilerControllerMultipleChildTest, EndToEnd) {
|
|||||||
|
|
||||||
bool renderer_was_skipped = false;
|
bool renderer_was_skipped = false;
|
||||||
for (const auto [process_type, num_allocations] : kProcessesToTest) {
|
for (const auto [process_type, num_allocations] : kProcessesToTest) {
|
||||||
if (process_type != ProfilerProcessType::kBrowser) {
|
if (process_type != ProcessType::kBrowser) {
|
||||||
// Skip the first renderer.
|
// Skip the first renderer.
|
||||||
bool should_profile = true;
|
bool should_profile = true;
|
||||||
if (process_type == ProfilerProcessType::kRenderer &&
|
if (process_type == ProcessType::kRenderer && !renderer_was_skipped) {
|
||||||
!renderer_was_skipped) {
|
|
||||||
should_profile = false;
|
should_profile = false;
|
||||||
renderer_was_skipped = true;
|
renderer_was_skipped = true;
|
||||||
}
|
}
|
||||||
|
@@ -4,14 +4,19 @@
|
|||||||
|
|
||||||
#include "components/heap_profiling/in_process/heap_profiler_parameters.h"
|
#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/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/metrics/field_trial_params.h"
|
||||||
#include "base/notreached.h"
|
|
||||||
#include "base/numerics/safe_conversions.h"
|
|
||||||
#include "base/time/time.h"
|
#include "base/time/time.h"
|
||||||
|
#include "base/values.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "components/sampling_profiler/process_type.h"
|
#include "components/sampling_profiler/process_type.h"
|
||||||
|
#include "components/variations/variations_switches.h"
|
||||||
|
|
||||||
namespace heap_profiling {
|
namespace heap_profiling {
|
||||||
|
|
||||||
@@ -49,60 +54,65 @@ constexpr double kDefaultStableProbability = 0.01;
|
|||||||
// provider if it's on a non-stable channel.
|
// provider if it's on a non-stable channel.
|
||||||
constexpr double kDefaultNonStableProbability = 0.5;
|
constexpr double kDefaultNonStableProbability = 0.5;
|
||||||
|
|
||||||
// The probability of including a child process in each snapshot that's taken,
|
constexpr HeapProfilerParameters kDefaultHeapProfilerParameters{
|
||||||
// as a percentage from 0 to 100. Defaults to 100, but can be set lower to
|
.is_supported = false,
|
||||||
// sub-sample process types that are very common (mainly renderers) to keep data
|
// If a process overrides `is_supported`, use the following defaults.
|
||||||
// volume low. Samples from child processes are weighted in inverse proportion
|
.stable_probability = kDefaultStableProbability,
|
||||||
// to the snapshot probability to normalize the aggregated results. Set to 0 to
|
.nonstable_probability = kDefaultNonStableProbability,
|
||||||
// disable sampling a process completely.
|
.sampling_rate_bytes = kDefaultSamplingRateBytes,
|
||||||
|
.collection_interval = kDefaultCollectionInterval,
|
||||||
|
};
|
||||||
|
|
||||||
constexpr base::FeatureParam<int> kGpuSnapshotProbability{
|
// Feature parameters.
|
||||||
&kHeapProfilerReporting, "gpu-prob-pct", 100};
|
|
||||||
|
|
||||||
constexpr base::FeatureParam<int> kNetworkSnapshotProbability{
|
// JSON-encoded parameter map that will set the default parameters for the
|
||||||
&kHeapProfilerReporting, "network-prob-pct", 100};
|
// 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
|
// JSON-encoded parameter map that will override the default parameters for the
|
||||||
// evaluated (2024-08) the 50th %ile of renderer process count
|
// browser process.
|
||||||
// (Memory.RenderProcessHost.Count.All) ranged from 8 on Windows to 18 on Mac.
|
constexpr base::FeatureParam<std::string> kBrowserProcessParameters{
|
||||||
// 10% is an easy default between 1/18 and 1/8.
|
&kHeapProfilerReporting, "browser-process-params", ""};
|
||||||
constexpr base::FeatureParam<int> kRendererSnapshotProbability{
|
|
||||||
&kHeapProfilerReporting, "renderer-prob-pct", 10};
|
|
||||||
|
|
||||||
// Sample 50% of utility processes by default, because last time this was
|
// JSON-encoded parameter map that will override the default parameters for
|
||||||
// evaluated (2024-08) the profiler collected 1.8x as many snapshots on Mac and
|
// renderer processes.
|
||||||
// 2.4x as many snapshots on Windows for each browser process snapshot.
|
constexpr base::FeatureParam<std::string> kRendererProcessParameters{
|
||||||
constexpr base::FeatureParam<int> kUtilitySnapshotProbability{
|
&kHeapProfilerReporting, "renderer-process-params", ""};
|
||||||
&kHeapProfilerReporting, "utility-prob-pct", 50};
|
|
||||||
|
|
||||||
// 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{
|
// JSON-encoded parameter map that will override the default parameters for
|
||||||
&kHeapProfilerReporting, "browser-sampling-rate-bytes",
|
// utility processes.
|
||||||
kDefaultSamplingRateBytes};
|
constexpr base::FeatureParam<std::string> kUtilityProcessParameters{
|
||||||
|
&kHeapProfilerReporting, "utility-process-params", ""};
|
||||||
|
|
||||||
// Use half the threshold used in the browser process, because last time it was
|
// JSON-encoded parameter map that will override the default parameters for the
|
||||||
// validated the GPU process allocated a bit over half as much memory at the
|
// network process.
|
||||||
// median.
|
constexpr base::FeatureParam<std::string> kNetworkProcessParameters{
|
||||||
constexpr base::FeatureParam<int> kGpuSamplingRateBytes{
|
&kHeapProfilerReporting, "network-process-params", ""};
|
||||||
&kHeapProfilerReporting, "gpu-sampling-rate-bytes",
|
|
||||||
kDefaultSamplingRateBytes / 2};
|
|
||||||
|
|
||||||
constexpr base::FeatureParam<int> kNetworkSamplingRateBytes{
|
// Interprets `value` as a positive number of minutes, and writes the converted
|
||||||
&kHeapProfilerReporting, "network-sampling-rate-bytes",
|
// value to `result`. If `value` contains anything other than a positive
|
||||||
kDefaultSamplingRateBytes};
|
// integer, returns false to indicate a conversion failure.
|
||||||
|
bool ConvertCollectionInterval(const base::Value* value,
|
||||||
constexpr base::FeatureParam<int> kRendererSamplingRateBytes{
|
base::TimeDelta* result) {
|
||||||
&kHeapProfilerReporting, "renderer-sampling-rate-bytes",
|
if (!value) {
|
||||||
kDefaultSamplingRateBytes};
|
// Missing values are ok, so report success without updating `result`.
|
||||||
|
return 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
|
if (value->is_int()) {
|
||||||
// 6% to 11% as many samples per snapshot in the utility process, depending on
|
const int minutes = value->GetInt();
|
||||||
// platform.
|
if (minutes > 0) {
|
||||||
constexpr base::FeatureParam<int> kUtilitySamplingRateBytes{
|
*result = base::Minutes(minutes);
|
||||||
&kHeapProfilerReporting, "utility-sampling-rate-bytes",
|
return true;
|
||||||
kDefaultSamplingRateBytes / 10};
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -110,65 +120,141 @@ BASE_FEATURE(kHeapProfilerReporting,
|
|||||||
"HeapProfilerReporting",
|
"HeapProfilerReporting",
|
||||||
base::FEATURE_ENABLED_BY_DEFAULT);
|
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||||
|
|
||||||
const base::FeatureParam<double> kStableProbability{
|
// TODO(crbug.com/40840943): The process sampling probabilities are separate
|
||||||
&kHeapProfilerReporting, "stable-probability", kDefaultStableProbability};
|
// 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{
|
const base::FeatureParam<int> kGpuSnapshotProbability{&kHeapProfilerReporting,
|
||||||
&kHeapProfilerReporting, "nonstable-probability",
|
"gpu-prob-pct", 100};
|
||||||
kDefaultNonStableProbability};
|
|
||||||
|
|
||||||
const base::FeatureParam<base::TimeDelta> kCollectionInterval{
|
const base::FeatureParam<int> kNetworkSnapshotProbability{
|
||||||
&kHeapProfilerReporting, "collection-interval", kDefaultCollectionInterval};
|
&kHeapProfilerReporting, "network-prob-pct", 100};
|
||||||
|
|
||||||
size_t GetSamplingRateForProcess(
|
// Sample 10% of renderer processes by default, because last time this was
|
||||||
sampling_profiler::ProfilerProcessType process_type) {
|
// evaluated (2024-08) the 50th %ile of renderer process count
|
||||||
int sampling_rate_bytes;
|
// (Memory.RenderProcessHost.Count.All) ranged from 8 on Windows to 18 on Mac.
|
||||||
switch (process_type) {
|
// 10% is an easy default between 1/18 and 1/8.
|
||||||
case sampling_profiler::ProfilerProcessType::kBrowser:
|
const base::FeatureParam<int> kRendererSnapshotProbability{
|
||||||
sampling_rate_bytes = kBrowserSamplingRateBytes.Get();
|
&kHeapProfilerReporting, "renderer-prob-pct", 10};
|
||||||
break;
|
|
||||||
case sampling_profiler::ProfilerProcessType::kRenderer:
|
// Sample 50% of utility processes by default, because last time this was
|
||||||
sampling_rate_bytes = kRendererSamplingRateBytes.Get();
|
// evaluated (2024-08) the profiler collected 1.8x as many snapshots on Mac and
|
||||||
break;
|
// 2.4x as many snapshots on Windows for each browser process snapshot.
|
||||||
case sampling_profiler::ProfilerProcessType::kGpu:
|
const base::FeatureParam<int> kUtilitySnapshotProbability{
|
||||||
sampling_rate_bytes = kGpuSamplingRateBytes.Get();
|
&kHeapProfilerReporting, "utility-prob-pct", 50};
|
||||||
break;
|
|
||||||
case sampling_profiler::ProfilerProcessType::kUtility:
|
// static
|
||||||
sampling_rate_bytes = kUtilitySamplingRateBytes.Get();
|
void HeapProfilerParameters::RegisterJSONConverter(
|
||||||
break;
|
base::JSONValueConverter<HeapProfilerParameters>* converter) {
|
||||||
case sampling_profiler::ProfilerProcessType::kNetworkService:
|
converter->RegisterBoolField("is-supported",
|
||||||
sampling_rate_bytes = kNetworkSamplingRateBytes.Get();
|
&HeapProfilerParameters::is_supported);
|
||||||
break;
|
converter->RegisterDoubleField("stable-probability",
|
||||||
case sampling_profiler::ProfilerProcessType::kUnknown:
|
&HeapProfilerParameters::stable_probability);
|
||||||
default:
|
converter->RegisterDoubleField(
|
||||||
// Profiler should not be enabled for these process types.
|
"nonstable-probability", &HeapProfilerParameters::nonstable_probability);
|
||||||
NOTREACHED();
|
converter->RegisterIntField("sampling-rate-bytes",
|
||||||
}
|
&HeapProfilerParameters::sampling_rate_bytes);
|
||||||
return base::saturated_cast<size_t>(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) {
|
sampling_profiler::ProfilerProcessType process_type) {
|
||||||
int snapshot_probability_pct;
|
using Process = sampling_profiler::ProfilerProcessType;
|
||||||
|
|
||||||
|
HeapProfilerParameters params = kDefaultHeapProfilerParameters;
|
||||||
|
|
||||||
|
// Apply per-process defaults.
|
||||||
switch (process_type) {
|
switch (process_type) {
|
||||||
case sampling_profiler::ProfilerProcessType::kGpu:
|
case Process::kBrowser:
|
||||||
snapshot_probability_pct = kGpuSnapshotProbability.Get();
|
params.is_supported = true;
|
||||||
break;
|
break;
|
||||||
case sampling_profiler::ProfilerProcessType::kNetworkService:
|
case Process::kNetworkService:
|
||||||
snapshot_probability_pct = kNetworkSnapshotProbability.Get();
|
params.is_supported = true;
|
||||||
break;
|
break;
|
||||||
case sampling_profiler::ProfilerProcessType::kRenderer:
|
case Process::kGpu:
|
||||||
snapshot_probability_pct = kRendererSnapshotProbability.Get();
|
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;
|
break;
|
||||||
case sampling_profiler::ProfilerProcessType::kUtility:
|
case Process::kRenderer:
|
||||||
snapshot_probability_pct = kUtilitySnapshotProbability.Get();
|
params.is_supported = true;
|
||||||
break;
|
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:
|
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);
|
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||||
return snapshot_probability_pct;
|
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
|
} // namespace heap_profiling
|
||||||
|
@@ -5,7 +5,10 @@
|
|||||||
#ifndef COMPONENTS_HEAP_PROFILING_IN_PROCESS_HEAP_PROFILER_PARAMETERS_H_
|
#ifndef COMPONENTS_HEAP_PROFILING_IN_PROCESS_HEAP_PROFILER_PARAMETERS_H_
|
||||||
#define 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/feature_list.h"
|
||||||
|
#include "base/json/json_value_converter.h"
|
||||||
#include "base/metrics/field_trial_params.h"
|
#include "base/metrics/field_trial_params.h"
|
||||||
#include "base/time/time.h"
|
#include "base/time/time.h"
|
||||||
#include "components/sampling_profiler/process_type.h"
|
#include "components/sampling_profiler/process_type.h"
|
||||||
@@ -20,24 +23,54 @@ namespace heap_profiling {
|
|||||||
// reporting is enabled.
|
// reporting is enabled.
|
||||||
BASE_DECLARE_FEATURE(kHeapProfilerReporting);
|
BASE_DECLARE_FEATURE(kHeapProfilerReporting);
|
||||||
|
|
||||||
// Chance that this client will report heap samples through a metrics
|
// The probability of including a child process in each snapshot that's taken,
|
||||||
// provider if it's on the stable channel.
|
// as a percentage from 0 to 100. Defaults to 100, but can be set lower to
|
||||||
extern const base::FeatureParam<double> kStableProbability;
|
// 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
|
// Parameters to control the heap profiler.
|
||||||
// provider if it's on a non-stable channel.
|
struct HeapProfilerParameters {
|
||||||
extern const base::FeatureParam<double> kNonStableProbability;
|
// True if heap profiling is supported, false otherwise.
|
||||||
|
bool is_supported = false;
|
||||||
|
|
||||||
// Mean time between snapshots.
|
// Chance that this client will report heap samples through a metrics
|
||||||
extern const base::FeatureParam<base::TimeDelta> kCollectionInterval;
|
// provider if it's on the stable channel.
|
||||||
|
double stable_probability = 0.0;
|
||||||
|
|
||||||
// Returns the sampling rate in bytes to use for `process_type`.
|
// Chance that this client will report heap samples through a metrics
|
||||||
size_t GetSamplingRateForProcess(
|
// provider if it's on a non-stable channel.
|
||||||
sampling_profiler::ProfilerProcessType process_type);
|
double nonstable_probability = 0.0;
|
||||||
|
|
||||||
// Returns the probability of sampling a `process_type` process in each
|
// Mean heap sampling interval in bytes.
|
||||||
// snapshot, from 0 to 100.
|
int sampling_rate_bytes = 0;
|
||||||
int GetSnapshotProbabilityForProcess(
|
|
||||||
|
// 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);
|
sampling_profiler::ProfilerProcessType process_type);
|
||||||
|
|
||||||
} // namespace heap_profiling
|
} // 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