forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
memory_peak_detector.h
184 lines (145 loc) · 7.24 KB
/
memory_peak_detector.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// 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_