summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/base.gyp2
-rw-r--r--base/base.gypi2
-rw-r--r--base/trace_event.h9
-rw-r--r--base/trace_event_win.cc114
-rw-r--r--base/trace_event_win.h145
-rw-r--r--base/trace_event_win_unittest.cc282
6 files changed, 551 insertions, 3 deletions
diff --git a/base/base.gyp b/base/base.gyp
index 23201d2..88f8702 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -140,6 +140,7 @@
'time_win_unittest.cc',
'timer_unittest.cc',
'tools_sanity_unittest.cc',
+ 'trace_event_win_unittest.cc',
'tracked_objects_unittest.cc',
'tuple_unittest.cc',
'utf_offset_string_conversions_unittest.cc',
@@ -206,6 +207,7 @@
'scoped_variant_win_unittest.cc',
'system_monitor_unittest.cc',
'time_win_unittest.cc',
+ 'trace_event_win_unittest.cc',
'win_util_unittest.cc',
'wmi_util_unittest.cc',
],
diff --git a/base/base.gypi b/base/base.gypi
index b5c3f31..ecec5cd 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -249,6 +249,7 @@
'time_win.cc',
'timer.cc',
'timer.h',
+ 'trace_event_win.cc',
'trace_event.cc',
'trace_event.h',
'tracked.cc',
@@ -353,6 +354,7 @@
'file_descriptor_shuffle.cc',
'message_pump_libevent.cc',
'string16.cc',
+ 'trace_event.cc',
],
},],
],
diff --git a/base/trace_event.h b/base/trace_event.h
index 46b8a37..7537b97 100644
--- a/base/trace_event.h
+++ b/base/trace_event.h
@@ -18,8 +18,10 @@
#include "build/build_config.h"
#if defined(OS_WIN)
-#include <windows.h>
-#endif
+// On Windows we always pull in an alternative implementation
+// which logs to Event Tracing for Windows.
+#include "base/trace_event_win.h"
+#else // defined(OS_WIN)
#include <string>
@@ -34,7 +36,7 @@
#define TRACE_EVENT_END(name, id, extra) ((void) 0)
#define TRACE_EVENT_INSTANT(name, id, extra) ((void) 0)
-#else
+#else // CHROMIUM_ENABLE_TRACE_EVENT
// Use the following macros rather than using the TraceLog class directly as the
// underlying implementation may change in the future. Here's a sample usage:
// TRACE_EVENT_BEGIN("v8.run", documentId, scriptLocation);
@@ -129,5 +131,6 @@ class TraceLog {
};
} // namespace base
+#endif // defined(OS_WIN)
#endif // BASE_TRACE_EVENT_H_
diff --git a/base/trace_event_win.cc b/base/trace_event_win.cc
new file mode 100644
index 0000000..eae6782
--- /dev/null
+++ b/base/trace_event_win.cc
@@ -0,0 +1,114 @@
+// 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/trace_event_win.h"
+
+#include "base/atomicops.h"
+#include "base/logging_win.h"
+#include "base/singleton.h"
+#include <initguid.h> // NOLINT
+
+namespace base {
+
+// {3DADA31D-19EF-4dc1-B345-037927193422}
+const GUID kChromeTraceProviderName = {
+ 0x3dada31d, 0x19ef, 0x4dc1, 0xb3, 0x45, 0x3, 0x79, 0x27, 0x19, 0x34, 0x22 };
+
+// {B967AE67-BB22-49d7-9406-55D91EE1D560}
+const GUID kTraceEventClass32 = {
+ 0xb967ae67, 0xbb22, 0x49d7, 0x94, 0x6, 0x55, 0xd9, 0x1e, 0xe1, 0xd5, 0x60 };
+
+// {97BE602D-2930-4ac3-8046-B6763B631DFE}
+const GUID kTraceEventClass64 = {
+ 0x97be602d, 0x2930, 0x4ac3, 0x80, 0x46, 0xb6, 0x76, 0x3b, 0x63, 0x1d, 0xfe};
+
+
+TraceLog::TraceLog() : EtwTraceProvider(base::kChromeTraceProviderName) {
+ Register();
+}
+
+TraceLog* TraceLog::Get() {
+ return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog>>::get();
+}
+
+bool TraceLog::StartTracing() {
+ return true;
+}
+
+void TraceLog::TraceEvent(const char* name,
+ size_t name_len,
+ base::TraceLog::EventType type,
+ const void* id,
+ const char* extra,
+ size_t extra_len) {
+ // Make sure we don't touch NULL.
+ if (name == NULL)
+ name = "";
+ if (extra == NULL)
+ extra = "";
+
+ EtwEventType etw_type = 0;
+ switch (type) {
+ case base::TraceLog::EVENT_BEGIN:
+ etw_type = base::kTraceEventTypeBegin;
+ break;
+ case base::TraceLog::EVENT_END:
+ etw_type = base::kTraceEventTypeEnd;
+ break;
+
+ case base::TraceLog::EVENT_INSTANT:
+ etw_type = base::kTraceEventTypeInstant;
+ break;
+
+ default:
+ NOTREACHED() << "Unknown event type";
+ etw_type = base::kTraceEventTypeInstant;
+ break;
+ }
+
+ EtwMofEvent<5> event(base::kTraceEventClass32,
+ etw_type,
+ TRACE_LEVEL_INFORMATION);
+ event.SetField(0, name_len + 1, name);
+ event.SetField(1, sizeof(id), &id);
+ event.SetField(2, extra_len + 1, extra);
+
+ // See whether we're to capture a backtrace.
+ void* backtrace[32];
+ if (enable_flags() & base::CAPTURE_STACK_TRACE) {
+ DWORD hash = 0;
+ DWORD depth = CaptureStackBackTrace(0,
+ arraysize(backtrace),
+ backtrace,
+ &hash);
+ event.SetField(3, sizeof(depth), &depth);
+ event.SetField(4, sizeof(backtrace[0]) * depth, backtrace);
+ }
+
+ // Trace the event.
+ Log(event.get());
+}
+
+void TraceLog::Trace(const char* name,
+ size_t name_len,
+ EventType type,
+ const void* id,
+ const char* extra,
+ size_t extra_len) {
+ TraceLog* provider = TraceLog::Get();
+ if (provider && provider->IsTracing()) {
+ // Compute the name & extra lengths if not supplied already.
+ if (name_len == -1)
+ name_len = (name == NULL) ? 0 : strlen(name);
+ if (extra_len == -1)
+ extra_len = (extra == NULL) ? 0 : strlen(extra);
+
+ provider->TraceEvent(name, name_len, type, id, extra, extra_len);
+ }
+}
+
+void TraceLog::Resurrect() {
+ StaticMemorySingletonTraits<TraceLog>::Resurrect();
+}
+
+} // namespace base
diff --git a/base/trace_event_win.h b/base/trace_event_win.h
new file mode 100644
index 0000000..14f768f
--- /dev/null
+++ b/base/trace_event_win.h
@@ -0,0 +1,145 @@
+// 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.
+
+// This file contains the Windows-specific declarations for trace_event.h.
+#ifndef BASE_TRACE_EVENT_WIN_H_
+#define BASE_TRACE_EVENT_WIN_H_
+
+#include <string>
+#include "base/event_trace_provider_win.h"
+
+#define TRACE_EVENT_BEGIN(name, id, extra) \
+ base::TraceLog::Trace(name, \
+ base::TraceLog::EVENT_BEGIN, \
+ reinterpret_cast<const void*>(id), \
+ extra);
+
+#define TRACE_EVENT_END(name, id, extra) \
+ base::TraceLog::Trace(name, \
+ base::TraceLog::EVENT_END, \
+ reinterpret_cast<const void*>(id), \
+ extra);
+
+#define TRACE_EVENT_INSTANT(name, id, extra) \
+ base::TraceLog::Trace(name, \
+ base::TraceLog::EVENT_INSTANT, \
+ reinterpret_cast<const void*>(id), \
+ extra);
+
+// Fwd.
+template <typename Type>
+struct StaticMemorySingletonTraits;
+
+namespace base {
+
+// This EtwTraceProvider subclass implements ETW logging
+// for the macros above on Windows.
+class TraceLog : public EtwTraceProvider {
+ public:
+ enum EventType {
+ EVENT_BEGIN,
+ EVENT_END,
+ EVENT_INSTANT
+ };
+
+ // Start logging trace events.
+ // This is a noop in this implementation.
+ static bool StartTracing();
+
+ // Trace begin/end/instant events, this is the bottleneck implementation
+ // all the others defer to.
+ // Allowing the use of std::string for name or extra is a convenience,
+ // whereas passing name or extra as a const char* avoids the construction
+ // of temporary std::string instances.
+ // If -1 is passed for name_len or extra_len, the strlen of the string will
+ // be used for length.
+ static void Trace(const char* name,
+ size_t name_len,
+ EventType type,
+ const void* id,
+ const char* extra,
+ size_t extra_len);
+
+ // Allows passing extra as a std::string for convenience.
+ static void Trace(const char* name,
+ EventType type,
+ const void* id,
+ const std::string& extra) {
+ return Trace(name, -1, type, id, extra.c_str(), extra.length());
+ }
+
+ // Allows passing extra as a const char* to avoid constructing temporary
+ // std::string instances where not needed.
+ static void Trace(const char* name,
+ EventType type,
+ const void* id,
+ const char* extra) {
+ return Trace(name, -1, type, id, extra, -1);
+ }
+
+ // Retrieves the singleton.
+ // Note that this may return NULL post-AtExit processing.
+ static TraceLog* Get();
+
+ // Returns true iff tracing is turned on.
+ bool IsTracing() {
+ return enable_level() >= TRACE_LEVEL_INFORMATION;
+ }
+
+ // Emit a trace of type |type| containing |name|, |id|, and |extra|.
+ // Note: |name| and |extra| must be NULL, or a zero-terminated string of
+ // length |name_len| or |extra_len| respectively.
+ // Note: if name_len or extra_len are -1, the length of the corresponding
+ // string will be used.
+ void TraceEvent(const char* name,
+ size_t name_len,
+ base::TraceLog::EventType type,
+ const void* id,
+ const char* extra,
+ size_t extra_len);
+
+ // Exposed for unittesting only, allows resurrecting our
+ // singleton instance post-AtExit processing.
+ static void Resurrect();
+
+ private:
+ // Ensure only the provider can construct us.
+ friend struct StaticMemorySingletonTraits<TraceLog>;
+ TraceLog();
+
+ DISALLOW_COPY_AND_ASSIGN(TraceLog);
+};
+
+// The ETW trace provider GUID.
+extern const GUID kChromeTraceProviderName;
+
+// The ETW event class GUID for 32 bit events.
+extern const GUID kTraceEventClass32;
+
+// The ETW event class GUID for 64 bit events.
+extern const GUID kTraceEventClass64;
+
+// The ETW event types, IDs 0x00-0x09 are reserved, so start at 0x10.
+const EtwEventType kTraceEventTypeBegin = 0x10;
+const EtwEventType kTraceEventTypeEnd = 0x11;
+const EtwEventType kTraceEventTypeInstant = 0x12;
+
+// If this flag is set in enable flags
+enum TraceEventFlags {
+ CAPTURE_STACK_TRACE = 0x0001,
+};
+
+// The event format consists of:
+// The "name" string as a zero-terminated ASCII string.
+// The id pointer in the machine bitness.
+// The "extra" string as a zero-terminated ASCII string.
+// Optionally the stack trace, consisting of a DWORD "depth", followed
+// by an array of void* (machine bitness) of length "depth".
+
+// Forward decl.
+struct TraceLogSingletonTraits;
+
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_WIN_H_
diff --git a/base/trace_event_win_unittest.cc b/base/trace_event_win_unittest.cc
new file mode 100644
index 0000000..50769c5
--- /dev/null
+++ b/base/trace_event_win_unittest.cc
@@ -0,0 +1,282 @@
+// 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.
+#include "base/trace_event.h"
+#include <strstream>
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/event_trace_consumer_win.h"
+#include "base/event_trace_controller_win.h"
+#include "base/win_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include <initguid.h> // NOLINT - must be last include.
+
+namespace {
+using testing::_;
+using testing::AnyNumber;
+using testing::InSequence;
+using testing::Ge;
+using testing::Le;
+using testing::NotNull;
+
+// Data for unittests traces.
+const char kEmpty[] = "";
+const char kName[] = "unittest.trace_name";
+const char kExtra[] = "UnittestDummyExtraString";
+const void* kId = kName;
+
+const wchar_t kTestSessionName[] = L"TraceEvent unittest session";
+
+MATCHER_P(BufferStartsWith, str, "Buffer starts with") {
+ return memcmp(arg, str.c_str(), str.length()) == 0;
+}
+
+// Duplicated from <evntrace.h> to fix link problems.
+DEFINE_GUID( /* 68fdd900-4a3e-11d1-84f4-0000f80464e3 */
+ kEventTraceGuid,
+ 0x68fdd900,
+ 0x4a3e,
+ 0x11d1,
+ 0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3);
+
+class TestEventConsumer: public EtwTraceConsumerBase<TestEventConsumer> {
+ public:
+ TestEventConsumer() {
+ EXPECT_TRUE(current_ == NULL);
+ current_ = this;
+ }
+
+ ~TestEventConsumer() {
+ EXPECT_TRUE(current_ == this);
+ current_ = NULL;
+ }
+
+ MOCK_METHOD4(Event, void(REFGUID event_class,
+ EtwEventType event_type,
+ size_t buf_len,
+ const void* buf));
+
+ static void ProcessEvent(EVENT_TRACE* event) {
+ ASSERT_TRUE(current_ != NULL);
+ current_->Event(event->Header.Guid,
+ event->Header.Class.Type,
+ event->MofLength,
+ event->MofData);
+ }
+
+ private:
+ static TestEventConsumer* current_;
+};
+
+TestEventConsumer* TestEventConsumer::current_ = NULL;
+
+class TraceEventTest: public testing::Test {
+ public:
+ void SetUp() {
+ bool is_xp = win_util::GetWinVersion() < win_util::WINVERSION_VISTA;
+
+ // Resurrect and initialize the TraceLog singleton instance.
+ // On Vista and better, we need the provider registered before we
+ // start the private, in-proc session, but on XP we need the global
+ // session created and the provider enabled before we register our
+ // provider.
+ if (!is_xp) {
+ base::TraceLog::Resurrect();
+ base::TraceLog* tracelog = base::TraceLog::Get();
+ ASSERT_TRUE(tracelog != NULL);
+ }
+
+ // Create the log file.
+ ASSERT_TRUE(file_util::CreateTemporaryFile(&log_file_));
+
+ // Create a private log session on the file.
+ EtwTraceProperties prop;
+ ASSERT_HRESULT_SUCCEEDED(prop.SetLoggerFileName(log_file_.value().c_str()));
+ EVENT_TRACE_PROPERTIES& p = *prop.get();
+ p.Wnode.ClientContext = 1; // QPC timer accuracy.
+ p.LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL; // Sequential log.
+
+ // On Vista and later, we create a private in-process log session, because
+ // otherwise we'd need administrator privileges. Unfortunately we can't
+ // do the same on XP and better, because the semantics of a private
+ // logger session are different, and the IN_PROC flag is not supported.
+ if (!is_xp) {
+ p.LogFileMode |= EVENT_TRACE_PRIVATE_IN_PROC | // In-proc for non-admin.
+ EVENT_TRACE_PRIVATE_LOGGER_MODE; // Process-private log.
+ }
+
+ p.MaximumFileSize = 100; // 100M file size.
+ p.FlushTimer = 1; // 1 second flush lag.
+ ASSERT_HRESULT_SUCCEEDED(controller_.Start(kTestSessionName, &prop));
+
+ // Enable the TraceLog provider GUID.
+ ASSERT_HRESULT_SUCCEEDED(
+ controller_.EnableProvider(base::kChromeTraceProviderName,
+ TRACE_LEVEL_INFORMATION,
+ 0));
+
+ if (is_xp) {
+ base::TraceLog::Resurrect();
+ base::TraceLog* tracelog = base::TraceLog::Get();
+ ASSERT_TRUE(tracelog != NULL);
+ }
+ }
+
+ void TearDown() {
+ EtwTraceProperties prop;
+ if (controller_.session() != 0)
+ EXPECT_HRESULT_SUCCEEDED(controller_.Stop(&prop));
+
+ if (!log_file_.value().empty())
+ file_util::Delete(log_file_, false);
+ }
+
+ void ExpectEvent(REFGUID guid,
+ EtwEventType type,
+ const char* name,
+ size_t name_len,
+ const void* id,
+ const char* extra,
+ size_t extra_len) {
+ // Build the trace event buffer we expect will result from this.
+ std::stringbuf str;
+ str.sputn(name, name_len + 1);
+ str.sputn(reinterpret_cast<const char*>(&id), sizeof(id));
+ str.sputn(extra, extra_len + 1);
+
+ // And set up the expectation for the event callback.
+ EXPECT_CALL(consumer_, Event(guid,
+ type,
+ testing::Ge(str.str().length()),
+ BufferStartsWith(str.str())));
+ }
+
+ void ExpectPlayLog() {
+ // Ignore EventTraceGuid events.
+ EXPECT_CALL(consumer_, Event(kEventTraceGuid, _, _, _))
+ .Times(AnyNumber());
+ }
+
+ void PlayLog() {
+ EtwTraceProperties prop;
+ EXPECT_HRESULT_SUCCEEDED(controller_.Stop(&prop));
+ ASSERT_HRESULT_SUCCEEDED(
+ consumer_.OpenFileSession(log_file_.value().c_str()));
+
+ ASSERT_HRESULT_SUCCEEDED(consumer_.Consume());
+ }
+
+ private:
+ EtwTraceController controller_;
+ FilePath log_file_;
+ TestEventConsumer consumer_;
+};
+
+} // namespace
+
+
+TEST_F(TraceEventTest, TraceLog) {
+ ExpectPlayLog();
+
+ // The events should arrive in the same sequence as the expects.
+ InSequence in_sequence;
+
+ // Full argument version, passing lengths explicitly.
+ base::TraceLog::Trace(kName,
+ strlen(kName),
+ base::TraceLog::EVENT_BEGIN,
+ kId,
+ kExtra,
+ strlen(kExtra));
+
+ ExpectEvent(base::kTraceEventClass32,
+ base::kTraceEventTypeBegin,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ // Const char* version.
+ base::TraceLog::Trace(static_cast<const char*>(kName),
+ base::TraceLog::EVENT_END,
+ kId,
+ static_cast<const char*>(kExtra));
+
+ ExpectEvent(base::kTraceEventClass32,
+ base::kTraceEventTypeEnd,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ // std::string extra version.
+ base::TraceLog::Trace(static_cast<const char*>(kName),
+ base::TraceLog::EVENT_INSTANT,
+ kId,
+ std::string(kExtra));
+
+ ExpectEvent(base::kTraceEventClass32,
+ base::kTraceEventTypeInstant,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+
+ // Test for sanity on NULL inputs.
+ base::TraceLog::Trace(NULL,
+ 0,
+ base::TraceLog::EVENT_BEGIN,
+ kId,
+ NULL,
+ 0);
+
+ ExpectEvent(base::kTraceEventClass32,
+ base::kTraceEventTypeBegin,
+ kEmpty, 0,
+ kId,
+ kEmpty, 0);
+
+ base::TraceLog::Trace(NULL,
+ -1,
+ base::TraceLog::EVENT_END,
+ kId,
+ NULL,
+ -1);
+
+ ExpectEvent(base::kTraceEventClass32,
+ base::kTraceEventTypeEnd,
+ kEmpty, 0,
+ kId,
+ kEmpty, 0);
+
+ PlayLog();
+}
+
+TEST_F(TraceEventTest, Macros) {
+ ExpectPlayLog();
+
+ // The events should arrive in the same sequence as the expects.
+ InSequence in_sequence;
+
+ TRACE_EVENT_BEGIN(kName, kId, kExtra);
+ ExpectEvent(base::kTraceEventClass32,
+ base::kTraceEventTypeBegin,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ TRACE_EVENT_END(kName, kId, kExtra);
+ ExpectEvent(base::kTraceEventClass32,
+ base::kTraceEventTypeEnd,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ TRACE_EVENT_INSTANT(kName, kId, kExtra);
+ ExpectEvent(base::kTraceEventClass32,
+ base::kTraceEventTypeInstant,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ PlayLog();
+}