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/files/file_path.h" 14 #include "base/files/file_util.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[] reinterpret_cast<char*>(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_.IsValid()); 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_.Get(), consumer_thread_.Get() }; 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_.Get(), 0) == WAIT_OBJECT_0) 169 return S_OK; 170 DWORD exit_code = 0; 171 if (::GetExitCodeThread(consumer_thread_.Get(), &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_.Get(), INFINITE) != 183 WAIT_OBJECT_0) { 184 return HRESULT_FROM_WIN32(::GetLastError()); 185 } 186 187 DWORD exit_code = 0; 188 if (::GetExitCodeThread(consumer_thread_.Get(), &exit_code)) 189 return exit_code; 190 191 return HRESULT_FROM_WIN32(::GetLastError()); 192 } 193 194 TestConsumer consumer_; 195 ScopedHandle consumer_ready_; 196 ScopedHandle consumer_thread_; 197 }; 198 199 } // namespace 200 201 TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) { 202 EtwTraceController controller; 203 if (controller.StartRealtimeSession(session_name_.c_str(), 100 * 1024) == 204 E_ACCESSDENIED) { 205 VLOG(1) << "You must be an administrator to run this test on Vista"; 206 return; 207 } 208 209 // Start the consumer_. 210 ASSERT_HRESULT_SUCCEEDED(StartConsumerThread()); 211 212 // Wait around for the consumer_ thread a bit. 213 ASSERT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(consumer_thread_.Get(), 50)); 214 ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL)); 215 216 // The consumer_ returns success on session stop. 217 ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread()); 218 } 219 220 namespace { 221 222 // {57E47923-A549-476f-86CA-503D57F59E62} 223 DEFINE_GUID( 224 kTestEventType, 225 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62); 226 227 } // namespace 228 229 TEST_F(EtwTraceConsumerRealtimeTest, ConsumeEvent) { 230 EtwTraceController controller; 231 if (controller.StartRealtimeSession(session_name_.c_str(), 100 * 1024) == 232 E_ACCESSDENIED) { 233 VLOG(1) << "You must be an administrator to run this test on Vista"; 234 return; 235 } 236 237 ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider( 238 test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); 239 240 EtwTraceProvider provider(test_provider_); 241 ASSERT_EQ(ERROR_SUCCESS, provider.Register()); 242 243 // Start the consumer_. 244 ASSERT_HRESULT_SUCCEEDED(StartConsumerThread()); 245 ASSERT_EQ(0, TestConsumer::events_.size()); 246 247 EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR); 248 EXPECT_EQ(ERROR_SUCCESS, provider.Log(&event.header)); 249 EXPECT_EQ(WAIT_OBJECT_0, 250 ::WaitForSingleObject(TestConsumer::sank_event_.Get(), INFINITE)); 251 ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL)); 252 ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread()); 253 ASSERT_NE(0u, TestConsumer::events_.size()); 254 } 255 256 namespace { 257 258 // We run events through a file session to assert that 259 // the content comes through. 260 class EtwTraceConsumerDataTest: public EtwTraceConsumerBaseTest { 261 public: 262 EtwTraceConsumerDataTest() { 263 } 264 265 virtual void SetUp() { 266 EtwTraceConsumerBaseTest::SetUp(); 267 268 EtwTraceProperties prop; 269 EtwTraceController::Stop(session_name_.c_str(), &prop); 270 271 // Create a temp dir for this test. 272 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 273 // Construct a temp file name in our dir. 274 temp_file_ = temp_dir_.path().Append(L"test.etl"); 275 } 276 277 virtual void TearDown() { 278 EXPECT_TRUE(base::DeleteFile(temp_file_, false)); 279 280 EtwTraceConsumerBaseTest::TearDown(); 281 } 282 283 HRESULT LogEventToTempSession(PEVENT_TRACE_HEADER header) { 284 EtwTraceController controller; 285 286 // Set up a file session. 287 HRESULT hr = controller.StartFileSession(session_name_.c_str(), 288 temp_file_.value().c_str()); 289 if (FAILED(hr)) 290 return hr; 291 292 // Enable our provider. 293 EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider( 294 test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); 295 296 EtwTraceProvider provider(test_provider_); 297 // Then register our provider, means we get a session handle immediately. 298 EXPECT_EQ(ERROR_SUCCESS, provider.Register()); 299 // Trace the event, it goes to the temp file. 300 EXPECT_EQ(ERROR_SUCCESS, provider.Log(header)); 301 EXPECT_HRESULT_SUCCEEDED(controller.DisableProvider(test_provider_)); 302 EXPECT_HRESULT_SUCCEEDED(provider.Unregister()); 303 EXPECT_HRESULT_SUCCEEDED(controller.Flush(NULL)); 304 EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL)); 305 306 return S_OK; 307 } 308 309 HRESULT ConsumeEventFromTempSession() { 310 // Now consume the event(s). 311 TestConsumer consumer_; 312 HRESULT hr = consumer_.OpenFileSession(temp_file_.value().c_str()); 313 if (SUCCEEDED(hr)) 314 hr = consumer_.Consume(); 315 consumer_.Close(); 316 // And nab the result. 317 events_.swap(TestConsumer::events_); 318 return hr; 319 } 320 321 HRESULT RoundTripEvent(PEVENT_TRACE_HEADER header, PEVENT_TRACE* trace) { 322 base::DeleteFile(temp_file_, false); 323 324 HRESULT hr = LogEventToTempSession(header); 325 if (SUCCEEDED(hr)) 326 hr = ConsumeEventFromTempSession(); 327 328 if (FAILED(hr)) 329 return hr; 330 331 // We should now have the event in the queue. 332 if (events_.empty()) 333 return E_FAIL; 334 335 *trace = &events_.back(); 336 return S_OK; 337 } 338 339 EventQueue events_; 340 ScopedTempDir temp_dir_; 341 FilePath temp_file_; 342 }; 343 344 } // namespace 345 346 347 TEST_F(EtwTraceConsumerDataTest, RoundTrip) { 348 EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR); 349 350 static const char kData[] = "This is but test data"; 351 event.fields[0].DataPtr = reinterpret_cast<ULONG64>(kData); 352 event.fields[0].Length = sizeof(kData); 353 354 PEVENT_TRACE trace = NULL; 355 HRESULT hr = RoundTripEvent(&event.header, &trace); 356 if (hr == E_ACCESSDENIED) { 357 VLOG(1) << "You must be an administrator to run this test on Vista"; 358 return; 359 } 360 ASSERT_HRESULT_SUCCEEDED(hr) << "RoundTripEvent failed"; 361 ASSERT_TRUE(trace != NULL); 362 ASSERT_EQ(sizeof(kData), trace->MofLength); 363 ASSERT_STREQ(kData, reinterpret_cast<const char*>(trace->MofData)); 364 } 365 366 } // namespace win 367 } // namespace base 368