// 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 #include #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 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::max(); for (const Config::Trigger& trigger : config.triggers) { DCHECK_GT(trigger.period_ms, 0u); switch (trigger.level_of_detail) { case MemoryDumpLevelOfDetail::VM_REGIONS_ONLY_FOR_HEAP_PROFILER: // There is no use case to request a periodic dump which contains // details that are useful only for the heap-profiler. NOTREACHED(); return; 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