]>
Commit | Line | Data |
---|---|---|
7beafee9 | 1 | ///////////////////////////////////////////////////////////////////////////// |
80fdcdb9 | 2 | // Name: src/msw/stackwalk.cpp |
7beafee9 VZ |
3 | // Purpose: wxStackWalker implementation for Win32 |
4 | // Author: Vadim Zeitlin | |
7dbb4353 | 5 | // Modified by: Artur Bac 2010-10-01 AMD64 Port |
7beafee9 | 6 | // Created: 2005-01-08 |
7beafee9 VZ |
7 | // Copyright: (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org> |
8 | // Licence: wxWindows licence | |
9 | ///////////////////////////////////////////////////////////////////////////// | |
10 | ||
11 | // ============================================================================ | |
12 | // declarations | |
13 | // ============================================================================ | |
14 | ||
15 | // ---------------------------------------------------------------------------- | |
16 | // headers | |
17 | // ---------------------------------------------------------------------------- | |
18 | ||
19 | #include "wx/wxprec.h" | |
20 | ||
21 | #ifdef __BORLANDC__ | |
22 | #pragma hdrstop | |
23 | #endif | |
24 | ||
25 | #if wxUSE_STACKWALKER | |
26 | ||
60431236 WS |
27 | #ifndef WX_PRECOMP |
28 | #include "wx/string.h" | |
29 | #endif | |
30 | ||
7beafee9 VZ |
31 | #include "wx/stackwalk.h" |
32 | ||
33 | #include "wx/msw/debughlp.h" | |
34 | ||
35 | #if wxUSE_DBGHELP | |
36 | ||
37 | // ============================================================================ | |
38 | // implementation | |
39 | // ============================================================================ | |
40 | ||
41 | // ---------------------------------------------------------------------------- | |
42 | // wxStackFrame | |
43 | // ---------------------------------------------------------------------------- | |
44 | ||
45 | void wxStackFrame::OnGetName() | |
46 | { | |
47 | if ( m_hasName ) | |
48 | return; | |
49 | ||
50 | m_hasName = true; | |
51 | ||
52 | // get the name of the function for this stack frame entry | |
53 | static const size_t MAX_NAME_LEN = 1024; | |
7dbb4353 | 54 | BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_NAME_LEN]; |
7beafee9 VZ |
55 | wxZeroMemory(symbolBuffer); |
56 | ||
7dbb4353 VZ |
57 | PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer; |
58 | pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); | |
7beafee9 VZ |
59 | pSymbol->MaxNameLen = MAX_NAME_LEN; |
60 | ||
61 | DWORD64 symDisplacement = 0; | |
7dbb4353 | 62 | if ( !wxDbgHelpDLL::SymFromAddr |
7beafee9 VZ |
63 | ( |
64 | ::GetCurrentProcess(), | |
65 | GetSymAddr(), | |
66 | &symDisplacement, | |
67 | pSymbol | |
68 | ) ) | |
69 | { | |
7dbb4353 | 70 | wxDbgHelpDLL::LogError(wxT("SymFromAddr")); |
7beafee9 VZ |
71 | return; |
72 | } | |
7dbb4353 VZ |
73 | |
74 | m_name = wxString::FromAscii(pSymbol->Name); | |
7beafee9 VZ |
75 | m_offset = symDisplacement; |
76 | } | |
77 | ||
78 | void wxStackFrame::OnGetLocation() | |
79 | { | |
80 | if ( m_hasLocation ) | |
81 | return; | |
82 | ||
83 | m_hasLocation = true; | |
84 | ||
85 | // get the source line for this stack frame entry | |
7dbb4353 | 86 | IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) }; |
7beafee9 | 87 | DWORD dwLineDisplacement; |
7dbb4353 | 88 | if ( !wxDbgHelpDLL::SymGetLineFromAddr |
7beafee9 VZ |
89 | ( |
90 | ::GetCurrentProcess(), | |
91 | GetSymAddr(), | |
92 | &dwLineDisplacement, | |
93 | &lineInfo | |
94 | ) ) | |
95 | { | |
96 | // it is normal that we don't have source info for some symbols, | |
97 | // notably all the ones from the system DLLs... | |
7dbb4353 | 98 | //wxDbgHelpDLL::LogError(wxT("SymGetLineFromAddr")); |
7beafee9 VZ |
99 | return; |
100 | } | |
7dbb4353 VZ |
101 | |
102 | m_filename = wxString::FromAscii(lineInfo.FileName); | |
7beafee9 VZ |
103 | m_line = lineInfo.LineNumber; |
104 | } | |
105 | ||
106 | bool | |
107 | wxStackFrame::GetParam(size_t n, | |
108 | wxString *type, | |
109 | wxString *name, | |
110 | wxString *value) const | |
111 | { | |
112 | if ( !DoGetParamCount() ) | |
113 | ConstCast()->OnGetParam(); | |
114 | ||
115 | if ( n >= DoGetParamCount() ) | |
116 | return false; | |
117 | ||
118 | if ( type ) | |
119 | *type = m_paramTypes[n]; | |
120 | if ( name ) | |
121 | *name = m_paramNames[n]; | |
122 | if ( value ) | |
123 | *value = m_paramValues[n]; | |
124 | ||
125 | return true; | |
126 | } | |
127 | ||
7dbb4353 | 128 | void wxStackFrame::OnParam(PSYMBOL_INFO pSymInfo) |
7beafee9 | 129 | { |
f31a4098 | 130 | m_paramTypes.Add(wxEmptyString); |
7beafee9 | 131 | |
7dbb4353 | 132 | m_paramNames.Add(wxString::FromAscii(pSymInfo->Name)); |
7beafee9 VZ |
133 | |
134 | // if symbol information is corrupted and we crash, the exception is going | |
135 | // to be ignored when we're called from WalkFromException() because of the | |
3103e8a9 JS |
136 | // exception handler there returning EXCEPTION_CONTINUE_EXECUTION, but we'd |
137 | // be left in an inconsistent state, so deal with it explicitly here (even | |
138 | // if normally we should never crash, of course...) | |
7beafee9 VZ |
139 | #ifdef _CPPUNWIND |
140 | try | |
141 | #else | |
142 | __try | |
143 | #endif | |
144 | { | |
145 | // as it is a parameter (and not a global var), it is always offset by | |
146 | // the frame address | |
147 | DWORD_PTR pValue = m_addrFrame + pSymInfo->Address; | |
148 | m_paramValues.Add(wxDbgHelpDLL::DumpSymbol(pSymInfo, (void *)pValue)); | |
149 | } | |
150 | #ifdef _CPPUNWIND | |
151 | catch ( ... ) | |
152 | #else | |
153 | __except ( EXCEPTION_EXECUTE_HANDLER ) | |
154 | #endif | |
155 | { | |
f31a4098 | 156 | m_paramValues.Add(wxEmptyString); |
7beafee9 VZ |
157 | } |
158 | } | |
159 | ||
160 | BOOL CALLBACK | |
7dbb4353 | 161 | EnumSymbolsProc(PSYMBOL_INFO pSymInfo, ULONG WXUNUSED(SymSize), PVOID data) |
7beafee9 | 162 | { |
5c33522f | 163 | wxStackFrame *frame = static_cast<wxStackFrame *>(data); |
7beafee9 VZ |
164 | |
165 | // we're only interested in parameters | |
166 | if ( pSymInfo->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER ) | |
167 | { | |
168 | frame->OnParam(pSymInfo); | |
169 | } | |
60431236 | 170 | |
7beafee9 VZ |
171 | // return true to continue enumeration, false would have stopped it |
172 | return TRUE; | |
173 | } | |
174 | ||
175 | void wxStackFrame::OnGetParam() | |
176 | { | |
177 | // use SymSetContext to get just the locals/params for this frame | |
178 | IMAGEHLP_STACK_FRAME imagehlpStackFrame; | |
179 | wxZeroMemory(imagehlpStackFrame); | |
180 | imagehlpStackFrame.InstructionOffset = GetSymAddr(); | |
181 | if ( !wxDbgHelpDLL::SymSetContext | |
182 | ( | |
183 | ::GetCurrentProcess(), | |
184 | &imagehlpStackFrame, | |
185 | 0 // unused | |
186 | ) ) | |
187 | { | |
188 | // for symbols from kernel DLL we might not have access to their | |
189 | // address, this is not a real error | |
190 | if ( ::GetLastError() != ERROR_INVALID_ADDRESS ) | |
191 | { | |
9a83f860 | 192 | wxDbgHelpDLL::LogError(wxT("SymSetContext")); |
7beafee9 VZ |
193 | } |
194 | ||
195 | return; | |
196 | } | |
197 | ||
7dbb4353 | 198 | if ( !wxDbgHelpDLL::SymEnumSymbols |
7beafee9 VZ |
199 | ( |
200 | ::GetCurrentProcess(), | |
201 | NULL, // DLL base: use current context | |
202 | NULL, // no mask, get all symbols | |
203 | EnumSymbolsProc, // callback | |
204 | this // data to pass to it | |
205 | ) ) | |
206 | { | |
7dbb4353 | 207 | wxDbgHelpDLL::LogError(wxT("SymEnumSymbols")); |
7beafee9 VZ |
208 | } |
209 | } | |
210 | ||
211 | ||
212 | // ---------------------------------------------------------------------------- | |
213 | // wxStackWalker | |
214 | // ---------------------------------------------------------------------------- | |
215 | ||
ef81fe8b | 216 | void wxStackWalker::WalkFrom(const CONTEXT *pCtx, size_t skip, size_t maxDepth) |
7beafee9 VZ |
217 | { |
218 | if ( !wxDbgHelpDLL::Init() ) | |
219 | { | |
b3a6d269 VZ |
220 | // don't log a user-visible error message here because the stack trace |
221 | // is only needed for debugging/diagnostics anyhow and we shouldn't | |
222 | // confuse the user by complaining that we couldn't generate it | |
9a83f860 | 223 | wxLogDebug(wxT("Failed to get stack backtrace: %s"), |
7beafee9 | 224 | wxDbgHelpDLL::GetErrorMessage().c_str()); |
1d7ea62e | 225 | return; |
7beafee9 VZ |
226 | } |
227 | ||
228 | // according to MSDN, the first parameter should be just a unique value and | |
229 | // not process handle (although the parameter is prototyped as "HANDLE | |
230 | // hProcess") and actually it advises to use the process id and not handle | |
231 | // for Win9x, but then we need to use the same value in StackWalk() call | |
232 | // below which should be a real handle... so this is what we use | |
233 | const HANDLE hProcess = ::GetCurrentProcess(); | |
234 | ||
7dbb4353 | 235 | if ( !wxDbgHelpDLL::SymInitialize |
7beafee9 VZ |
236 | ( |
237 | hProcess, | |
238 | NULL, // use default symbol search path | |
239 | TRUE // load symbols for all loaded modules | |
240 | ) ) | |
241 | { | |
7dbb4353 | 242 | wxDbgHelpDLL::LogError(wxT("SymInitialize")); |
7beafee9 VZ |
243 | |
244 | return; | |
245 | } | |
246 | ||
247 | CONTEXT ctx = *pCtx; // will be modified by StackWalk() | |
248 | ||
249 | DWORD dwMachineType; | |
250 | ||
251 | // initialize the initial frame: currently we can do it for x86 only | |
252 | STACKFRAME sf; | |
253 | wxZeroMemory(sf); | |
254 | ||
603c73db VZ |
255 | #if defined(_M_AMD64) |
256 | sf.AddrPC.Offset = ctx.Rip; | |
257 | sf.AddrPC.Mode = AddrModeFlat; | |
258 | sf.AddrStack.Offset = ctx.Rsp; | |
259 | sf.AddrStack.Mode = AddrModeFlat; | |
260 | sf.AddrFrame.Offset = ctx.Rbp; | |
261 | sf.AddrFrame.Mode = AddrModeFlat; | |
262 | ||
263 | dwMachineType = IMAGE_FILE_MACHINE_AMD64; | |
264 | #elif defined(_M_IX86) | |
7beafee9 VZ |
265 | sf.AddrPC.Offset = ctx.Eip; |
266 | sf.AddrPC.Mode = AddrModeFlat; | |
267 | sf.AddrStack.Offset = ctx.Esp; | |
268 | sf.AddrStack.Mode = AddrModeFlat; | |
269 | sf.AddrFrame.Offset = ctx.Ebp; | |
270 | sf.AddrFrame.Mode = AddrModeFlat; | |
271 | ||
272 | dwMachineType = IMAGE_FILE_MACHINE_I386; | |
273 | #else | |
274 | #error "Need to initialize STACKFRAME on non x86" | |
275 | #endif // _M_IX86 | |
276 | ||
277 | // iterate over all stack frames | |
ef81fe8b | 278 | for ( size_t nLevel = 0; nLevel < maxDepth; nLevel++ ) |
7beafee9 VZ |
279 | { |
280 | // get the next stack frame | |
281 | if ( !wxDbgHelpDLL::StackWalk | |
282 | ( | |
283 | dwMachineType, | |
284 | hProcess, | |
285 | ::GetCurrentThread(), | |
286 | &sf, | |
287 | &ctx, | |
288 | NULL, // read memory function (default) | |
289 | wxDbgHelpDLL::SymFunctionTableAccess, | |
290 | wxDbgHelpDLL::SymGetModuleBase, | |
291 | NULL // address translator for 16 bit | |
292 | ) ) | |
293 | { | |
294 | if ( ::GetLastError() ) | |
9a83f860 | 295 | wxDbgHelpDLL::LogError(wxT("StackWalk")); |
7beafee9 VZ |
296 | |
297 | break; | |
298 | } | |
299 | ||
300 | // don't show this frame itself in the output | |
301 | if ( nLevel >= skip ) | |
302 | { | |
303 | wxStackFrame frame(nLevel - skip, | |
3f49efdb | 304 | wxUIntToPtr(sf.AddrPC.Offset), |
7beafee9 VZ |
305 | sf.AddrFrame.Offset); |
306 | ||
307 | OnStackFrame(frame); | |
308 | } | |
309 | } | |
310 | ||
7beafee9 VZ |
311 | if ( !wxDbgHelpDLL::SymCleanup(hProcess) ) |
312 | { | |
9a83f860 | 313 | wxDbgHelpDLL::LogError(wxT("SymCleanup")); |
7beafee9 | 314 | } |
7beafee9 VZ |
315 | } |
316 | ||
ef81fe8b | 317 | void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS *ep, size_t skip, size_t maxDepth) |
7beafee9 | 318 | { |
ef81fe8b | 319 | WalkFrom(ep->ContextRecord, skip, maxDepth); |
7beafee9 VZ |
320 | } |
321 | ||
4db307e1 VZ |
322 | #if wxUSE_ON_FATAL_EXCEPTION |
323 | ||
ef81fe8b | 324 | void wxStackWalker::WalkFromException(size_t maxDepth) |
7beafee9 VZ |
325 | { |
326 | extern EXCEPTION_POINTERS *wxGlobalSEInformation; | |
327 | ||
328 | wxCHECK_RET( wxGlobalSEInformation, | |
9a83f860 | 329 | wxT("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") ); |
7beafee9 VZ |
330 | |
331 | // don't skip any frames, the first one is where we crashed | |
ef81fe8b | 332 | WalkFrom(wxGlobalSEInformation, 0, maxDepth); |
7beafee9 VZ |
333 | } |
334 | ||
4db307e1 VZ |
335 | #endif // wxUSE_ON_FATAL_EXCEPTION |
336 | ||
a82c2299 | 337 | void wxStackWalker::Walk(size_t skip, size_t WXUNUSED(maxDepth)) |
7beafee9 VZ |
338 | { |
339 | // to get a CONTEXT for the current location, simply force an exception and | |
340 | // get EXCEPTION_POINTERS from it | |
341 | // | |
342 | // note: | |
4db307e1 | 343 | // 1. we additionally skip RaiseException() and WalkFrom() frames |
7beafee9 VZ |
344 | // 2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it |
345 | // shouldn't have been according to the docs | |
346 | __try | |
347 | { | |
348 | RaiseException(0x1976, 0, 0, NULL); | |
349 | } | |
350 | __except( WalkFrom((EXCEPTION_POINTERS *)GetExceptionInformation(), | |
351 | skip + 2), EXCEPTION_CONTINUE_EXECUTION ) | |
352 | { | |
4db307e1 VZ |
353 | // never executed because the above expression always evaluates to |
354 | // EXCEPTION_CONTINUE_EXECUTION | |
7beafee9 VZ |
355 | } |
356 | } | |
357 | ||
358 | #else // !wxUSE_DBGHELP | |
359 | ||
2289dcea VZ |
360 | // ============================================================================ |
361 | // stubs | |
362 | // ============================================================================ | |
363 | ||
364 | // ---------------------------------------------------------------------------- | |
365 | // wxStackFrame | |
366 | // ---------------------------------------------------------------------------- | |
367 | ||
368 | void wxStackFrame::OnGetName() | |
369 | { | |
370 | } | |
371 | ||
372 | void wxStackFrame::OnGetLocation() | |
373 | { | |
374 | } | |
375 | ||
376 | bool | |
377 | wxStackFrame::GetParam(size_t WXUNUSED(n), | |
378 | wxString * WXUNUSED(type), | |
379 | wxString * WXUNUSED(name), | |
380 | wxString * WXUNUSED(value)) const | |
381 | { | |
382 | return false; | |
383 | } | |
384 | ||
7dbb4353 | 385 | void wxStackFrame::OnParam(_SYMBOL_INFO * WXUNUSED(pSymInfo)) |
2289dcea VZ |
386 | { |
387 | } | |
388 | ||
389 | void wxStackFrame::OnGetParam() | |
390 | { | |
391 | } | |
392 | ||
393 | // ---------------------------------------------------------------------------- | |
394 | // wxStackWalker | |
395 | // ---------------------------------------------------------------------------- | |
396 | ||
397 | void | |
057bf0be VZ |
398 | wxStackWalker::WalkFrom(const CONTEXT * WXUNUSED(pCtx), |
399 | size_t WXUNUSED(skip), | |
400 | size_t WXUNUSED(maxDepth)) | |
2289dcea VZ |
401 | { |
402 | } | |
403 | ||
404 | void | |
405 | wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS * WXUNUSED(ep), | |
057bf0be VZ |
406 | size_t WXUNUSED(skip), |
407 | size_t WXUNUSED(maxDepth)) | |
2289dcea VZ |
408 | { |
409 | } | |
410 | ||
4db307e1 | 411 | #if wxUSE_ON_FATAL_EXCEPTION |
ef81fe8b | 412 | void wxStackWalker::WalkFromException(size_t WXUNUSED(maxDepth)) |
2289dcea VZ |
413 | { |
414 | } | |
4db307e1 | 415 | #endif // wxUSE_ON_FATAL_EXCEPTION |
2289dcea | 416 | |
0034bd13 | 417 | void wxStackWalker::Walk(size_t WXUNUSED(skip), size_t WXUNUSED(maxDepth)) |
2289dcea VZ |
418 | { |
419 | } | |
7beafee9 VZ |
420 | |
421 | #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP | |
422 | ||
423 | #endif // wxUSE_STACKWALKER | |
424 |