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 #include "base/trace_event/memory_dump_scheduler.h" | 5 #include "base/trace_event/memory_dump_scheduler.h" |
6 | 6 |
7 #include "base/process/process_metrics.h" | 7 #include "base/process/process_metrics.h" |
8 #include "base/single_thread_task_runner.h" | 8 #include "base/single_thread_task_runner.h" |
9 #include "base/threading/thread_task_runner_handle.h" | 9 #include "base/threading/thread_task_runner_handle.h" |
10 #include "base/trace_event/memory_dump_manager.h" | 10 #include "base/trace_event/memory_dump_manager.h" |
(...skipping 15 matching lines...) Expand all Loading... |
26 scoped_refptr<SingleThreadTaskRunner> polling_task_runner) | 26 scoped_refptr<SingleThreadTaskRunner> polling_task_runner) |
27 : mdm_(mdm), polling_state_(polling_task_runner) {} | 27 : mdm_(mdm), polling_state_(polling_task_runner) {} |
28 | 28 |
29 MemoryDumpScheduler::~MemoryDumpScheduler() {} | 29 MemoryDumpScheduler::~MemoryDumpScheduler() {} |
30 | 30 |
31 void MemoryDumpScheduler::AddTrigger(MemoryDumpType trigger_type, | 31 void MemoryDumpScheduler::AddTrigger(MemoryDumpType trigger_type, |
32 MemoryDumpLevelOfDetail level_of_detail, | 32 MemoryDumpLevelOfDetail level_of_detail, |
33 uint32_t min_time_between_dumps_ms) { | 33 uint32_t min_time_between_dumps_ms) { |
34 if (trigger_type == MemoryDumpType::PEAK_MEMORY_USAGE) { | 34 if (trigger_type == MemoryDumpType::PEAK_MEMORY_USAGE) { |
35 DCHECK(!periodic_state_.is_configured); | 35 DCHECK(!periodic_state_.is_configured); |
36 DCHECK(!polling_state_.is_configured); | 36 DCHECK_EQ(PollingTriggerState::DISABLED, polling_state_.current_state); |
37 DCHECK_NE(0u, min_time_between_dumps_ms); | 37 DCHECK_NE(0u, min_time_between_dumps_ms); |
38 | 38 |
39 polling_state_.level_of_detail = level_of_detail; | 39 polling_state_.level_of_detail = level_of_detail; |
40 polling_state_.min_polls_between_dumps = | 40 polling_state_.min_polls_between_dumps = |
41 (min_time_between_dumps_ms + polling_state_.polling_interval_ms - 1) / | 41 (min_time_between_dumps_ms + polling_state_.polling_interval_ms - 1) / |
42 polling_state_.polling_interval_ms; | 42 polling_state_.polling_interval_ms; |
43 polling_state_.is_configured = true; | 43 polling_state_.current_state = PollingTriggerState::CONFIGURED; |
44 } else if (trigger_type == MemoryDumpType::PERIODIC_INTERVAL) { | 44 } else if (trigger_type == MemoryDumpType::PERIODIC_INTERVAL) { |
45 DCHECK(!polling_state_.is_configured); | 45 DCHECK_EQ(PollingTriggerState::DISABLED, polling_state_.current_state); |
46 periodic_state_.is_configured = true; | 46 periodic_state_.is_configured = true; |
47 DCHECK_NE(0u, min_time_between_dumps_ms); | 47 DCHECK_NE(0u, min_time_between_dumps_ms); |
48 switch (level_of_detail) { | 48 switch (level_of_detail) { |
49 case MemoryDumpLevelOfDetail::BACKGROUND: | 49 case MemoryDumpLevelOfDetail::BACKGROUND: |
50 break; | 50 break; |
51 case MemoryDumpLevelOfDetail::LIGHT: | 51 case MemoryDumpLevelOfDetail::LIGHT: |
52 DCHECK_EQ(0u, periodic_state_.light_dump_period_ms); | 52 DCHECK_EQ(0u, periodic_state_.light_dump_period_ms); |
53 periodic_state_.light_dump_period_ms = min_time_between_dumps_ms; | 53 periodic_state_.light_dump_period_ms = min_time_between_dumps_ms; |
54 break; | 54 break; |
55 case MemoryDumpLevelOfDetail::DETAILED: | 55 case MemoryDumpLevelOfDetail::DETAILED: |
(...skipping 20 matching lines...) Expand all Loading... |
76 periodic_state_.min_timer_period_ms; | 76 periodic_state_.min_timer_period_ms; |
77 | 77 |
78 periodic_state_.dump_count = 0; | 78 periodic_state_.dump_count = 0; |
79 periodic_state_.timer.Start( | 79 periodic_state_.timer.Start( |
80 FROM_HERE, | 80 FROM_HERE, |
81 TimeDelta::FromMilliseconds(periodic_state_.min_timer_period_ms), | 81 TimeDelta::FromMilliseconds(periodic_state_.min_timer_period_ms), |
82 Bind(&MemoryDumpScheduler::RequestPeriodicGlobalDump, Unretained(this))); | 82 Bind(&MemoryDumpScheduler::RequestPeriodicGlobalDump, Unretained(this))); |
83 } | 83 } |
84 | 84 |
85 void MemoryDumpScheduler::NotifyPollingSupported() { | 85 void MemoryDumpScheduler::NotifyPollingSupported() { |
86 if (!polling_state_.is_configured || polling_state_.is_polling_enabled) | 86 if (polling_state_.current_state != PollingTriggerState::CONFIGURED) |
87 return; | 87 return; |
88 polling_state_.is_polling_enabled = true; | |
89 for (uint32_t i = 0; i < PollingTriggerState::kMaxNumMemorySamples; ++i) | |
90 polling_state_.last_memory_totals_kb[i] = 0; | |
91 polling_state_.last_memory_totals_kb_index = 0; | |
92 polling_state_.num_polls_from_last_dump = 0; | |
93 polling_state_.last_dump_memory_total = 0; | |
94 | 88 |
95 if (!polling_state_.memory_increase_threshold) { | 89 polling_state_.current_state = PollingTriggerState::ENABLED; |
96 polling_state_.memory_increase_threshold = kDefaultMemoryIncreaseThreshold; | 90 polling_state_.ResetTotals(); |
97 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ | |
98 defined(OS_ANDROID) | |
99 // Set threshold to 1% of total system memory. | |
100 SystemMemoryInfoKB meminfo; | |
101 bool res = GetSystemMemoryInfo(&meminfo); | |
102 if (res) | |
103 polling_state_.memory_increase_threshold = (meminfo.total / 100) * 1024; | |
104 #endif | |
105 } | |
106 | 91 |
107 polling_state_.polling_task_runner->PostTask( | 92 polling_state_.polling_task_runner->PostTask( |
108 FROM_HERE, | 93 FROM_HERE, |
109 Bind(&MemoryDumpScheduler::PollMemoryOnPollingThread, Unretained(this))); | 94 Bind(&MemoryDumpScheduler::PollMemoryOnPollingThread, Unretained(this))); |
110 } | 95 } |
111 | 96 |
| 97 void MemoryDumpScheduler::NotifyDumpTriggered() { |
| 98 if (polling_state_.polling_task_runner && |
| 99 polling_state_.polling_task_runner->RunsTasksOnCurrentThread()) { |
| 100 polling_state_.polling_task_runner->PostTask( |
| 101 FROM_HERE, |
| 102 Bind(&MemoryDumpScheduler::NotifyDumpTriggered, Unretained(this))); |
| 103 return; |
| 104 } |
| 105 if (polling_state_.current_state != PollingTriggerState::ENABLED) |
| 106 return; |
| 107 |
| 108 polling_state_.ResetTotals(); |
| 109 } |
| 110 |
112 void MemoryDumpScheduler::DisableAllTriggers() { | 111 void MemoryDumpScheduler::DisableAllTriggers() { |
113 if (periodic_state_.timer.IsRunning()) | 112 if (periodic_state_.timer.IsRunning()) |
114 periodic_state_.timer.Stop(); | 113 periodic_state_.timer.Stop(); |
115 DisablePolling(); | 114 DisablePolling(); |
116 } | 115 } |
117 | 116 |
118 void MemoryDumpScheduler::DisablePolling() { | 117 void MemoryDumpScheduler::DisablePolling() { |
119 if (ThreadTaskRunnerHandle::Get() != polling_state_.polling_task_runner) { | 118 if (polling_state_.polling_task_runner->RunsTasksOnCurrentThread()) { |
120 if (polling_state_.polling_task_runner->PostTask( | 119 if (polling_state_.polling_task_runner->PostTask( |
121 FROM_HERE, | 120 FROM_HERE, |
122 Bind(&MemoryDumpScheduler::DisablePolling, Unretained(this)))) | 121 Bind(&MemoryDumpScheduler::DisablePolling, Unretained(this)))) |
123 return; | 122 return; |
124 } | 123 } |
125 polling_state_.is_polling_enabled = false; | 124 polling_state_.current_state = PollingTriggerState::DISABLED; |
126 polling_state_.is_configured = false; | |
127 polling_state_.polling_task_runner = nullptr; | 125 polling_state_.polling_task_runner = nullptr; |
128 } | 126 } |
129 | 127 |
130 // static | 128 // static |
131 void MemoryDumpScheduler::SetPollingIntervalForTesting(uint32_t interval) { | 129 void MemoryDumpScheduler::SetPollingIntervalForTesting(uint32_t interval) { |
132 g_polling_interval_ms_for_testing = interval; | 130 g_polling_interval_ms_for_testing = interval; |
133 } | 131 } |
134 | 132 |
135 bool MemoryDumpScheduler::IsPeriodicTimerRunningForTesting() { | 133 bool MemoryDumpScheduler::IsPeriodicTimerRunningForTesting() { |
136 return periodic_state_.timer.IsRunning(); | 134 return periodic_state_.timer.IsRunning(); |
137 } | 135 } |
138 | 136 |
139 void MemoryDumpScheduler::RequestPeriodicGlobalDump() { | 137 void MemoryDumpScheduler::RequestPeriodicGlobalDump() { |
140 MemoryDumpLevelOfDetail level_of_detail = MemoryDumpLevelOfDetail::BACKGROUND; | 138 MemoryDumpLevelOfDetail level_of_detail = MemoryDumpLevelOfDetail::BACKGROUND; |
141 if (periodic_state_.light_dumps_rate > 0 && | 139 if (periodic_state_.light_dumps_rate > 0 && |
142 periodic_state_.dump_count % periodic_state_.light_dumps_rate == 0) | 140 periodic_state_.dump_count % periodic_state_.light_dumps_rate == 0) |
143 level_of_detail = MemoryDumpLevelOfDetail::LIGHT; | 141 level_of_detail = MemoryDumpLevelOfDetail::LIGHT; |
144 if (periodic_state_.heavy_dumps_rate > 0 && | 142 if (periodic_state_.heavy_dumps_rate > 0 && |
145 periodic_state_.dump_count % periodic_state_.heavy_dumps_rate == 0) | 143 periodic_state_.dump_count % periodic_state_.heavy_dumps_rate == 0) |
146 level_of_detail = MemoryDumpLevelOfDetail::DETAILED; | 144 level_of_detail = MemoryDumpLevelOfDetail::DETAILED; |
147 ++periodic_state_.dump_count; | 145 ++periodic_state_.dump_count; |
148 | 146 |
149 mdm_->RequestGlobalDump(MemoryDumpType::PERIODIC_INTERVAL, level_of_detail); | 147 mdm_->RequestGlobalDump(MemoryDumpType::PERIODIC_INTERVAL, level_of_detail); |
150 } | 148 } |
151 | 149 |
152 void MemoryDumpScheduler::PollMemoryOnPollingThread() { | 150 void MemoryDumpScheduler::PollMemoryOnPollingThread() { |
153 if (!polling_state_.is_configured) | 151 if (polling_state_.current_state != PollingTriggerState::ENABLED) |
154 return; | 152 return; |
155 | 153 |
156 uint64_t polled_memory = 0; | 154 uint64_t polled_memory = 0; |
157 bool res = mdm_->PollFastMemoryTotal(&polled_memory); | 155 bool res = mdm_->PollFastMemoryTotal(&polled_memory); |
158 DCHECK(res); | 156 DCHECK(res); |
159 if (polling_state_.level_of_detail == MemoryDumpLevelOfDetail::DETAILED) { | 157 if (polling_state_.level_of_detail == MemoryDumpLevelOfDetail::DETAILED) { |
160 TRACE_COUNTER1(MemoryDumpManager::kTraceCategory, "PolledMemoryMB", | 158 TRACE_COUNTER1(MemoryDumpManager::kTraceCategory, "PolledMemoryMB", |
161 polled_memory / 1024 / 1024); | 159 polled_memory / 1024 / 1024); |
162 } | 160 } |
163 | 161 |
(...skipping 29 matching lines...) Expand all Loading... |
193 } else if (polling_state_.min_polls_between_dumps > | 191 } else if (polling_state_.min_polls_between_dumps > |
194 polling_state_.num_polls_from_last_dump) { | 192 polling_state_.num_polls_from_last_dump) { |
195 return false; | 193 return false; |
196 } | 194 } |
197 | 195 |
198 int64_t increase_from_last_dump = | 196 int64_t increase_from_last_dump = |
199 current_memory_total - polling_state_.last_dump_memory_total; | 197 current_memory_total - polling_state_.last_dump_memory_total; |
200 should_dump |= | 198 should_dump |= |
201 increase_from_last_dump > polling_state_.memory_increase_threshold; | 199 increase_from_last_dump > polling_state_.memory_increase_threshold; |
202 should_dump |= IsCurrentSamplePeak(current_memory_total); | 200 should_dump |= IsCurrentSamplePeak(current_memory_total); |
203 if (should_dump) { | 201 if (should_dump) |
204 polling_state_.last_dump_memory_total = current_memory_total; | 202 polling_state_.ResetTotals(); |
205 polling_state_.num_polls_from_last_dump = 0; | |
206 for (uint32_t i = 0; i < PollingTriggerState::kMaxNumMemorySamples; ++i) | |
207 polling_state_.last_memory_totals_kb[i] = 0; | |
208 polling_state_.last_memory_totals_kb_index = 0; | |
209 } | |
210 return should_dump; | 203 return should_dump; |
211 } | 204 } |
212 | 205 |
213 bool MemoryDumpScheduler::IsCurrentSamplePeak( | 206 bool MemoryDumpScheduler::IsCurrentSamplePeak( |
214 uint64_t current_memory_total_bytes) { | 207 uint64_t current_memory_total_bytes) { |
215 uint64_t current_memory_total_kb = current_memory_total_bytes / 1024; | 208 uint64_t current_memory_total_kb = current_memory_total_bytes / 1024; |
216 polling_state_.last_memory_totals_kb_index = | 209 polling_state_.last_memory_totals_kb_index = |
217 (polling_state_.last_memory_totals_kb_index + 1) % | 210 (polling_state_.last_memory_totals_kb_index + 1) % |
218 PollingTriggerState::kMaxNumMemorySamples; | 211 PollingTriggerState::kMaxNumMemorySamples; |
219 uint64_t mean = 0; | 212 uint64_t mean = 0; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
258 heavy_dumps_rate(0), | 251 heavy_dumps_rate(0), |
259 light_dump_period_ms(0), | 252 light_dump_period_ms(0), |
260 heavy_dump_period_ms(0) {} | 253 heavy_dump_period_ms(0) {} |
261 | 254 |
262 MemoryDumpScheduler::PeriodicTriggerState::~PeriodicTriggerState() { | 255 MemoryDumpScheduler::PeriodicTriggerState::~PeriodicTriggerState() { |
263 DCHECK(!timer.IsRunning()); | 256 DCHECK(!timer.IsRunning()); |
264 } | 257 } |
265 | 258 |
266 MemoryDumpScheduler::PollingTriggerState::PollingTriggerState( | 259 MemoryDumpScheduler::PollingTriggerState::PollingTriggerState( |
267 scoped_refptr<SingleThreadTaskRunner> polling_task_runner) | 260 scoped_refptr<SingleThreadTaskRunner> polling_task_runner) |
268 : is_configured(false), | 261 : current_state(DISABLED), |
269 is_polling_enabled(false), | |
270 level_of_detail(MemoryDumpLevelOfDetail::FIRST), | 262 level_of_detail(MemoryDumpLevelOfDetail::FIRST), |
271 polling_task_runner(polling_task_runner), | 263 polling_task_runner(polling_task_runner), |
272 polling_interval_ms(g_polling_interval_ms_for_testing | 264 polling_interval_ms(g_polling_interval_ms_for_testing |
273 ? g_polling_interval_ms_for_testing | 265 ? g_polling_interval_ms_for_testing |
274 : kMemoryTotalsPollingInterval), | 266 : kMemoryTotalsPollingInterval), |
275 min_polls_between_dumps(0), | 267 min_polls_between_dumps(0), |
276 num_polls_from_last_dump(0), | 268 num_polls_from_last_dump(-1), |
277 last_dump_memory_total(0), | 269 last_dump_memory_total(0), |
278 memory_increase_threshold(0), | 270 memory_increase_threshold(0), |
279 last_memory_totals_kb_index(0) {} | 271 last_memory_totals_kb_index(0) {} |
280 | 272 |
281 MemoryDumpScheduler::PollingTriggerState::~PollingTriggerState() { | 273 MemoryDumpScheduler::PollingTriggerState::~PollingTriggerState() { |
282 DCHECK(!polling_task_runner); | 274 DCHECK(!polling_task_runner); |
283 } | 275 } |
284 | 276 |
| 277 void MemoryDumpScheduler::PollingTriggerState::ResetTotals() { |
| 278 if (!memory_increase_threshold) { |
| 279 memory_increase_threshold = kDefaultMemoryIncreaseThreshold; |
| 280 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ |
| 281 defined(OS_ANDROID) |
| 282 // Set threshold to 1% of total system memory. |
| 283 SystemMemoryInfoKB meminfo; |
| 284 bool res = GetSystemMemoryInfo(&meminfo); |
| 285 if (res) |
| 286 memory_increase_threshold = (meminfo.total / 100) * 1024; |
| 287 #endif |
| 288 } |
| 289 |
| 290 // Update the |last_dump_memory_total|'s value from the totals if it's not |
| 291 // first poll. |
| 292 if (num_polls_from_last_dump >= 0 && |
| 293 last_memory_totals_kb[last_memory_totals_kb_index]) { |
| 294 last_dump_memory_total = |
| 295 last_memory_totals_kb[last_memory_totals_kb_index] * 1024; |
| 296 } |
| 297 num_polls_from_last_dump = 0; |
| 298 for (uint32_t i = 0; i < kMaxNumMemorySamples; ++i) |
| 299 last_memory_totals_kb[i] = 0; |
| 300 last_memory_totals_kb_index = 0; |
| 301 } |
| 302 |
285 } // namespace trace_event | 303 } // namespace trace_event |
286 } // namespace base | 304 } // namespace base |
OLD | NEW |