// 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 #include #include #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>; using GetDumpProvidersFunction = RepeatingCallback; 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&, 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 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_