• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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