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
21namespace {
22
23using base::win::EtwMofEvent;
24using base::win::EtwTraceController;
25using base::win::EtwTraceConsumerBase;
26using base::win::EtwTraceProperties;
27using base::win::EtwTraceProvider;
28
29typedef std::list<EVENT_TRACE> EventQueue;
30
31class TestConsumer: public EtwTraceConsumerBase<TestConsumer> {
32 public:
33  TestConsumer() {
34    sank_event_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
35    ClearQueue();
36  }
37
38  ~TestConsumer() {
39    ClearQueue();
40    sank_event_.Close();
41  }
42
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
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
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
75base::win::ScopedHandle TestConsumer::sank_event_;
76EventQueue TestConsumer::events_;
77
78const wchar_t* const kTestSessionName = L"TestLogSession";
79
80class EtwTraceConsumerBaseTest: public testing::Test {
81 public:
82  virtual void SetUp() {
83    EtwTraceProperties ignore;
84    EtwTraceController::Stop(kTestSessionName, &ignore);
85  }
86};
87
88}  // namespace
89
90TEST_F(EtwTraceConsumerBaseTest, Initialize) {
91  TestConsumer consumer_;
92}
93
94TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) {
95  TestConsumer consumer_;
96
97  ASSERT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName));
98}
99
100TEST_F(EtwTraceConsumerBaseTest, ConsumerImmediateFailureWhenNoSession) {
101  TestConsumer consumer_;
102
103  ASSERT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName));
104  ASSERT_HRESULT_FAILED(consumer_.Consume());
105}
106
107namespace {
108
109class EtwTraceConsumerRealtimeTest: public testing::Test {
110 public:
111  virtual void SetUp() {
112    ASSERT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName));
113  }
114
115  virtual void TearDown() {
116    consumer_.Close();
117  }
118
119  DWORD ConsumerThread() {
120    ::SetEvent(consumer_ready_.Get());
121
122    HRESULT hr = consumer_.Consume();
123    return hr;
124  }
125
126  static DWORD WINAPI ConsumerThreadMainProc(void* arg) {
127    return reinterpret_cast<EtwTraceConsumerRealtimeTest*>(arg)->
128        ConsumerThread();
129  }
130
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.
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
185TEST_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
206namespace {
207
208// {036B8F65-8DF3-46e4-ABFC-6985C43D59BA}
209DEFINE_GUID(kTestProvider,
210  0x36b8f65, 0x8df3, 0x46e4, 0xab, 0xfc, 0x69, 0x85, 0xc4, 0x3d, 0x59, 0xba);
211
212// {57E47923-A549-476f-86CA-503D57F59E62}
213DEFINE_GUID(kTestEventType,
214  0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62);
215
216}  // namespace
217
218TEST_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
247namespace {
248
249// We run events through a file session to assert that
250// the content comes through.
251class EtwTraceConsumerDataTest: public testing::Test {
252 public:
253  EtwTraceConsumerDataTest() {
254  }
255
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
263  virtual void TearDown() {
264    EXPECT_TRUE(file_util::Delete(temp_file_, false));
265    EtwTraceProperties ignore;
266    EtwTraceController::Stop(kTestSessionName, &ignore);
267  }
268
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
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
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
332TEST_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