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