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 |