Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: base/trace_event/memory_dump_scheduler_unittest.cc

Issue 2799023002: memory-infra: Switch to MemoryPeakDetector and simplify MemoryDumpScheduler (Closed)
Patch Set: rebase bind -> bindonce Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/trace_event/memory_dump_scheduler.cc ('k') | base/trace_event/memory_peak_detector.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/trace_event/memory_dump_scheduler.h" 5 #include "base/trace_event/memory_dump_scheduler.h"
6 6
7 #include <memory> 7 #include <memory>
8 8
9 #include "base/bind.h"
9 #include "base/single_thread_task_runner.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"
10 #include "testing/gtest/include/gtest/gtest.h" 14 #include "testing/gtest/include/gtest/gtest.h"
11 15
16 using ::testing::Invoke;
17 using ::testing::_;
18
12 namespace base { 19 namespace base {
13 namespace trace_event { 20 namespace trace_event {
14 21
15 class MemoryDumpSchedulerPollingTest : public testing::Test { 22 namespace {
23
24 // Wrapper to use gmock on a callback.
25 struct CallbackWrapper {
26 MOCK_METHOD1(OnTick, void(MemoryDumpLevelOfDetail));
27 };
28
29 } // namespace
30
31 class MemoryDumpSchedulerTest : public testing::Test {
16 public: 32 public:
17 static const uint32_t kMinPollsToDump = 5; 33 struct FriendDeleter {
34 void operator()(MemoryDumpScheduler* inst) { delete inst; }
35 };
18 36
19 MemoryDumpSchedulerPollingTest() 37 MemoryDumpSchedulerTest() : testing::Test() {}
20 : testing::Test(),
21 num_samples_tracked_(
22 MemoryDumpScheduler::PollingTriggerState::kMaxNumMemorySamples) {}
23 38
24 void SetUp() override { 39 void SetUp() override {
25 MemoryDumpScheduler::SetPollingIntervalForTesting(1); 40 bg_thread_.reset(new Thread("MemoryDumpSchedulerTest Thread"));
26 uint32_t kMinPollsToDump = 5; 41 bg_thread_->Start();
27 mds_ = MemoryDumpScheduler::GetInstance(); 42 scheduler_.reset(new MemoryDumpScheduler());
28 mds_->Setup(nullptr, nullptr);
29 mds_->AddTrigger(MemoryDumpType::PEAK_MEMORY_USAGE,
30 MemoryDumpLevelOfDetail::LIGHT, kMinPollsToDump);
31 mds_->polling_state_->ResetTotals();
32 mds_->polling_state_->current_state =
33 MemoryDumpScheduler::PollingTriggerState::ENABLED;
34 } 43 }
35 44
36 void TearDown() override { 45 void TearDown() override {
37 mds_->polling_state_->current_state = 46 bg_thread_.reset();
38 MemoryDumpScheduler::PollingTriggerState::DISABLED; 47 scheduler_.reset();
39 } 48 }
40 49
41 protected: 50 protected:
42 bool ShouldTriggerDump(uint64_t total) { 51 std::unique_ptr<MemoryDumpScheduler, FriendDeleter> scheduler_;
43 return mds_->ShouldTriggerDump(total); 52 std::unique_ptr<Thread> bg_thread_;
44 } 53 CallbackWrapper on_tick_;
45
46 uint32_t num_samples_tracked_;
47 MemoryDumpScheduler* mds_;
48 }; 54 };
49 55
50 TEST_F(MemoryDumpSchedulerPollingTest, PeakDetection) { 56 TEST_F(MemoryDumpSchedulerTest, SingleTrigger) {
51 for (uint32_t i = 0; i < num_samples_tracked_ * 6; ++i) { 57 const uint32_t kPeriodMs = 1;
52 // Memory is increased in steps and dumps must be triggered at every step. 58 const auto kLevelOfDetail = MemoryDumpLevelOfDetail::DETAILED;
53 uint64_t total = (2 + (i / (2 * num_samples_tracked_))) * 1024 * 1204; 59 const uint32_t kTicks = 5;
54 bool did_trigger = ShouldTriggerDump(total); 60 WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL,
55 // Dumps must be triggered only at specific iterations. 61 WaitableEvent::InitialState::NOT_SIGNALED);
56 bool should_have_triggered = i == 0; 62 MemoryDumpScheduler::Config config;
57 should_have_triggered |= 63 config.triggers.push_back({kLevelOfDetail, kPeriodMs});
58 (i > num_samples_tracked_) && (i % (2 * num_samples_tracked_) == 1); 64 config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
59 if (should_have_triggered) { 65
60 ASSERT_TRUE(did_trigger) << "Dump wasn't triggered at " << i; 66 testing::InSequence sequence;
61 } else { 67 EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
62 ASSERT_FALSE(did_trigger) << "Unexpected dump at " << i; 68 EXPECT_CALL(on_tick_, OnTick(_))
63 } 69 .WillRepeatedly(Invoke(
64 } 70 [&evt, kLevelOfDetail](MemoryDumpLevelOfDetail level_of_detail) {
71 EXPECT_EQ(kLevelOfDetail, level_of_detail);
72 evt.Signal();
73 }));
74
75 // Check that Stop() before Start() doesn't cause any error.
76 scheduler_->Stop();
77
78 const TimeTicks tstart = TimeTicks::Now();
79 scheduler_->Start(config, bg_thread_->task_runner());
80 evt.Wait();
81 const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF();
82
83 // It takes N-1 ms to perform N ticks of 1ms each.
84 EXPECT_GE(time_ms, kPeriodMs * (kTicks - 1));
85
86 // Check that stopping twice doesn't cause any problems.
87 scheduler_->Stop();
88 scheduler_->Stop();
65 } 89 }
66 90
67 TEST_F(MemoryDumpSchedulerPollingTest, SlowGrowthDetection) { 91 TEST_F(MemoryDumpSchedulerTest, MultipleTriggers) {
68 for (uint32_t i = 0; i < 15; ++i) { 92 const uint32_t kPeriodLightMs = 3;
69 // Record 1GiB of increase in each call. Dumps are triggered with 1% w.r.t 93 const uint32_t kPeriodDetailedMs = 9;
70 // system's total memory. 94 WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL,
71 uint64_t total = static_cast<uint64_t>(i + 1) * 1024 * 1024 * 1024; 95 WaitableEvent::InitialState::NOT_SIGNALED);
72 bool did_trigger = ShouldTriggerDump(total); 96 MemoryDumpScheduler::Config config;
73 bool should_have_triggered = i % kMinPollsToDump == 0; 97 const MemoryDumpLevelOfDetail kLight = MemoryDumpLevelOfDetail::LIGHT;
74 if (should_have_triggered) { 98 const MemoryDumpLevelOfDetail kDetailed = MemoryDumpLevelOfDetail::DETAILED;
75 ASSERT_TRUE(did_trigger) << "Dump wasn't triggered at " << i; 99 config.triggers.push_back({kLight, kPeriodLightMs});
76 } else { 100 config.triggers.push_back({kDetailed, kPeriodDetailedMs});
77 ASSERT_FALSE(did_trigger) << "Unexpected dump at " << i; 101 config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
78 } 102
79 } 103 TimeTicks t1, t2, t3;
104
105 testing::InSequence sequence;
106 EXPECT_CALL(on_tick_, OnTick(kDetailed))
107 .WillOnce(
108 Invoke([&t1](MemoryDumpLevelOfDetail) { t1 = TimeTicks::Now(); }));
109 EXPECT_CALL(on_tick_, OnTick(kLight)).Times(1);
110 EXPECT_CALL(on_tick_, OnTick(kLight)).Times(1);
111 EXPECT_CALL(on_tick_, OnTick(kDetailed))
112 .WillOnce(
113 Invoke([&t2](MemoryDumpLevelOfDetail) { t2 = TimeTicks::Now(); }));
114 EXPECT_CALL(on_tick_, OnTick(kLight))
115 .WillOnce(
116 Invoke([&t3](MemoryDumpLevelOfDetail) { t3 = TimeTicks::Now(); }));
117
118 // Rationale for WillRepeatedly and not just WillOnce: Extra ticks might
119 // happen if the Stop() takes time. Not an interesting case, but we need to
120 // avoid gmock to shout in that case.
121 EXPECT_CALL(on_tick_, OnTick(_))
122 .WillRepeatedly(
123 Invoke([&evt](MemoryDumpLevelOfDetail) { evt.Signal(); }));
124
125 scheduler_->Start(config, bg_thread_->task_runner());
126 evt.Wait();
127 scheduler_->Stop();
128 EXPECT_GE((t2 - t1).InMillisecondsF(), kPeriodDetailedMs);
129 EXPECT_GE((t3 - t2).InMillisecondsF(), kPeriodLightMs);
80 } 130 }
81 131
82 TEST_F(MemoryDumpSchedulerPollingTest, NotifyDumpTriggered) { 132 TEST_F(MemoryDumpSchedulerTest, StartStopQuickly) {
83 for (uint32_t i = 0; i < num_samples_tracked_ * 6; ++i) { 133 const uint32_t kPeriodMs = 1;
84 uint64_t total = (2 + (i / (2 * num_samples_tracked_))) * 1024 * 1204; 134 const uint32_t kTicks = 10;
85 if (i % num_samples_tracked_ == 0) 135 WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL,
86 mds_->NotifyDumpTriggered(); 136 WaitableEvent::InitialState::NOT_SIGNALED);
87 bool did_trigger = ShouldTriggerDump(total); 137 MemoryDumpScheduler::Config config;
88 // Dumps should never be triggered since NotifyDumpTriggered() is called 138 config.triggers.push_back({MemoryDumpLevelOfDetail::DETAILED, kPeriodMs});
89 // frequently. 139 config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
90 EXPECT_NE(0u, mds_->polling_state_->last_dump_memory_total); 140
91 EXPECT_GT(num_samples_tracked_ - 1, 141 testing::InSequence sequence;
92 mds_->polling_state_->last_memory_totals_kb_index); 142 EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
93 EXPECT_LT(static_cast<int64_t>( 143 EXPECT_CALL(on_tick_, OnTick(_))
94 total - mds_->polling_state_->last_dump_memory_total), 144 .WillRepeatedly(
95 mds_->polling_state_->memory_increase_threshold); 145 Invoke([&evt](MemoryDumpLevelOfDetail) { evt.Signal(); }));
96 ASSERT_FALSE(did_trigger && i) << "Unexpected dump at " << i; 146
147 const TimeTicks tstart = TimeTicks::Now();
148 for (int i = 0; i < 5; i++) {
149 scheduler_->Stop();
150 scheduler_->Start(config, bg_thread_->task_runner());
97 } 151 }
152 evt.Wait();
153 const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF();
154 scheduler_->Stop();
155
156 // It takes N-1 ms to perform N ticks of 1ms each.
157 EXPECT_GE(time_ms, kPeriodMs * (kTicks - 1));
158 }
159
160 TEST_F(MemoryDumpSchedulerTest, StopAndStartOnAnotherThread) {
161 const uint32_t kPeriodMs = 1;
162 const uint32_t kTicks = 3;
163 WaitableEvent evt(WaitableEvent::ResetPolicy::MANUAL,
164 WaitableEvent::InitialState::NOT_SIGNALED);
165 MemoryDumpScheduler::Config config;
166 config.triggers.push_back({MemoryDumpLevelOfDetail::DETAILED, kPeriodMs});
167 config.callback = Bind(&CallbackWrapper::OnTick, Unretained(&on_tick_));
168
169 scoped_refptr<TaskRunner> expected_task_runner = bg_thread_->task_runner();
170 testing::InSequence sequence;
171 EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
172 EXPECT_CALL(on_tick_, OnTick(_))
173 .WillRepeatedly(
174 Invoke([&evt, expected_task_runner](MemoryDumpLevelOfDetail) {
175 EXPECT_TRUE(expected_task_runner->RunsTasksOnCurrentThread());
176 evt.Signal();
177 }));
178
179 scheduler_->Start(config, bg_thread_->task_runner());
180 evt.Wait();
181 scheduler_->Stop();
182 bg_thread_->Stop();
183
184 bg_thread_.reset(new Thread("MemoryDumpSchedulerTest Thread 2"));
185 bg_thread_->Start();
186 evt.Reset();
187 expected_task_runner = bg_thread_->task_runner();
188 scheduler_->Start(config, bg_thread_->task_runner());
189 EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
190 EXPECT_CALL(on_tick_, OnTick(_))
191 .WillRepeatedly(
192 Invoke([&evt, expected_task_runner](MemoryDumpLevelOfDetail) {
193 EXPECT_TRUE(expected_task_runner->RunsTasksOnCurrentThread());
194 evt.Signal();
195 }));
196 evt.Wait();
197 scheduler_->Stop();
98 } 198 }
99 199
100 } // namespace trace_event 200 } // namespace trace_event
101 } // namespace base 201 } // namespace base
OLDNEW
« no previous file with comments | « base/trace_event/memory_dump_scheduler.cc ('k') | base/trace_event/memory_peak_detector.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698