Home | History | Annotate | Download | only in debug
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/debug/stack_trace.h"
      6 
      7 #include <windows.h>
      8 #include <dbghelp.h>
      9 
     10 #include <iostream>
     11 
     12 #include "base/basictypes.h"
     13 #include "base/logging.h"
     14 #include "base/memory/singleton.h"
     15 #include "base/synchronization/lock.h"
     16 
     17 namespace base {
     18 namespace debug {
     19 
     20 namespace {
     21 
     22 // SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family
     23 // of functions.  The Sym* family of functions may only be invoked by one
     24 // thread at a time.  SymbolContext code may access a symbol server over the
     25 // network while holding the lock for this singleton.  In the case of high
     26 // latency, this code will adversly affect performance.
     27 //
     28 // There is also a known issue where this backtrace code can interact
     29 // badly with breakpad if breakpad is invoked in a separate thread while
     30 // we are using the Sym* functions.  This is because breakpad does now
     31 // share a lock with this function.  See this related bug:
     32 //
     33 //   http://code.google.com/p/google-breakpad/issues/detail?id=311
     34 //
     35 // This is a very unlikely edge case, and the current solution is to
     36 // just ignore it.
     37 class SymbolContext {
     38  public:
     39   static SymbolContext* GetInstance() {
     40     // We use a leaky singleton because code may call this during process
     41     // termination.
     42     return
     43       Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get();
     44   }
     45 
     46   // Returns the error code of a failed initialization.
     47   DWORD init_error() const {
     48     return init_error_;
     49   }
     50 
     51   // For the given trace, attempts to resolve the symbols, and output a trace
     52   // to the ostream os.  The format for each line of the backtrace is:
     53   //
     54   //    <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
     55   //
     56   // This function should only be called if Init() has been called.  We do not
     57   // LOG(FATAL) here because this code is called might be triggered by a
     58   // LOG(FATAL) itself.
     59   void OutputTraceToStream(const void* const* trace,
     60                            int count,
     61                            std::ostream* os) {
     62     base::AutoLock lock(lock_);
     63 
     64     for (size_t i = 0; (i < count) && os->good(); ++i) {
     65       const int kMaxNameLength = 256;
     66       DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]);
     67 
     68       // Code adapted from MSDN example:
     69       // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
     70       ULONG64 buffer[
     71         (sizeof(SYMBOL_INFO) +
     72           kMaxNameLength * sizeof(wchar_t) +
     73           sizeof(ULONG64) - 1) /
     74         sizeof(ULONG64)];
     75       memset(buffer, 0, sizeof(buffer));
     76 
     77       // Initialize symbol information retrieval structures.
     78       DWORD64 sym_displacement = 0;
     79       PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
     80       symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
     81       symbol->MaxNameLen = kMaxNameLength - 1;
     82       BOOL has_symbol = SymFromAddr(GetCurrentProcess(), frame,
     83                                     &sym_displacement, symbol);
     84 
     85       // Attempt to retrieve line number information.
     86       DWORD line_displacement = 0;
     87       IMAGEHLP_LINE64 line = {};
     88       line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
     89       BOOL has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame,
     90                                            &line_displacement, &line);
     91 
     92       // Output the backtrace line.
     93       (*os) << "\t";
     94       if (has_symbol) {
     95         (*os) << symbol->Name << " [0x" << trace[i] << "+"
     96               << sym_displacement << "]";
     97       } else {
     98         // If there is no symbol informtion, add a spacer.
     99         (*os) << "(No symbol) [0x" << trace[i] << "]";
    100       }
    101       if (has_line) {
    102         (*os) << " (" << line.FileName << ":" << line.LineNumber << ")";
    103       }
    104       (*os) << "\n";
    105     }
    106   }
    107 
    108  private:
    109   friend struct DefaultSingletonTraits<SymbolContext>;
    110 
    111   SymbolContext() : init_error_(ERROR_SUCCESS) {
    112     // Initializes the symbols for the process.
    113     // Defer symbol load until they're needed, use undecorated names, and
    114     // get line numbers.
    115     SymSetOptions(SYMOPT_DEFERRED_LOADS |
    116                   SYMOPT_UNDNAME |
    117                   SYMOPT_LOAD_LINES);
    118     if (SymInitialize(GetCurrentProcess(), NULL, TRUE)) {
    119       init_error_ = ERROR_SUCCESS;
    120     } else {
    121       init_error_ = GetLastError();
    122       // TODO(awong): Handle error: SymInitialize can fail with
    123       // ERROR_INVALID_PARAMETER.
    124       // When it fails, we should not call debugbreak since it kills the current
    125       // process (prevents future tests from running or kills the browser
    126       // process).
    127       DLOG(ERROR) << "SymInitialize failed: " << init_error_;
    128     }
    129   }
    130 
    131   DWORD init_error_;
    132   base::Lock lock_;
    133   DISALLOW_COPY_AND_ASSIGN(SymbolContext);
    134 };
    135 
    136 }  // namespace
    137 
    138 StackTrace::StackTrace() {
    139   // When walking our own stack, use CaptureStackBackTrace().
    140   count_ = CaptureStackBackTrace(0, arraysize(trace_), trace_, NULL);
    141 }
    142 
    143 StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
    144   // When walking an exception stack, we need to use StackWalk64().
    145   count_ = 0;
    146   // Initialize stack walking.
    147   STACKFRAME64 stack_frame;
    148   memset(&stack_frame, 0, sizeof(stack_frame));
    149 #if defined(_WIN64)
    150   int machine_type = IMAGE_FILE_MACHINE_AMD64;
    151   stack_frame.AddrPC.Offset = exception_pointers->ContextRecord->Rip;
    152   stack_frame.AddrFrame.Offset = exception_pointers->ContextRecord->Rbp;
    153   stack_frame.AddrStack.Offset = exception_pointers->ContextRecord->Rsp;
    154 #else
    155   int machine_type = IMAGE_FILE_MACHINE_I386;
    156   stack_frame.AddrPC.Offset = exception_pointers->ContextRecord->Eip;
    157   stack_frame.AddrFrame.Offset = exception_pointers->ContextRecord->Ebp;
    158   stack_frame.AddrStack.Offset = exception_pointers->ContextRecord->Esp;
    159 #endif
    160   stack_frame.AddrPC.Mode = AddrModeFlat;
    161   stack_frame.AddrFrame.Mode = AddrModeFlat;
    162   stack_frame.AddrStack.Mode = AddrModeFlat;
    163   while (StackWalk64(machine_type,
    164                      GetCurrentProcess(),
    165                      GetCurrentThread(),
    166                      &stack_frame,
    167                      exception_pointers->ContextRecord,
    168                      NULL,
    169                      &SymFunctionTableAccess64,
    170                      &SymGetModuleBase64,
    171                      NULL) &&
    172          count_ < arraysize(trace_)) {
    173     trace_[count_++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
    174   }
    175 }
    176 
    177 void StackTrace::PrintBacktrace() const {
    178   OutputToStream(&std::cerr);
    179 }
    180 
    181 void StackTrace::OutputToStream(std::ostream* os) const {
    182   SymbolContext* context = SymbolContext::GetInstance();
    183   DWORD error = context->init_error();
    184   if (error != ERROR_SUCCESS) {
    185     (*os) << "Error initializing symbols (" << error
    186           << ").  Dumping unresolved backtrace:\n";
    187     for (int i = 0; (i < count_) && os->good(); ++i) {
    188       (*os) << "\t" << trace_[i] << "\n";
    189     }
    190   } else {
    191     (*os) << "Backtrace:\n";
    192     context->OutputTraceToStream(trace_, count_, os);
    193   }
    194 }
    195 
    196 }  // namespace debug
    197 }  // namespace base
    198