OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include "base/debug_util.h" |
| 6 |
5 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <dbghelp.h> |
6 | 9 |
7 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
8 #include "base/debug_util.h" | 11 #include "base/lock.h" |
9 #include "logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/singleton.h" |
10 | 14 |
11 namespace { | 15 namespace { |
12 | 16 |
13 // Minimalist key reader. | 17 // Minimalist key reader. |
14 // Note: Does not use the CRT. | 18 // Note: Does not use the CRT. |
15 bool RegReadString(HKEY root, const wchar_t* subkey, | 19 bool RegReadString(HKEY root, const wchar_t* subkey, |
16 const wchar_t* value_name, wchar_t* buffer, int* len) { | 20 const wchar_t* value_name, wchar_t* buffer, int* len) { |
17 HKEY key = NULL; | 21 HKEY key = NULL; |
18 DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); | 22 DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); |
19 if (ERROR_SUCCESS != res || key == NULL) | 23 if (ERROR_SUCCESS != res || key == NULL) |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
55 i += 2; | 59 i += 2; |
56 } else { | 60 } else { |
57 if (current_output_len >= output_len) | 61 if (current_output_len >= output_len) |
58 return false; | 62 return false; |
59 output[current_output_len] = input[i]; | 63 output[current_output_len] = input[i]; |
60 } | 64 } |
61 } | 65 } |
62 return true; | 66 return true; |
63 } | 67 } |
64 | 68 |
| 69 // SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family |
| 70 // of functions. The Sym* family of functions may only be invoked by one |
| 71 // thread at a time. SymbolContext code may access a symbol server over the |
| 72 // network while holding the lock for this singleton. In the case of high |
| 73 // latency, this code will adversly affect performance. |
| 74 // |
| 75 // There is also a known issue where this backtrace code can interact |
| 76 // badly with breakpad if breakpad is invoked in a separate thread while |
| 77 // we are using the Sym* functions. This is because breakpad does now |
| 78 // share a lock with this function. See this related bug: |
| 79 // |
| 80 // http://code.google.com/p/google-breakpad/issues/detail?id=311 |
| 81 // |
| 82 // This is a very unlikely edge case, and the current solution is to |
| 83 // just ignore it. |
| 84 class SymbolContext { |
| 85 public: |
| 86 static SymbolContext* Get() { |
| 87 // We use a leaky singleton because code may call this during process |
| 88 // termination. |
| 89 return |
| 90 Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get(); |
| 91 } |
| 92 |
| 93 // Initializes the symbols for the process if it hasn't been done yet. |
| 94 // Subsequent calls will not reinitialize the symbol, but instead return |
| 95 // the error code from the first call. |
| 96 bool Init() { |
| 97 AutoLock lock(lock_); |
| 98 if (!initialized_) { |
| 99 process_ = GetCurrentProcess(); |
| 100 |
| 101 // Defer symbol load until they're needed, use undecorated names, and |
| 102 // get line numbers. |
| 103 SymSetOptions(SYMOPT_DEFERRED_LOADS | |
| 104 SYMOPT_UNDNAME | |
| 105 SYMOPT_LOAD_LINES); |
| 106 if (SymInitialize(process_, NULL, TRUE)) { |
| 107 init_error_ = ERROR_SUCCESS; |
| 108 } else { |
| 109 init_error_ = GetLastError(); |
| 110 } |
| 111 } |
| 112 |
| 113 initialized_ = true; |
| 114 return init_error_ == ERROR_SUCCESS; |
| 115 } |
| 116 |
| 117 // Returns the error code of a failed initialization. This should only be |
| 118 // called if Init() has been called. We do not LOG(FATAL) here because |
| 119 // this code is called might be triggered by a LOG(FATAL) itself. Instead, |
| 120 // we log an ERROR, and return ERROR_INVALID_DATA. |
| 121 DWORD init_error() { |
| 122 if (!initialized_) { |
| 123 LOG(ERROR) << "Calling GetInitError() before Init() was called. " |
| 124 << "Returning ERROR_INVALID_DATA."; |
| 125 return ERROR_INVALID_DATA; |
| 126 } |
| 127 |
| 128 return init_error_; |
| 129 } |
| 130 |
| 131 // Returns the process this was initialized for. This should only be |
| 132 // called if Init() has been called. We LOG(ERROR) in this situation. |
| 133 // LOG(FATAL) is not used because this code is might be triggered |
| 134 // by a LOG(FATAL) itself. |
| 135 HANDLE process() { |
| 136 if (!initialized_) { |
| 137 LOG(ERROR) << "Calling process() before Init() was called. " |
| 138 << "Returning NULL."; |
| 139 return NULL; |
| 140 } |
| 141 |
| 142 return process_; |
| 143 } |
| 144 |
| 145 // For the given trace, attempts to resolve the symbols, and output a trace |
| 146 // to the ostream os. The format for each line of the backtrace is: |
| 147 // |
| 148 // <tab>SymbolName[0xAddress+Offset] (FileName:LineNo) |
| 149 // |
| 150 // This function should only be called if Init() has been called. We do not |
| 151 // LOG(FATAL) here because this code is called might be triggered by a |
| 152 // LOG(FATAL) itself. |
| 153 void OutputTraceToStream(const std::vector<void*>& trace, std::ostream* os) { |
| 154 AutoLock lock(lock_); |
| 155 |
| 156 for (size_t i = 0; (i < trace.size()) && os->good(); ++i) { |
| 157 const int kMaxNameLength = 256; |
| 158 DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]); |
| 159 |
| 160 // Code adapted from MSDN example: |
| 161 // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx |
| 162 ULONG64 buffer[ |
| 163 (sizeof(SYMBOL_INFO) + |
| 164 kMaxNameLength * sizeof(wchar_t) + |
| 165 sizeof(ULONG64) - 1) / |
| 166 sizeof(ULONG64)]; |
| 167 |
| 168 // Initialize symbol information retrieval structures. |
| 169 DWORD64 sym_displacement = 0; |
| 170 PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]); |
| 171 symbol->SizeOfStruct = sizeof(SYMBOL_INFO); |
| 172 symbol->MaxNameLen = kMaxNameLength; |
| 173 BOOL has_symbol = SymFromAddr(process(), frame, |
| 174 &sym_displacement, symbol); |
| 175 |
| 176 // Attempt to retrieve line number information. |
| 177 DWORD line_displacement = 0; |
| 178 IMAGEHLP_LINE64 line = {}; |
| 179 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); |
| 180 BOOL has_line = SymGetLineFromAddr64(process(), frame, |
| 181 &line_displacement, &line); |
| 182 |
| 183 // Output the backtrace line. |
| 184 (*os) << "\t"; |
| 185 if (has_symbol) { |
| 186 (*os) << symbol->Name << " [0x" << trace[i] << "+" |
| 187 << sym_displacement << "]"; |
| 188 } else { |
| 189 // If there is no symbol informtion, add a spacer. |
| 190 (*os) << "(No symbol) [0x" << trace[i] << "]"; |
| 191 } |
| 192 if (has_line) { |
| 193 (*os) << " (" << line.FileName << ":" << line.LineNumber << ")"; |
| 194 } |
| 195 (*os) << "\n"; |
| 196 } |
| 197 } |
| 198 |
| 199 SymbolContext() |
| 200 : initialized_(false), |
| 201 process_(NULL), |
| 202 init_error_(ERROR_SUCCESS) { |
| 203 } |
| 204 |
| 205 private: |
| 206 Lock lock_; |
| 207 bool initialized_; |
| 208 HANDLE process_; |
| 209 DWORD init_error_; |
| 210 |
| 211 DISALLOW_COPY_AND_ASSIGN(SymbolContext); |
| 212 }; |
| 213 |
65 } // namespace | 214 } // namespace |
66 | 215 |
67 // Note: Does not use the CRT. | 216 // Note: Does not use the CRT. |
68 bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) { | 217 bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) { |
69 wchar_t reg_value[1026]; | 218 wchar_t reg_value[1026]; |
70 int len = arraysize(reg_value); | 219 int len = arraysize(reg_value); |
71 if (RegReadString(HKEY_LOCAL_MACHINE, | 220 if (RegReadString(HKEY_LOCAL_MACHINE, |
72 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", | 221 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", |
73 L"Debugger", reg_value, &len)) { | 222 L"Debugger", reg_value, &len)) { |
74 wchar_t command_line[1026]; | 223 wchar_t command_line[1026]; |
(...skipping 20 matching lines...) Expand all Loading... |
95 // static | 244 // static |
96 bool DebugUtil::BeingDebugged() { | 245 bool DebugUtil::BeingDebugged() { |
97 return ::IsDebuggerPresent() != 0; | 246 return ::IsDebuggerPresent() != 0; |
98 } | 247 } |
99 | 248 |
100 // static | 249 // static |
101 void DebugUtil::BreakDebugger() { | 250 void DebugUtil::BreakDebugger() { |
102 __debugbreak(); | 251 __debugbreak(); |
103 } | 252 } |
104 | 253 |
105 // TODO(port): not implemented on Windows | |
106 StackTrace::StackTrace() { | 254 StackTrace::StackTrace() { |
| 255 // From http://msdn.microsoft.com/en-us/library/bb204633(VS.85).aspx, |
| 256 // the sum of FramesToSkip and FramesToCapture must be less than 63, |
| 257 // so set it to 62. |
| 258 const int kMaxCallers = 62; |
| 259 |
| 260 void* callers[kMaxCallers]; |
| 261 // TODO(ajwong): Migrate this to StackWalk64. |
| 262 int count = CaptureStackBackTrace(0, kMaxCallers, callers, NULL); |
| 263 if (count > 0) { |
| 264 trace_.resize(count); |
| 265 memcpy(&trace_[0], callers, sizeof(callers[0]) * count); |
| 266 } else { |
| 267 trace_.resize(0); |
| 268 } |
107 } | 269 } |
108 | 270 |
109 void PrintBacktrace() { | 271 void StackTrace::PrintBacktrace() { |
110 NOTIMPLEMENTED(); | 272 OutputToStream(&std::cerr); |
111 } | 273 } |
| 274 |
| 275 void StackTrace::OutputToStream(std::ostream* os) { |
| 276 SymbolContext* context = SymbolContext::Get(); |
| 277 |
| 278 if (context->Init() != ERROR_SUCCESS) { |
| 279 DWORD error = context->init_error(); |
| 280 (*os) << "Error initializing symbols (" << error |
| 281 << "). Dumping unresolved backtrace:\n"; |
| 282 for (size_t i = 0; (i < trace_.size()) && os->good(); ++i) { |
| 283 (*os) << "\t" << trace_[i] << "\n"; |
| 284 } |
| 285 } else { |
| 286 (*os) << "Backtrace:\n"; |
| 287 context->OutputTraceToStream(trace_, os); |
| 288 } |
| 289 } |
OLD | NEW |