• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "base/trace_event/memory_dump_scheduler.h"
6 
7 #include <memory>
8 
9 #include "base/bind.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/threading/thread.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 
16 using ::testing::AtMost;
17 using ::testing::Invoke;
18 using ::testing::_;
19 
20 namespace base {
21 namespace trace_event {
22 
23 namespace {
24 
25 // Wrapper to use gmock on a callback.
26 struct CallbackWrapper {
27   MOCK_METHOD1(OnTick, void(MemoryDumpLevelOfDetail));
28 };
29 
30 }  // namespace
31 
32 class MemoryDumpSchedulerTest : public testing::Test {
33  public:
MemoryDumpSchedulerTest()34   MemoryDumpSchedulerTest()
35       : testing::Test(),
36         evt_(WaitableEvent::ResetPolicy::MANUAL,
37              WaitableEvent::InitialState::NOT_SIGNALED),
38         bg_thread_("MemoryDumpSchedulerTest Thread") {
39     bg_thread_.Start();
40   }
41 
42  protected:
43   MemoryDumpScheduler scheduler_;
44   WaitableEvent evt_;
45   CallbackWrapper on_tick_;
46   Thread bg_thread_;
47 };
48 
TEST_F(MemoryDumpSchedulerTest,SingleTrigger)49 TEST_F(MemoryDumpSchedulerTest, SingleTrigger) {
50   const uint32_t kPeriodMs = 1;
51   const auto kLevelOfDetail = MemoryDumpLevelOfDetail::DETAILED;
52   const uint32_t kTicks = 5;
53   MemoryDumpScheduler::Config config;
54   config.triggers.push_back({kLevelOfDetail, kPeriodMs});
55   config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
56 
57   testing::InSequence sequence;
58   EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
59   EXPECT_CALL(on_tick_, OnTick(_))
60       .WillRepeatedly(Invoke(
61           [this, kLevelOfDetail](MemoryDumpLevelOfDetail level_of_detail) {
62             EXPECT_EQ(kLevelOfDetail, level_of_detail);
63             this->evt_.Signal();
64           }));
65 
66   // Check that Stop() before Start() doesn't cause any error.
67   scheduler_.Stop();
68 
69   const TimeTicks tstart = TimeTicks::Now();
70   scheduler_.Start(config, bg_thread_.task_runner());
71   evt_.Wait();
72   const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF();
73 
74   // It takes N-1 ms to perform N ticks of 1ms each.
75   EXPECT_GE(time_ms, kPeriodMs * (kTicks - 1));
76 
77   // Check that stopping twice doesn't cause any problems.
78   scheduler_.Stop();
79   scheduler_.Stop();
80 }
81 
TEST_F(MemoryDumpSchedulerTest,MultipleTriggers)82 TEST_F(MemoryDumpSchedulerTest, MultipleTriggers) {
83   const uint32_t kPeriodLightMs = 3;
84   const uint32_t kPeriodDetailedMs = 9;
85   MemoryDumpScheduler::Config config;
86   const MemoryDumpLevelOfDetail kLight = MemoryDumpLevelOfDetail::LIGHT;
87   const MemoryDumpLevelOfDetail kDetailed = MemoryDumpLevelOfDetail::DETAILED;
88   config.triggers.push_back({kLight, kPeriodLightMs});
89   config.triggers.push_back({kDetailed, kPeriodDetailedMs});
90   config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
91 
92   TimeTicks t1, t2, t3;
93 
94   testing::InSequence sequence;
95   EXPECT_CALL(on_tick_, OnTick(kDetailed))
96       .WillOnce(
97           Invoke([&t1](MemoryDumpLevelOfDetail) { t1 = TimeTicks::Now(); }));
98   EXPECT_CALL(on_tick_, OnTick(kLight)).Times(1);
99   EXPECT_CALL(on_tick_, OnTick(kLight)).Times(1);
100   EXPECT_CALL(on_tick_, OnTick(kDetailed))
101       .WillOnce(
102           Invoke([&t2](MemoryDumpLevelOfDetail) { t2 = TimeTicks::Now(); }));
103   EXPECT_CALL(on_tick_, OnTick(kLight))
104       .WillOnce(
105           Invoke([&t3](MemoryDumpLevelOfDetail) { t3 = TimeTicks::Now(); }));
106 
107   // Rationale for WillRepeatedly and not just WillOnce: Extra ticks might
108   // happen if the Stop() takes time. Not an interesting case, but we need to
109   // avoid gmock to shout in that case.
110   EXPECT_CALL(on_tick_, OnTick(_))
111       .WillRepeatedly(
112           Invoke([this](MemoryDumpLevelOfDetail) { this->evt_.Signal(); }));
113 
114   scheduler_.Start(config, bg_thread_.task_runner());
115   evt_.Wait();
116   scheduler_.Stop();
117   EXPECT_GE((t2 - t1).InMillisecondsF(), kPeriodDetailedMs);
118   EXPECT_GE((t3 - t2).InMillisecondsF(), kPeriodLightMs);
119 }
120 
TEST_F(MemoryDumpSchedulerTest,StartStopQuickly)121 TEST_F(MemoryDumpSchedulerTest, StartStopQuickly) {
122   const uint32_t kPeriodMs = 3;
123   const uint32_t kQuickIterations = 5;
124   const uint32_t kDetailedTicks = 10;
125 
126   MemoryDumpScheduler::Config light_config;
127   light_config.triggers.push_back({MemoryDumpLevelOfDetail::LIGHT, kPeriodMs});
128   light_config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
129 
130   MemoryDumpScheduler::Config detailed_config;
131   detailed_config.triggers.push_back(
132       {MemoryDumpLevelOfDetail::DETAILED, kPeriodMs});
133   detailed_config.callback =
134       Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
135 
136   testing::InSequence sequence;
137   EXPECT_CALL(on_tick_, OnTick(MemoryDumpLevelOfDetail::LIGHT))
138       .Times(AtMost(kQuickIterations));
139   EXPECT_CALL(on_tick_, OnTick(MemoryDumpLevelOfDetail::DETAILED))
140       .Times(kDetailedTicks - 1);
141   EXPECT_CALL(on_tick_, OnTick(MemoryDumpLevelOfDetail::DETAILED))
142       .WillRepeatedly(
143           Invoke([this](MemoryDumpLevelOfDetail) { this->evt_.Signal(); }));
144 
145   const TimeTicks tstart = TimeTicks::Now();
146   for (unsigned int i = 0; i < kQuickIterations; i++) {
147     scheduler_.Start(light_config, bg_thread_.task_runner());
148     scheduler_.Stop();
149   }
150 
151   scheduler_.Start(detailed_config, bg_thread_.task_runner());
152 
153   evt_.Wait();
154   const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF();
155   scheduler_.Stop();
156 
157   // It takes N-1 ms to perform N ticks of 1ms each.
158   EXPECT_GE(time_ms, kPeriodMs * (kDetailedTicks - 1));
159 }
160 
TEST_F(MemoryDumpSchedulerTest,StopAndStartOnAnotherThread)161 TEST_F(MemoryDumpSchedulerTest, StopAndStartOnAnotherThread) {
162   const uint32_t kPeriodMs = 1;
163   const uint32_t kTicks = 3;
164   MemoryDumpScheduler::Config config;
165   config.triggers.push_back({MemoryDumpLevelOfDetail::DETAILED, kPeriodMs});
166   config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
167 
168   scoped_refptr<TaskRunner> expected_task_runner = bg_thread_.task_runner();
169   testing::InSequence sequence;
170   EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
171   EXPECT_CALL(on_tick_, OnTick(_))
172       .WillRepeatedly(
173           Invoke([this, expected_task_runner](MemoryDumpLevelOfDetail) {
174             EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
175             this->evt_.Signal();
176           }));
177 
178   scheduler_.Start(config, bg_thread_.task_runner());
179   evt_.Wait();
180   scheduler_.Stop();
181   bg_thread_.Stop();
182 
183   Thread bg_thread_2("MemoryDumpSchedulerTest Thread 2");
184   bg_thread_2.Start();
185   evt_.Reset();
186   expected_task_runner = bg_thread_2.task_runner();
187   EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
188   EXPECT_CALL(on_tick_, OnTick(_))
189       .WillRepeatedly(
190           Invoke([this, expected_task_runner](MemoryDumpLevelOfDetail) {
191             EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
192             this->evt_.Signal();
193           }));
194   scheduler_.Start(config, bg_thread_2.task_runner());
195   evt_.Wait();
196   scheduler_.Stop();
197 }
198 
199 }  // namespace trace_event
200 }  // namespace base
201