summaryrefslogtreecommitdiffstats
path: root/base/debug
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-26 04:07:50 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-26 04:07:50 +0000
commit58580359a452cb7c3b9580edc0843c3ab3d158df (patch)
tree964dbcc1505f4b9c2bbb5e7a64720861d604c8f3 /base/debug
parent23872906817de5d402b0c2da6d5f7ee6026378e6 (diff)
downloadchromium_src-58580359a452cb7c3b9580edc0843c3ab3d158df.zip
chromium_src-58580359a452cb7c3b9580edc0843c3ab3d158df.tar.gz
chromium_src-58580359a452cb7c3b9580edc0843c3ab3d158df.tar.bz2
Move debug-related stuff from base to the base/debug directory and use the
base::debug namespace. This splits apart debug_util into base/debugger and base/stack_trace There are still two functions in debug_util that I'm not sure what to do with. Since this uses the base::debug namespace, I removed the functions in debugger.h from the static class and just made them free functions in the namespace. TEST=it compiles BUG=none Review URL: http://codereview.chromium.org/3945002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63859 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/debug')
-rw-r--r--base/debug/debugger.cc25
-rw-r--r--base/debug/debugger.h39
-rw-r--r--base/debug/debugger_posix.cc165
-rw-r--r--base/debug/debugger_win.cc112
-rw-r--r--base/debug/leak_annotations.h28
-rw-r--r--base/debug/leak_tracker.h134
-rw-r--r--base/debug/leak_tracker_unittest.cc113
-rw-r--r--base/debug/stack_trace.cc21
-rw-r--r--base/debug/stack_trace.h63
-rw-r--r--base/debug/stack_trace_posix.cc199
-rw-r--r--base/debug/stack_trace_unittest.cc112
-rw-r--r--base/debug/stack_trace_win.cc197
-rw-r--r--base/debug/trace_event.cc166
-rw-r--r--base/debug/trace_event.h147
-rw-r--r--base/debug/trace_event_win.cc116
-rw-r--r--base/debug/trace_event_win.h151
-rw-r--r--base/debug/trace_event_win_unittest.cc308
17 files changed, 2096 insertions, 0 deletions
diff --git a/base/debug/debugger.cc b/base/debug/debugger.cc
new file mode 100644
index 0000000..9ca7e8d
--- /dev/null
+++ b/base/debug/debugger.cc
@@ -0,0 +1,25 @@
+// 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/debug/debugger.h"
+
+#include "base/platform_thread.h"
+
+namespace base {
+namespace debug {
+
+bool WaitForDebugger(int wait_seconds, bool silent) {
+ for (int i = 0; i < wait_seconds * 10; ++i) {
+ if (BeingDebugged()) {
+ if (!silent)
+ BreakDebugger();
+ return true;
+ }
+ PlatformThread::Sleep(100);
+ }
+ return false;
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/debugger.h b/base/debug/debugger.h
new file mode 100644
index 0000000..008d77d
--- /dev/null
+++ b/base/debug/debugger.h
@@ -0,0 +1,39 @@
+// 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 is a cross platform interface for helper functions related to
+// debuggers. You should use this to test if you're running under a debugger,
+// and if you would like to yield (breakpoint) into the debugger.
+
+#ifndef BASE_DEBUG_DEBUGGER_H
+#define BASE_DEBUG_DEBUGGER_H
+#pragma once
+
+namespace base {
+namespace debug {
+
+// Starts the registered system-wide JIT debugger to attach it to specified
+// process.
+bool SpawnDebuggerOnProcess(unsigned process_id);
+
+// Waits wait_seconds seconds for a debugger to attach to the current process.
+// When silent is false, an exception is thrown when a debugger is detected.
+bool WaitForDebugger(int wait_seconds, bool silent);
+
+// Returns true if the given process is being run under a debugger.
+//
+// On OS X, the underlying mechanism doesn't work when the sandbox is enabled.
+// To get around this, this function caches its value.
+//
+// WARNING: Because of this, on OS X, a call MUST be made to this function
+// BEFORE the sandbox is enabled.
+bool BeingDebugged();
+
+// Break into the debugger, assumes a debugger is present.
+void BreakDebugger();
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_DEBUGGER_H
diff --git a/base/debug/debugger_posix.cc b/base/debug/debugger_posix.cc
new file mode 100644
index 0000000..a5ab066
--- /dev/null
+++ b/base/debug/debugger_posix.cc
@@ -0,0 +1,165 @@
+// 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/debug/debugger.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#if defined(__GLIBCXX__)
+#include <cxxabi.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#endif
+
+#include <iostream>
+
+#include "base/basictypes.h"
+#include "base/compat_execinfo.h"
+#include "base/eintr_wrapper.h"
+#include "base/logging.h"
+#include "base/safe_strerror_posix.h"
+#include "base/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "base/stringprintf.h"
+
+#if defined(USE_SYMBOLIZE)
+#include "base/third_party/symbolize/symbolize.h"
+#endif
+
+namespace base {
+namespace debug {
+
+bool SpawnDebuggerOnProcess(unsigned /* process_id */) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+#if defined(OS_MACOSX)
+
+// Based on Apple's recommended method as described in
+// http://developer.apple.com/qa/qa2004/qa1361.html
+bool BeingDebugged() {
+ // If the process is sandboxed then we can't use the sysctl, so cache the
+ // value.
+ static bool is_set = false;
+ static bool being_debugged = false;
+
+ if (is_set) {
+ return being_debugged;
+ }
+
+ // Initialize mib, which tells sysctl what info we want. In this case,
+ // we're looking for information about a specific process ID.
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid()
+ };
+
+ // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE. The source and
+ // binary interfaces may change.
+ struct kinfo_proc info;
+ size_t info_size = sizeof(info);
+
+ int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0);
+ DCHECK_EQ(sysctl_result, 0);
+ if (sysctl_result != 0) {
+ is_set = true;
+ being_debugged = false;
+ return being_debugged;
+ }
+
+ // This process is being debugged if the P_TRACED flag is set.
+ is_set = true;
+ being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0;
+ return being_debugged;
+}
+
+#elif defined(OS_LINUX)
+
+// We can look in /proc/self/status for TracerPid. We are likely used in crash
+// handling, so we are careful not to use the heap or have side effects.
+// Another option that is common is to try to ptrace yourself, but then we
+// can't detach without forking(), and that's not so great.
+// static
+bool BeingDebugged() {
+ int status_fd = open("/proc/self/status", O_RDONLY);
+ if (status_fd == -1)
+ return false;
+
+ // We assume our line will be in the first 1024 characters and that we can
+ // read this much all at once. In practice this will generally be true.
+ // This simplifies and speeds up things considerably.
+ char buf[1024];
+
+ ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf)));
+ if (HANDLE_EINTR(close(status_fd)) < 0)
+ return false;
+
+ if (num_read <= 0)
+ return false;
+
+ StringPiece status(buf, num_read);
+ StringPiece tracer("TracerPid:\t");
+
+ StringPiece::size_type pid_index = status.find(tracer);
+ if (pid_index == StringPiece::npos)
+ return false;
+
+ // Our pid is 0 without a debugger, assume this for any pid starting with 0.
+ pid_index += tracer.size();
+ return pid_index < status.size() && status[pid_index] != '0';
+}
+
+#elif defined(OS_FREEBSD)
+
+bool DebugUtil::BeingDebugged() {
+ // TODO(benl): can we determine this under FreeBSD?
+ NOTIMPLEMENTED();
+ return false;
+}
+
+#endif // defined(OS_FREEBSD)
+
+// We want to break into the debugger in Debug mode, and cause a crash dump in
+// Release mode. Breakpad behaves as follows:
+//
+// +-------+-----------------+-----------------+
+// | OS | Dump on SIGTRAP | Dump on SIGABRT |
+// +-------+-----------------+-----------------+
+// | Linux | N | Y |
+// | Mac | Y | N |
+// +-------+-----------------+-----------------+
+//
+// Thus we do the following:
+// Linux: Debug mode, send SIGTRAP; Release mode, send SIGABRT.
+// Mac: Always send SIGTRAP.
+
+#if defined(NDEBUG) && !defined(OS_MACOSX)
+#define DEBUG_BREAK() abort()
+#elif defined(ARCH_CPU_ARM_FAMILY)
+#define DEBUG_BREAK() asm("bkpt 0")
+#else
+#define DEBUG_BREAK() asm("int3")
+#endif
+
+void BreakDebugger() {
+ DEBUG_BREAK();
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/debugger_win.cc b/base/debug/debugger_win.cc
new file mode 100644
index 0000000..d1d47cd
--- /dev/null
+++ b/base/debug/debugger_win.cc
@@ -0,0 +1,112 @@
+// 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/debug/debugger.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include "base/basictypes.h"
+#include "base/debug_util.h"
+#include "base/logging.h"
+
+namespace base {
+namespace debug {
+
+namespace {
+
+// Minimalist key reader.
+// Note: Does not use the CRT.
+bool RegReadString(HKEY root, const wchar_t* subkey,
+ const wchar_t* value_name, wchar_t* buffer, int* len) {
+ HKEY key = NULL;
+ DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key);
+ if (ERROR_SUCCESS != res || key == NULL)
+ return false;
+
+ DWORD type = 0;
+ DWORD buffer_size = *len * sizeof(wchar_t);
+ // We don't support REG_EXPAND_SZ.
+ res = RegQueryValueEx(key, value_name, NULL, &type,
+ reinterpret_cast<BYTE*>(buffer), &buffer_size);
+ if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) {
+ // Make sure the buffer is NULL terminated.
+ buffer[*len - 1] = 0;
+ *len = lstrlen(buffer);
+ RegCloseKey(key);
+ return true;
+ }
+ RegCloseKey(key);
+ return false;
+}
+
+// Replaces each "%ld" in input per a value. Not efficient but it works.
+// Note: Does not use the CRT.
+bool StringReplace(const wchar_t* input, int value, wchar_t* output,
+ int output_len) {
+ memset(output, 0, output_len*sizeof(wchar_t));
+ int input_len = lstrlen(input);
+
+ for (int i = 0; i < input_len; ++i) {
+ int current_output_len = lstrlen(output);
+
+ if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') {
+ // Make sure we have enough place left.
+ if ((current_output_len + 12) >= output_len)
+ return false;
+
+ // Cheap _itow().
+ wsprintf(output+current_output_len, L"%d", value);
+ i += 2;
+ } else {
+ if (current_output_len >= output_len)
+ return false;
+ output[current_output_len] = input[i];
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+// Note: Does not use the CRT.
+bool SpawnDebuggerOnProcess(unsigned process_id) {
+ wchar_t reg_value[1026];
+ int len = arraysize(reg_value);
+ if (RegReadString(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug",
+ L"Debugger", reg_value, &len)) {
+ wchar_t command_line[1026];
+ if (StringReplace(reg_value, process_id, command_line,
+ arraysize(command_line))) {
+ // We don't mind if the debugger is present because it will simply fail
+ // to attach to this process.
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ PROCESS_INFORMATION process_info = {0};
+
+ if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL,
+ &startup_info, &process_info)) {
+ CloseHandle(process_info.hThread);
+ WaitForInputIdle(process_info.hProcess, 10000);
+ CloseHandle(process_info.hProcess);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool BeingDebugged() {
+ return ::IsDebuggerPresent() != 0;
+}
+
+void BreakDebugger() {
+ if (DebugUtil::AreDialogsSuppressed())
+ _exit(1);
+ __debugbreak();
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/leak_annotations.h b/base/debug/leak_annotations.h
new file mode 100644
index 0000000..e1086fe
--- /dev/null
+++ b/base/debug/leak_annotations.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef BASE_DEBUG_LEAK_ANNOTATIONS_H_
+#define BASE_DEBUG_LEAK_ANNOTATIONS_H_
+#pragma once
+
+#include "build/build_config.h"
+
+#if defined(OS_LINUX) && defined(USE_HEAPCHECKER)
+
+#include "third_party/tcmalloc/chromium/src/google/heap-checker.h"
+
+// Annotate a program scope as having memory leaks. Tcmalloc's heap leak
+// checker will ignore them. Note that these annotations may mask real bugs
+// and should not be used in the production code.
+#define ANNOTATE_SCOPED_MEMORY_LEAK \
+ HeapLeakChecker::Disabler heap_leak_checker_disabler
+
+#else
+
+// If tcmalloc is not used, the annotations should be no-ops.
+#define ANNOTATE_SCOPED_MEMORY_LEAK
+
+#endif
+
+#endif // BASE_DEBUG_LEAK_ANNOTATIONS_H_
diff --git a/base/debug/leak_tracker.h b/base/debug/leak_tracker.h
new file mode 100644
index 0000000..8af82a9
--- /dev/null
+++ b/base/debug/leak_tracker.h
@@ -0,0 +1,134 @@
+// 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.
+
+#ifndef BASE_DEBUG_LEAK_TRACKER_H_
+#define BASE_DEBUG_LEAK_TRACKER_H_
+#pragma once
+
+// Only enable leak tracking in debug builds.
+#ifndef NDEBUG
+#define ENABLE_LEAK_TRACKER
+#endif
+
+#ifdef ENABLE_LEAK_TRACKER
+#include "base/debug/stack_trace.h"
+#include "base/linked_list.h"
+#include "base/logging.h"
+#endif // ENABLE_LEAK_TRACKER
+
+// LeakTracker is a helper to verify that all instances of a class
+// have been destroyed.
+//
+// It is particularly useful for classes that are bound to a single thread --
+// before destroying that thread, one can check that there are no remaining
+// instances of that class.
+//
+// For example, to enable leak tracking for class URLRequest, start by
+// adding a member variable of type LeakTracker<URLRequest>.
+//
+// class URLRequest {
+// ...
+// private:
+// base::LeakTracker<URLRequest> leak_tracker_;
+// };
+//
+//
+// Next, when we believe all instances of URLRequest have been deleted:
+//
+// LeakTracker<URLRequest>::CheckForLeaks();
+//
+// Should the check fail (because there are live instances of URLRequest),
+// then the allocation callstack for each leaked instances is dumped to
+// the error log.
+//
+// If ENABLE_LEAK_TRACKER is not defined, then the check has no effect.
+
+namespace base {
+namespace debug {
+
+#ifndef ENABLE_LEAK_TRACKER
+
+// If leak tracking is disabled, do nothing.
+template<typename T>
+class LeakTracker {
+ public:
+ static void CheckForLeaks() {}
+ static int NumLiveInstances() { return -1; }
+};
+
+#else
+
+// If leak tracking is enabled we track where the object was allocated from.
+
+template<typename T>
+class LeakTracker : public LinkNode<LeakTracker<T> > {
+ public:
+ LeakTracker() {
+ instances()->Append(this);
+ }
+
+ ~LeakTracker() {
+ this->RemoveFromList();
+ }
+
+ static void CheckForLeaks() {
+ // Walk the allocation list and print each entry it contains.
+ size_t count = 0;
+
+ // Copy the first 3 leak allocation callstacks onto the stack.
+ // This way if we hit the CHECK() in a release build, the leak
+ // information will be available in mini-dump.
+ const size_t kMaxStackTracesToCopyOntoStack = 3;
+ StackTrace stacktraces[kMaxStackTracesToCopyOntoStack];
+
+ for (LinkNode<LeakTracker<T> >* node = instances()->head();
+ node != instances()->end();
+ node = node->next()) {
+ StackTrace& allocation_stack = node->value()->allocation_stack_;
+
+ if (count < kMaxStackTracesToCopyOntoStack)
+ stacktraces[count] = allocation_stack;
+
+ ++count;
+ LOG(ERROR) << "Leaked " << node << " which was allocated by:";
+ allocation_stack.OutputToStream(&LOG_STREAM(ERROR));
+ }
+
+ CHECK_EQ(0u, count);
+
+ // Hack to keep |stacktraces| and |count| alive (so compiler
+ // doesn't optimize it out, and it will appear in mini-dumps).
+ if (count == 0x1234) {
+ for (size_t i = 0; i < kMaxStackTracesToCopyOntoStack; ++i)
+ stacktraces[i].PrintBacktrace();
+ }
+ }
+
+ static int NumLiveInstances() {
+ // Walk the allocation list and count how many entries it has.
+ int count = 0;
+ for (LinkNode<LeakTracker<T> >* node = instances()->head();
+ node != instances()->end();
+ node = node->next()) {
+ ++count;
+ }
+ return count;
+ }
+
+ private:
+ // Each specialization of LeakTracker gets its own static storage.
+ static LinkedList<LeakTracker<T> >* instances() {
+ static LinkedList<LeakTracker<T> > list;
+ return &list;
+ }
+
+ StackTrace allocation_stack_;
+};
+
+#endif // ENABLE_LEAK_TRACKER
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_LEAK_TRACKER_H_
diff --git a/base/debug/leak_tracker_unittest.cc b/base/debug/leak_tracker_unittest.cc
new file mode 100644
index 0000000..2e6a9a5
--- /dev/null
+++ b/base/debug/leak_tracker_unittest.cc
@@ -0,0 +1,113 @@
+// 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/debug/leak_tracker.h"
+#include "base/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace debug {
+
+namespace {
+
+class ClassA {
+ private:
+ LeakTracker<ClassA> leak_tracker_;
+};
+
+class ClassB {
+ private:
+ LeakTracker<ClassB> leak_tracker_;
+};
+
+#ifndef ENABLE_LEAK_TRACKER
+
+// If leak tracking is disabled, we should do nothing.
+TEST(LeakTrackerTest, NotEnabled) {
+ EXPECT_EQ(-1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(-1, LeakTracker<ClassB>::NumLiveInstances());
+
+ // Use scoped_ptr so compiler doesn't complain about unused variables.
+ scoped_ptr<ClassA> a1(new ClassA);
+ scoped_ptr<ClassB> b1(new ClassB);
+ scoped_ptr<ClassB> b2(new ClassB);
+
+ EXPECT_EQ(-1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(-1, LeakTracker<ClassB>::NumLiveInstances());
+}
+
+#else
+
+TEST(LeakTrackerTest, Basic) {
+ {
+ ClassA a1;
+
+ EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances());
+
+ ClassB b1;
+ ClassB b2;
+
+ EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances());
+
+ scoped_ptr<ClassA> a2(new ClassA);
+
+ EXPECT_EQ(2, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances());
+
+ a2.reset();
+
+ EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances());
+ }
+
+ EXPECT_EQ(0, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances());
+}
+
+// Try some orderings of create/remove to hit different cases in the linked-list
+// assembly.
+TEST(LeakTrackerTest, LinkedList) {
+ EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances());
+
+ scoped_ptr<ClassA> a1(new ClassA);
+ scoped_ptr<ClassA> a2(new ClassA);
+ scoped_ptr<ClassA> a3(new ClassA);
+ scoped_ptr<ClassA> a4(new ClassA);
+
+ EXPECT_EQ(4, LeakTracker<ClassA>::NumLiveInstances());
+
+ // Remove the head of the list (a1).
+ a1.reset();
+ EXPECT_EQ(3, LeakTracker<ClassA>::NumLiveInstances());
+
+ // Remove the tail of the list (a4).
+ a4.reset();
+ EXPECT_EQ(2, LeakTracker<ClassA>::NumLiveInstances());
+
+ // Append to the new tail of the list (a3).
+ scoped_ptr<ClassA> a5(new ClassA);
+ EXPECT_EQ(3, LeakTracker<ClassA>::NumLiveInstances());
+
+ a2.reset();
+ a3.reset();
+
+ EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
+
+ a5.reset();
+ EXPECT_EQ(0, LeakTracker<ClassA>::NumLiveInstances());
+}
+
+TEST(LeakTrackerTest, NoOpCheckForLeaks) {
+ // There are no live instances of ClassA, so this should do nothing.
+ LeakTracker<ClassA>::CheckForLeaks();
+}
+
+#endif // ENABLE_LEAK_TRACKER
+
+} // namespace
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/stack_trace.cc b/base/debug/stack_trace.cc
new file mode 100644
index 0000000..5be4c5c
--- /dev/null
+++ b/base/debug/stack_trace.cc
@@ -0,0 +1,21 @@
+// 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/debug/stack_trace.h"
+
+namespace base {
+namespace debug {
+
+StackTrace::~StackTrace() {
+}
+
+const void *const *StackTrace::Addresses(size_t* count) {
+ *count = count_;
+ if (count_)
+ return trace_;
+ return NULL;
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h
new file mode 100644
index 0000000..8afc32c
--- /dev/null
+++ b/base/debug/stack_trace.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef BASE_DEBUG_STACK_TRACE_H_
+#define BASE_DEBUG_STACK_TRACE_H_
+#pragma once
+
+#include <iosfwd>
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+struct _EXCEPTION_POINTERS;
+#endif
+
+namespace base {
+namespace debug {
+
+// A stacktrace can be helpful in debugging. For example, you can include a
+// stacktrace member in a object (probably around #ifndef NDEBUG) so that you
+// can later see where the given object was created from.
+class StackTrace {
+ public:
+ // Creates a stacktrace from the current location.
+ StackTrace();
+
+#if defined(OS_WIN)
+ // Creates a stacktrace for an exception.
+ // Note: this function will throw an import not found (StackWalk64) exception
+ // on system without dbghelp 5.1.
+ StackTrace(_EXCEPTION_POINTERS* exception_pointers);
+#endif
+
+ // Copying and assignment are allowed with the default functions.
+
+ ~StackTrace();
+
+ // Gets an array of instruction pointer values. |*count| will be set to the
+ // number of elements in the returned array.
+ const void* const* Addresses(size_t* count);
+
+ // Prints a backtrace to stderr
+ void PrintBacktrace();
+
+ // Resolves backtrace to symbols and write to stream.
+ void OutputToStream(std::ostream* os);
+
+ private:
+ // From http://msdn.microsoft.com/en-us/library/bb204633.aspx,
+ // the sum of FramesToSkip and FramesToCapture must be less than 63,
+ // so set it to 62. Even if on POSIX it could be a larger value, it usually
+ // doesn't give much more information.
+ static const int kMaxTraces = 62;
+
+ void* trace_[kMaxTraces];
+ int count_;
+};
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_STACK_TRACE_H_
diff --git a/base/debug/stack_trace_posix.cc b/base/debug/stack_trace_posix.cc
new file mode 100644
index 0000000..e4b0ef2
--- /dev/null
+++ b/base/debug/stack_trace_posix.cc
@@ -0,0 +1,199 @@
+// 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/debug/stack_trace.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#if defined(__GLIBCXX__)
+#include <cxxabi.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#endif
+
+#include <iostream>
+
+#include "base/basictypes.h"
+#include "base/compat_execinfo.h"
+#include "base/eintr_wrapper.h"
+#include "base/logging.h"
+#include "base/safe_strerror_posix.h"
+#include "base/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "base/stringprintf.h"
+
+#if defined(USE_SYMBOLIZE)
+#include "base/third_party/symbolize/symbolize.h"
+#endif
+
+namespace base {
+namespace debug {
+
+namespace {
+
+// The prefix used for mangled symbols, per the Itanium C++ ABI:
+// http://www.codesourcery.com/cxx-abi/abi.html#mangling
+const char kMangledSymbolPrefix[] = "_Z";
+
+// Characters that can be used for symbols, generated by Ruby:
+// (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join
+const char kSymbolCharacters[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+
+#if !defined(USE_SYMBOLIZE)
+// Demangles C++ symbols in the given text. Example:
+//
+// "sconsbuild/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]"
+// =>
+// "sconsbuild/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]"
+void DemangleSymbols(std::string* text) {
+#if defined(__GLIBCXX__)
+
+ std::string::size_type search_from = 0;
+ while (search_from < text->size()) {
+ // Look for the start of a mangled symbol, from search_from.
+ std::string::size_type mangled_start =
+ text->find(kMangledSymbolPrefix, search_from);
+ if (mangled_start == std::string::npos) {
+ break; // Mangled symbol not found.
+ }
+
+ // Look for the end of the mangled symbol.
+ std::string::size_type mangled_end =
+ text->find_first_not_of(kSymbolCharacters, mangled_start);
+ if (mangled_end == std::string::npos) {
+ mangled_end = text->size();
+ }
+ std::string mangled_symbol =
+ text->substr(mangled_start, mangled_end - mangled_start);
+
+ // Try to demangle the mangled symbol candidate.
+ int status = 0;
+ scoped_ptr_malloc<char> demangled_symbol(
+ abi::__cxa_demangle(mangled_symbol.c_str(), NULL, 0, &status));
+ if (status == 0) { // Demangling is successful.
+ // Remove the mangled symbol.
+ text->erase(mangled_start, mangled_end - mangled_start);
+ // Insert the demangled symbol.
+ text->insert(mangled_start, demangled_symbol.get());
+ // Next time, we'll start right after the demangled symbol we inserted.
+ search_from = mangled_start + strlen(demangled_symbol.get());
+ } else {
+ // Failed to demangle. Retry after the "_Z" we just found.
+ search_from = mangled_start + 2;
+ }
+ }
+
+#endif // defined(__GLIBCXX__)
+}
+#endif // !defined(USE_SYMBOLIZE)
+
+// Gets the backtrace as a vector of strings. If possible, resolve symbol
+// names and attach these. Otherwise just use raw addresses. Returns true
+// if any symbol name is resolved. Returns false on error and *may* fill
+// in |error_message| if an error message is available.
+bool GetBacktraceStrings(void **trace, int size,
+ std::vector<std::string>* trace_strings,
+ std::string* error_message) {
+ bool symbolized = false;
+
+#if defined(USE_SYMBOLIZE)
+ for (int i = 0; i < size; ++i) {
+ char symbol[1024];
+ // Subtract by one as return address of function may be in the next
+ // function when a function is annotated as noreturn.
+ if (google::Symbolize(static_cast<char *>(trace[i]) - 1,
+ symbol, sizeof(symbol))) {
+ // Don't call DemangleSymbols() here as the symbol is demangled by
+ // google::Symbolize().
+ trace_strings->push_back(
+ base::StringPrintf("%s [%p]", symbol, trace[i]));
+ symbolized = true;
+ } else {
+ trace_strings->push_back(base::StringPrintf("%p", trace[i]));
+ }
+ }
+#else
+ scoped_ptr_malloc<char*> trace_symbols(backtrace_symbols(trace, size));
+ if (trace_symbols.get()) {
+ for (int i = 0; i < size; ++i) {
+ std::string trace_symbol = trace_symbols.get()[i];
+ DemangleSymbols(&trace_symbol);
+ trace_strings->push_back(trace_symbol);
+ }
+ symbolized = true;
+ } else {
+ if (error_message)
+ *error_message = safe_strerror(errno);
+ for (int i = 0; i < size; ++i) {
+ trace_strings->push_back(base::StringPrintf("%p", trace[i]));
+ }
+ }
+#endif // defined(USE_SYMBOLIZE)
+
+ return symbolized;
+}
+
+} // namespace
+
+StackTrace::StackTrace() {
+#if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
+ if (backtrace == NULL) {
+ count_ = 0;
+ return;
+ }
+#endif
+ // Though the backtrace API man page does not list any possible negative
+ // return values, we take no chance.
+ count_ = std::max(backtrace(trace_, arraysize(trace_)), 0);
+}
+
+void StackTrace::PrintBacktrace() {
+#if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
+ if (backtrace_symbols_fd == NULL)
+ return;
+#endif
+ fflush(stderr);
+ std::vector<std::string> trace_strings;
+ GetBacktraceStrings(trace_, count_, &trace_strings, NULL);
+ for (size_t i = 0; i < trace_strings.size(); ++i) {
+ std::cerr << "\t" << trace_strings[i] << "\n";
+ }
+}
+
+void StackTrace::OutputToStream(std::ostream* os) {
+#if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
+ if (backtrace_symbols == NULL)
+ return;
+#endif
+ std::vector<std::string> trace_strings;
+ std::string error_message;
+ if (GetBacktraceStrings(trace_, count_, &trace_strings, &error_message)) {
+ (*os) << "Backtrace:\n";
+ } else {
+ if (!error_message.empty())
+ error_message = " (" + error_message + ")";
+ (*os) << "Unable to get symbols for backtrace" << error_message << ". "
+ << "Dumping raw addresses in trace:\n";
+ }
+
+ for (size_t i = 0; i < trace_strings.size(); ++i) {
+ (*os) << "\t" << trace_strings[i] << "\n";
+ }
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/stack_trace_unittest.cc b/base/debug/stack_trace_unittest.cc
new file mode 100644
index 0000000..e8e1e19
--- /dev/null
+++ b/base/debug/stack_trace_unittest.cc
@@ -0,0 +1,112 @@
+// 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 <sstream>
+#include <string>
+
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace debug {
+
+// Note: On Linux, this test currently only fully works on Debug builds.
+// See comments in the #ifdef soup if you intend to change this.
+// Flaky, crbug.com/32070.
+TEST(StackTrace, FLAKY_OutputToStream) {
+ StackTrace trace;
+
+ // Dump the trace into a string.
+ std::ostringstream os;
+ trace.OutputToStream(&os);
+ std::string backtrace_message = os.str();
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG
+ // Stack traces require an extra data table that bloats our binaries,
+ // so they're turned off for release builds. We stop the test here,
+ // at least letting us verify that the calls don't crash.
+ return;
+#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG
+
+ size_t frames_found = 0;
+ trace.Addresses(&frames_found);
+ ASSERT_GE(frames_found, 5u) <<
+ "No stack frames found. Skipping rest of test.";
+
+ // Check if the output has symbol initialization warning. If it does, fail.
+ ASSERT_EQ(backtrace_message.find("Dumping unresolved backtrace"),
+ std::string::npos) <<
+ "Unable to resolve symbols. Skipping rest of test.";
+
+#if defined(OS_MACOSX)
+#if 0
+ // Disabled due to -fvisibility=hidden in build config.
+
+ // Symbol resolution via the backtrace_symbol function does not work well
+ // in OS X.
+ // See this thread:
+ //
+ // http://lists.apple.com/archives/darwin-dev/2009/Mar/msg00111.html
+ //
+ // Just check instead that we find our way back to the "start" symbol
+ // which should be the first symbol in the trace.
+ //
+ // TODO(port): Find a more reliable way to resolve symbols.
+
+ // Expect to at least find main.
+ EXPECT_TRUE(backtrace_message.find("start") != std::string::npos)
+ << "Expected to find start in backtrace:\n"
+ << backtrace_message;
+
+#endif
+#elif defined(__GLIBCXX__)
+ // This branch is for gcc-compiled code, but not Mac due to the
+ // above #if.
+ // Expect a demangled symbol.
+ EXPECT_TRUE(backtrace_message.find("testing::Test::Run()") !=
+ std::string::npos)
+ << "Expected a demangled symbol in backtrace:\n"
+ << backtrace_message;
+
+#elif 0
+ // This is the fall-through case; it used to cover Windows.
+ // But it's disabled because of varying buildbot configs;
+ // some lack symbols.
+
+ // Expect to at least find main.
+ EXPECT_TRUE(backtrace_message.find("main") != std::string::npos)
+ << "Expected to find main in backtrace:\n"
+ << backtrace_message;
+
+#if defined(OS_WIN)
+// MSVC doesn't allow the use of C99's __func__ within C++, so we fake it with
+// MSVC's __FUNCTION__ macro.
+#define __func__ __FUNCTION__
+#endif
+
+ // Expect to find this function as well.
+ // Note: This will fail if not linked with -rdynamic (aka -export_dynamic)
+ EXPECT_TRUE(backtrace_message.find(__func__) != std::string::npos)
+ << "Expected to find " << __func__ << " in backtrace:\n"
+ << backtrace_message;
+
+#endif // define(OS_MACOSX)
+}
+
+// The test is used for manual testing, e.g., to see the raw output.
+TEST(StackTrace, DebugOutputToStream) {
+ StackTrace trace;
+ std::ostringstream os;
+ trace.OutputToStream(&os);
+ VLOG(1) << os.str();
+}
+
+// The test is used for manual testing, e.g., to see the raw output.
+TEST(StackTrace, DebugPrintBacktrace) {
+ StackTrace().PrintBacktrace();
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/stack_trace_win.cc b/base/debug/stack_trace_win.cc
new file mode 100644
index 0000000..653d234
--- /dev/null
+++ b/base/debug/stack_trace_win.cc
@@ -0,0 +1,197 @@
+// 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/debug/stack_trace.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <iostream>
+
+#include "base/basictypes.h"
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/singleton.h"
+
+namespace base {
+namespace debug {
+
+namespace {
+
+// SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family
+// of functions. The Sym* family of functions may only be invoked by one
+// thread at a time. SymbolContext code may access a symbol server over the
+// network while holding the lock for this singleton. In the case of high
+// latency, this code will adversly affect performance.
+//
+// There is also a known issue where this backtrace code can interact
+// badly with breakpad if breakpad is invoked in a separate thread while
+// we are using the Sym* functions. This is because breakpad does now
+// share a lock with this function. See this related bug:
+//
+// http://code.google.com/p/google-breakpad/issues/detail?id=311
+//
+// This is a very unlikely edge case, and the current solution is to
+// just ignore it.
+class SymbolContext {
+ public:
+ static SymbolContext* Get() {
+ // We use a leaky singleton because code may call this during process
+ // termination.
+ return
+ Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get();
+ }
+
+ // Returns the error code of a failed initialization.
+ DWORD init_error() const {
+ return init_error_;
+ }
+
+ // For the given trace, attempts to resolve the symbols, and output a trace
+ // to the ostream os. The format for each line of the backtrace is:
+ //
+ // <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
+ //
+ // This function should only be called if Init() has been called. We do not
+ // LOG(FATAL) here because this code is called might be triggered by a
+ // LOG(FATAL) itself.
+ void OutputTraceToStream(const void* const* trace,
+ int count,
+ std::ostream* os) {
+ AutoLock lock(lock_);
+
+ for (size_t i = 0; (i < count) && os->good(); ++i) {
+ const int kMaxNameLength = 256;
+ DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]);
+
+ // Code adapted from MSDN example:
+ // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
+ ULONG64 buffer[
+ (sizeof(SYMBOL_INFO) +
+ kMaxNameLength * sizeof(wchar_t) +
+ sizeof(ULONG64) - 1) /
+ sizeof(ULONG64)];
+ memset(buffer, 0, sizeof(buffer));
+
+ // Initialize symbol information retrieval structures.
+ DWORD64 sym_displacement = 0;
+ PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = kMaxNameLength - 1;
+ BOOL has_symbol = SymFromAddr(GetCurrentProcess(), frame,
+ &sym_displacement, symbol);
+
+ // Attempt to retrieve line number information.
+ DWORD line_displacement = 0;
+ IMAGEHLP_LINE64 line = {};
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ BOOL has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame,
+ &line_displacement, &line);
+
+ // Output the backtrace line.
+ (*os) << "\t";
+ if (has_symbol) {
+ (*os) << symbol->Name << " [0x" << trace[i] << "+"
+ << sym_displacement << "]";
+ } else {
+ // If there is no symbol informtion, add a spacer.
+ (*os) << "(No symbol) [0x" << trace[i] << "]";
+ }
+ if (has_line) {
+ (*os) << " (" << line.FileName << ":" << line.LineNumber << ")";
+ }
+ (*os) << "\n";
+ }
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<SymbolContext>;
+
+ SymbolContext() : init_error_(ERROR_SUCCESS) {
+ // Initializes the symbols for the process.
+ // Defer symbol load until they're needed, use undecorated names, and
+ // get line numbers.
+ SymSetOptions(SYMOPT_DEFERRED_LOADS |
+ SYMOPT_UNDNAME |
+ SYMOPT_LOAD_LINES);
+ if (SymInitialize(GetCurrentProcess(), NULL, TRUE)) {
+ init_error_ = ERROR_SUCCESS;
+ } else {
+ init_error_ = GetLastError();
+ // TODO(awong): Handle error: SymInitialize can fail with
+ // ERROR_INVALID_PARAMETER.
+ // When it fails, we should not call debugbreak since it kills the current
+ // process (prevents future tests from running or kills the browser
+ // process).
+ DLOG(ERROR) << "SymInitialize failed: " << init_error_;
+ }
+ }
+
+ DWORD init_error_;
+ Lock lock_;
+ DISALLOW_COPY_AND_ASSIGN(SymbolContext);
+};
+
+} // namespace
+
+StackTrace::StackTrace() {
+ // When walking our own stack, use CaptureStackBackTrace().
+ count_ = CaptureStackBackTrace(0, arraysize(trace_), trace_, NULL);
+}
+
+StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
+ // When walking an exception stack, we need to use StackWalk64().
+ count_ = 0;
+ // Initialize stack walking.
+ STACKFRAME64 stack_frame;
+ memset(&stack_frame, 0, sizeof(stack_frame));
+#if defined(_WIN64)
+ int machine_type = IMAGE_FILE_MACHINE_AMD64;
+ stack_frame.AddrPC.Offset = exception_pointers->ContextRecord->Rip;
+ stack_frame.AddrFrame.Offset = exception_pointers->ContextRecord->Rbp;
+ stack_frame.AddrStack.Offset = exception_pointers->ContextRecord->Rsp;
+#else
+ int machine_type = IMAGE_FILE_MACHINE_I386;
+ stack_frame.AddrPC.Offset = exception_pointers->ContextRecord->Eip;
+ stack_frame.AddrFrame.Offset = exception_pointers->ContextRecord->Ebp;
+ stack_frame.AddrStack.Offset = exception_pointers->ContextRecord->Esp;
+#endif
+ stack_frame.AddrPC.Mode = AddrModeFlat;
+ stack_frame.AddrFrame.Mode = AddrModeFlat;
+ stack_frame.AddrStack.Mode = AddrModeFlat;
+ while (StackWalk64(machine_type,
+ GetCurrentProcess(),
+ GetCurrentThread(),
+ &stack_frame,
+ exception_pointers->ContextRecord,
+ NULL,
+ &SymFunctionTableAccess64,
+ &SymGetModuleBase64,
+ NULL) &&
+ count_ < arraysize(trace_)) {
+ trace_[count_++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
+ }
+}
+
+void StackTrace::PrintBacktrace() {
+ OutputToStream(&std::cerr);
+}
+
+void StackTrace::OutputToStream(std::ostream* os) {
+ SymbolContext* context = SymbolContext::Get();
+ DWORD error = context->init_error();
+ if (error != ERROR_SUCCESS) {
+ (*os) << "Error initializing symbols (" << error
+ << "). Dumping unresolved backtrace:\n";
+ for (int i = 0; (i < count_) && os->good(); ++i) {
+ (*os) << "\t" << trace_[i] << "\n";
+ }
+ } else {
+ (*os) << "Backtrace:\n";
+ context->OutputTraceToStream(trace_, count_, os);
+ }
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/trace_event.cc b/base/debug/trace_event.cc
new file mode 100644
index 0000000..4d1d315
--- /dev/null
+++ b/base/debug/trace_event.cc
@@ -0,0 +1,166 @@
+// 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/debug/trace_event.h"
+
+#include "base/format_macros.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/platform_thread.h"
+#include "base/process_util.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "base/time.h"
+
+#define USE_UNRELIABLE_NOW
+
+namespace base {
+namespace debug {
+
+static const char* kEventTypeNames[] = {
+ "BEGIN",
+ "END",
+ "INSTANT"
+};
+
+static const FilePath::CharType* kLogFileName =
+ FILE_PATH_LITERAL("trace_%d.log");
+
+TraceLog::TraceLog() : enabled_(false), log_file_(NULL) {
+ base::ProcessHandle proc = base::GetCurrentProcessHandle();
+#if !defined(OS_MACOSX)
+ process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc));
+#else
+ // The default port provider is sufficient to get data for the current
+ // process.
+ process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc,
+ NULL));
+#endif
+}
+
+TraceLog::~TraceLog() {
+ Stop();
+}
+
+// static
+bool TraceLog::IsTracing() {
+ TraceLog* trace = Singleton<TraceLog>::get();
+ return trace->enabled_;
+}
+
+// static
+bool TraceLog::StartTracing() {
+ TraceLog* trace = Singleton<TraceLog>::get();
+ return trace->Start();
+}
+
+bool TraceLog::Start() {
+ if (enabled_)
+ return true;
+ enabled_ = OpenLogFile();
+ if (enabled_) {
+ Log("var raw_trace_events = [\n");
+ trace_start_time_ = TimeTicks::Now();
+ timer_.Start(TimeDelta::FromMilliseconds(250), this, &TraceLog::Heartbeat);
+ }
+ return enabled_;
+}
+
+// static
+void TraceLog::StopTracing() {
+ TraceLog* trace = Singleton<TraceLog>::get();
+ return trace->Stop();
+}
+
+void TraceLog::Stop() {
+ if (enabled_) {
+ enabled_ = false;
+ Log("];\n");
+ CloseLogFile();
+ timer_.Stop();
+ }
+}
+
+void TraceLog::Heartbeat() {
+ std::string cpu = StringPrintf("%.0f", process_metrics_->GetCPUUsage());
+ TRACE_EVENT_INSTANT("heartbeat.cpu", 0, cpu);
+}
+
+void TraceLog::CloseLogFile() {
+ if (log_file_) {
+ file_util::CloseFile(log_file_);
+ }
+}
+
+bool TraceLog::OpenLogFile() {
+ FilePath::StringType pid_filename =
+ StringPrintf(kLogFileName, base::GetCurrentProcId());
+ FilePath log_file_path;
+ if (!PathService::Get(base::DIR_EXE, &log_file_path))
+ return false;
+ log_file_path = log_file_path.Append(pid_filename);
+ log_file_ = file_util::OpenFile(log_file_path, "a");
+ if (!log_file_) {
+ // try the current directory
+ log_file_ = file_util::OpenFile(FilePath(pid_filename), "a");
+ if (!log_file_) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TraceLog::Trace(const std::string& name,
+ EventType type,
+ const void* id,
+ const std::wstring& extra,
+ const char* file,
+ int line) {
+ if (!enabled_)
+ return;
+ Trace(name, type, id, WideToUTF8(extra), file, line);
+}
+
+void TraceLog::Trace(const std::string& name,
+ EventType type,
+ const void* id,
+ const std::string& extra,
+ const char* file,
+ int line) {
+ if (!enabled_)
+ return;
+
+#ifdef USE_UNRELIABLE_NOW
+ TimeTicks tick = TimeTicks::HighResNow();
+#else
+ TimeTicks tick = TimeTicks::Now();
+#endif
+ TimeDelta delta = tick - trace_start_time_;
+ int64 usec = delta.InMicroseconds();
+ std::string msg =
+ StringPrintf("{'pid':'0x%lx', 'tid':'0x%lx', 'type':'%s', "
+ "'name':'%s', 'id':'%p', 'extra':'%s', 'file':'%s', "
+ "'line_number':'%d', 'usec_begin': %" PRId64 "},\n",
+ static_cast<unsigned long>(base::GetCurrentProcId()),
+ static_cast<unsigned long>(PlatformThread::CurrentId()),
+ kEventTypeNames[type],
+ name.c_str(),
+ id,
+ extra.c_str(),
+ file,
+ line,
+ usec);
+
+ Log(msg);
+}
+
+void TraceLog::Log(const std::string& msg) {
+ AutoLock lock(file_lock_);
+
+ fprintf(log_file_, "%s", msg.c_str());
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/trace_event.h b/base/debug/trace_event.h
new file mode 100644
index 0000000..49ba4bd
--- /dev/null
+++ b/base/debug/trace_event.h
@@ -0,0 +1,147 @@
+// 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.
+
+// Trace events to track application performance. Events consist of a name
+// a type (BEGIN, END or INSTANT), a tracking id and extra string data.
+// In addition, the current process id, thread id, a timestamp down to the
+// microsecond and a file and line number of the calling location.
+//
+// The current implementation logs these events into a log file of the form
+// trace_<pid>.log where it's designed to be post-processed to generate a
+// trace report. In the future, it may use another mechansim to facilitate
+// real-time analysis.
+
+#ifndef BASE_DEBUG_TRACE_EVENT_H_
+#define BASE_DEBUG_TRACE_EVENT_H_
+#pragma once
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+// On Windows we always pull in an alternative implementation
+// which logs to Event Tracing for Windows.
+//
+// Note that the Windows implementation is always enabled, irrespective the
+// value of the CHROMIUM_ENABLE_TRACE_EVENT define. The Windows implementation
+// is controlled by Event Tracing for Windows, which will turn tracing on only
+// if there is someone listening for the events it generates.
+#include "base/debug/trace_event_win.h"
+#else // defined(OS_WIN)
+
+#include <string>
+
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "base/singleton.h"
+#include "base/time.h"
+#include "base/timer.h"
+
+#ifndef CHROMIUM_ENABLE_TRACE_EVENT
+#define TRACE_EVENT_BEGIN(name, id, extra) ((void) 0)
+#define TRACE_EVENT_END(name, id, extra) ((void) 0)
+#define TRACE_EVENT_INSTANT(name, id, extra) ((void) 0)
+
+#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);
+// RunScript(script);
+// TRACE_EVENT_END("v8.run", documentId, scriptLocation);
+
+// Record that an event (of name, id) has begun. All BEGIN events should have
+// corresponding END events with a matching (name, id).
+#define TRACE_EVENT_BEGIN(name, id, extra) \
+ Singleton<base::debug::TraceLog>::get()->Trace( \
+ name, \
+ base::debug::TraceLog::EVENT_BEGIN, \
+ reinterpret_cast<const void*>(id), \
+ extra, \
+ __FILE__, \
+ __LINE__)
+
+// Record that an event (of name, id) has ended. All END events should have
+// corresponding BEGIN events with a matching (name, id).
+#define TRACE_EVENT_END(name, id, extra) \
+ Singleton<base::debug::TraceLog>::get()->Trace( \
+ name, \
+ base::debug::TraceLog::EVENT_END, \
+ reinterpret_cast<const void*>(id), \
+ extra, \
+ __FILE__, \
+ __LINE__)
+
+// Record that an event (of name, id) with no duration has happened.
+#define TRACE_EVENT_INSTANT(name, id, extra) \
+ Singleton<base::debug::TraceLog>::get()->Trace( \
+ name, \
+ base::debug::TraceLog::EVENT_INSTANT, \
+ reinterpret_cast<const void*>(id), \
+ extra, \
+ __FILE__, \
+ __LINE__)
+#endif // CHROMIUM_ENABLE_TRACE_EVENT
+
+namespace base {
+
+class ProcessMetrics;
+
+namespace debug {
+
+class TraceLog {
+ public:
+ enum EventType {
+ EVENT_BEGIN,
+ EVENT_END,
+ EVENT_INSTANT
+ };
+
+ // Is tracing currently enabled.
+ static bool IsTracing();
+ // Start logging trace events.
+ static bool StartTracing();
+ // Stop logging trace events.
+ static void StopTracing();
+
+ // Log a trace event of (name, type, id) with the optional extra string.
+ void Trace(const std::string& name,
+ EventType type,
+ const void* id,
+ const std::wstring& extra,
+ const char* file,
+ int line);
+ void Trace(const std::string& name,
+ EventType type,
+ const void* id,
+ const std::string& extra,
+ const char* file,
+ int line);
+
+ private:
+ // This allows constructor and destructor to be private and usable only
+ // by the Singleton class.
+ friend struct DefaultSingletonTraits<TraceLog>;
+
+ TraceLog();
+ ~TraceLog();
+ bool OpenLogFile();
+ void CloseLogFile();
+ bool Start();
+ void Stop();
+ void Heartbeat();
+ void Log(const std::string& msg);
+
+ bool enabled_;
+ FILE* log_file_;
+ Lock file_lock_;
+ TimeTicks trace_start_time_;
+ scoped_ptr<base::ProcessMetrics> process_metrics_;
+ RepeatingTimer<TraceLog> timer_;
+};
+
+} // namespace debug
+} // namespace base
+
+#endif // defined(OS_WIN)
+
+#endif // BASE_DEBUG_TRACE_EVENT_H_
diff --git a/base/debug/trace_event_win.cc b/base/debug/trace_event_win.cc
new file mode 100644
index 0000000..129a992
--- /dev/null
+++ b/base/debug/trace_event_win.cc
@@ -0,0 +1,116 @@
+// 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/debug/trace_event_win.h"
+
+#include "base/logging.h"
+#include "base/singleton.h"
+#include <initguid.h> // NOLINT
+
+namespace base {
+namespace debug {
+
+// {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(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,
+ 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 TraceLog::EVENT_BEGIN:
+ etw_type = kTraceEventTypeBegin;
+ break;
+ case TraceLog::EVENT_END:
+ etw_type = kTraceEventTypeEnd;
+ break;
+
+ case TraceLog::EVENT_INSTANT:
+ etw_type = kTraceEventTypeInstant;
+ break;
+
+ default:
+ NOTREACHED() << "Unknown event type";
+ etw_type = kTraceEventTypeInstant;
+ break;
+ }
+
+ EtwMofEvent<5> event(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() & 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 debug
+} // namespace base
diff --git a/base/debug/trace_event_win.h b/base/debug/trace_event_win.h
new file mode 100644
index 0000000..669667b
--- /dev/null
+++ b/base/debug/trace_event_win.h
@@ -0,0 +1,151 @@
+// 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_DEBUG_TRACE_EVENT_WIN_H_
+#define BASE_DEBUG_TRACE_EVENT_WIN_H_
+#pragma once
+
+#include <string>
+#include "base/event_trace_provider_win.h"
+
+#define TRACE_EVENT_BEGIN(name, id, extra) \
+ base::debug::TraceLog::Trace( \
+ name, \
+ base::debug::TraceLog::EVENT_BEGIN, \
+ reinterpret_cast<const void*>(id), \
+ extra);
+
+#define TRACE_EVENT_END(name, id, extra) \
+ base::debug::TraceLog::Trace( \
+ name, \
+ base::debug::TraceLog::EVENT_END, \
+ reinterpret_cast<const void*>(id), \
+ extra);
+
+#define TRACE_EVENT_INSTANT(name, id, extra) \
+ base::debug::TraceLog::Trace( \
+ name, \
+ base::debug::TraceLog::EVENT_INSTANT, \
+ reinterpret_cast<const void*>(id), \
+ extra);
+
+// Fwd.
+template <typename Type>
+struct StaticMemorySingletonTraits;
+
+namespace base {
+namespace debug {
+
+// 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,
+ 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;
+
+} // nemspace debug
+} // namespace base
+
+#endif // BASE_DEBUG_TRACE_EVENT_WIN_H_
diff --git a/base/debug/trace_event_win_unittest.cc b/base/debug/trace_event_win_unittest.cc
new file mode 100644
index 0000000..a818b9b
--- /dev/null
+++ b/base/debug/trace_event_win_unittest.cc
@@ -0,0 +1,308 @@
+// 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/debug/trace_event.h"
+
+#include <strstream>
+
+#include "base/at_exit.h"
+#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/windows_version.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include <initguid.h> // NOLINT - must be last include.
+
+namespace base {
+namespace debug {
+
+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:
+ TraceEventTest() {
+ }
+
+ void SetUp() {
+ bool is_xp = win::GetVersion() < base::win::VERSION_VISTA;
+
+ if (is_xp) {
+ // Tear down any dangling session from an earlier failing test.
+ EtwTraceProperties ignore;
+ EtwTraceController::Stop(kTestSessionName, &ignore);
+ }
+
+ // 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.
+ TraceLog* tracelog = NULL;
+ if (!is_xp) {
+ TraceLog::Resurrect();
+ tracelog = TraceLog::Get();
+ ASSERT_TRUE(tracelog != NULL);
+ ASSERT_FALSE(tracelog->IsTracing());
+ }
+
+ // 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(kChromeTraceProviderName,
+ TRACE_LEVEL_INFORMATION,
+ 0));
+
+ if (is_xp) {
+ TraceLog::Resurrect();
+ tracelog = TraceLog::Get();
+ }
+ ASSERT_TRUE(tracelog != NULL);
+ EXPECT_TRUE(tracelog->IsTracing());
+ }
+
+ 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_.Flush(&prop));
+ EXPECT_HRESULT_SUCCEEDED(controller_.Stop(&prop));
+ ASSERT_HRESULT_SUCCEEDED(
+ consumer_.OpenFileSession(log_file_.value().c_str()));
+
+ ASSERT_HRESULT_SUCCEEDED(consumer_.Consume());
+ }
+
+ private:
+ // We want our singleton torn down after each test.
+ ShadowingAtExitManager at_exit_manager_;
+ 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.
+ TraceLog::Trace(kName,
+ strlen(kName),
+ TraceLog::EVENT_BEGIN,
+ kId,
+ kExtra,
+ strlen(kExtra));
+
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeBegin,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ // Const char* version.
+ TraceLog::Trace(static_cast<const char*>(kName),
+ TraceLog::EVENT_END,
+ kId,
+ static_cast<const char*>(kExtra));
+
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeEnd,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ // std::string extra version.
+ TraceLog::Trace(static_cast<const char*>(kName),
+ TraceLog::EVENT_INSTANT,
+ kId,
+ std::string(kExtra));
+
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeInstant,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+
+ // Test for sanity on NULL inputs.
+ TraceLog::Trace(NULL,
+ 0,
+ TraceLog::EVENT_BEGIN,
+ kId,
+ NULL,
+ 0);
+
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeBegin,
+ kEmpty, 0,
+ kId,
+ kEmpty, 0);
+
+ TraceLog::Trace(NULL,
+ -1,
+ TraceLog::EVENT_END,
+ kId,
+ NULL,
+ -1);
+
+ ExpectEvent(kTraceEventClass32,
+ 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(kTraceEventClass32,
+ kTraceEventTypeBegin,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ TRACE_EVENT_END(kName, kId, kExtra);
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeEnd,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ TRACE_EVENT_INSTANT(kName, kId, kExtra);
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeInstant,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ PlayLog();
+}
+
+} // namespace debug
+} // namespace base