OLD | NEW |
---|---|
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef BASE_TRACE_EVENT_MEMORY_PEAK_DETECTOR_H_ | 5 #ifndef BASE_TRACE_EVENT_MEMORY_PEAK_DETECTOR_H_ |
6 #define BASE_TRACE_EVENT_MEMORY_PEAK_DETECTOR_H_ | 6 #define BASE_TRACE_EVENT_MEMORY_PEAK_DETECTOR_H_ |
7 | 7 |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include <memory> | 10 #include <memory> |
11 #include <vector> | 11 #include <vector> |
12 | 12 |
13 #include "base/base_export.h" | 13 #include "base/base_export.h" |
14 #include "base/callback.h" | 14 #include "base/callback.h" |
15 #include "base/macros.h" | 15 #include "base/macros.h" |
16 #include "base/memory/ref_counted.h" | 16 #include "base/memory/ref_counted.h" |
17 | 17 |
18 namespace base { | 18 namespace base { |
19 | 19 |
20 class SequencedTaskRunner; | 20 class SequencedTaskRunner; |
21 | 21 |
22 namespace trace_event { | 22 namespace trace_event { |
23 | 23 |
24 struct MemoryDumpProviderInfo; | 24 struct MemoryDumpProviderInfo; |
25 | 25 |
26 // Detects temporally local memory peaks. Peak detection is based on | |
27 // continuously querying memory usage using MemoryDumpprovider(s) that support | |
28 // fast polling (e.g., ProcessMetricsDumpProvider which under the hoods reads | |
29 // /proc/PID/statm on Linux) and using a cobination of: | |
30 // - An static threshold (currently 1% of total system memory). | |
31 // - Sliding window stddev analysis. | |
32 // Design doc: https://goo.gl/0kOU4A . | |
26 // This class is NOT thread-safe, the caller has to ensure linearization of | 33 // This class is NOT thread-safe, the caller has to ensure linearization of |
27 // the calls to the public methods. In any case, the public methods do NOT have | 34 // the calls to the public methods. In any case, the public methods do NOT have |
28 // to be called from the |task_runner| on which the polling tasks run. | 35 // to be called from the |task_runner| on which the polling tasks run. |
29 class BASE_EXPORT MemoryPeakDetector { | 36 class BASE_EXPORT MemoryPeakDetector { |
30 public: | 37 public: |
31 using OnPeakDetectedCallback = RepeatingClosure; | 38 using OnPeakDetectedCallback = RepeatingClosure; |
32 using DumpProvidersList = std::vector<scoped_refptr<MemoryDumpProviderInfo>>; | 39 using DumpProvidersList = std::vector<scoped_refptr<MemoryDumpProviderInfo>>; |
33 using GetDumpProvidersFunction = RepeatingCallback<void(DumpProvidersList*)>; | 40 using GetDumpProvidersFunction = RepeatingCallback<void(DumpProvidersList*)>; |
34 | 41 |
35 enum State { | 42 enum State { |
36 NOT_INITIALIZED = 0, // Before Setup() | 43 NOT_INITIALIZED = 0, // Before Setup() |
37 DISABLED, // Before Start() or after Stop(). | 44 DISABLED, // Before Start() or after Stop(). |
38 ENABLED, // After Start() but no dump_providers_ are available. | 45 ENABLED, // After Start() but no dump_providers_ are available. |
39 RUNNING // After Start(). The PollMemoryAndDetectPeak() task is scheduled. | 46 RUNNING // After Start(). The PollMemoryAndDetectPeak() task is scheduled. |
40 }; | 47 }; |
41 | 48 |
49 // Peak detector configuration, passed to Start(). | |
50 struct Config { | |
51 uint32_t polling_interval_ms; | |
52 uint32_t min_time_between_peaks_ms; | |
53 bool enable_verbose_poll_tracing; | |
ssid
2017/04/05 21:23:47
Nice to have some comment on the config members.
Primiano Tucci (use gerrit)
2017/04/06 20:07:10
done
| |
54 }; | |
55 | |
42 static MemoryPeakDetector* GetInstance(); | 56 static MemoryPeakDetector* GetInstance(); |
43 | 57 |
44 // Configures the peak detector, binding the polling tasks on the given | 58 // Configures the peak detector, binding the polling tasks on the given |
45 // thread. Setup() can be called several times, provided that: (1) Stop() | 59 // thread. Setup() can be called several times, provided that: (1) Stop() |
46 // is called; (2a) the previous task_runner is flushed or (2b) the task_runner | 60 // is called; (2a) the previous task_runner is flushed or (2b) the task_runner |
47 // remains the same. | 61 // remains the same. |
48 // GetDumpProvidersFunction: is the function that will be invoked to get | 62 // GetDumpProvidersFunction: is the function that will be invoked to get |
49 // an updated list of polling-capable dump providers. This is really just | 63 // an updated list of polling-capable dump providers. This is really just |
50 // MemoryDumpManager::GetDumpProvidersForPolling, but this extra level of | 64 // MemoryDumpManager::GetDumpProvidersForPolling, but this extra level of |
51 // indirection allows easier testing. | 65 // indirection allows easier testing. |
52 // SequencedTaskRunner: the task runner where PollMemoryAndDetectPeak() will | 66 // SequencedTaskRunner: the task runner where PollMemoryAndDetectPeak() will |
53 // be periodically called. | 67 // be periodically called. |
54 // OnPeakDetectedCallback: a callback that will be invoked on the | 68 // OnPeakDetectedCallback: a callback that will be invoked on the |
55 // given task runner when a memory peak is detected. | 69 // given task runner when a memory peak is detected. |
56 void Setup(const GetDumpProvidersFunction&, | 70 void Setup(const GetDumpProvidersFunction&, |
57 const scoped_refptr<SequencedTaskRunner>&, | 71 const scoped_refptr<SequencedTaskRunner>&, |
58 const OnPeakDetectedCallback&); | 72 const OnPeakDetectedCallback&); |
59 | 73 |
60 // Releases the |task_runner_| and the bound callbacks. | 74 // Releases the |task_runner_| and the bound callbacks. |
61 void TearDown(); | 75 void TearDown(); |
62 | 76 |
63 // This posts a task onto the passed task runner which refreshes the list of | 77 // This posts a task onto the passed task runner which refreshes the list of |
64 // dump providers via the GetDumpProvidersFunction. If at least one dump | 78 // dump providers via the GetDumpProvidersFunction. If at least one dump |
65 // provider is available, this starts immediately polling on the task runner. | 79 // provider is available, this starts immediately polling on the task runner. |
66 // If not, the detector remains in the ENABLED state and will start polling | 80 // If not, the detector remains in the ENABLED state and will start polling |
67 // automatically (i.e. without requiring another call to Start()) on the | 81 // automatically (i.e. without requiring another call to Start()) on the |
68 // next call to NotifyMemoryDumpProvidersChanged(). | 82 // next call to NotifyMemoryDumpProvidersChanged(). |
69 void Start(); | 83 void Start(Config); |
70 | 84 |
71 // Stops the polling on the task runner (if was active at all). This doesn't | 85 // Stops the polling on the task runner (if was active at all). This doesn't |
72 // wait for the task runner to drain pending tasks, so it is possible that | 86 // wait for the task runner to drain pending tasks, so it is possible that |
73 // a polling will happen concurrently (or in the immediate future) with the | 87 // a polling will happen concurrently (or in the immediate future) with the |
74 // Stop() call. It is responsibility of the caller to drain or synchronize | 88 // Stop() call. It is responsibility of the caller to drain or synchronize |
75 // with the task runner. | 89 // with the task runner. |
76 void Stop(); | 90 void Stop(); |
77 | 91 |
92 // Clears the history of the peak detector, both for the sliding window and | |
93 // static threshold logic, if Start()-ed, otherwise it's a no-op. The intended | |
94 // use case is to prevent that a memory dump is triggered due to peak | |
95 // detection soon after another dump has been triggered for other reasons. | |
96 void Clear(); | |
ssid
2017/04/05 21:23:47
MemoryPeakDetector::Clear() sounds like this would
Primiano Tucci (use gerrit)
2017/04/06 20:07:10
But the comment says "If start()-ed, otherwise it'
ssid
2017/04/10 20:55:06
I tried to explain this in the comment below.
Primiano Tucci (use gerrit)
2017/04/11 11:12:09
See my other comment. That suggests to me that we
| |
97 | |
78 // Used by MemoryDumpManager to notify that the list of polling-capable dump | 98 // Used by MemoryDumpManager to notify that the list of polling-capable dump |
79 // providers has changed. The peak detector will reload the list on the next | 99 // providers has changed. The peak detector will reload the list on the next |
80 // polling task. This function can be called before Setup(), in which | 100 // polling task. This function can be called before Setup(), in which |
81 // case will be just a no-op. | 101 // case will be just a no-op. |
82 void NotifyMemoryDumpProvidersChanged(); | 102 void NotifyMemoryDumpProvidersChanged(); |
83 | 103 |
104 void SetStaticThresholdForTesting(uint64_t static_threshold_bytes); | |
105 | |
84 private: | 106 private: |
85 friend class MemoryPeakDetectorTest; | 107 friend class MemoryPeakDetectorTest; |
86 | 108 |
109 static constexpr uint32_t kSlidingWindowNumSamples = 50; | |
110 | |
87 MemoryPeakDetector(); | 111 MemoryPeakDetector(); |
88 ~MemoryPeakDetector(); | 112 ~MemoryPeakDetector(); |
89 | 113 |
90 // All these methods are always called on the |task_runner_|. | 114 // All these methods are always called on the |task_runner_|. |
91 void StartInternal(); | 115 void StartInternal(Config); |
92 void StopInternal(); | 116 void StopInternal(); |
93 void TearDownInternal(); | 117 void TearDownInternal(); |
94 void ReloadDumpProvidersAndStartPollingIfNeeded(); | 118 void ReloadDumpProvidersAndStartPollingIfNeeded(); |
95 void PollMemoryAndDetectPeak(uint32_t expected_generation); | 119 void PollMemoryAndDetectPeak(uint32_t expected_generation); |
120 bool DetectPeakUsingSlidingWindowStddev(uint64_t last_sample_bytes); | |
121 void ResetPollHistory(); | |
96 | 122 |
97 // It is safe to call these testing methods only on the |task_runner_|. | 123 // It is safe to call these testing methods only on the |task_runner_|. |
98 State state_for_testing() const { return state_; } | 124 State state_for_testing() const { return state_; } |
99 uint32_t poll_tasks_count_for_testing() const { | 125 uint32_t poll_tasks_count_for_testing() const { |
100 return poll_tasks_count_for_testing_; | 126 return poll_tasks_count_for_testing_; |
101 } | 127 } |
102 | 128 |
103 // The task runner where all the internal calls are posted onto. This field | 129 // The task runner where all the internal calls are posted onto. This field |
104 // must be NOT be accessed by the tasks posted on the |task_runner_| because | 130 // must be NOT be accessed by the tasks posted on the |task_runner_| because |
105 // there might still be outstanding tasks on the |task_runner_| while this | 131 // there might still be outstanding tasks on the |task_runner_| while this |
(...skipping 14 matching lines...) Expand all Loading... | |
120 DumpProvidersList dump_providers_; | 146 DumpProvidersList dump_providers_; |
121 | 147 |
122 // The generation is incremented every time the |state_| is changed and causes | 148 // The generation is incremented every time the |state_| is changed and causes |
123 // PollMemoryAndDetectPeak() to early out if the posted task doesn't match the | 149 // PollMemoryAndDetectPeak() to early out if the posted task doesn't match the |
124 // most recent |generation_|. This allows to drop on the floor outstanding | 150 // most recent |generation_|. This allows to drop on the floor outstanding |
125 // PostDelayedTask that refer to an old sequence that was later Stop()-ed or | 151 // PostDelayedTask that refer to an old sequence that was later Stop()-ed or |
126 // disabled because of NotifyMemoryDumpProvidersChanged(). | 152 // disabled because of NotifyMemoryDumpProvidersChanged(). |
127 uint32_t generation_; | 153 uint32_t generation_; |
128 | 154 |
129 State state_; | 155 State state_; |
130 uint32_t polling_interval_ms_; | 156 |
157 // Config passed to Start(), only valid when |state_| = {ENABLED, RUNNING}. | |
158 Config config_; | |
159 | |
160 int64_t static_threshold_bytes_; | |
161 uint32_t skip_polls_; | |
162 uint64_t last_dump_memory_total_; | |
163 uint64_t samples_bytes_[kSlidingWindowNumSamples]; | |
ssid
2017/04/05 21:23:47
Samples were stored in kb because of the overflows
Primiano Tucci (use gerrit)
2017/04/06 20:07:10
I moved the stddev to floats, should be fine now.
| |
164 uint32_t samples_index_; | |
131 uint32_t poll_tasks_count_for_testing_; | 165 uint32_t poll_tasks_count_for_testing_; |
132 | 166 |
133 DISALLOW_COPY_AND_ASSIGN(MemoryPeakDetector); | 167 DISALLOW_COPY_AND_ASSIGN(MemoryPeakDetector); |
134 }; | 168 }; |
135 | 169 |
136 } // namespace trace_event | 170 } // namespace trace_event |
137 } // namespace base | 171 } // namespace base |
138 | 172 |
139 #endif // BASE_TRACE_EVENT_MEMORY_PEAK_DETECTOR_H_ | 173 #endif // BASE_TRACE_EVENT_MEMORY_PEAK_DETECTOR_H_ |
OLD | NEW |