OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include <stdint.h> | |
6 | |
7 #include <memory> | |
8 #include <tuple> | |
9 | |
10 #include "base/callback.h" | |
11 #include "base/run_loop.h" | |
12 #include "base/threading/thread_task_runner_handle.h" | |
13 #include "base/trace_event/memory_dump_manager.h" | |
14 #include "base/trace_event/memory_dump_provider.h" | |
15 #include "base/trace_event/trace_event.h" | |
16 #include "components/tracing/child/child_memory_dump_manager_delegate_impl.h" | |
17 #include "components/tracing/child/child_trace_message_filter.h" | |
18 #include "components/tracing/common/tracing_messages.h" | |
19 #include "content/public/test/render_view_test.h" | |
20 #include "testing/gmock/include/gmock/gmock.h" | |
21 | |
22 using base::trace_event::MemoryDumpManager; | |
23 using base::trace_event::MemoryDumpRequestArgs; | |
24 using base::trace_event::MemoryDumpArgs; | |
25 using base::trace_event::MemoryDumpLevelOfDetail; | |
26 using base::trace_event::MemoryDumpType; | |
27 using testing::_; | |
28 using testing::Return; | |
29 | |
30 namespace tracing { | |
31 | |
32 // A mock dump provider, used to check that dump requests actually end up | |
33 // creating memory dumps. | |
34 class MockDumpProvider : public base::trace_event::MemoryDumpProvider { | |
35 public: | |
36 MOCK_METHOD2(OnMemoryDump, | |
37 bool(const MemoryDumpArgs& args, | |
38 base::trace_event::ProcessMemoryDump* pmd)); | |
39 }; | |
40 | |
41 class ChildTracingTest : public content::RenderViewTest, public IPC::Listener { | |
42 public: | |
43 // Used as callback argument for MemoryDumpManager::RequestGlobalDump(): | |
44 void OnMemoryDumpCallback(uint64_t dump_guid, bool status) { | |
45 last_callback_dump_guid_ = dump_guid; | |
46 last_callback_status_ = status; | |
47 ++callback_call_count_; | |
48 } | |
49 | |
50 protected: | |
51 void SetUp() override { | |
52 // RenderViewTest::SetUp causes additional registrations, so we first | |
53 // register the mock dump provider and ignore registrations from then on. | |
54 // In addition to the mock dump provider, the TraceLog has already | |
55 // registered itself by now; this cannot be prevented easily. | |
56 mock_dump_provider_.reset(new MockDumpProvider()); | |
57 MemoryDumpManager::GetInstance()->RegisterDumpProvider( | |
58 mock_dump_provider_.get(), "MockDumpProvider", | |
59 base::ThreadTaskRunnerHandle::Get()); | |
60 MemoryDumpManager::GetInstance() | |
61 ->set_dumper_registrations_ignored_for_testing(true); | |
62 | |
63 RenderViewTest::SetUp(); | |
64 | |
65 callback_call_count_ = 0; | |
66 last_callback_dump_guid_ = 0; | |
67 last_callback_status_ = false; | |
68 wait_for_ipc_message_type_ = 0; | |
69 callback_ = base::Bind(&ChildTracingTest::OnMemoryDumpCallback, | |
70 base::Unretained(this)); | |
71 task_runner_ = base::ThreadTaskRunnerHandle::Get(); | |
72 ctmf_ = make_scoped_refptr(new ChildTraceMessageFilter(task_runner_.get())); | |
73 render_thread_->AddFilter(ctmf_.get()); | |
74 | |
75 // Add a filter to the TestSink which allows to WaitForIPCMessage() by | |
76 // posting a nested RunLoop closure when a given IPC Message is seen. | |
77 render_thread_->sink().AddFilter(this); | |
78 | |
79 // Getting an instance of |ChildMemoryDumpManagerDelegateImpl| calls | |
80 // |MemoryDumpManager::Initialize| with the correct delegate. | |
81 ChildMemoryDumpManagerDelegateImpl::GetInstance(); | |
82 } | |
83 | |
84 void TearDown() override { | |
85 render_thread_->sink().RemoveFilter(this); | |
86 RenderViewTest::TearDown(); | |
87 MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | |
88 mock_dump_provider_.get()); | |
89 mock_dump_provider_.reset(); | |
90 ctmf_ = nullptr; | |
91 task_runner_ = nullptr; | |
92 } | |
93 | |
94 // IPC::Filter implementation. | |
95 bool OnMessageReceived(const IPC::Message& message) override { | |
96 if (message.type() == wait_for_ipc_message_type_) { | |
97 DCHECK(!wait_for_ipc_closure_.is_null()); | |
98 task_runner_->PostTask(FROM_HERE, wait_for_ipc_closure_); | |
99 } | |
100 // Always propagate messages to the sink, never consume them here. | |
101 return false; | |
102 } | |
103 | |
104 const IPC::Message* WaitForIPCMessage(uint32_t message_type) { | |
105 base::RunLoop run_loop; | |
106 wait_for_ipc_message_type_ = message_type; | |
107 wait_for_ipc_closure_ = run_loop.QuitClosure(); | |
108 run_loop.Run(); | |
109 wait_for_ipc_message_type_ = 0; | |
110 wait_for_ipc_closure_.Reset(); | |
111 return render_thread_->sink().GetUniqueMessageMatching(message_type); | |
112 } | |
113 | |
114 // Simulates a synthetic browser -> child (this process) IPC message. | |
115 void SimulateSyntheticMessageFromBrowser(const IPC::Message& msg) { | |
116 ctmf_->OnMessageReceived(msg); | |
117 } | |
118 | |
119 void EnableTracingWithMemoryDumps() { | |
120 // Re-enabling tracing could crash these tests https://crbug.com/656729 . | |
121 if (base::trace_event::TraceLog::GetInstance()->IsEnabled()) { | |
122 FAIL() << "Tracing seems to be already enabled. " | |
123 "Very likely this is because the startup tracing file " | |
124 "has been leaked from a previous test."; | |
125 } | |
126 | |
127 std::string category_filter = "-*,"; // Disable all other trace categories. | |
128 category_filter += MemoryDumpManager::kTraceCategory; | |
129 base::trace_event::TraceConfig trace_config(category_filter, ""); | |
130 TracingMsg_BeginTracing msg(trace_config.ToString(), base::TimeTicks(), 0); | |
131 SimulateSyntheticMessageFromBrowser(msg); | |
132 } | |
133 | |
134 void DisableTracing() { | |
135 SimulateSyntheticMessageFromBrowser(TracingMsg_EndTracing()); | |
136 } | |
137 | |
138 // Simulates a synthetic browser -> child process memory dump request and | |
139 // checks that the child actually sends a response to that. | |
140 void RequestProcessMemoryDumpAndCheckResponse(uint64_t dump_guid) { | |
141 SimulateSyntheticMessageFromBrowser(TracingMsg_ProcessMemoryDumpRequest( | |
142 {dump_guid, MemoryDumpType::EXPLICITLY_TRIGGERED, | |
143 MemoryDumpLevelOfDetail::DETAILED})); | |
144 | |
145 // Check that a child -> browser response to the local dump request is sent. | |
146 const IPC::Message* msg = | |
147 WaitForIPCMessage(TracingHostMsg_ProcessMemoryDumpResponse::ID); | |
148 EXPECT_NE(nullptr, msg); | |
149 | |
150 // Check that the |dump_guid| and the |success| fields are properly set. | |
151 TracingHostMsg_ProcessMemoryDumpResponse::Param params; | |
152 TracingHostMsg_ProcessMemoryDumpResponse::Read(msg, ¶ms); | |
153 const uint64_t resp_guid = std::get<0>(params); | |
154 const bool resp_success = std::get<1>(params); | |
155 EXPECT_EQ(dump_guid, resp_guid); | |
156 EXPECT_TRUE(resp_success); | |
157 } | |
158 | |
159 // Retrieves the MemoryDumpRequestArgs of the global memory dump request that | |
160 // this child process tried to send to the browser. Fails if either none or | |
161 // multiple requests were sent. | |
162 MemoryDumpRequestArgs GetInterceptedGlobalMemoryDumpRequest() { | |
163 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching( | |
164 TracingHostMsg_GlobalMemoryDumpRequest::ID); | |
165 EXPECT_NE(nullptr, msg); | |
166 TracingHostMsg_GlobalMemoryDumpRequest::Param params; | |
167 TracingHostMsg_GlobalMemoryDumpRequest::Read(msg, ¶ms); | |
168 MemoryDumpRequestArgs args = std::get<0>(params); | |
169 EXPECT_NE(0U, args.dump_guid); | |
170 return args; | |
171 } | |
172 | |
173 scoped_refptr<ChildTraceMessageFilter> ctmf_; | |
174 std::unique_ptr<MockDumpProvider> mock_dump_provider_; | |
175 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
176 base::trace_event::MemoryDumpCallback callback_; | |
177 uint32_t wait_for_ipc_message_type_; | |
178 base::Closure wait_for_ipc_closure_; | |
179 uint32_t callback_call_count_; | |
180 uint64_t last_callback_dump_guid_; | |
181 bool last_callback_status_; | |
182 }; | |
183 | |
184 // Covers the case of some browser-initiated memory dumps. | |
185 #if defined(OS_ANDROID) | |
186 // Flaky on Android. http://crbug.com/620734. | |
187 #define MAYBE_BrowserInitiatedMemoryDumps DISABLED_BrowserInitiatedMemoryDumps | |
188 #else | |
189 #define MAYBE_BrowserInitiatedMemoryDumps BrowserInitiatedMemoryDumps | |
190 #endif | |
191 TEST_F(ChildTracingTest, MAYBE_BrowserInitiatedMemoryDumps) { | |
192 const uint32_t kNumDumps = 3; | |
193 | |
194 EnableTracingWithMemoryDumps(); | |
195 EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_, _)) | |
196 .Times(kNumDumps) | |
197 .WillRepeatedly(Return(true)); | |
198 | |
199 for (uint32_t i = 0; i < kNumDumps; ++i) { | |
200 render_thread_->sink().ClearMessages(); | |
201 RequestProcessMemoryDumpAndCheckResponse(i + 1); | |
202 } | |
203 | |
204 DisableTracing(); | |
205 } | |
206 | |
207 // Covers the case of one simple child-initiated memory dump without callback, | |
208 // simulating a global memory dump request to the browser (+ response). | |
209 TEST_F(ChildTracingTest, SingleChildInitiatedMemoryDump) { | |
210 EnableTracingWithMemoryDumps(); | |
211 | |
212 // Expect that our mock dump provider is called when the emulated memory dump | |
213 // request (browser -> child) is sent. | |
214 EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_, _)) | |
215 .Times(1) | |
216 .WillRepeatedly(Return(true)); | |
217 | |
218 // Send the global memory dump request to the browser. | |
219 render_thread_->sink().ClearMessages(); | |
220 MemoryDumpManager::GetInstance()->RequestGlobalDump( | |
221 MemoryDumpType::EXPLICITLY_TRIGGERED, MemoryDumpLevelOfDetail::DETAILED); | |
222 base::RunLoop().RunUntilIdle(); | |
223 | |
224 // Check that the child -> browser global dump request IPC is actually sent. | |
225 MemoryDumpRequestArgs args = GetInterceptedGlobalMemoryDumpRequest(); | |
226 EXPECT_EQ(MemoryDumpType::EXPLICITLY_TRIGGERED, args.dump_type); | |
227 | |
228 // Emulate a browser -> child process dump request and corresponding response. | |
229 RequestProcessMemoryDumpAndCheckResponse(args.dump_guid); | |
230 | |
231 // Send a synthetic browser -> child global memory dump response. | |
232 SimulateSyntheticMessageFromBrowser( | |
233 TracingMsg_GlobalMemoryDumpResponse(args.dump_guid, true)); | |
234 | |
235 DisableTracing(); | |
236 } | |
237 | |
238 // Covers the case of a global memory dump being requested while another one is | |
239 // in progress and has not been acknowledged by the browser. The second request | |
240 // is expected to fail immediately, while the first one is expected to suceed. | |
241 TEST_F(ChildTracingTest, OverlappingChildInitiatedMemoryDumps) { | |
242 EnableTracingWithMemoryDumps(); | |
243 | |
244 // Expect that our mock dump provider is called only once. | |
245 EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_, _)) | |
246 .Times(1) | |
247 .WillRepeatedly(Return(true)); | |
248 | |
249 // Send the global memory dump request to the browser. | |
250 render_thread_->sink().ClearMessages(); | |
251 MemoryDumpManager::GetInstance()->RequestGlobalDump( | |
252 MemoryDumpType::EXPLICITLY_TRIGGERED, MemoryDumpLevelOfDetail::DETAILED, | |
253 callback_); | |
254 base::RunLoop().RunUntilIdle(); | |
255 | |
256 // Check that the child -> browser global dump request IPC is actually sent. | |
257 MemoryDumpRequestArgs args = GetInterceptedGlobalMemoryDumpRequest(); | |
258 EXPECT_EQ(MemoryDumpType::EXPLICITLY_TRIGGERED, args.dump_type); | |
259 | |
260 // Emulate a browser -> child process dump request and corresponding response. | |
261 RequestProcessMemoryDumpAndCheckResponse(args.dump_guid); | |
262 | |
263 // Before the response for the first global dump is sent, send another one, | |
264 // and expect that to fail. | |
265 render_thread_->sink().ClearMessages(); | |
266 MemoryDumpManager::GetInstance()->RequestGlobalDump( | |
267 MemoryDumpType::EXPLICITLY_TRIGGERED, MemoryDumpLevelOfDetail::DETAILED, | |
268 callback_); | |
269 base::RunLoop().RunUntilIdle(); | |
270 | |
271 EXPECT_EQ(1u, callback_call_count_); | |
272 EXPECT_FALSE(last_callback_status_); | |
273 // Whatever the guid of the second failing request is, it cannot possibly be | |
274 // equal to the guid of the first request. | |
275 EXPECT_NE(args.dump_guid, last_callback_dump_guid_); | |
276 | |
277 // Also, check that no request has been forwarded to the browser (because the | |
278 // first request is still outstanding and has not received any response yet). | |
279 EXPECT_EQ(nullptr, render_thread_->sink().GetUniqueMessageMatching( | |
280 TracingHostMsg_GlobalMemoryDumpRequest::ID)); | |
281 | |
282 // Now send a synthetic browser -> child response to the first request. | |
283 SimulateSyntheticMessageFromBrowser( | |
284 TracingMsg_GlobalMemoryDumpResponse(args.dump_guid, true)); | |
285 | |
286 // Verify that the the callback for the first request is finally called. | |
287 EXPECT_EQ(2u, callback_call_count_); | |
288 EXPECT_EQ(args.dump_guid, last_callback_dump_guid_); | |
289 EXPECT_TRUE(last_callback_status_); | |
290 | |
291 DisableTracing(); | |
292 } | |
293 | |
294 // Covers the case of five child-initiated global memory dumps. Each global dump | |
295 // request has a callback, which is expected to fail for 3 out of 5 cases. | |
296 TEST_F(ChildTracingTest, MultipleChildInitiatedMemoryDumpWithFailures) { | |
297 const uint32_t kNumRequests = 5; | |
298 MemoryDumpType kDumpType = MemoryDumpType::EXPLICITLY_TRIGGERED; | |
299 | |
300 EnableTracingWithMemoryDumps(); | |
301 EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_, _)) | |
302 .Times(kNumRequests) | |
303 .WillRepeatedly(Return(true)); | |
304 | |
305 for (uint32_t i = 0; i < kNumRequests; ++i) { | |
306 render_thread_->sink().ClearMessages(); | |
307 MemoryDumpManager::GetInstance()->RequestGlobalDump( | |
308 kDumpType, MemoryDumpLevelOfDetail::DETAILED, callback_); | |
309 base::RunLoop().RunUntilIdle(); | |
310 | |
311 MemoryDumpRequestArgs args = GetInterceptedGlobalMemoryDumpRequest(); | |
312 EXPECT_EQ(kDumpType, args.dump_type); | |
313 RequestProcessMemoryDumpAndCheckResponse(args.dump_guid); | |
314 | |
315 const bool success = (i & 1) ? true : false; | |
316 SimulateSyntheticMessageFromBrowser( | |
317 TracingMsg_GlobalMemoryDumpResponse(args.dump_guid, success)); | |
318 EXPECT_EQ(i + 1, callback_call_count_); | |
319 EXPECT_EQ(args.dump_guid, last_callback_dump_guid_); | |
320 EXPECT_EQ(success, last_callback_status_); | |
321 } | |
322 | |
323 DisableTracing(); | |
324 } | |
325 | |
326 } // namespace tracing | |
OLD | NEW |