summaryrefslogtreecommitdiffstats
path: root/base/win
diff options
context:
space:
mode:
authorsiggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-06 15:39:40 +0000
committersiggi@chromium.org <siggi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-06 15:39:40 +0000
commitba50d192694e3bbdd99d153205e7fcef236abf8a (patch)
treed4621c1cbad3e63cd87c40689c0ae5daaafab1fe /base/win
parentf2505c82c17948056214d3437d53620610fba837 (diff)
downloadchromium_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.h150
-rw-r--r--base/win/event_trace_consumer_unittest.cc346
-rw-r--r--base/win/event_trace_controller.cc173
-rw-r--r--base/win/event_trace_controller.h150
-rw-r--r--base/win/event_trace_controller_unittest.cc208
-rw-r--r--base/win/event_trace_provider.cc134
-rw-r--r--base/win/event_trace_provider.h174
-rw-r--r--base/win/event_trace_provider_unittest.cc110
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, &registration_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());
+}