| 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 |