1 // Copyright 2015 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.
5 #include "base/trace_event/memory_dump_manager.h"
7 #include "base/bind_helpers.h"
8 #include "base/memory/scoped_vector.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/threading/thread.h"
12 #include "base/trace_event/memory_dump_provider.h"
13 #include "base/trace_event/process_memory_dump.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
18 using testing::Invoke
;
19 using testing::Return
;
22 namespace trace_event
{
24 // Testing MemoryDumpManagerDelegate which short-circuits dump requests locally
25 // instead of performing IPC dances.
26 class MemoryDumpManagerDelegateForTesting
: public MemoryDumpManagerDelegate
{
28 void RequestGlobalMemoryDump(
29 const base::trace_event::MemoryDumpRequestArgs
& args
,
30 const MemoryDumpCallback
& callback
) override
{
31 CreateProcessDump(args
, callback
);
34 bool IsCoordinatorProcess() const override
{ return false; }
37 class MemoryDumpManagerTest
: public testing::Test
{
39 void SetUp() override
{
40 message_loop_
.reset(new MessageLoop());
41 mdm_
.reset(new MemoryDumpManager());
42 MemoryDumpManager::SetInstanceForTesting(mdm_
.get());
43 ASSERT_EQ(mdm_
, MemoryDumpManager::GetInstance());
44 MemoryDumpManager::GetInstance()->Initialize();
45 MemoryDumpManager::GetInstance()->SetDelegate(&delegate_
);
48 void TearDown() override
{
49 MemoryDumpManager::SetInstanceForTesting(nullptr);
51 message_loop_
.reset();
52 TraceLog::DeleteForTesting();
55 void DumpCallbackAdapter(scoped_refptr
<SingleThreadTaskRunner
> task_runner
,
59 task_runner
->PostTask(FROM_HERE
, closure
);
63 const char* kTraceCategory
= MemoryDumpManager::kTraceCategoryForTesting
;
65 void EnableTracing(const char* category
) {
66 TraceLog::GetInstance()->SetEnabled(
67 TraceConfig(category
, ""), TraceLog::RECORDING_MODE
);
70 void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); }
72 scoped_ptr
<MemoryDumpManager
> mdm_
;
75 scoped_ptr
<MessageLoop
> message_loop_
;
76 MemoryDumpManagerDelegateForTesting delegate_
;
78 // We want our singleton torn down after each test.
79 ShadowingAtExitManager at_exit_manager_
;
82 class MockDumpProvider
: public MemoryDumpProvider
{
84 MockDumpProvider() : last_session_state_(nullptr) {}
86 // Ctor used by the RespectTaskRunnerAffinity test.
87 explicit MockDumpProvider(
88 const scoped_refptr
<SingleThreadTaskRunner
>& task_runner
)
89 : last_session_state_(nullptr), task_runner_(task_runner
) {}
91 virtual ~MockDumpProvider() {}
93 MOCK_METHOD1(OnMemoryDump
, bool(ProcessMemoryDump
* pmd
));
95 // OnMemoryDump() override for the RespectTaskRunnerAffinity test.
96 bool OnMemoryDump_CheckTaskRunner(ProcessMemoryDump
* pmd
) {
97 EXPECT_TRUE(task_runner_
->RunsTasksOnCurrentThread());
101 // OnMemoryDump() override for the SharedSessionState test.
102 bool OnMemoryDump_CheckSessionState(ProcessMemoryDump
* pmd
) {
103 MemoryDumpSessionState
* cur_session_state
= pmd
->session_state().get();
104 if (last_session_state_
)
105 EXPECT_EQ(last_session_state_
, cur_session_state
);
106 last_session_state_
= cur_session_state
;
111 MemoryDumpSessionState
* last_session_state_
;
112 scoped_refptr
<SingleThreadTaskRunner
> task_runner_
;
115 TEST_F(MemoryDumpManagerTest
, SingleDumper
) {
116 MockDumpProvider mdp
;
117 mdm_
->RegisterDumpProvider(&mdp
);
119 // Check that the dumper is not called if the memory category is not enabled.
120 EnableTracing("foo-and-bar-but-not-memory");
121 EXPECT_CALL(mdp
, OnMemoryDump(_
)).Times(0);
122 mdm_
->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED
);
125 // Now repeat enabling the memory category and check that the dumper is
126 // invoked this time.
127 EnableTracing(kTraceCategory
);
128 EXPECT_CALL(mdp
, OnMemoryDump(_
)).Times(3).WillRepeatedly(Return(true));
129 for (int i
= 0; i
< 3; ++i
)
130 mdm_
->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED
);
133 mdm_
->UnregisterDumpProvider(&mdp
);
135 // Finally check the unregister logic (no calls to the mdp after unregister).
136 EnableTracing(kTraceCategory
);
137 EXPECT_CALL(mdp
, OnMemoryDump(_
)).Times(0);
138 mdm_
->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED
);
139 TraceLog::GetInstance()->SetDisabled();
142 TEST_F(MemoryDumpManagerTest
, SharedSessionState
) {
143 MockDumpProvider mdp1
;
144 MockDumpProvider mdp2
;
145 mdm_
->RegisterDumpProvider(&mdp1
);
146 mdm_
->RegisterDumpProvider(&mdp2
);
148 EnableTracing(kTraceCategory
);
149 EXPECT_CALL(mdp1
, OnMemoryDump(_
))
152 Invoke(&mdp1
, &MockDumpProvider::OnMemoryDump_CheckSessionState
));
153 EXPECT_CALL(mdp2
, OnMemoryDump(_
))
156 Invoke(&mdp2
, &MockDumpProvider::OnMemoryDump_CheckSessionState
));
158 for (int i
= 0; i
< 2; ++i
)
159 mdm_
->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED
);
164 TEST_F(MemoryDumpManagerTest
, MultipleDumpers
) {
165 MockDumpProvider mdp1
;
166 MockDumpProvider mdp2
;
169 mdm_
->RegisterDumpProvider(&mdp1
);
170 EnableTracing(kTraceCategory
);
171 EXPECT_CALL(mdp1
, OnMemoryDump(_
)).Times(1).WillRepeatedly(Return(true));
172 EXPECT_CALL(mdp2
, OnMemoryDump(_
)).Times(0);
173 mdm_
->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED
);
176 // Invert: enable mdp1 and disable mdp2.
177 mdm_
->UnregisterDumpProvider(&mdp1
);
178 mdm_
->RegisterDumpProvider(&mdp2
);
179 EnableTracing(kTraceCategory
);
180 EXPECT_CALL(mdp1
, OnMemoryDump(_
)).Times(0);
181 EXPECT_CALL(mdp2
, OnMemoryDump(_
)).Times(1).WillRepeatedly(Return(true));
182 mdm_
->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED
);
185 // Enable both mdp1 and mdp2.
186 mdm_
->RegisterDumpProvider(&mdp1
);
187 EnableTracing(kTraceCategory
);
188 EXPECT_CALL(mdp1
, OnMemoryDump(_
)).Times(1).WillRepeatedly(Return(true));
189 EXPECT_CALL(mdp2
, OnMemoryDump(_
)).Times(1).WillRepeatedly(Return(true));
190 mdm_
->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED
);
194 // Checks that the MemoryDumpManager respects the thread affinity when a
195 // MemoryDumpProvider specifies a task_runner(). The test starts creating 8
196 // threads and registering a MemoryDumpProvider on each of them. At each
197 // iteration, one thread is removed, to check the live unregistration logic.
198 TEST_F(MemoryDumpManagerTest
, RespectTaskRunnerAffinity
) {
199 const uint32 kNumInitialThreads
= 8;
201 ScopedVector
<Thread
> threads
;
202 ScopedVector
<MockDumpProvider
> mdps
;
204 // Create the threads and setup the expectations. Given that at each iteration
205 // we will pop out one thread/MemoryDumpProvider, each MDP is supposed to be
206 // invoked a number of times equal to its index.
207 for (uint32 i
= kNumInitialThreads
; i
> 0; --i
) {
208 threads
.push_back(new Thread("test thread"));
209 threads
.back()->Start();
210 mdps
.push_back(new MockDumpProvider(threads
.back()->task_runner()));
211 MockDumpProvider
* mdp
= mdps
.back();
212 mdm_
->RegisterDumpProvider(mdp
, threads
.back()->task_runner());
213 EXPECT_CALL(*mdp
, OnMemoryDump(_
))
216 Invoke(mdp
, &MockDumpProvider::OnMemoryDump_CheckTaskRunner
));
219 EnableTracing(kTraceCategory
);
221 while (!threads
.empty()) {
224 MemoryDumpCallback callback
=
225 Bind(&MemoryDumpManagerTest::DumpCallbackAdapter
, Unretained(this),
226 MessageLoop::current()->task_runner(), run_loop
.QuitClosure());
227 mdm_
->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED
, callback
);
228 // This nested message loop (|run_loop|) will be quit if and only if
229 // the RequestGlobalDump callback is invoked.
233 // Unregister a MDP and destroy one thread at each iteration to check the
234 // live unregistration logic. The unregistration needs to happen on the same
235 // thread the MDP belongs to.
238 Closure unregistration
=
239 Bind(&MemoryDumpManager::UnregisterDumpProvider
,
240 Unretained(mdm_
.get()), Unretained(mdps
.back()));
241 threads
.back()->task_runner()->PostTaskAndReply(FROM_HERE
, unregistration
,
242 run_loop
.QuitClosure());
246 threads
.back()->Stop();
253 // Enable both dump providers, make sure that mdp gets disabled after 3 failures
254 // and not disabled after 1.
255 TEST_F(MemoryDumpManagerTest
, DisableFailingDumpers
) {
256 MockDumpProvider mdp1
;
257 MockDumpProvider mdp2
;
259 mdm_
->RegisterDumpProvider(&mdp1
);
260 mdm_
->RegisterDumpProvider(&mdp2
);
261 EnableTracing(kTraceCategory
);
263 EXPECT_CALL(mdp1
, OnMemoryDump(_
))
264 .Times(MemoryDumpManager::kMaxConsecutiveFailuresCount
)
265 .WillRepeatedly(Return(false));
266 EXPECT_CALL(mdp2
, OnMemoryDump(_
))
267 .Times(1 + MemoryDumpManager::kMaxConsecutiveFailuresCount
)
268 .WillOnce(Return(false))
269 .WillRepeatedly(Return(true));
270 for (int i
= 0; i
< 1 + MemoryDumpManager::kMaxConsecutiveFailuresCount
;
272 mdm_
->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED
);
278 } // namespace trace_event