1 // Copyright (c) 2012 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 // Unit tests for event trace consumer base class.
6 #include "base/win/event_trace_consumer.h"
7
8 #include <list>
9
10 #include <objbase.h>
11
12 #include "base/basictypes.h"
13 #include "base/file_util.h"
14 #include "base/files/file_path.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/logging.h"
17 #include "base/process/process.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/win/event_trace_controller.h"
20 #include "base/win/event_trace_provider.h"
21 #include "base/win/scoped_handle.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23
24 #include <initguid.h> // NOLINT - has to be last
25
26 namespace base {
27 namespace win {
28
29 namespace {
30
31 typedef std::list<EVENT_TRACE> EventQueue;
32
33 class TestConsumer: public EtwTraceConsumerBase<TestConsumer> {
34 public:
TestConsumer()35 TestConsumer() {
36 sank_event_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
37 ClearQueue();
38 }
39
~TestConsumer()40 ~TestConsumer() {
41 ClearQueue();
42 sank_event_.Close();
43 }
44
ClearQueue()45 void ClearQueue() {
46 EventQueue::const_iterator it(events_.begin()), end(events_.end());
47
48 for (; it != end; ++it) {
49 delete [] it->MofData;
50 }
51
52 events_.clear();
53 }
54
EnqueueEvent(EVENT_TRACE * event)55 static void EnqueueEvent(EVENT_TRACE* event) {
56 events_.push_back(*event);
57 EVENT_TRACE& back = events_.back();
58
59 if (NULL != event->MofData && 0 != event->MofLength) {
60 back.MofData = new char[event->MofLength];
61 memcpy(back.MofData, event->MofData, event->MofLength);
62 }
63 }
64
ProcessEvent(EVENT_TRACE * event)65 static void ProcessEvent(EVENT_TRACE* event) {
66 EnqueueEvent(event);
67 ::SetEvent(sank_event_.Get());
68 }
69
70 static ScopedHandle sank_event_;
71 static EventQueue events_;
72
73 private:
74 DISALLOW_COPY_AND_ASSIGN(TestConsumer);
75 };
76
77 ScopedHandle TestConsumer::sank_event_;
78 EventQueue TestConsumer::events_;
79
80 class EtwTraceConsumerBaseTest: public testing::Test {
81 public:
EtwTraceConsumerBaseTest()82 EtwTraceConsumerBaseTest()
83 : session_name_(StringPrintf(L"TestSession-%d",
84 Process::Current().pid())) {
85 }
86
SetUp()87 virtual void SetUp() {
88 // Cleanup any potentially dangling sessions.
89 EtwTraceProperties ignore;
90 EtwTraceController::Stop(session_name_.c_str(), &ignore);
91
92 // Allocate a new GUID for each provider test.
93 ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&test_provider_));
94 }
95
TearDown()96 virtual void TearDown() {
97 // Cleanup any potentially danging sessions.
98 EtwTraceProperties ignore;
99 EtwTraceController::Stop(session_name_.c_str(), &ignore);
100 }
101
102 protected:
103 GUID test_provider_;
104 std::wstring session_name_;
105 };
106
107 } // namespace
108
TEST_F(EtwTraceConsumerBaseTest,Initialize)109 TEST_F(EtwTraceConsumerBaseTest, Initialize) {
110 TestConsumer consumer_;
111 }
112
TEST_F(EtwTraceConsumerBaseTest,OpenRealtimeSucceedsWhenNoSession)113 TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) {
114 TestConsumer consumer_;
115
116 ASSERT_HRESULT_SUCCEEDED(
117 consumer_.OpenRealtimeSession(session_name_.c_str()));
118 }
119
TEST_F(EtwTraceConsumerBaseTest,ConsumerImmediateFailureWhenNoSession)120 TEST_F(EtwTraceConsumerBaseTest, ConsumerImmediateFailureWhenNoSession) {
121 TestConsumer consumer_;
122
123 ASSERT_HRESULT_SUCCEEDED(
124 consumer_.OpenRealtimeSession(session_name_.c_str()));
125 ASSERT_HRESULT_FAILED(consumer_.Consume());
126 }
127
128 namespace {
129
130 class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest {
131 public:
SetUp()132 virtual void SetUp() {
133 EtwTraceConsumerBaseTest::SetUp();
134
135 ASSERT_HRESULT_SUCCEEDED(
136 consumer_.OpenRealtimeSession(session_name_.c_str()));
137 }
138
TearDown()139 virtual void TearDown() {
140 consumer_.Close();
141
142 EtwTraceConsumerBaseTest::TearDown();
143 }
144
ConsumerThread()145 DWORD ConsumerThread() {
146 ::SetEvent(consumer_ready_.Get());
147
148 HRESULT hr = consumer_.Consume();
149 return hr;
150 }
151
ConsumerThreadMainProc(void * arg)152 static DWORD WINAPI ConsumerThreadMainProc(void* arg) {
153 return reinterpret_cast<EtwTraceConsumerRealtimeTest*>(arg)->
154 ConsumerThread();
155 }
156
StartConsumerThread()157 HRESULT StartConsumerThread() {
158 consumer_ready_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
159 EXPECT_TRUE(consumer_ready_ != NULL);
160 consumer_thread_.Set(::CreateThread(NULL, 0, ConsumerThreadMainProc,
161 this, 0, NULL));
162 if (NULL == consumer_thread_.Get())
163 return HRESULT_FROM_WIN32(::GetLastError());
164
165 HRESULT hr = S_OK;
166 HANDLE events[] = { consumer_ready_, consumer_thread_ };
167 DWORD result = ::WaitForMultipleObjects(arraysize(events), events,
168 FALSE, INFINITE);
169 switch (result) {
170 case WAIT_OBJECT_0:
171 // The event was set, the consumer_ is ready.
172 return S_OK;
173 case WAIT_OBJECT_0 + 1: {
174 // The thread finished. This may race with the event, so check
175 // explicitly for the event here, before concluding there's trouble.
176 if (WAIT_OBJECT_0 == ::WaitForSingleObject(consumer_ready_, 0))
177 return S_OK;
178 DWORD exit_code = 0;
179 if (::GetExitCodeThread(consumer_thread_, &exit_code))
180 return exit_code;
181 else
182 return HRESULT_FROM_WIN32(::GetLastError());
183 break;
184 }
185 default:
186 return E_UNEXPECTED;
187 break;
188 }
189
190 return hr;
191 }
192
193 // Waits for consumer_ thread to exit, and returns its exit code.
JoinConsumerThread()194 HRESULT JoinConsumerThread() {
195 if (WAIT_OBJECT_0 != ::WaitForSingleObject(consumer_thread_, INFINITE))
196 return HRESULT_FROM_WIN32(::GetLastError());
197
198 DWORD exit_code = 0;
199 if (::GetExitCodeThread(consumer_thread_, &exit_code))
200 return exit_code;
201
202 return HRESULT_FROM_WIN32(::GetLastError());
203 }
204
205 TestConsumer consumer_;
206 ScopedHandle consumer_ready_;
207 ScopedHandle consumer_thread_;
208 };
209
210 } // namespace
211
TEST_F(EtwTraceConsumerRealtimeTest,ConsumerReturnsWhenSessionClosed)212 TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) {
213 EtwTraceController controller;
214
215 HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(),
216 100 * 1024);
217 if (hr == E_ACCESSDENIED) {
218 VLOG(1) << "You must be an administrator to run this test on Vista";
219 return;
220 }
221
222 // Start the consumer_.
223 ASSERT_HRESULT_SUCCEEDED(StartConsumerThread());
224
225 // Wait around for the consumer_ thread a bit.
226 ASSERT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(consumer_thread_, 50));
227
228 ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL));
229
230 // The consumer_ returns success on session stop.
231 ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread());
232 }
233
234 namespace {
235
236 // {57E47923-A549-476f-86CA-503D57F59E62}
237 DEFINE_GUID(kTestEventType,
238 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62);
239
240 } // namespace
241
TEST_F(EtwTraceConsumerRealtimeTest,ConsumeEvent)242 TEST_F(EtwTraceConsumerRealtimeTest, ConsumeEvent) {
243 EtwTraceController controller;
244 HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(),
245 100 * 1024);
246 if (hr == E_ACCESSDENIED) {
247 VLOG(1) << "You must be an administrator to run this test on Vista";
248 return;
249 }
250
251 ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_,
252 TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
253
254 EtwTraceProvider provider(test_provider_);
255 ASSERT_EQ(ERROR_SUCCESS, provider.Register());
256
257 // Start the consumer_.
258 ASSERT_HRESULT_SUCCEEDED(StartConsumerThread());
259
260 ASSERT_EQ(0, TestConsumer::events_.size());
261
262 EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR);
263 EXPECT_EQ(ERROR_SUCCESS, provider.Log(&event.header));
264
265 EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(TestConsumer::sank_event_,
266 INFINITE));
267 ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL));
268 ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread());
269 ASSERT_NE(0u, TestConsumer::events_.size());
270 }
271
272 namespace {
273
274 // We run events through a file session to assert that
275 // the content comes through.
276 class EtwTraceConsumerDataTest: public EtwTraceConsumerBaseTest {
277 public:
EtwTraceConsumerDataTest()278 EtwTraceConsumerDataTest() {
279 }
280
SetUp()281 virtual void SetUp() {
282 EtwTraceConsumerBaseTest::SetUp();
283
284 EtwTraceProperties prop;
285 EtwTraceController::Stop(session_name_.c_str(), &prop);
286
287 // Create a temp dir for this test.
288 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
289 // Construct a temp file name in our dir.
290 temp_file_ = temp_dir_.path().Append(L"test.etl");
291 }
292
TearDown()293 virtual void TearDown() {
294 EXPECT_TRUE(base::DeleteFile(temp_file_, false));
295
296 EtwTraceConsumerBaseTest::TearDown();
297 }
298
LogEventToTempSession(PEVENT_TRACE_HEADER header)299 HRESULT LogEventToTempSession(PEVENT_TRACE_HEADER header) {
300 EtwTraceController controller;
301
302 // Set up a file session.
303 HRESULT hr = controller.StartFileSession(session_name_.c_str(),
304 temp_file_.value().c_str());
305 if (FAILED(hr))
306 return hr;
307
308 // Enable our provider.
309 EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_,
310 TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
311
312 EtwTraceProvider provider(test_provider_);
313 // Then register our provider, means we get a session handle immediately.
314 EXPECT_EQ(ERROR_SUCCESS, provider.Register());
315 // Trace the event, it goes to the temp file.
316 EXPECT_EQ(ERROR_SUCCESS, provider.Log(header));
317 EXPECT_HRESULT_SUCCEEDED(controller.DisableProvider(test_provider_));
318 EXPECT_HRESULT_SUCCEEDED(provider.Unregister());
319 EXPECT_HRESULT_SUCCEEDED(controller.Flush(NULL));
320 EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
321
322 return S_OK;
323 }
324
ConsumeEventFromTempSession()325 HRESULT ConsumeEventFromTempSession() {
326 // Now consume the event(s).
327 TestConsumer consumer_;
328 HRESULT hr = consumer_.OpenFileSession(temp_file_.value().c_str());
329 if (SUCCEEDED(hr))
330 hr = consumer_.Consume();
331 consumer_.Close();
332 // And nab the result.
333 events_.swap(TestConsumer::events_);
334 return hr;
335 }
336
RoundTripEvent(PEVENT_TRACE_HEADER header,PEVENT_TRACE * trace)337 HRESULT RoundTripEvent(PEVENT_TRACE_HEADER header, PEVENT_TRACE* trace) {
338 base::DeleteFile(temp_file_, false);
339
340 HRESULT hr = LogEventToTempSession(header);
341 if (SUCCEEDED(hr))
342 hr = ConsumeEventFromTempSession();
343
344 if (FAILED(hr))
345 return hr;
346
347 // We should now have the event in the queue.
348 if (events_.empty())
349 return E_FAIL;
350
351 *trace = &events_.back();
352 return S_OK;
353 }
354
355 EventQueue events_;
356 ScopedTempDir temp_dir_;
357 FilePath temp_file_;
358 };
359
360 } // namespace
361
362
TEST_F(EtwTraceConsumerDataTest,RoundTrip)363 TEST_F(EtwTraceConsumerDataTest, RoundTrip) {
364 EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR);
365
366 static const char kData[] = "This is but test data";
367 event.fields[0].DataPtr = reinterpret_cast<ULONG64>(kData);
368 event.fields[0].Length = sizeof(kData);
369
370 PEVENT_TRACE trace = NULL;
371 HRESULT hr = RoundTripEvent(&event.header, &trace);
372 if (hr == E_ACCESSDENIED) {
373 VLOG(1) << "You must be an administrator to run this test on Vista";
374 return;
375 }
376 ASSERT_HRESULT_SUCCEEDED(hr) << "RoundTripEvent failed";
377 ASSERT_TRUE(NULL != trace);
378 ASSERT_EQ(sizeof(kData), trace->MofLength);
379 ASSERT_STREQ(kData, reinterpret_cast<const char*>(trace->MofData));
380 }
381
382 } // namespace win
383 } // namespace base
384