| // 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 immediately. | 
 |   SequencedTaskRunnerHandle::Get()->PostTask( | 
 |       FROM_HERE, | 
 |       BindOnce(&MemoryDumpScheduler::Tick, Unretained(this), ++generation_)); | 
 | } | 
 |  | 
 | void MemoryDumpScheduler::StopInternal() { | 
 |   period_ms_ = 0; | 
 |   generation_++; | 
 |   callback_.Reset(); | 
 | } | 
 |  | 
 | void MemoryDumpScheduler::Tick(uint32_t expected_generation) { | 
 |   if (period_ms_ == 0 || generation_ != expected_generation) | 
 |     return; | 
 |  | 
 |   SequencedTaskRunnerHandle::Get()->PostDelayedTask( | 
 |       FROM_HERE, | 
 |       BindOnce(&MemoryDumpScheduler::Tick, Unretained(this), | 
 |                expected_generation), | 
 |       TimeDelta::FromMilliseconds(period_ms_)); | 
 |  | 
 |   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); | 
 | } | 
 |  | 
 | MemoryDumpScheduler::Config::Config() {} | 
 | MemoryDumpScheduler::Config::~Config() {} | 
 | MemoryDumpScheduler::Config::Config(const MemoryDumpScheduler::Config&) = | 
 |     default; | 
 |  | 
 | }  // namespace trace_event | 
 | }  // namespace base |