summaryrefslogtreecommitdiffstats
path: root/base/debug_util_win.cc
diff options
context:
space:
mode:
authorajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-24 00:13:08 +0000
committerajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-24 00:13:08 +0000
commit96fd00320f4eef1cc52f68939f86b319f6d2095f (patch)
tree8fd31ba95099df9a246e95792162e2ab812128d6 /base/debug_util_win.cc
parent719ceedfb713882fb1308fc99cac13962d5c8fb8 (diff)
downloadchromium_src-96fd00320f4eef1cc52f68939f86b319f6d2095f.zip
chromium_src-96fd00320f4eef1cc52f68939f86b319f6d2095f.tar.gz
chromium_src-96fd00320f4eef1cc52f68939f86b319f6d2095f.tar.bz2
Print backtraces on FATAL log messages in debug mode.
This provides basic support for looking up backtrace information on GNU libc systems and in Windows. The code is only enabled for FATAL log messages in debug mode. In a release build, it is unlikely that symbols will be available making the backtrace less useful. Review URL: http://codereview.chromium.org/62140 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14391 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/debug_util_win.cc')
-rw-r--r--base/debug_util_win.cc190
1 files changed, 184 insertions, 6 deletions
diff --git a/base/debug_util_win.cc b/base/debug_util_win.cc
index a41f1b3..5201283 100644
--- a/base/debug_util_win.cc
+++ b/base/debug_util_win.cc
@@ -1,12 +1,16 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-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/debug_util.h"
+
#include <windows.h>
+#include <dbghelp.h>
#include "base/basictypes.h"
-#include "base/debug_util.h"
-#include "logging.h"
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/singleton.h"
namespace {
@@ -62,6 +66,151 @@ bool StringReplace(const wchar_t* input, int value, wchar_t* output,
return true;
}
+// 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();
+ }
+
+ // Initializes the symbols for the process if it hasn't been done yet.
+ // Subsequent calls will not reinitialize the symbol, but instead return
+ // the error code from the first call.
+ bool Init() {
+ AutoLock lock(lock_);
+ if (!initialized_) {
+ process_ = GetCurrentProcess();
+
+ // 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(process_, NULL, TRUE)) {
+ init_error_ = ERROR_SUCCESS;
+ } else {
+ init_error_ = GetLastError();
+ }
+ }
+
+ initialized_ = true;
+ return init_error_ == ERROR_SUCCESS;
+ }
+
+ // Returns the error code of a failed initialization. This 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. Instead,
+ // we log an ERROR, and return ERROR_INVALID_DATA.
+ DWORD init_error() {
+ if (!initialized_) {
+ LOG(ERROR) << "Calling GetInitError() before Init() was called. "
+ << "Returning ERROR_INVALID_DATA.";
+ return ERROR_INVALID_DATA;
+ }
+
+ return init_error_;
+ }
+
+ // Returns the process this was initialized for. This should only be
+ // called if Init() has been called. We LOG(ERROR) in this situation.
+ // LOG(FATAL) is not used because this code is might be triggered
+ // by a LOG(FATAL) itself.
+ HANDLE process() {
+ if (!initialized_) {
+ LOG(ERROR) << "Calling process() before Init() was called. "
+ << "Returning NULL.";
+ return NULL;
+ }
+
+ return process_;
+ }
+
+ // 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 std::vector<void*>& trace, std::ostream* os) {
+ AutoLock lock(lock_);
+
+ for (size_t i = 0; (i < trace.size()) && 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)];
+
+ // 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;
+ BOOL has_symbol = SymFromAddr(process(), 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(process(), 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";
+ }
+ }
+
+ SymbolContext()
+ : initialized_(false),
+ process_(NULL),
+ init_error_(ERROR_SUCCESS) {
+ }
+
+ private:
+ Lock lock_;
+ bool initialized_;
+ HANDLE process_;
+ DWORD init_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(SymbolContext);
+};
+
} // namespace
// Note: Does not use the CRT.
@@ -102,10 +251,39 @@ void DebugUtil::BreakDebugger() {
__debugbreak();
}
-// TODO(port): not implemented on Windows
StackTrace::StackTrace() {
+ // From http://msdn.microsoft.com/en-us/library/bb204633(VS.85).aspx,
+ // the sum of FramesToSkip and FramesToCapture must be less than 63,
+ // so set it to 62.
+ const int kMaxCallers = 62;
+
+ void* callers[kMaxCallers];
+ // TODO(ajwong): Migrate this to StackWalk64.
+ int count = CaptureStackBackTrace(0, kMaxCallers, callers, NULL);
+ if (count > 0) {
+ trace_.resize(count);
+ memcpy(&trace_[0], callers, sizeof(callers[0]) * count);
+ } else {
+ trace_.resize(0);
+ }
}
-void PrintBacktrace() {
- NOTIMPLEMENTED();
+void StackTrace::PrintBacktrace() {
+ OutputToStream(&std::cerr);
+}
+
+void StackTrace::OutputToStream(std::ostream* os) {
+ SymbolContext* context = SymbolContext::Get();
+
+ if (context->Init() != ERROR_SUCCESS) {
+ DWORD error = context->init_error();
+ (*os) << "Error initializing symbols (" << error
+ << "). Dumping unresolved backtrace:\n";
+ for (size_t i = 0; (i < trace_.size()) && os->good(); ++i) {
+ (*os) << "\t" << trace_[i] << "\n";
+ }
+ } else {
+ (*os) << "Backtrace:\n";
+ context->OutputTraceToStream(trace_, os);
+ }
}