| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef BASE_TRACE_EVENT_MEMORY_PEAK_DETECTOR_H_ |
| #define BASE_TRACE_EVENT_MEMORY_PEAK_DETECTOR_H_ |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/base_export.h" |
| #include "base/callback.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| |
| namespace base { |
| |
| class SequencedTaskRunner; |
| |
| namespace trace_event { |
| |
| struct MemoryDumpProviderInfo; |
| |
| // Detects temporally local memory peaks. Peak detection is based on |
| // continuously querying memory usage using MemoryDumpprovider(s) that support |
| // fast polling (e.g., ProcessMetricsDumpProvider which under the hoods reads |
| // /proc/PID/statm on Linux) and using a combination of: |
| // - An static threshold (currently 1% of total system memory). |
| // - Sliding window stddev analysis. |
| // Design doc: https://goo.gl/0kOU4A . |
| // This class is NOT thread-safe, the caller has to ensure linearization of |
| // the calls to the public methods. In any case, the public methods do NOT have |
| // to be called from the |task_runner| on which the polling tasks run. |
| class BASE_EXPORT MemoryPeakDetector { |
| public: |
| using OnPeakDetectedCallback = RepeatingClosure; |
| using DumpProvidersList = std::vector<scoped_refptr<MemoryDumpProviderInfo>>; |
| using GetDumpProvidersFunction = RepeatingCallback<void(DumpProvidersList*)>; |
| |
| enum State { |
| NOT_INITIALIZED = 0, // Before Setup() |
| DISABLED, // Before Start() or after Stop(). |
| ENABLED, // After Start() but no dump_providers_ are available. |
| RUNNING // After Start(). The PollMemoryAndDetectPeak() task is scheduled. |
| }; |
| |
| // Peak detector configuration, passed to Start(). |
| struct BASE_EXPORT Config { |
| Config(); |
| Config(uint32_t polling_interval_ms, |
| uint32_t min_time_between_peaks_ms, |
| bool enable_verbose_poll_tracing); |
| |
| // The rate at which memory will be polled. Polls will happen on the task |
| // runner passed to Setup(). |
| uint32_t polling_interval_ms; |
| |
| // Two consecutive peak detection callbacks will happen at least |
| // |min_time_between_peaks_ms| apart from each other. |
| uint32_t min_time_between_peaks_ms; |
| |
| // When enabled causes a TRACE_COUNTER event to be injected in the trace |
| // for each poll (if tracing is enabled). |
| bool enable_verbose_poll_tracing; |
| }; |
| |
| static MemoryPeakDetector* GetInstance(); |
| |
| // Configures the peak detector, binding the polling tasks on the given |
| // thread. Setup() can be called several times, provided that: (1) Stop() |
| // is called; (2a) the previous task_runner is flushed or (2b) the task_runner |
| // remains the same. |
| // GetDumpProvidersFunction: is the function that will be invoked to get |
| // an updated list of polling-capable dump providers. This is really just |
| // MemoryDumpManager::GetDumpProvidersForPolling, but this extra level of |
| // indirection allows easier testing. |
| // SequencedTaskRunner: the task runner where PollMemoryAndDetectPeak() will |
| // be periodically called. |
| // OnPeakDetectedCallback: a callback that will be invoked on the |
| // given task runner when a memory peak is detected. |
| void Setup(const GetDumpProvidersFunction&, |
| const scoped_refptr<SequencedTaskRunner>&, |
| const OnPeakDetectedCallback&); |
| |
| // Releases the |task_runner_| and the bound callbacks. |
| void TearDown(); |
| |
| // This posts a task onto the passed task runner which refreshes the list of |
| // dump providers via the GetDumpProvidersFunction. If at least one dump |
| // provider is available, this starts immediately polling on the task runner. |
| // If not, the detector remains in the ENABLED state and will start polling |
| // automatically (i.e. without requiring another call to Start()) on the |
| // next call to NotifyMemoryDumpProvidersChanged(). |
| void Start(Config); |
| |
| // Stops the polling on the task runner (if was active at all). This doesn't |
| // wait for the task runner to drain pending tasks, so it is possible that |
| // a polling will happen concurrently (or in the immediate future) with the |
| // Stop() call. It is responsibility of the caller to drain or synchronize |
| // with the task runner. |
| void Stop(); |
| |
| // If Start()-ed, prevents that a peak callback is triggered before the next |
| // |min_time_between_peaks_ms|. No-op if the peak detector is not enabled. |
| void Throttle(); |
| |
| // Used by MemoryDumpManager to notify that the list of polling-capable dump |
| // providers has changed. The peak detector will reload the list on the next |
| // polling task. This function can be called before Setup(), in which |
| // case will be just a no-op. |
| void NotifyMemoryDumpProvidersChanged(); |
| |
| void SetStaticThresholdForTesting(uint64_t static_threshold_bytes); |
| |
| private: |
| friend class MemoryPeakDetectorTest; |
| |
| static constexpr uint32_t kSlidingWindowNumSamples = 50; |
| |
| MemoryPeakDetector(); |
| ~MemoryPeakDetector(); |
| |
| // All these methods are always called on the |task_runner_|. |
| void StartInternal(Config); |
| void StopInternal(); |
| void TearDownInternal(); |
| void ReloadDumpProvidersAndStartPollingIfNeeded(); |
| void PollMemoryAndDetectPeak(uint32_t expected_generation); |
| bool DetectPeakUsingSlidingWindowStddev(uint64_t last_sample_bytes); |
| void ResetPollHistory(bool keep_last_sample = false); |
| |
| // It is safe to call these testing methods only on the |task_runner_|. |
| State state_for_testing() const { return state_; } |
| uint32_t poll_tasks_count_for_testing() const { |
| return poll_tasks_count_for_testing_; |
| } |
| |
| // The task runner where all the internal calls are posted onto. This field |
| // must be NOT be accessed by the tasks posted on the |task_runner_| because |
| // there might still be outstanding tasks on the |task_runner_| while this |
| // refptr is reset. This can only be safely accessed by the public methods |
| // above, which the client of this class is supposed to call sequentially. |
| scoped_refptr<SequencedTaskRunner> task_runner_; |
| |
| // After the Setup() call, the fields below, must be accessed only from |
| // the |task_runner_|. |
| |
| // Bound function to get an updated list of polling-capable dump providers. |
| GetDumpProvidersFunction get_dump_providers_function_; |
| |
| // The callback to invoke when peaks are detected. |
| OnPeakDetectedCallback on_peak_detected_callback_; |
| |
| // List of polling-aware dump providers to invoke upon each poll. |
| DumpProvidersList dump_providers_; |
| |
| // The generation is incremented every time the |state_| is changed and causes |
| // PollMemoryAndDetectPeak() to early out if the posted task doesn't match the |
| // most recent |generation_|. This allows to drop on the floor outstanding |
| // PostDelayedTask that refer to an old sequence that was later Stop()-ed or |
| // disabled because of NotifyMemoryDumpProvidersChanged(). |
| uint32_t generation_; |
| |
| State state_; |
| |
| // Config passed to Start(), only valid when |state_| = {ENABLED, RUNNING}. |
| Config config_; |
| |
| uint64_t static_threshold_bytes_; |
| uint32_t skip_polls_; |
| uint64_t last_dump_memory_total_; |
| uint64_t samples_bytes_[kSlidingWindowNumSamples]; |
| uint32_t samples_index_; |
| uint32_t poll_tasks_count_for_testing_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MemoryPeakDetector); |
| }; |
| |
| } // namespace trace_event |
| } // namespace base |
| |
| #endif // BASE_TRACE_EVENT_MEMORY_PEAK_DETECTOR_H_ |