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