| // 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. |
| |
| #include "base/trace_event/memory_dump_scheduler.h" |
| |
| #include <algorithm> |
| #include <limits> |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| |
| namespace base { |
| namespace trace_event { |
| |
| // static |
| MemoryDumpScheduler* MemoryDumpScheduler::GetInstance() { |
| static MemoryDumpScheduler* instance = new MemoryDumpScheduler(); |
| return instance; |
| } |
| |
| MemoryDumpScheduler::MemoryDumpScheduler() : period_ms_(0), generation_(0) {} |
| MemoryDumpScheduler::~MemoryDumpScheduler() { |
| // Hit only in tests. Check that tests don't leave without stopping. |
| DCHECK(!is_enabled_for_testing()); |
| } |
| |
| void MemoryDumpScheduler::Start( |
| MemoryDumpScheduler::Config config, |
| scoped_refptr<SequencedTaskRunner> task_runner) { |
| DCHECK(!task_runner_); |
| task_runner_ = task_runner; |
| task_runner->PostTask(FROM_HERE, BindOnce(&MemoryDumpScheduler::StartInternal, |
| Unretained(this), config)); |
| } |
| |
| void MemoryDumpScheduler::Stop() { |
| if (!task_runner_) |
| return; |
| task_runner_->PostTask(FROM_HERE, BindOnce(&MemoryDumpScheduler::StopInternal, |
| Unretained(this))); |
| task_runner_ = nullptr; |
| } |
| |
| void MemoryDumpScheduler::StartInternal(MemoryDumpScheduler::Config config) { |
| uint32_t light_dump_period_ms = 0; |
| uint32_t heavy_dump_period_ms = 0; |
| uint32_t min_period_ms = std::numeric_limits<uint32_t>::max(); |
| for (const Config::Trigger& trigger : config.triggers) { |
| DCHECK_GT(trigger.period_ms, 0u); |
| switch (trigger.level_of_detail) { |
| case MemoryDumpLevelOfDetail::BACKGROUND: |
| break; |
| case MemoryDumpLevelOfDetail::LIGHT: |
| DCHECK_EQ(0u, light_dump_period_ms); |
| light_dump_period_ms = trigger.period_ms; |
| break; |
| case MemoryDumpLevelOfDetail::DETAILED: |
| DCHECK_EQ(0u, heavy_dump_period_ms); |
| heavy_dump_period_ms = trigger.period_ms; |
| break; |
| } |
| min_period_ms = std::min(min_period_ms, trigger.period_ms); |
| } |
| |
| DCHECK_EQ(0u, light_dump_period_ms % min_period_ms); |
| DCHECK_EQ(0u, heavy_dump_period_ms % min_period_ms); |
| DCHECK(!config.callback.is_null()); |
| callback_ = config.callback; |
| period_ms_ = min_period_ms; |
| tick_count_ = 0; |
| light_dump_rate_ = light_dump_period_ms / min_period_ms; |
| heavy_dump_rate_ = heavy_dump_period_ms / min_period_ms; |
| |
| // Trigger the first dump after 200ms. |
| // TODO(lalitm): this is a tempoarary hack to delay the first scheduled dump |
| // so that the child processes get tracing enabled notification via IPC. |
| // See crbug.com/770151. |
| SequencedTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, |
| BindOnce(&MemoryDumpScheduler::Tick, Unretained(this), ++generation_), |
| TimeDelta::FromMilliseconds(200)); |
| } |
| |
| void MemoryDumpScheduler::StopInternal() { |
| period_ms_ = 0; |
| generation_++; |
| callback_.Reset(); |
| } |
| |
| void MemoryDumpScheduler::Tick(uint32_t expected_generation) { |
| if (period_ms_ == 0 || generation_ != expected_generation) |
| return; |
| |
| MemoryDumpLevelOfDetail level_of_detail = MemoryDumpLevelOfDetail::BACKGROUND; |
| if (light_dump_rate_ > 0 && tick_count_ % light_dump_rate_ == 0) |
| level_of_detail = MemoryDumpLevelOfDetail::LIGHT; |
| if (heavy_dump_rate_ > 0 && tick_count_ % heavy_dump_rate_ == 0) |
| level_of_detail = MemoryDumpLevelOfDetail::DETAILED; |
| tick_count_++; |
| |
| callback_.Run(level_of_detail); |
| |
| SequencedTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, |
| BindOnce(&MemoryDumpScheduler::Tick, Unretained(this), |
| expected_generation), |
| TimeDelta::FromMilliseconds(period_ms_)); |
| } |
| |
| MemoryDumpScheduler::Config::Config() = default; |
| MemoryDumpScheduler::Config::~Config() = default; |
| MemoryDumpScheduler::Config::Config(const MemoryDumpScheduler::Config&) = |
| default; |
| |
| } // namespace trace_event |
| } // namespace base |