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