|  | // 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_ |