1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/trace_event/memory_dump_scheduler.h"
6
7 #include <algorithm>
8 #include <limits>
9
10 #include "base/check_op.h"
11 #include "base/functional/bind.h"
12 #include "base/task/sequenced_task_runner.h"
13 #include "base/time/time.h"
14
15 namespace base {
16 namespace trace_event {
17
18 // static
GetInstance()19 MemoryDumpScheduler* MemoryDumpScheduler::GetInstance() {
20 static MemoryDumpScheduler* instance = new MemoryDumpScheduler();
21 return instance;
22 }
23
24 MemoryDumpScheduler::MemoryDumpScheduler() = default;
~MemoryDumpScheduler()25 MemoryDumpScheduler::~MemoryDumpScheduler() {
26 // Hit only in tests. Check that tests don't leave without stopping.
27 DCHECK(!is_enabled_for_testing());
28 }
29
Start(MemoryDumpScheduler::Config config,scoped_refptr<SequencedTaskRunner> task_runner)30 void MemoryDumpScheduler::Start(
31 MemoryDumpScheduler::Config config,
32 scoped_refptr<SequencedTaskRunner> task_runner) {
33 DCHECK(!task_runner_);
34 task_runner_ = task_runner;
35 task_runner->PostTask(FROM_HERE, BindOnce(&MemoryDumpScheduler::StartInternal,
36 Unretained(this), config));
37 }
38
Stop()39 void MemoryDumpScheduler::Stop() {
40 if (!task_runner_)
41 return;
42 task_runner_->PostTask(FROM_HERE, BindOnce(&MemoryDumpScheduler::StopInternal,
43 Unretained(this)));
44 task_runner_ = nullptr;
45 }
46
StartInternal(MemoryDumpScheduler::Config config)47 void MemoryDumpScheduler::StartInternal(MemoryDumpScheduler::Config config) {
48 uint32_t light_dump_period_ms = 0;
49 uint32_t heavy_dump_period_ms = 0;
50 uint32_t min_period_ms = std::numeric_limits<uint32_t>::max();
51 for (const Config::Trigger& trigger : config.triggers) {
52 DCHECK_GT(trigger.period_ms, 0u);
53 switch (trigger.level_of_detail) {
54 case MemoryDumpLevelOfDetail::kBackground:
55 break;
56 case MemoryDumpLevelOfDetail::kLight:
57 DCHECK_EQ(0u, light_dump_period_ms);
58 light_dump_period_ms = trigger.period_ms;
59 break;
60 case MemoryDumpLevelOfDetail::kDetailed:
61 DCHECK_EQ(0u, heavy_dump_period_ms);
62 heavy_dump_period_ms = trigger.period_ms;
63 break;
64 }
65 min_period_ms = std::min(min_period_ms, trigger.period_ms);
66 }
67
68 DCHECK_EQ(0u, light_dump_period_ms % min_period_ms);
69 DCHECK_EQ(0u, heavy_dump_period_ms % min_period_ms);
70 DCHECK(!config.callback.is_null());
71 callback_ = config.callback;
72 period_ms_ = min_period_ms;
73 tick_count_ = 0;
74 light_dump_rate_ = light_dump_period_ms / min_period_ms;
75 heavy_dump_rate_ = heavy_dump_period_ms / min_period_ms;
76
77 // Trigger the first dump after 200ms.
78 // TODO(lalitm): this is a tempoarary hack to delay the first scheduled dump
79 // so that the child processes get tracing enabled notification via IPC.
80 // See crbug.com/770151.
81 SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
82 FROM_HERE,
83 BindOnce(&MemoryDumpScheduler::Tick, Unretained(this), ++generation_),
84 Milliseconds(200));
85 }
86
StopInternal()87 void MemoryDumpScheduler::StopInternal() {
88 period_ms_ = 0;
89 generation_++;
90 callback_.Reset();
91 }
92
Tick(uint32_t expected_generation)93 void MemoryDumpScheduler::Tick(uint32_t expected_generation) {
94 if (period_ms_ == 0 || generation_ != expected_generation)
95 return;
96
97 MemoryDumpLevelOfDetail level_of_detail =
98 MemoryDumpLevelOfDetail::kBackground;
99 if (light_dump_rate_ > 0 && tick_count_ % light_dump_rate_ == 0)
100 level_of_detail = MemoryDumpLevelOfDetail::kLight;
101 if (heavy_dump_rate_ > 0 && tick_count_ % heavy_dump_rate_ == 0)
102 level_of_detail = MemoryDumpLevelOfDetail::kDetailed;
103 tick_count_++;
104
105 callback_.Run(level_of_detail);
106
107 SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
108 FROM_HERE,
109 BindOnce(&MemoryDumpScheduler::Tick, Unretained(this),
110 expected_generation),
111 Milliseconds(period_ms_));
112 }
113
114 MemoryDumpScheduler::Config::Config() = default;
115 MemoryDumpScheduler::Config::~Config() = default;
116 MemoryDumpScheduler::Config::Config(const MemoryDumpScheduler::Config&) =
117 default;
118
119 } // namespace trace_event
120 } // namespace base
121