diff options
author | siggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-06 15:39:40 +0000 |
---|---|---|
committer | siggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-06 15:39:40 +0000 |
commit | ba50d192694e3bbdd99d153205e7fcef236abf8a (patch) | |
tree | d4621c1cbad3e63cd87c40689c0ae5daaafab1fe /base/win | |
parent | f2505c82c17948056214d3437d53620610fba837 (diff) | |
download | chromium_src-ba50d192694e3bbdd99d153205e7fcef236abf8a.zip chromium_src-ba50d192694e3bbdd99d153205e7fcef236abf8a.tar.gz chromium_src-ba50d192694e3bbdd99d153205e7fcef236abf8a.tar.bz2 |
Move Windows-specific Event Tracing for Windows implementation to base/win.
fix users of same.
BUG=none
TEST=unittests
Review URL: http://codereview.chromium.org/4517004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65310 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/win')
-rw-r--r-- | base/win/event_trace_consumer.h | 150 | ||||
-rw-r--r-- | base/win/event_trace_consumer_unittest.cc | 346 | ||||
-rw-r--r-- | base/win/event_trace_controller.cc | 173 | ||||
-rw-r--r-- | base/win/event_trace_controller.h | 150 | ||||
-rw-r--r-- | base/win/event_trace_controller_unittest.cc | 208 | ||||
-rw-r--r-- | base/win/event_trace_provider.cc | 134 | ||||
-rw-r--r-- | base/win/event_trace_provider.h | 174 | ||||
-rw-r--r-- | base/win/event_trace_provider_unittest.cc | 110 |
8 files changed, 1445 insertions, 0 deletions
diff --git a/base/win/event_trace_consumer.h b/base/win/event_trace_consumer.h new file mode 100644 index 0000000..1ceb9ee --- /dev/null +++ b/base/win/event_trace_consumer.h @@ -0,0 +1,150 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Declaration of a Windows event trace consumer base class. +#ifndef BASE_WIN_EVENT_TRACE_CONSUMER_H_ +#define BASE_WIN_EVENT_TRACE_CONSUMER_H_ +#pragma once + +#include <windows.h> +#include <wmistr.h> +#include <evntrace.h> +#include <vector> +#include "base/basictypes.h" + +namespace base { +namespace win { + +// This class is a base class that makes it easier to consume events +// from realtime or file sessions. Concrete consumers need to sublass +// a specialization of this class and override the ProcessEvent and/or +// the ProcessBuffer methods to implement the event consumption logic. +// Usage might look like: +// class MyConsumer: public EtwTraceConsumerBase<MyConsumer, 1> { +// protected: +// static VOID WINAPI ProcessEvent(PEVENT_TRACE event); +// }; +// +// MyConsumer consumer; +// consumer.OpenFileSession(file_path); +// consumer.Consume(); +template <class ImplClass> +class EtwTraceConsumerBase { + public: + // Constructs a closed consumer. + EtwTraceConsumerBase() { + } + + ~EtwTraceConsumerBase() { + Close(); + } + + // Opens the named realtime session, which must be existent. + // Note: You can use OpenRealtimeSession or OpenFileSession + // to open as many as MAXIMUM_WAIT_OBJECTS (63) sessions at + // any one time, though only one of them may be a realtime + // session. + HRESULT OpenRealtimeSession(const wchar_t* session_name); + + // Opens the event trace log in "file_name", which must be a full or + // relative path to an existing event trace log file. + // Note: You can use OpenRealtimeSession or OpenFileSession + // to open as many as kNumSessions at any one time. + HRESULT OpenFileSession(const wchar_t* file_name); + + // Consume all open sessions from beginning to end. + HRESULT Consume(); + + // Close all open sessions. + HRESULT Close(); + + protected: + // Override in subclasses to handle events. + static void ProcessEvent(EVENT_TRACE* event) { + } + // Override in subclasses to handle buffers. + static bool ProcessBuffer(EVENT_TRACE_LOGFILE* buffer) { + return true; // keep going + } + + protected: + // Currently open sessions. + std::vector<TRACEHANDLE> trace_handles_; + + private: + // These delegate to ImplClass callbacks with saner signatures. + static void WINAPI ProcessEventCallback(EVENT_TRACE* event) { + ImplClass::ProcessEvent(event); + } + static ULONG WINAPI ProcessBufferCallback(PEVENT_TRACE_LOGFILE buffer) { + return ImplClass::ProcessBuffer(buffer); + } + + DISALLOW_COPY_AND_ASSIGN(EtwTraceConsumerBase); +}; + +template <class ImplClass> inline +HRESULT EtwTraceConsumerBase<ImplClass>::OpenRealtimeSession( + const wchar_t* session_name) { + EVENT_TRACE_LOGFILE logfile = {}; + logfile.LoggerName = const_cast<wchar_t*>(session_name); + logfile.LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + logfile.BufferCallback = &ProcessBufferCallback; + logfile.EventCallback = &ProcessEventCallback; + logfile.Context = this; + TRACEHANDLE trace_handle = ::OpenTrace(&logfile); + if (reinterpret_cast<TRACEHANDLE>(INVALID_HANDLE_VALUE) == trace_handle) + return HRESULT_FROM_WIN32(::GetLastError()); + + trace_handles_.push_back(trace_handle); + return S_OK; +} + +template <class ImplClass> inline +HRESULT EtwTraceConsumerBase<ImplClass>::OpenFileSession( + const wchar_t* file_name) { + EVENT_TRACE_LOGFILE logfile = {}; + logfile.LogFileName = const_cast<wchar_t*>(file_name); + logfile.BufferCallback = &ProcessBufferCallback; + logfile.EventCallback = &ProcessEventCallback; + logfile.Context = this; + TRACEHANDLE trace_handle = ::OpenTrace(&logfile); + if (reinterpret_cast<TRACEHANDLE>(INVALID_HANDLE_VALUE) == trace_handle) + return HRESULT_FROM_WIN32(::GetLastError()); + + trace_handles_.push_back(trace_handle); + return S_OK; +} + +template <class ImplClass> inline +HRESULT EtwTraceConsumerBase<ImplClass>::Consume() { + ULONG err = ::ProcessTrace(&trace_handles_[0], + trace_handles_.size(), + NULL, + NULL); + return HRESULT_FROM_WIN32(err); +} + +template <class ImplClass> inline +HRESULT EtwTraceConsumerBase<ImplClass>::Close() { + HRESULT hr = S_OK; + for (size_t i = 0; i < trace_handles_.size(); ++i) { + if (NULL != trace_handles_[i]) { + ULONG ret = ::CloseTrace(trace_handles_[i]); + trace_handles_[i] = NULL; + + if (FAILED(HRESULT_FROM_WIN32(ret))) + hr = HRESULT_FROM_WIN32(ret); + } + + trace_handles_.clear(); + } + + return hr; +} + +} // namespace win +} // namespace base + +#endif // BASE_WIN_EVENT_TRACE_CONSUMER_H_ diff --git a/base/win/event_trace_consumer_unittest.cc b/base/win/event_trace_consumer_unittest.cc new file mode 100644 index 0000000..f11f459 --- /dev/null +++ b/base/win/event_trace_consumer_unittest.cc @@ -0,0 +1,346 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Unit tests for event trace consumer_ base class. +#include "base/win/event_trace_consumer.h" +#include <list> +#include "base/basictypes.h" +#include "base/win/event_trace_controller.h" +#include "base/win/event_trace_provider.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include <initguid.h> // NOLINT - has to be last + +namespace { + +using base::win::EtwMofEvent; +using base::win::EtwTraceController; +using base::win::EtwTraceConsumerBase; +using base::win::EtwTraceProperties; +using base::win::EtwTraceProvider; + +typedef std::list<EVENT_TRACE> EventQueue; + +class TestConsumer: public EtwTraceConsumerBase<TestConsumer> { + public: + TestConsumer() { + sank_event_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL)); + ClearQueue(); + } + + ~TestConsumer() { + ClearQueue(); + sank_event_.Close(); + } + + void ClearQueue() { + EventQueue::const_iterator it(events_.begin()), end(events_.end()); + + for (; it != end; ++it) { + delete [] it->MofData; + } + + events_.clear(); + } + + static void EnqueueEvent(EVENT_TRACE* event) { + events_.push_back(*event); + EVENT_TRACE& back = events_.back(); + + if (NULL != event->MofData && 0 != event->MofLength) { + back.MofData = new char[event->MofLength]; + memcpy(back.MofData, event->MofData, event->MofLength); + } + } + + static void ProcessEvent(EVENT_TRACE* event) { + EnqueueEvent(event); + ::SetEvent(sank_event_.Get()); + } + + static ScopedHandle sank_event_; + static EventQueue events_; + + private: + DISALLOW_COPY_AND_ASSIGN(TestConsumer); +}; + +ScopedHandle TestConsumer::sank_event_; +EventQueue TestConsumer::events_; + +const wchar_t* const kTestSessionName = L"TestLogSession"; + +class EtwTraceConsumerBaseTest: public testing::Test { + public: + virtual void SetUp() { + EtwTraceProperties ignore; + EtwTraceController::Stop(kTestSessionName, &ignore); + } +}; + +} // namespace + +TEST_F(EtwTraceConsumerBaseTest, Initialize) { + TestConsumer consumer_; +} + +TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) { + TestConsumer consumer_; + + ASSERT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName)); +} + +TEST_F(EtwTraceConsumerBaseTest, ConsumerImmediateFailureWhenNoSession) { + TestConsumer consumer_; + + ASSERT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName)); + ASSERT_HRESULT_FAILED(consumer_.Consume()); +} + +namespace { + +class EtwTraceConsumerRealtimeTest: public testing::Test { + public: + virtual void SetUp() { + ASSERT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName)); + } + + virtual void TearDown() { + consumer_.Close(); + } + + DWORD ConsumerThread() { + ::SetEvent(consumer_ready_.Get()); + + HRESULT hr = consumer_.Consume(); + return hr; + } + + static DWORD WINAPI ConsumerThreadMainProc(void* arg) { + return reinterpret_cast<EtwTraceConsumerRealtimeTest*>(arg)-> + ConsumerThread(); + } + + HRESULT StartConsumerThread() { + consumer_ready_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL)); + EXPECT_TRUE(consumer_ready_ != NULL); + consumer_thread_.Set(::CreateThread(NULL, 0, ConsumerThreadMainProc, + this, 0, NULL)); + if (NULL == consumer_thread_.Get()) + return HRESULT_FROM_WIN32(::GetLastError()); + + HRESULT hr = S_OK; + HANDLE events[] = { consumer_ready_, consumer_thread_ }; + DWORD result = ::WaitForMultipleObjects(arraysize(events), events, + FALSE, INFINITE); + switch (result) { + case WAIT_OBJECT_0: + // The event was set, the consumer_ is ready. + return S_OK; + case WAIT_OBJECT_0 + 1: { + // The thread finished. This may race with the event, so check + // explicitly for the event here, before concluding there's trouble. + if (WAIT_OBJECT_0 == ::WaitForSingleObject(consumer_ready_, 0)) + return S_OK; + DWORD exit_code = 0; + if (::GetExitCodeThread(consumer_thread_, &exit_code)) + return exit_code; + else + return HRESULT_FROM_WIN32(::GetLastError()); + break; + } + default: + return E_UNEXPECTED; + break; + } + + return hr; + } + + // Waits for consumer_ thread to exit, and returns its exit code. + HRESULT JoinConsumerThread() { + if (WAIT_OBJECT_0 != ::WaitForSingleObject(consumer_thread_, INFINITE)) + return HRESULT_FROM_WIN32(::GetLastError()); + + DWORD exit_code = 0; + if (::GetExitCodeThread(consumer_thread_, &exit_code)) + return exit_code; + + return HRESULT_FROM_WIN32(::GetLastError()); + } + + TestConsumer consumer_; + ScopedHandle consumer_ready_; + ScopedHandle consumer_thread_; +}; +} // namespace + +TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) { + EtwTraceController controller; + + HRESULT hr = controller.StartRealtimeSession(kTestSessionName, 100 * 1024); + if (hr == E_ACCESSDENIED) { + VLOG(1) << "You must be an administrator to run this test on Vista"; + return; + } + + // Start the consumer_. + ASSERT_HRESULT_SUCCEEDED(StartConsumerThread()); + + // Wait around for the consumer_ thread a bit. + ASSERT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(consumer_thread_, 50)); + + ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL)); + + // The consumer_ returns success on session stop. + ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread()); +} + +namespace { + +// {036B8F65-8DF3-46e4-ABFC-6985C43D59BA} +DEFINE_GUID(kTestProvider, + 0x36b8f65, 0x8df3, 0x46e4, 0xab, 0xfc, 0x69, 0x85, 0xc4, 0x3d, 0x59, 0xba); + +// {57E47923-A549-476f-86CA-503D57F59E62} +DEFINE_GUID(kTestEventType, + 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62); + +} // namespace + +TEST_F(EtwTraceConsumerRealtimeTest, ConsumeEvent) { + EtwTraceController controller; + HRESULT hr = controller.StartRealtimeSession(kTestSessionName, 100 * 1024); + if (hr == E_ACCESSDENIED) { + VLOG(1) << "You must be an administrator to run this test on Vista"; + return; + } + + ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider(kTestProvider, + TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); + + EtwTraceProvider provider(kTestProvider); + ASSERT_EQ(ERROR_SUCCESS, provider.Register()); + + // Start the consumer_. + ASSERT_HRESULT_SUCCEEDED(StartConsumerThread()); + + ASSERT_EQ(0, TestConsumer::events_.size()); + + EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR); + EXPECT_EQ(ERROR_SUCCESS, provider.Log(&event.header)); + + EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(TestConsumer::sank_event_, + INFINITE)); + ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL)); + ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread()); + ASSERT_NE(0u, TestConsumer::events_.size()); +} + +namespace { + +// We run events through a file session to assert that +// the content comes through. +class EtwTraceConsumerDataTest: public testing::Test { + public: + EtwTraceConsumerDataTest() { + } + + virtual void SetUp() { + EtwTraceProperties prop; + EtwTraceController::Stop(kTestSessionName, &prop); + // Construct a temp file name. + ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_)); + } + + virtual void TearDown() { + EXPECT_TRUE(file_util::Delete(temp_file_, false)); + EtwTraceProperties ignore; + EtwTraceController::Stop(kTestSessionName, &ignore); + } + + HRESULT LogEventToTempSession(PEVENT_TRACE_HEADER header) { + EtwTraceController controller; + + // Set up a file session. + HRESULT hr = controller.StartFileSession(kTestSessionName, + temp_file_.value().c_str()); + if (FAILED(hr)) + return hr; + + // Enable our provider. + EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(kTestProvider, + TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); + + EtwTraceProvider provider(kTestProvider); + // Then register our provider, means we get a session handle immediately. + EXPECT_EQ(ERROR_SUCCESS, provider.Register()); + // Trace the event, it goes to the temp file. + EXPECT_EQ(ERROR_SUCCESS, provider.Log(header)); + EXPECT_HRESULT_SUCCEEDED(controller.DisableProvider(kTestProvider)); + EXPECT_HRESULT_SUCCEEDED(provider.Unregister()); + EXPECT_HRESULT_SUCCEEDED(controller.Flush(NULL)); + EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL)); + + return S_OK; + } + + HRESULT ConsumeEventFromTempSession() { + // Now consume the event(s). + TestConsumer consumer_; + HRESULT hr = consumer_.OpenFileSession(temp_file_.value().c_str()); + if (SUCCEEDED(hr)) + hr = consumer_.Consume(); + consumer_.Close(); + // And nab the result. + events_.swap(TestConsumer::events_); + return hr; + } + + HRESULT RoundTripEvent(PEVENT_TRACE_HEADER header, PEVENT_TRACE* trace) { + file_util::Delete(temp_file_, false); + + HRESULT hr = LogEventToTempSession(header); + if (SUCCEEDED(hr)) + hr = ConsumeEventFromTempSession(); + + if (FAILED(hr)) + return hr; + + // We should now have the event in the queue. + if (events_.empty()) + return E_FAIL; + + *trace = &events_.back(); + return S_OK; + } + + EventQueue events_; + FilePath temp_file_; +}; + +} // namespace + + +TEST_F(EtwTraceConsumerDataTest, RoundTrip) { + EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR); + + static const char kData[] = "This is but test data"; + event.fields[0].DataPtr = reinterpret_cast<ULONG64>(kData); + event.fields[0].Length = sizeof(kData); + + PEVENT_TRACE trace = NULL; + HRESULT hr = RoundTripEvent(&event.header, &trace); + if (hr == E_ACCESSDENIED) { + VLOG(1) << "You must be an administrator to run this test on Vista"; + return; + } + ASSERT_TRUE(NULL != trace); + ASSERT_EQ(sizeof(kData), trace->MofLength); + ASSERT_STREQ(kData, reinterpret_cast<const char*>(trace->MofData)); +} diff --git a/base/win/event_trace_controller.cc b/base/win/event_trace_controller.cc new file mode 100644 index 0000000..0391fbc --- /dev/null +++ b/base/win/event_trace_controller.cc @@ -0,0 +1,173 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Implementation of a Windows event trace controller class. +#include "base/win/event_trace_controller.h" +#include "base/logging.h" + +namespace base { +namespace win { + +EtwTraceProperties::EtwTraceProperties() { + memset(buffer_, 0, sizeof(buffer_)); + EVENT_TRACE_PROPERTIES* prop = get(); + + prop->Wnode.BufferSize = sizeof(buffer_); + prop->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + prop->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + prop->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + + sizeof(wchar_t) * kMaxStringLen; +} + +HRESULT EtwTraceProperties::SetLoggerName(const wchar_t* logger_name) { + size_t len = wcslen(logger_name) + 1; + if (kMaxStringLen < len) + return E_INVALIDARG; + + memcpy(buffer_ + get()->LoggerNameOffset, + logger_name, + sizeof(wchar_t) * len); + return S_OK; +} + +HRESULT EtwTraceProperties::SetLoggerFileName(const wchar_t* logger_file_name) { + size_t len = wcslen(logger_file_name) + 1; + if (kMaxStringLen < len) + return E_INVALIDARG; + + memcpy(buffer_ + get()->LogFileNameOffset, + logger_file_name, + sizeof(wchar_t) * len); + return S_OK; +} + +EtwTraceController::EtwTraceController() : session_(NULL) { +} + +EtwTraceController::~EtwTraceController() { + Stop(NULL); +} + +HRESULT EtwTraceController::Start(const wchar_t* session_name, + EtwTraceProperties* prop) { + DCHECK(NULL == session_ && session_name_.empty()); + EtwTraceProperties ignore; + if (prop == NULL) + prop = &ignore; + + HRESULT hr = Start(session_name, prop, &session_); + if (SUCCEEDED(hr)) + session_name_ = session_name; + + return hr; +} + +HRESULT EtwTraceController::StartFileSession(const wchar_t* session_name, + const wchar_t* logfile_path, bool realtime) { + DCHECK(NULL == session_ && session_name_.empty()); + + EtwTraceProperties prop; + prop.SetLoggerFileName(logfile_path); + EVENT_TRACE_PROPERTIES& p = *prop.get(); + p.Wnode.ClientContext = 1; // QPC timer accuracy. + p.LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL; // Sequential log. + if (realtime) + p.LogFileMode |= EVENT_TRACE_REAL_TIME_MODE; + + p.MaximumFileSize = 100; // 100M file size. + p.FlushTimer = 30; // 30 seconds flush lag. + return Start(session_name, &prop); +} + +HRESULT EtwTraceController::StartRealtimeSession(const wchar_t* session_name, + size_t buffer_size) { + DCHECK(NULL == session_ && session_name_.empty()); + EtwTraceProperties prop; + EVENT_TRACE_PROPERTIES& p = *prop.get(); + p.LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY; + p.FlushTimer = 1; // flush every second. + p.BufferSize = 16; // 16 K buffers. + p.LogFileNameOffset = 0; + return Start(session_name, &prop); +} + +HRESULT EtwTraceController::EnableProvider(REFGUID provider, UCHAR level, + ULONG flags) { + ULONG error = ::EnableTrace(TRUE, flags, level, &provider, session_); + return HRESULT_FROM_WIN32(error); +} + +HRESULT EtwTraceController::DisableProvider(REFGUID provider) { + ULONG error = ::EnableTrace(FALSE, 0, 0, &provider, session_); + return HRESULT_FROM_WIN32(error); +} + +HRESULT EtwTraceController::Stop(EtwTraceProperties* properties) { + EtwTraceProperties ignore; + if (properties == NULL) + properties = &ignore; + + ULONG error = ::ControlTrace(session_, NULL, properties->get(), + EVENT_TRACE_CONTROL_STOP); + if (ERROR_SUCCESS != error) + return HRESULT_FROM_WIN32(error); + + session_ = NULL; + session_name_.clear(); + return S_OK; +} + +HRESULT EtwTraceController::Flush(EtwTraceProperties* properties) { + EtwTraceProperties ignore; + if (properties == NULL) + properties = &ignore; + + ULONG error = ::ControlTrace(session_, NULL, properties->get(), + EVENT_TRACE_CONTROL_FLUSH); + if (ERROR_SUCCESS != error) + return HRESULT_FROM_WIN32(error); + + return S_OK; +} + +HRESULT EtwTraceController::Start(const wchar_t* session_name, + EtwTraceProperties* properties, TRACEHANDLE* session_handle) { + DCHECK(properties != NULL); + ULONG err = ::StartTrace(session_handle, session_name, properties->get()); + return HRESULT_FROM_WIN32(err); +} + +HRESULT EtwTraceController::Query(const wchar_t* session_name, + EtwTraceProperties* properties) { + ULONG err = ::ControlTrace(NULL, session_name, properties->get(), + EVENT_TRACE_CONTROL_QUERY); + return HRESULT_FROM_WIN32(err); +}; + +HRESULT EtwTraceController::Update(const wchar_t* session_name, + EtwTraceProperties* properties) { + DCHECK(properties != NULL); + ULONG err = ::ControlTrace(NULL, session_name, properties->get(), + EVENT_TRACE_CONTROL_UPDATE); + return HRESULT_FROM_WIN32(err); +} + +HRESULT EtwTraceController::Stop(const wchar_t* session_name, + EtwTraceProperties* properties) { + DCHECK(properties != NULL); + ULONG err = ::ControlTrace(NULL, session_name, properties->get(), + EVENT_TRACE_CONTROL_STOP); + return HRESULT_FROM_WIN32(err); +} + +HRESULT EtwTraceController::Flush(const wchar_t* session_name, + EtwTraceProperties* properties) { + DCHECK(properties != NULL); + ULONG err = ::ControlTrace(NULL, session_name, properties->get(), + EVENT_TRACE_CONTROL_FLUSH); + return HRESULT_FROM_WIN32(err); +} + +} // namespace win +} // namespace base diff --git a/base/win/event_trace_controller.h b/base/win/event_trace_controller.h new file mode 100644 index 0000000..8eb172ed --- /dev/null +++ b/base/win/event_trace_controller.h @@ -0,0 +1,150 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Declaration of a Windows event trace controller class. +// The controller takes care of creating and manipulating event trace +// sessions. +// +// Event tracing for Windows is a system-provided service that provides +// logging control and high-performance transport for generic, binary trace +// events. Event trace providers register with the system by their name, +// which is a GUID, and can from that point forward receive callbacks that +// start or end tracing and that change their trace level and enable mask. +// +// A trace controller can create an event tracing session, which either +// sends events to a binary file, or to a realtime consumer, or both. +// +// A trace consumer consumes events from zero or one realtime session, +// as well as potentially from multiple binary trace files. +#ifndef BASE_WIN_EVENT_TRACE_CONTROLLER_H_ +#define BASE_WIN_EVENT_TRACE_CONTROLLER_H_ +#pragma once + +#include <windows.h> +#include <wmistr.h> +#include <evntrace.h> +#include <string> +#include "base/basictypes.h" + +namespace base { +namespace win { + +// Utility class to make it easier to work with EVENT_TRACE_PROPERTIES. +// The EVENT_TRACE_PROPERTIES structure contains information about an +// event tracing session. +class EtwTraceProperties { + public: + EtwTraceProperties(); + + EVENT_TRACE_PROPERTIES* get() { + return &properties_; + } + + const EVENT_TRACE_PROPERTIES* get() const { + return reinterpret_cast<const EVENT_TRACE_PROPERTIES*>(&properties_); + } + + const wchar_t* GetLoggerName() const { + return reinterpret_cast<const wchar_t *>(buffer_ + get()->LoggerNameOffset); + } + + // Copies logger_name to the properties structure. + HRESULT SetLoggerName(const wchar_t* logger_name); + const wchar_t* GetLoggerFileName() const { + return reinterpret_cast<const wchar_t*>(buffer_ + get()->LogFileNameOffset); + } + + // Copies logger_file_name to the properties structure. + HRESULT SetLoggerFileName(const wchar_t* logger_file_name); + + // Max string len for name and session name is 1024 per documentation. + static const size_t kMaxStringLen = 1024; + // Properties buffer allocates space for header and for + // max length for name and session name. + static const size_t kBufSize = sizeof(EVENT_TRACE_PROPERTIES) + + 2 * sizeof(wchar_t) * (kMaxStringLen); + + private: + // The EVENT_TRACE_PROPERTIES structure needs to be overlaid on a + // larger buffer to allow storing the logger name and logger file + // name contiguously with the structure. + union { + public: + // Our properties header. + EVENT_TRACE_PROPERTIES properties_; + // The actual size of the buffer is forced by this member. + char buffer_[kBufSize]; + }; + + DISALLOW_COPY_AND_ASSIGN(EtwTraceProperties); +}; + +// This class implements an ETW controller, which knows how to start and +// stop event tracing sessions, as well as controlling ETW provider +// log levels and enable bit masks under the session. +class EtwTraceController { + public: + EtwTraceController(); + ~EtwTraceController(); + + // Start a session with given name and properties. + HRESULT Start(const wchar_t* session_name, EtwTraceProperties* prop); + + // Starts a session tracing to a file with some default properties. + HRESULT StartFileSession(const wchar_t* session_name, + const wchar_t* logfile_path, + bool realtime = false); + + // Starts a realtime session with some default properties. + HRESULT StartRealtimeSession(const wchar_t* session_name, + size_t buffer_size); + + // Enables "provider" at "level" for this session. + // This will cause all providers registered with the GUID + // "provider" to start tracing at the new level, systemwide. + HRESULT EnableProvider(const GUID& provider, UCHAR level, + ULONG flags = 0xFFFFFFFF); + // Disables "provider". + HRESULT DisableProvider(const GUID& provider); + + // Stops our session and retrieve the new properties of the session, + // properties may be NULL. + HRESULT Stop(EtwTraceProperties* properties); + + // Flushes our session and retrieve the current properties, + // properties may be NULL. + HRESULT Flush(EtwTraceProperties* properties); + + // Static utility functions for controlling + // sessions we don't necessarily own. + static HRESULT Start(const wchar_t* session_name, + EtwTraceProperties* properties, + TRACEHANDLE* session_handle); + + static HRESULT Query(const wchar_t* session_name, + EtwTraceProperties* properties); + + static HRESULT Update(const wchar_t* session_name, + EtwTraceProperties* properties); + + static HRESULT Stop(const wchar_t* session_name, + EtwTraceProperties* properties); + static HRESULT Flush(const wchar_t* session_name, + EtwTraceProperties* properties); + + // Accessors. + TRACEHANDLE session() const { return session_; } + const wchar_t* session_name() const { return session_name_.c_str(); } + + private: + std::wstring session_name_; + TRACEHANDLE session_; + + DISALLOW_COPY_AND_ASSIGN(EtwTraceController); +}; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_EVENT_TRACE_CONTROLLER_H_ diff --git a/base/win/event_trace_controller_unittest.cc b/base/win/event_trace_controller_unittest.cc new file mode 100644 index 0000000..2b3cd66 --- /dev/null +++ b/base/win/event_trace_controller_unittest.cc @@ -0,0 +1,208 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Unit tests for event trace controller. +#include "base/win/event_trace_controller.h" +#include "base/win/event_trace_provider.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "base/sys_info.h" +#include "testing/gtest/include/gtest/gtest.h" +#include <initguid.h> // NOLINT - must be last. + +namespace { + +using base::win::EtwTraceController; +using base::win::EtwTraceProvider; +using base::win::EtwTraceProperties; + +const wchar_t kTestSessionName[] = L"TestLogSession"; + +// {0D236A42-CD18-4e3d-9975-DCEEA2106E05} +DEFINE_GUID(kTestProvider, + 0xd236a42, 0xcd18, 0x4e3d, 0x99, 0x75, 0xdc, 0xee, 0xa2, 0x10, 0x6e, 0x5); + +DEFINE_GUID(kGuidNull, + 0x0000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0); + +const ULONG kTestProviderFlags = 0xCAFEBABE; + +class TestingProvider: public EtwTraceProvider { + public: + explicit TestingProvider(const GUID& provider_name) + : EtwTraceProvider(provider_name) { + callback_event_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL)); + } + + void WaitForCallback() { + ::WaitForSingleObject(callback_event_.Get(), INFINITE); + ::ResetEvent(callback_event_.Get()); + } + + private: + virtual void OnEventsEnabled() { + ::SetEvent(callback_event_.Get()); + } + virtual void PostEventsDisabled() { + ::SetEvent(callback_event_.Get()); + } + + ScopedHandle callback_event_; + + DISALLOW_COPY_AND_ASSIGN(TestingProvider); +}; + +} // namespace + +TEST(EtwTraceTest, Cleanup) { + // Clean up potential leftover sessions from previous unsuccessful runs. + EtwTraceProperties ignore; + EtwTraceController::Stop(kTestSessionName, &ignore); +} + +TEST(EtwTracePropertiesTest, Initialization) { + EtwTraceProperties prop; + + EVENT_TRACE_PROPERTIES* p = prop.get(); + EXPECT_NE(0u, p->Wnode.BufferSize); + EXPECT_EQ(0u, p->Wnode.ProviderId); + EXPECT_EQ(0u, p->Wnode.HistoricalContext); + + EXPECT_TRUE(kGuidNull == p->Wnode.Guid); + EXPECT_EQ(0, p->Wnode.ClientContext); + EXPECT_EQ(WNODE_FLAG_TRACED_GUID, p->Wnode.Flags); + + EXPECT_EQ(0, p->BufferSize); + EXPECT_EQ(0, p->MinimumBuffers); + EXPECT_EQ(0, p->MaximumBuffers); + EXPECT_EQ(0, p->MaximumFileSize); + EXPECT_EQ(0, p->LogFileMode); + EXPECT_EQ(0, p->FlushTimer); + EXPECT_EQ(0, p->EnableFlags); + EXPECT_EQ(0, p->AgeLimit); + + EXPECT_EQ(0, p->NumberOfBuffers); + EXPECT_EQ(0, p->FreeBuffers); + EXPECT_EQ(0, p->EventsLost); + EXPECT_EQ(0, p->BuffersWritten); + EXPECT_EQ(0, p->LogBuffersLost); + EXPECT_EQ(0, p->RealTimeBuffersLost); + EXPECT_EQ(0, p->LoggerThreadId); + EXPECT_NE(0u, p->LogFileNameOffset); + EXPECT_NE(0u, p->LoggerNameOffset); +} + +TEST(EtwTracePropertiesTest, Strings) { + EtwTraceProperties prop; + + ASSERT_STREQ(L"", prop.GetLoggerFileName()); + ASSERT_STREQ(L"", prop.GetLoggerName()); + + std::wstring name(1023, L'A'); + ASSERT_HRESULT_SUCCEEDED(prop.SetLoggerFileName(name.c_str())); + ASSERT_HRESULT_SUCCEEDED(prop.SetLoggerName(name.c_str())); + ASSERT_STREQ(name.c_str(), prop.GetLoggerFileName()); + ASSERT_STREQ(name.c_str(), prop.GetLoggerName()); + + std::wstring name2(1024, L'A'); + ASSERT_HRESULT_FAILED(prop.SetLoggerFileName(name2.c_str())); + ASSERT_HRESULT_FAILED(prop.SetLoggerName(name2.c_str())); +} + +TEST(EtwTraceControllerTest, Initialize) { + EtwTraceController controller; + + EXPECT_EQ(NULL, controller.session()); + EXPECT_STREQ(L"", controller.session_name()); +} + +TEST(EtwTraceControllerTest, StartRealTimeSession) { + EtwTraceController controller; + + HRESULT hr = controller.StartRealtimeSession(kTestSessionName, 100 * 1024); + if (hr == E_ACCESSDENIED) { + VLOG(1) << "You must be an administrator to run this test on Vista"; + return; + } + + EXPECT_TRUE(NULL != controller.session()); + EXPECT_STREQ(kTestSessionName, controller.session_name()); + + EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL)); + EXPECT_EQ(NULL, controller.session()); + EXPECT_STREQ(L"", controller.session_name()); +} + +TEST(EtwTraceControllerTest, StartFileSession) { + FilePath temp; + + ASSERT_HRESULT_SUCCEEDED(file_util::CreateTemporaryFile(&temp)); + + EtwTraceController controller; + HRESULT hr = controller.StartFileSession(kTestSessionName, + temp.value().c_str()); + if (hr == E_ACCESSDENIED) { + VLOG(1) << "You must be an administrator to run this test on Vista"; + return; + } + + EXPECT_TRUE(NULL != controller.session()); + EXPECT_STREQ(kTestSessionName, controller.session_name()); + + EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL)); + EXPECT_EQ(NULL, controller.session()); + EXPECT_STREQ(L"", controller.session_name()); +} + +TEST(EtwTraceControllerTest, EnableDisable) { + TestingProvider provider(kTestProvider); + + EXPECT_EQ(ERROR_SUCCESS, provider.Register()); + EXPECT_EQ(NULL, provider.session_handle()); + + EtwTraceController controller; + HRESULT hr = controller.StartRealtimeSession(kTestSessionName, 100 * 1024); + if (hr == E_ACCESSDENIED) { + VLOG(1) << "You must be an administrator to run this test on Vista"; + return; + } + + EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(kTestProvider, + TRACE_LEVEL_VERBOSE, kTestProviderFlags)); + + provider.WaitForCallback(); + + EXPECT_EQ(TRACE_LEVEL_VERBOSE, provider.enable_level()); + EXPECT_EQ(kTestProviderFlags, provider.enable_flags()); + + EXPECT_HRESULT_SUCCEEDED(controller.DisableProvider(kTestProvider)); + + provider.WaitForCallback(); + + EXPECT_EQ(0, provider.enable_level()); + EXPECT_EQ(0, provider.enable_flags()); + + EXPECT_EQ(ERROR_SUCCESS, provider.Unregister()); + + // Enable the provider again, before registering. + EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(kTestProvider, + TRACE_LEVEL_VERBOSE, kTestProviderFlags)); + + // Register the provider again, the settings above + // should take immediate effect. + EXPECT_EQ(ERROR_SUCCESS, provider.Register()); + + EXPECT_EQ(TRACE_LEVEL_VERBOSE, provider.enable_level()); + EXPECT_EQ(kTestProviderFlags, provider.enable_flags()); + + EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL)); + + provider.WaitForCallback(); + + // Session should have wound down. + EXPECT_EQ(0, provider.enable_level()); + EXPECT_EQ(0, provider.enable_flags()); +} diff --git a/base/win/event_trace_provider.cc b/base/win/event_trace_provider.cc new file mode 100644 index 0000000..332fac3 --- /dev/null +++ b/base/win/event_trace_provider.cc @@ -0,0 +1,134 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +#include "base/win/event_trace_provider.h" +#include <windows.h> +#include <cguid.h> + +namespace base { +namespace win { + +TRACE_GUID_REGISTRATION EtwTraceProvider::obligatory_guid_registration_ = { + &GUID_NULL, + NULL +}; + +EtwTraceProvider::EtwTraceProvider(const GUID& provider_name) + : provider_name_(provider_name), registration_handle_(NULL), + session_handle_(NULL), enable_flags_(0), enable_level_(0) { +} + +EtwTraceProvider::EtwTraceProvider() + : provider_name_(GUID_NULL), registration_handle_(NULL), + session_handle_(NULL), enable_flags_(0), enable_level_(0) { +} + +EtwTraceProvider::~EtwTraceProvider() { + Unregister(); +} + +ULONG EtwTraceProvider::EnableEvents(void* buffer) { + session_handle_ = ::GetTraceLoggerHandle(buffer); + if (NULL == session_handle_) { + return ::GetLastError(); + } + + enable_flags_ = ::GetTraceEnableFlags(session_handle_); + enable_level_ = ::GetTraceEnableLevel(session_handle_); + + // Give subclasses a chance to digest the state change. + OnEventsEnabled(); + + return ERROR_SUCCESS; +} + +ULONG EtwTraceProvider::DisableEvents() { + // Give subclasses a chance to digest the state change. + OnEventsDisabled(); + + enable_level_ = 0; + enable_flags_ = 0; + session_handle_ = NULL; + + PostEventsDisabled(); + + return ERROR_SUCCESS; +} + +ULONG EtwTraceProvider::Callback(WMIDPREQUESTCODE request, void* buffer) { + switch (request) { + case WMI_ENABLE_EVENTS: + return EnableEvents(buffer); + case WMI_DISABLE_EVENTS: + return DisableEvents(); + default: + return ERROR_INVALID_PARAMETER; + } + // Not reached. +} + +ULONG WINAPI EtwTraceProvider::ControlCallback(WMIDPREQUESTCODE request, + void* context, ULONG *reserved, void* buffer) { + EtwTraceProvider *provider = reinterpret_cast<EtwTraceProvider*>(context); + + return provider->Callback(request, buffer); +} + +ULONG EtwTraceProvider::Register() { + if (provider_name_ == GUID_NULL) + return ERROR_INVALID_NAME; + + return ::RegisterTraceGuids(ControlCallback, this, &provider_name_, + 1, &obligatory_guid_registration_, NULL, NULL, ®istration_handle_); +} + +ULONG EtwTraceProvider::Unregister() { + ULONG ret = ::UnregisterTraceGuids(registration_handle_); + + // Make sure we don't log anything from here on. + enable_level_ = 0; + enable_flags_ = 0; + session_handle_ = NULL; + registration_handle_ = NULL; + + return ret; +} + +ULONG EtwTraceProvider::Log(const EtwEventClass& event_class, + EtwEventType type, EtwEventLevel level, const char *message) { + if (NULL == session_handle_ || enable_level_ < level) + return ERROR_SUCCESS; // No one listening. + + EtwMofEvent<1> event(event_class, type, level); + + event.fields[0].DataPtr = reinterpret_cast<ULONG64>(message); + event.fields[0].Length = message ? + static_cast<ULONG>(sizeof(message[0]) * (1 + strlen(message))) : 0; + + return ::TraceEvent(session_handle_, &event.header); +} + +ULONG EtwTraceProvider::Log(const EtwEventClass& event_class, + EtwEventType type, EtwEventLevel level, const wchar_t *message) { + if (NULL == session_handle_ || enable_level_ < level) + return ERROR_SUCCESS; // No one listening. + + EtwMofEvent<1> event(event_class, type, level); + + event.fields[0].DataPtr = reinterpret_cast<ULONG64>(message); + event.fields[0].Length = message ? + static_cast<ULONG>(sizeof(message[0]) * (1 + wcslen(message))) : 0; + + return ::TraceEvent(session_handle_, &event.header); +} + +ULONG EtwTraceProvider::Log(EVENT_TRACE_HEADER* event) { + if (enable_level_ < event->Class.Level) + return ERROR_SUCCESS; + + return ::TraceEvent(session_handle_, event); +} + +} // namespace win +} // namespace base diff --git a/base/win/event_trace_provider.h b/base/win/event_trace_provider.h new file mode 100644 index 0000000..6614c91 --- /dev/null +++ b/base/win/event_trace_provider.h @@ -0,0 +1,174 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Declaration of a Windows event trace provider class, to allow using +// Windows Event Tracing for logging transport and control. +#ifndef BASE_WIN_EVENT_TRACE_PROVIDER_H_ +#define BASE_WIN_EVENT_TRACE_PROVIDER_H_ +#pragma once + +#include <windows.h> +#include <wmistr.h> +#include <evntrace.h> +#include "base/basictypes.h" + +namespace base { +namespace win { + +typedef GUID EtwEventClass; +typedef UCHAR EtwEventType; +typedef UCHAR EtwEventLevel; +typedef USHORT EtwEventVersion; +typedef ULONG EtwEventFlags; + +// Base class is a POD for correctness. +template <size_t N> struct EtwMofEventBase { + EVENT_TRACE_HEADER header; + MOF_FIELD fields[N]; +}; + +// Utility class to auto-initialize event trace header structures. +template <size_t N> class EtwMofEvent: public EtwMofEventBase<N> { + public: + typedef EtwMofEventBase<N> Super; + + EtwMofEvent() { + memset(static_cast<Super*>(this), 0, sizeof(Super)); + } + + EtwMofEvent(const EtwEventClass& event_class, EtwEventType type, + EtwEventLevel level) { + memset(static_cast<Super*>(this), 0, sizeof(Super)); + header.Size = sizeof(Super); + header.Guid = event_class; + header.Class.Type = type; + header.Class.Level = level; + header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR; + } + + EtwMofEvent(const EtwEventClass& event_class, EtwEventType type, + EtwEventVersion version, EtwEventLevel level) { + memset(static_cast<Super*>(this), 0, sizeof(Super)); + header.Size = sizeof(Super); + header.Guid = event_class; + header.Class.Type = type; + header.Class.Version = version; + header.Class.Level = level; + header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR; + } + + void SetField(int field, size_t size, const void *data) { + // DCHECK(field < N); + if ((field < N) && (size <= kuint32max)) { + fields[field].DataPtr = reinterpret_cast<ULONG64>(data); + fields[field].Length = static_cast<ULONG>(size); + } + } + + EVENT_TRACE_HEADER* get() { return& header; } + + private: + DISALLOW_COPY_AND_ASSIGN(EtwMofEvent); +}; + +// Trace provider with Event Tracing for Windows. The trace provider +// registers with ETW by its name which is a GUID. ETW calls back to +// the object whenever the trace level or enable flags for this provider +// name changes. +// Users of this class can test whether logging is currently enabled at +// a particular trace level, and whether particular enable flags are set, +// before other resources are consumed to generate and issue the log +// messages themselves. +class EtwTraceProvider { + public: + // Creates an event trace provider identified by provider_name, which + // will be the name registered with Event Tracing for Windows (ETW). + explicit EtwTraceProvider(const GUID& provider_name); + + // Creates an unnamed event trace provider, the provider must be given + // a name before registration. + EtwTraceProvider(); + virtual ~EtwTraceProvider(); + + // Registers the trace provider with Event Tracing for Windows. + // Note: from this point forward ETW may call the provider's control + // callback. If the provider's name is enabled in some trace session + // already, the callback may occur recursively from this call, so + // call this only when you're ready to handle callbacks. + ULONG Register(); + // Unregisters the trace provider with ETW. + ULONG Unregister(); + + // Accessors. + void set_provider_name(const GUID& provider_name) { + provider_name_ = provider_name; + } + const GUID& provider_name() const { return provider_name_; } + TRACEHANDLE registration_handle() const { return registration_handle_; } + TRACEHANDLE session_handle() const { return session_handle_; } + EtwEventFlags enable_flags() const { return enable_flags_; } + EtwEventLevel enable_level() const { return enable_level_; } + + // Returns true iff logging should be performed for "level" and "flags". + // Note: flags is treated as a bitmask, and should normally have a single + // bit set, to test whether to log for a particular sub "facility". + bool ShouldLog(EtwEventLevel level, EtwEventFlags flags) { + return NULL != session_handle_ && level >= enable_level_ && + (0 != (flags & enable_flags_)); + } + + // Simple wrappers to log Unicode and ANSI strings. + // Do nothing if !ShouldLog(level, 0xFFFFFFFF). + ULONG Log(const EtwEventClass& event_class, EtwEventType type, + EtwEventLevel level, const char *message); + ULONG Log(const EtwEventClass& event_class, EtwEventType type, + EtwEventLevel level, const wchar_t *message); + + // Log the provided event. + ULONG Log(EVENT_TRACE_HEADER* event); + + protected: + // Called after events have been enabled, override in subclasses + // to set up state or log at the start of a session. + // Note: This function may be called ETW's thread and may be racy, + // bring your own locking if needed. + virtual void OnEventsEnabled() {} + + // Called just before events are disabled, override in subclasses + // to tear down state or log at the end of a session. + // Note: This function may be called ETW's thread and may be racy, + // bring your own locking if needed. + virtual void OnEventsDisabled() {} + + // Called just after events have been disabled, override in subclasses + // to tear down state at the end of a session. At this point it's + // to late to log anything to the session. + // Note: This function may be called ETW's thread and may be racy, + // bring your own locking if needed. + virtual void PostEventsDisabled() {} + + private: + ULONG EnableEvents(PVOID buffer); + ULONG DisableEvents(); + ULONG Callback(WMIDPREQUESTCODE request, PVOID buffer); + static ULONG WINAPI ControlCallback(WMIDPREQUESTCODE request, PVOID context, + ULONG *reserved, PVOID buffer); + + GUID provider_name_; + TRACEHANDLE registration_handle_; + TRACEHANDLE session_handle_; + EtwEventFlags enable_flags_; + EtwEventLevel enable_level_; + + // We don't use this, but on XP we're obliged to pass one in to + // RegisterTraceGuids. Non-const, because that's how the API needs it. + static TRACE_GUID_REGISTRATION obligatory_guid_registration_; + + DISALLOW_COPY_AND_ASSIGN(EtwTraceProvider); +}; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_EVENT_TRACE_PROVIDER_H_ diff --git a/base/win/event_trace_provider_unittest.cc b/base/win/event_trace_provider_unittest.cc new file mode 100644 index 0000000..55b5ae6 --- /dev/null +++ b/base/win/event_trace_provider_unittest.cc @@ -0,0 +1,110 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Unit tests for event trace provider. +#include "base/win/event_trace_provider.h" +#include <new> +#include "testing/gtest/include/gtest/gtest.h" +#include <initguid.h> // NOLINT - has to be last + +namespace { + +using base::win::EtwTraceProvider; +using base::win::EtwMofEvent; + +// {7F0FD37F-FA3C-4cd6-9242-DF60967A2CB2} +DEFINE_GUID(kTestProvider, + 0x7f0fd37f, 0xfa3c, 0x4cd6, 0x92, 0x42, 0xdf, 0x60, 0x96, 0x7a, 0x2c, 0xb2); + +// {7F0FD37F-FA3C-4cd6-9242-DF60967A2CB2} +DEFINE_GUID(kTestEventClass, + 0x7f0fd37f, 0xfa3c, 0x4cd6, 0x92, 0x42, 0xdf, 0x60, 0x96, 0x7a, 0x2c, 0xb2); + +} // namespace + +TEST(EtwTraceProviderTest, ToleratesPreCreateInvocations) { + // Because the trace provider is used in logging, it's important that + // it be possible to use static provider instances without regard to + // whether they've been constructed or destructed. + // The interface of the class is designed to tolerate this usage. + char buf[sizeof(EtwTraceProvider)] = {0}; + EtwTraceProvider& provider = reinterpret_cast<EtwTraceProvider&>(buf); + + EXPECT_EQ(NULL, provider.registration_handle()); + EXPECT_EQ(NULL, provider.session_handle()); + EXPECT_EQ(0, provider.enable_flags()); + EXPECT_EQ(0, provider.enable_level()); + + EXPECT_FALSE(provider.ShouldLog(TRACE_LEVEL_FATAL, 0xfffffff)); + + // We expect these not to crash. + provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, "foo"); + provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, L"foo"); + + EtwMofEvent<1> dummy(kTestEventClass, 0, TRACE_LEVEL_FATAL); + DWORD data = 0; + dummy.SetField(0, sizeof(data), &data); + provider.Log(dummy.get()); + + // Placement-new the provider into our buffer. + new (buf) EtwTraceProvider(kTestProvider); + + // Registration is now safe. + EXPECT_EQ(ERROR_SUCCESS, provider.Register()); + + // Destruct the instance, this should unregister it. + provider.EtwTraceProvider::~EtwTraceProvider(); + + // And post-destruction, all of the above should still be safe. + EXPECT_EQ(NULL, provider.registration_handle()); + EXPECT_EQ(NULL, provider.session_handle()); + EXPECT_EQ(0, provider.enable_flags()); + EXPECT_EQ(0, provider.enable_level()); + + EXPECT_FALSE(provider.ShouldLog(TRACE_LEVEL_FATAL, 0xfffffff)); + + // We expect these not to crash. + provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, "foo"); + provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, L"foo"); + provider.Log(dummy.get()); +} + +TEST(EtwTraceProviderTest, Initialize) { + EtwTraceProvider provider(kTestProvider); + + EXPECT_EQ(NULL, provider.registration_handle()); + EXPECT_EQ(NULL, provider.session_handle()); + EXPECT_EQ(0, provider.enable_flags()); + EXPECT_EQ(0, provider.enable_level()); +} + +TEST(EtwTraceProviderTest, Register) { + EtwTraceProvider provider(kTestProvider); + + ASSERT_EQ(ERROR_SUCCESS, provider.Register()); + EXPECT_NE(NULL, provider.registration_handle()); + ASSERT_EQ(ERROR_SUCCESS, provider.Unregister()); + EXPECT_EQ(NULL, provider.registration_handle()); +} + +TEST(EtwTraceProviderTest, RegisterWithNoNameFails) { + EtwTraceProvider provider; + + EXPECT_TRUE(provider.Register() != ERROR_SUCCESS); +} + +TEST(EtwTraceProviderTest, Enable) { + EtwTraceProvider provider(kTestProvider); + + ASSERT_EQ(ERROR_SUCCESS, provider.Register()); + EXPECT_NE(NULL, provider.registration_handle()); + + // No session so far. + EXPECT_EQ(NULL, provider.session_handle()); + EXPECT_EQ(0, provider.enable_flags()); + EXPECT_EQ(0, provider.enable_level()); + + ASSERT_EQ(ERROR_SUCCESS, provider.Unregister()); + EXPECT_EQ(NULL, provider.registration_handle()); +} |