| OLD | NEW |
| 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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" | 5 #include "base/debug/stack_trace.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <dbghelp.h> | 8 #include <dbghelp.h> |
| 9 | 9 |
| 10 #include <iostream> | 10 #include <iostream> |
| 11 | 11 |
| 12 #include "base/basictypes.h" | 12 #include "base/basictypes.h" |
| 13 #include "base/lock.h" | 13 #include "base/lock.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/singleton.h" | 15 #include "base/singleton.h" |
| 16 | 16 |
| 17 namespace base { |
| 18 namespace debug { |
| 19 |
| 17 namespace { | 20 namespace { |
| 18 | 21 |
| 19 // Minimalist key reader. | |
| 20 // Note: Does not use the CRT. | |
| 21 bool RegReadString(HKEY root, const wchar_t* subkey, | |
| 22 const wchar_t* value_name, wchar_t* buffer, int* len) { | |
| 23 HKEY key = NULL; | |
| 24 DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); | |
| 25 if (ERROR_SUCCESS != res || key == NULL) | |
| 26 return false; | |
| 27 | |
| 28 DWORD type = 0; | |
| 29 DWORD buffer_size = *len * sizeof(wchar_t); | |
| 30 // We don't support REG_EXPAND_SZ. | |
| 31 res = RegQueryValueEx(key, value_name, NULL, &type, | |
| 32 reinterpret_cast<BYTE*>(buffer), &buffer_size); | |
| 33 if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) { | |
| 34 // Make sure the buffer is NULL terminated. | |
| 35 buffer[*len - 1] = 0; | |
| 36 *len = lstrlen(buffer); | |
| 37 RegCloseKey(key); | |
| 38 return true; | |
| 39 } | |
| 40 RegCloseKey(key); | |
| 41 return false; | |
| 42 } | |
| 43 | |
| 44 // Replaces each "%ld" in input per a value. Not efficient but it works. | |
| 45 // Note: Does not use the CRT. | |
| 46 bool StringReplace(const wchar_t* input, int value, wchar_t* output, | |
| 47 int output_len) { | |
| 48 memset(output, 0, output_len*sizeof(wchar_t)); | |
| 49 int input_len = lstrlen(input); | |
| 50 | |
| 51 for (int i = 0; i < input_len; ++i) { | |
| 52 int current_output_len = lstrlen(output); | |
| 53 | |
| 54 if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') { | |
| 55 // Make sure we have enough place left. | |
| 56 if ((current_output_len + 12) >= output_len) | |
| 57 return false; | |
| 58 | |
| 59 // Cheap _itow(). | |
| 60 wsprintf(output+current_output_len, L"%d", value); | |
| 61 i += 2; | |
| 62 } else { | |
| 63 if (current_output_len >= output_len) | |
| 64 return false; | |
| 65 output[current_output_len] = input[i]; | |
| 66 } | |
| 67 } | |
| 68 return true; | |
| 69 } | |
| 70 | |
| 71 // SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family | 22 // SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family |
| 72 // of functions. The Sym* family of functions may only be invoked by one | 23 // of functions. The Sym* family of functions may only be invoked by one |
| 73 // thread at a time. SymbolContext code may access a symbol server over the | 24 // thread at a time. SymbolContext code may access a symbol server over the |
| 74 // network while holding the lock for this singleton. In the case of high | 25 // network while holding the lock for this singleton. In the case of high |
| 75 // latency, this code will adversly affect performance. | 26 // latency, this code will adversly affect performance. |
| 76 // | 27 // |
| 77 // There is also a known issue where this backtrace code can interact | 28 // There is also a known issue where this backtrace code can interact |
| 78 // badly with breakpad if breakpad is invoked in a separate thread while | 29 // badly with breakpad if breakpad is invoked in a separate thread while |
| 79 // we are using the Sym* functions. This is because breakpad does now | 30 // we are using the Sym* functions. This is because breakpad does now |
| 80 // share a lock with this function. See this related bug: | 31 // share a lock with this function. See this related bug: |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 177 } | 128 } |
| 178 } | 129 } |
| 179 | 130 |
| 180 DWORD init_error_; | 131 DWORD init_error_; |
| 181 Lock lock_; | 132 Lock lock_; |
| 182 DISALLOW_COPY_AND_ASSIGN(SymbolContext); | 133 DISALLOW_COPY_AND_ASSIGN(SymbolContext); |
| 183 }; | 134 }; |
| 184 | 135 |
| 185 } // namespace | 136 } // namespace |
| 186 | 137 |
| 187 // Note: Does not use the CRT. | |
| 188 bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) { | |
| 189 wchar_t reg_value[1026]; | |
| 190 int len = arraysize(reg_value); | |
| 191 if (RegReadString(HKEY_LOCAL_MACHINE, | |
| 192 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", | |
| 193 L"Debugger", reg_value, &len)) { | |
| 194 wchar_t command_line[1026]; | |
| 195 if (StringReplace(reg_value, process_id, command_line, | |
| 196 arraysize(command_line))) { | |
| 197 // We don't mind if the debugger is present because it will simply fail | |
| 198 // to attach to this process. | |
| 199 STARTUPINFO startup_info = {0}; | |
| 200 startup_info.cb = sizeof(startup_info); | |
| 201 PROCESS_INFORMATION process_info = {0}; | |
| 202 | |
| 203 if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, | |
| 204 &startup_info, &process_info)) { | |
| 205 CloseHandle(process_info.hThread); | |
| 206 WaitForInputIdle(process_info.hProcess, 10000); | |
| 207 CloseHandle(process_info.hProcess); | |
| 208 return true; | |
| 209 } | |
| 210 } | |
| 211 } | |
| 212 return false; | |
| 213 } | |
| 214 | |
| 215 // static | |
| 216 bool DebugUtil::BeingDebugged() { | |
| 217 return ::IsDebuggerPresent() != 0; | |
| 218 } | |
| 219 | |
| 220 // static | |
| 221 void DebugUtil::BreakDebugger() { | |
| 222 if (suppress_dialogs_) | |
| 223 _exit(1); | |
| 224 | |
| 225 __debugbreak(); | |
| 226 } | |
| 227 | |
| 228 StackTrace::StackTrace() { | 138 StackTrace::StackTrace() { |
| 229 // When walking our own stack, use CaptureStackBackTrace(). | 139 // When walking our own stack, use CaptureStackBackTrace(). |
| 230 count_ = CaptureStackBackTrace(0, arraysize(trace_), trace_, NULL); | 140 count_ = CaptureStackBackTrace(0, arraysize(trace_), trace_, NULL); |
| 231 } | 141 } |
| 232 | 142 |
| 233 StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) { | 143 StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) { |
| 234 // When walking an exception stack, we need to use StackWalk64(). | 144 // When walking an exception stack, we need to use StackWalk64(). |
| 235 count_ = 0; | 145 count_ = 0; |
| 236 // Initialize stack walking. | 146 // Initialize stack walking. |
| 237 STACKFRAME64 stack_frame; | 147 STACKFRAME64 stack_frame; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 275 (*os) << "Error initializing symbols (" << error | 185 (*os) << "Error initializing symbols (" << error |
| 276 << "). Dumping unresolved backtrace:\n"; | 186 << "). Dumping unresolved backtrace:\n"; |
| 277 for (int i = 0; (i < count_) && os->good(); ++i) { | 187 for (int i = 0; (i < count_) && os->good(); ++i) { |
| 278 (*os) << "\t" << trace_[i] << "\n"; | 188 (*os) << "\t" << trace_[i] << "\n"; |
| 279 } | 189 } |
| 280 } else { | 190 } else { |
| 281 (*os) << "Backtrace:\n"; | 191 (*os) << "Backtrace:\n"; |
| 282 context->OutputTraceToStream(trace_, count_, os); | 192 context->OutputTraceToStream(trace_, count_, os); |
| 283 } | 193 } |
| 284 } | 194 } |
| 195 |
| 196 } // namespace debug |
| 197 } // namespace base |
| OLD | NEW |