| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COMPONENTS_HEAP_PROFILING_IN_PROCESS_HEAP_PROFILER_CONTROLLER_H_ |
| #define COMPONENTS_HEAP_PROFILING_IN_PROCESS_HEAP_PROFILER_CONTROLLER_H_ |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| |
| #include "base/functional/callback.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/sequence_checker.h" |
| #include "base/synchronization/atomic_flag.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/time/time.h" |
| #include "base/types/pass_key.h" |
| #include "components/sampling_profiler/process_type.h" |
| #include "components/version_info/channel.h" |
| |
| namespace base { |
| class CommandLine; |
| } |
| |
| namespace heap_profiling { |
| |
| class BrowserProcessSnapshotController; |
| class ChildProcessSnapshotController; |
| |
| // HeapProfilerController controls collection of sampled heap allocation |
| // snapshots for the current process. |
| class HeapProfilerController { |
| public: |
| // Returns the HeapProfilerController for this process or null if none exists. |
| static HeapProfilerController* GetInstance(); |
| |
| // Checks if heap profiling should be enabled for this process. If so, starts |
| // sampling heap allocations immediately but does not schedule snapshots of |
| // the samples until StartIfEnabled() is called. `channel` is used to |
| // determine the probability that this client will be opted in to profiling. |
| // `process_type` is the current process, which can be retrieved with |
| // GetProfilerProcessType in base/profiler/process_type.h. |
| HeapProfilerController(version_info::Channel channel, |
| sampling_profiler::ProfilerProcessType process_type); |
| |
| HeapProfilerController(const HeapProfilerController&) = delete; |
| HeapProfilerController& operator=(const HeapProfilerController&) = delete; |
| |
| ~HeapProfilerController(); |
| |
| // Returns true iff heap allocations are being sampled for this process. If |
| // this returns true, snapshots can be scheduled by calling StartIfEnabled(). |
| bool IsEnabled() const { return profiling_enabled_; } |
| |
| // Starts periodic heap snapshot collection. Does nothing except record a |
| // metric if heap profiling is disabled. |
| // Returns true if heap profiling is enabled and was successfully started, |
| // false otherwise. |
| bool StartIfEnabled(); |
| |
| // Get the synthetic field trial configuration. If a synthetic field trial |
| // should be registered, returns true and writes the field trial details to |
| // `trial_name` and `group_name`. Otherwise, returns false and does not modify |
| // `trial_name` and `group_name`. Must only be called in the browser process. |
| bool GetSyntheticFieldTrial(std::string& trial_name, |
| std::string& group_name) const; |
| |
| // Uses the exact parameter values for the sampling interval and time between |
| // samples, instead of a distribution around those values. This must be called |
| // before Start. |
| void SuppressRandomnessForTesting(); |
| |
| // Sets a callback that will be invoked in tests when the first snapshot is |
| // scheduled after StartIfEnabled() is called. This lets tests quit a RunLoop |
| // once the profiler has a chance to collect a snapshot. |
| void SetFirstSnapshotCallbackForTesting(base::OnceClosure callback); |
| |
| // Appends a switch to enable or disable profiling for the given |
| // `child_process_type` to `command_line`. |
| void AppendCommandLineSwitchForChildProcess( |
| base::CommandLine* command_line, |
| sampling_profiler::ProfilerProcessType child_process_type, |
| int child_process_id) const; |
| |
| // Returns the BrowserProcessSnapshotController or nullptr if heap profiling |
| // is disabled. |
| BrowserProcessSnapshotController* GetBrowserProcessSnapshotController() const; |
| |
| // Triggers an immediate snapshot in a child process. In the browser process, |
| // snapshots are scheduled internally by the HeapProfilerController. |
| // `process_probability_pct` and `process_index` will be recorded in the |
| // profile metadata so that samples from a single profile can be distinguished |
| // and scaled to represent the full population. |
| void TakeSnapshotInChildProcess(base::PassKey<ChildProcessSnapshotController>, |
| uint32_t process_probability_pct, |
| size_t process_index); |
| |
| // Triggers a snapshot in the child process, in order to log metrics about it, |
| // but doesn't upload it. This should be called instead of |
| // TakeSnapshotInChildProcess() for processes that are randomly excluded from |
| // a scheduled snapshot, to make sure they're included in metrics. |
| void LogMetricsWithoutSnapshotInChildProcess( |
| base::PassKey<ChildProcessSnapshotController>); |
| |
| // Allows unit tests to call AppendCommandLineSwitchForChildProcess without |
| // creating a HeapProfilerController. `snapshot_controller` should be null if |
| // profiling is disabled in the browser process. |
| static void AppendCommandLineSwitchForTesting( |
| base::CommandLine* command_line, |
| sampling_profiler::ProfilerProcessType child_process_type, |
| int child_process_id, |
| BrowserProcessSnapshotController* snapshot_controller); |
| |
| private: |
| using ProcessType = sampling_profiler::ProfilerProcessType; |
| using StoppedFlag = base::RefCountedData<base::AtomicFlag>; |
| |
| // Parameters to control the snapshot sampling and reporting. This is |
| // move-only so that it can be safely passed between threads to the static |
| // snapshot functions. |
| struct SnapshotParams { |
| // Creates params for a repeating snapshot. |
| SnapshotParams(std::optional<base::TimeDelta> mean_interval, |
| bool use_random_interval, |
| scoped_refptr<StoppedFlag> stopped, |
| ProcessType process_type, |
| base::TimeTicks profiler_creation_time, |
| base::OnceClosure on_first_snapshot_callback); |
| |
| // Creates params for a single snapshot. |
| SnapshotParams(scoped_refptr<StoppedFlag> stopped, |
| ProcessType process_type, |
| base::TimeTicks profiler_creation_time, |
| uint32_t process_probability_pct, |
| size_t process_index, |
| base::OnceClosure on_first_snapshot_callback); |
| |
| ~SnapshotParams(); |
| |
| // Move-only. |
| SnapshotParams(const SnapshotParams& other) = delete; |
| SnapshotParams& operator=(const SnapshotParams& other) = delete; |
| SnapshotParams(SnapshotParams&& other); |
| SnapshotParams& operator=(SnapshotParams&& other); |
| |
| // Mean interval until the next snapshot or nullopt if the params are used |
| // only for a single snapshot. |
| std::optional<base::TimeDelta> mean_interval; |
| |
| // If true, generate a random time centered around `mean_interval`. |
| // Otherwise use `mean_interval` exactly. |
| bool use_random_interval = false; |
| |
| // Atomic flag to signal that no more snapshots should be taken. |
| scoped_refptr<StoppedFlag> stopped; |
| |
| // Process being sampled. |
| ProcessType process_type = ProcessType::kUnknown; |
| |
| // Time the profiler was created. |
| base::TimeTicks profiler_creation_time; |
| |
| // Metadata to record with the profile. The default values are correct for |
| // the browser process, where one HeapProfiler always samples one process. |
| uint32_t process_probability_pct = 100; |
| size_t process_index = 0; |
| |
| // A callback to invoke for the first snapshot. Will be null for the |
| // following snapshots. For testing. |
| base::OnceClosure on_first_snapshot_callback; |
| |
| // A callback to trigger snapshots in all known child processes. Only used |
| // in the browser process. |
| base::RepeatingClosure trigger_child_process_snapshot_closure; |
| }; |
| |
| // Shared implementation of AppendCommandLineSwitchForChildProcess and |
| // AppendCommandLineSwitchForTesting. |
| static void AppendCommandLineSwitchInternal( |
| base::CommandLine* command_line, |
| ProcessType child_process_type, |
| int child_process_id, |
| BrowserProcessSnapshotController* snapshot_controller); |
| |
| // Schedules the next call to TakeSnapshot. |
| static void ScheduleNextSnapshot(SnapshotParams params); |
| |
| // Takes a heap snapshot unless the `params.stopped` flag is set. If this is |
| // the first call to TakeSnapshot() after StartIfEnabled(), invokes |
| // `on_snapshot_callback` first. |
| static void TakeSnapshot(SnapshotParams params); |
| |
| // Processes the most recent snapshot and sends it to CallStackProfileBuilder. |
| static void RetrieveAndSendSnapshot( |
| ProcessType process_type, |
| base::TimeDelta time_since_profiler_creation, |
| uint32_t process_probability_pct, |
| size_t process_index); |
| |
| const ProcessType process_type_; |
| bool profiling_enabled_; |
| |
| // Group name for the synthetic field trial, or nullopt for none. |
| std::optional<std::string> synthetic_field_trial_group_; |
| |
| // Stores the time the HeapProfilerController was created, which will be close |
| // to the process creation time. This is used instead of |
| // base::Process::CreationTime() to get a TimeTicks value which won't be |
| // affected by clock skew. |
| const base::TimeTicks creation_time_ = base::TimeTicks::Now(); |
| |
| SEQUENCE_CHECKER(sequence_checker_); |
| |
| // This flag is set when the HeapProfilerController is torn down, to stop |
| // profiling. It is the only member that should be referenced by the static |
| // functions, to be sure that they can run on the thread pool while |
| // HeapProfilerController is deleted on the main thread. |
| scoped_refptr<StoppedFlag> stopped_ GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // A task runner to trigger snapshots in the background. |
| scoped_refptr<base::SequencedTaskRunner> snapshot_task_runner_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // A controller that notifies the HeapProfilerController in child processes to |
| // take a snapshot at the same time as this HeapProfilerController. Created |
| // only in the browser process. |
| std::unique_ptr<BrowserProcessSnapshotController> |
| browser_process_snapshot_controller_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // If true, the sampling interval and time between samples won't have any |
| // random variance added so that tests are repeatable. |
| bool suppress_randomness_for_testing_ GUARDED_BY_CONTEXT(sequence_checker_) = |
| false; |
| |
| // A callback to call before the first scheduled snapshot in tests. |
| base::OnceClosure on_first_snapshot_callback_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| }; |
| |
| } // namespace heap_profiling |
| |
| #endif // COMPONENTS_HEAP_PROFILING_IN_PROCESS_HEAP_PROFILER_CONTROLLER_H_ |