OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/trace_event/memory_dump_scheduler.h" | |
6 | |
7 #include "base/process/process_metrics.h" | |
8 #include "base/single_thread_task_runner.h" | |
9 #include "base/threading/thread_task_runner_handle.h" | |
10 #include "base/trace_event/memory_dump_manager.h" | |
11 #include "build/build_config.h" | |
12 | |
13 namespace base { | |
14 namespace trace_event { | |
15 | |
16 namespace { | |
17 const uint32_t kMemoryTotalsPollingInterval = 25; | |
18 uint32_t g_polling_interval_for_testing = 0; | |
19 } // namespace | |
20 | |
21 MemoryDumpScheduler::MemoryDumpScheduler( | |
22 MemoryDumpManager* mdm, | |
23 scoped_refptr<SingleThreadTaskRunner> polling_task_runner) | |
24 : periodic_dump_scheduler_(mdm), | |
25 peak_dump_scheduler_(mdm, polling_task_runner) {} | |
26 | |
27 MemoryDumpScheduler::~MemoryDumpScheduler() {} | |
28 | |
29 void MemoryDumpScheduler::AddTrigger(MemoryDumpType trigger_type, | |
30 MemoryDumpLevelOfDetail level_of_detail, | |
31 uint32_t min_time_between_dumps_ms) { | |
32 if (trigger_type == MemoryDumpType::PEAK_MEMORY_USAGE) { | |
33 DCHECK(!periodic_dump_scheduler_.is_setup()); | |
34 peak_dump_scheduler_.AddTrigger(level_of_detail, min_time_between_dumps_ms); | |
35 } else if (trigger_type == MemoryDumpType::PERIODIC_INTERVAL) { | |
36 DCHECK(!peak_dump_scheduler_.is_setup()); | |
37 periodic_dump_scheduler_.AddTrigger(level_of_detail, | |
38 min_time_between_dumps_ms); | |
39 } | |
40 } | |
41 | |
42 void MemoryDumpScheduler::NotifyPeriodicTriggerSupported() { | |
43 periodic_dump_scheduler_.NotifySupported(); | |
44 } | |
45 | |
46 void MemoryDumpScheduler::NotifyPollingSupported() { | |
47 peak_dump_scheduler_.NotifySupported(); | |
48 } | |
49 | |
50 void MemoryDumpScheduler::DisableAllTriggers() { | |
51 periodic_dump_scheduler_.Disable(); | |
52 peak_dump_scheduler_.Disable(); | |
53 } | |
54 | |
55 // static | |
56 void MemoryDumpScheduler::SetPollingIntervalForTesting(uint32_t interval) { | |
57 g_polling_interval_for_testing = interval; | |
58 } | |
59 | |
60 bool MemoryDumpScheduler::IsPeriodicTimerRunningForTesting() { | |
61 return periodic_dump_scheduler_.IsTimerRunning(); | |
62 } | |
63 | |
64 MemoryDumpScheduler::PeriodicDumpScheduler::PeriodicDumpScheduler( | |
65 MemoryDumpManager* mdm) | |
66 : mdm_(mdm), | |
67 is_setup_(false), | |
68 dump_count_(0), | |
69 min_timer_period_ms_(std::numeric_limits<uint32_t>::max()), | |
70 light_dumps_rate_(0), | |
71 heavy_dumps_rate_(0), | |
72 light_dump_period_ms(0), | |
73 heavy_dump_period_ms(0) {} | |
74 | |
75 MemoryDumpScheduler::PeriodicDumpScheduler::~PeriodicDumpScheduler() { | |
76 DCHECK(!IsTimerRunning()); | |
77 } | |
78 | |
79 void MemoryDumpScheduler::PeriodicDumpScheduler::AddTrigger( | |
80 MemoryDumpLevelOfDetail level_of_detail, | |
81 uint32_t min_time_between_dumps_ms) { | |
82 is_setup_ = true; | |
83 DCHECK_NE(0u, min_time_between_dumps_ms); | |
84 switch (level_of_detail) { | |
85 case MemoryDumpLevelOfDetail::BACKGROUND: | |
86 break; | |
87 case MemoryDumpLevelOfDetail::LIGHT: | |
88 DCHECK_EQ(0u, light_dump_period_ms); | |
89 light_dump_period_ms = min_time_between_dumps_ms; | |
90 break; | |
91 case MemoryDumpLevelOfDetail::DETAILED: | |
92 DCHECK_EQ(0u, heavy_dump_period_ms); | |
93 heavy_dump_period_ms = min_time_between_dumps_ms; | |
94 break; | |
95 } | |
96 min_timer_period_ms_ = | |
97 std::min(min_timer_period_ms_, min_time_between_dumps_ms); | |
98 } | |
99 | |
100 void MemoryDumpScheduler::PeriodicDumpScheduler::NotifySupported() { | |
101 if (!is_setup_) | |
102 return; | |
103 DCHECK_EQ(0u, light_dump_period_ms % min_timer_period_ms_); | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
I'd move these checks to AddTrigger, so the stackt
ssid
2017/02/14 02:05:40
Um, we will be doing the check multiple times when
| |
104 light_dumps_rate_ = light_dump_period_ms / min_timer_period_ms_; | |
105 DCHECK_EQ(0u, heavy_dump_period_ms % min_timer_period_ms_); | |
106 heavy_dumps_rate_ = heavy_dump_period_ms / min_timer_period_ms_; | |
107 | |
108 dump_count_ = 0; | |
109 timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(min_timer_period_ms_), | |
110 Bind(&MemoryDumpScheduler::PeriodicDumpScheduler:: | |
111 RequestPeriodicGlobalDump, | |
112 Unretained(this))); | |
113 } | |
114 | |
115 void MemoryDumpScheduler::PeriodicDumpScheduler::Disable() { | |
116 if (timer_.IsRunning()) | |
117 timer_.Stop(); | |
118 } | |
119 | |
120 void MemoryDumpScheduler::PeriodicDumpScheduler::RequestPeriodicGlobalDump() { | |
121 MemoryDumpLevelOfDetail level_of_detail = MemoryDumpLevelOfDetail::BACKGROUND; | |
122 if (light_dumps_rate_ > 0 && dump_count_ % light_dumps_rate_ == 0) | |
123 level_of_detail = MemoryDumpLevelOfDetail::LIGHT; | |
124 if (heavy_dumps_rate_ > 0 && dump_count_ % heavy_dumps_rate_ == 0) | |
125 level_of_detail = MemoryDumpLevelOfDetail::DETAILED; | |
126 ++dump_count_; | |
127 | |
128 mdm_->RequestGlobalDump(MemoryDumpType::PERIODIC_INTERVAL, level_of_detail); | |
129 } | |
130 | |
131 MemoryDumpScheduler::PeakDumpScheduler::PeakDumpScheduler( | |
132 MemoryDumpManager* mdm, | |
133 scoped_refptr<SingleThreadTaskRunner> polling_task_runner) | |
134 : mdm_(mdm), | |
135 is_setup_(0), | |
136 dump_mode_(MemoryDumpLevelOfDetail::FIRST), | |
137 polling_task_runner_(polling_task_runner), | |
138 polling_interval_(g_polling_interval_for_testing | |
139 ? g_polling_interval_for_testing | |
140 : kMemoryTotalsPollingInterval), | |
141 min_polls_between_dumps_(0), | |
142 num_polls_from_last_dump_(0), | |
143 last_dump_memory_total_(0) { | |
144 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ | |
145 defined(OS_ANDROID) | |
146 // Set threshold to 1% of total system memory. | |
147 SystemMemoryInfoKB meminfo; | |
148 bool res = GetSystemMemoryInfo(&meminfo); | |
149 if (res) | |
150 memory_increase_threshold_ = (meminfo.total / 100) * 1024; | |
151 #else | |
152 memory_increase_threshold_ = 50 * 1024 * 1024; // 50MiB | |
153 #endif | |
154 } | |
155 | |
156 MemoryDumpScheduler::PeakDumpScheduler::~PeakDumpScheduler() { | |
157 DCHECK(!polling_task_runner_) << "Polling was not disabled safely"; | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
remove the string comment, the code in the dcheck
ssid
2017/02/14 02:05:40
Done.
| |
158 } | |
159 | |
160 void MemoryDumpScheduler::PeakDumpScheduler::AddTrigger( | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
shouldn't this method just be called Configure() o
ssid
2017/02/14 02:05:40
Removed this method since this is a struct.
| |
161 MemoryDumpLevelOfDetail level_of_detail, | |
162 uint32_t min_time_between_dumps_ms) { | |
163 DCHECK(!is_setup_); | |
164 DCHECK_NE(0u, min_time_between_dumps_ms); | |
165 | |
166 dump_mode_ = level_of_detail; | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
just call the variable level_of_detail_ ? never be
ssid
2017/02/14 02:05:40
Done.
| |
167 min_polls_between_dumps_ = | |
168 (min_time_between_dumps_ms + polling_interval_ - 1) / polling_interval_; | |
169 is_setup_ = true; | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
s/is_setup_/is_initialized_/ (or is_configured_)
ssid
2017/02/14 02:05:40
Done.
| |
170 } | |
171 | |
172 void MemoryDumpScheduler::PeakDumpScheduler::NotifySupported() { | |
173 if (!is_setup_) | |
174 return; | |
175 num_polls_from_last_dump_ = 0; | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:20
can you add some logic (either a check or a return
ssid
2017/02/14 02:05:40
Done.
| |
176 last_dump_memory_total_ = 0; | |
177 polling_task_runner_->PostTask( | |
178 FROM_HERE, | |
179 Bind(&MemoryDumpScheduler::PeakDumpScheduler::PollMemoryOnPollingThread, | |
180 Unretained(this))); | |
181 } | |
182 | |
183 void MemoryDumpScheduler::PeakDumpScheduler::Disable() { | |
184 if (ThreadTaskRunnerHandle::Get() != polling_task_runner_) { | |
185 if (polling_task_runner_->PostTask( | |
186 FROM_HERE, Bind(&MemoryDumpScheduler::PeakDumpScheduler::Disable, | |
187 Unretained(this)))) | |
188 return; | |
189 } | |
190 is_setup_ = false; | |
191 polling_task_runner_ = nullptr; | |
192 } | |
193 | |
194 void MemoryDumpScheduler::PeakDumpScheduler::PollMemoryOnPollingThread() { | |
195 if (!is_setup_) | |
196 return; | |
197 | |
198 uint64_t metric = 0; | |
199 bool res = mdm_->PollFastMemoryTotal(&metric); | |
200 DCHECK(res); | |
201 if (dump_mode_ == MemoryDumpLevelOfDetail::DETAILED) { | |
202 TRACE_COUNTER1(MemoryDumpManager::kTraceCategory, "MemoryMetricMB", | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
MemoryMetricMB -> PolledMemoryMB ?
ssid
2017/02/14 02:05:40
Done.
| |
203 metric / 1024 / 1024); | |
204 } | |
205 | |
206 if (ShouldTriggerDump(metric)) | |
207 mdm_->RequestGlobalDump(MemoryDumpType::PEAK_MEMORY_USAGE, dump_mode_); | |
208 | |
209 // TODO(ssid): Use RequestSchedulerCallback, crbug.com/607533. | |
210 ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
211 FROM_HERE, | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
out of curiosity why reposting instead of using Ti
ssid
2017/02/14 02:05:40
I did not use timer here because in future we plan
| |
212 Bind(&MemoryDumpScheduler::PeakDumpScheduler::PollMemoryOnPollingThread, | |
213 Unretained(this)), | |
214 TimeDelta::FromMilliseconds(polling_interval_)); | |
215 } | |
216 | |
217 bool MemoryDumpScheduler::PeakDumpScheduler::ShouldTriggerDump( | |
218 uint64_t current_memory_total) { | |
219 if (current_memory_total == 0) | |
220 return false; | |
221 | |
222 bool should_dump = false; | |
223 ++num_polls_from_last_dump_; | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
can you make all this in early-out style, without
ssid
2017/02/14 02:05:40
The code would look like:
if (polling_state_.las
| |
224 if (last_dump_memory_total_ == 0) { | |
225 // If it's first sample then trigger memory dump. | |
226 should_dump |= true; | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
well, really no need for the |= here :) just = tru
ssid
2017/02/14 02:05:40
Done.
| |
227 } else if (min_polls_between_dumps_ > num_polls_from_last_dump_) { | |
228 return false; | |
229 } | |
230 | |
231 int64_t increase_from_last_dump = | |
232 current_memory_total - last_dump_memory_total_; | |
233 should_dump |= increase_from_last_dump > memory_increase_threshold_; | |
234 if (should_dump) { | |
235 last_dump_memory_total_ = current_memory_total; | |
236 num_polls_from_last_dump_ = 0; | |
237 TRACE_EVENT_INSTANT1(MemoryDumpManager::kTraceCategory, | |
238 "Peak memory dump Triggered", | |
Primiano Tucci (use gerrit)
2017/02/03 20:08:19
I'd move this above in Poll...OnThread, so this is
ssid
2017/02/14 02:05:40
Done.
| |
239 TRACE_EVENT_SCOPE_PROCESS, "total_usage_MB", | |
240 current_memory_total / 1024 / 1024); | |
241 } | |
242 return should_dump; | |
243 } | |
244 | |
245 } // namespace trace_event | |
246 } // namespace base | |
OLD | NEW |