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
3 // found in the LICENSE file.
5 #include "base/debug_util.h"
12 #include "base/basictypes.h"
13 #include "base/lock.h"
14 #include "base/logging.h"
15 #include "base/singleton.h"
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
) {
24 DWORD res
= RegOpenKeyEx(root
, subkey
, 0, KEY_READ
, &key
);
25 if (ERROR_SUCCESS
!= res
|| key
== NULL
)
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.
36 *len
= lstrlen(buffer
);
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
,
48 memset(output
, 0, output_len
*sizeof(wchar_t));
49 int input_len
= lstrlen(input
);
51 for (int i
= 0; i
< input_len
; ++i
) {
52 int current_output_len
= lstrlen(output
);
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
)
60 wsprintf(output
+current_output_len
, L
"%d", value
);
63 if (current_output_len
>= output_len
)
65 output
[current_output_len
] = input
[i
];
71 // 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
73 // 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
75 // latency, this code will adversly affect performance.
77 // 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
79 // we are using the Sym* functions. This is because breakpad does now
80 // share a lock with this function. See this related bug:
82 // http://code.google.com/p/google-breakpad/issues/detail?id=311
84 // This is a very unlikely edge case, and the current solution is to
88 static SymbolContext
* Get() {
89 // We use a leaky singleton because code may call this during process
92 Singleton
<SymbolContext
, LeakySingletonTraits
<SymbolContext
> >::get();
95 // Returns the error code of a failed initialization.
96 DWORD
init_error() const {
100 // For the given trace, attempts to resolve the symbols, and output a trace
101 // to the ostream os. The format for each line of the backtrace is:
103 // <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
105 // This function should only be called if Init() has been called. We do not
106 // LOG(FATAL) here because this code is called might be triggered by a
107 // LOG(FATAL) itself.
108 void OutputTraceToStream(const void* const* trace
,
111 AutoLock
lock(lock_
);
113 for (size_t i
= 0; (i
< count
) && os
->good(); ++i
) {
114 const int kMaxNameLength
= 256;
115 DWORD_PTR frame
= reinterpret_cast<DWORD_PTR
>(trace
[i
]);
117 // Code adapted from MSDN example:
118 // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
120 (sizeof(SYMBOL_INFO
) +
121 kMaxNameLength
* sizeof(wchar_t) +
122 sizeof(ULONG64
) - 1) /
124 memset(buffer
, 0, sizeof(buffer
));
126 // Initialize symbol information retrieval structures.
127 DWORD64 sym_displacement
= 0;
128 PSYMBOL_INFO symbol
= reinterpret_cast<PSYMBOL_INFO
>(&buffer
[0]);
129 symbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
130 symbol
->MaxNameLen
= kMaxNameLength
- 1;
131 BOOL has_symbol
= SymFromAddr(GetCurrentProcess(), frame
,
132 &sym_displacement
, symbol
);
134 // Attempt to retrieve line number information.
135 DWORD line_displacement
= 0;
136 IMAGEHLP_LINE64 line
= {};
137 line
.SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
138 BOOL has_line
= SymGetLineFromAddr64(GetCurrentProcess(), frame
,
139 &line_displacement
, &line
);
141 // Output the backtrace line.
144 (*os
) << symbol
->Name
<< " [0x" << trace
[i
] << "+"
145 << sym_displacement
<< "]";
147 // If there is no symbol informtion, add a spacer.
148 (*os
) << "(No symbol) [0x" << trace
[i
] << "]";
151 (*os
) << " (" << line
.FileName
<< ":" << line
.LineNumber
<< ")";
158 friend struct DefaultSingletonTraits
<SymbolContext
>;
160 SymbolContext() : init_error_(ERROR_SUCCESS
) {
161 // Initializes the symbols for the process.
162 // Defer symbol load until they're needed, use undecorated names, and
164 SymSetOptions(SYMOPT_DEFERRED_LOADS
|
167 if (SymInitialize(GetCurrentProcess(), NULL
, TRUE
)) {
168 init_error_
= ERROR_SUCCESS
;
170 init_error_
= GetLastError();
171 // TODO(awong): Handle error: SymInitialize can fail with
172 // ERROR_INVALID_PARAMETER.
173 // When it fails, we should not call debugbreak since it kills the current
174 // process (prevents future tests from running or kills the browser
176 DLOG(ERROR
) << "SymInitialize failed: " << init_error_
;
182 DISALLOW_COPY_AND_ASSIGN(SymbolContext
);
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};
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
);
216 bool DebugUtil::BeingDebugged() {
217 return ::IsDebuggerPresent() != 0;
221 void DebugUtil::BreakDebugger() {
225 StackTrace::StackTrace() {
226 // When walking our own stack, use CaptureStackBackTrace().
227 count_
= CaptureStackBackTrace(0, arraysize(trace_
), trace_
, NULL
);
230 StackTrace::StackTrace(EXCEPTION_POINTERS
* exception_pointers
) {
231 // When walking an exception stack, we need to use StackWalk64().
233 // Initialize stack walking.
234 STACKFRAME64 stack_frame
;
235 memset(&stack_frame
, 0, sizeof(stack_frame
));
237 int machine_type
= IMAGE_FILE_MACHINE_AMD64
;
238 stack_frame
.AddrPC
.Offset
= exception_pointers
->ContextRecord
->Rip
;
239 stack_frame
.AddrFrame
.Offset
= exception_pointers
->ContextRecord
->Rbp
;
240 stack_frame
.AddrStack
.Offset
= exception_pointers
->ContextRecord
->Rsp
;
242 int machine_type
= IMAGE_FILE_MACHINE_I386
;
243 stack_frame
.AddrPC
.Offset
= exception_pointers
->ContextRecord
->Eip
;
244 stack_frame
.AddrFrame
.Offset
= exception_pointers
->ContextRecord
->Ebp
;
245 stack_frame
.AddrStack
.Offset
= exception_pointers
->ContextRecord
->Esp
;
247 stack_frame
.AddrPC
.Mode
= AddrModeFlat
;
248 stack_frame
.AddrFrame
.Mode
= AddrModeFlat
;
249 stack_frame
.AddrStack
.Mode
= AddrModeFlat
;
250 while (StackWalk64(machine_type
,
254 exception_pointers
->ContextRecord
,
256 &SymFunctionTableAccess64
,
259 count_
< arraysize(trace_
)) {
260 trace_
[count_
++] = reinterpret_cast<void*>(stack_frame
.AddrPC
.Offset
);
264 void StackTrace::PrintBacktrace() {
265 OutputToStream(&std::cerr
);
268 void StackTrace::OutputToStream(std::ostream
* os
) {
269 SymbolContext
* context
= SymbolContext::Get();
270 DWORD error
= context
->init_error();
271 if (error
!= ERROR_SUCCESS
) {
272 (*os
) << "Error initializing symbols (" << error
273 << "). Dumping unresolved backtrace:\n";
274 for (int i
= 0; (i
< count_
) && os
->good(); ++i
) {
275 (*os
) << "\t" << trace_
[i
] << "\n";
278 (*os
) << "Backtrace:\n";
279 context
->OutputTraceToStream(trace_
, count_
, os
);