Home | History | Annotate | Download | only in win
      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:
     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 
     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:
     82   virtual void SetUp() {
     83     EtwTraceProperties ignore;
     84     EtwTraceController::Stop(kTestSessionName, &ignore);
     85   }
     86 };
     87 
     88 }  // namespace
     89 
     90 TEST_F(EtwTraceConsumerBaseTest, Initialize) {
     91   TestConsumer consumer_;
     92 }
     93 
     94 TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) {
     95   TestConsumer consumer_;
     96 
     97   ASSERT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName));
     98 }
     99 
    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:
    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 
    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 
    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:
    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 
    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