1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/stackwalk.cpp
3 // Purpose: wxStackWalker implementation for Win32
4 // Author: Vadim Zeitlin
5 // Modified by: Artur Bac 2010-10-01 AMD64 Port
7 // Copyright: (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 #include "wx/wxprec.h"
28 #include "wx/string.h"
31 #include "wx/stackwalk.h"
33 #include "wx/msw/debughlp.h"
37 // ============================================================================
39 // ============================================================================
41 // ----------------------------------------------------------------------------
43 // ----------------------------------------------------------------------------
45 void wxStackFrame::OnGetName()
52 // get the name of the function for this stack frame entry
53 static const size_t MAX_NAME_LEN
= 1024;
54 BYTE symbolBuffer
[sizeof(SYMBOL_INFO
) + MAX_NAME_LEN
];
55 wxZeroMemory(symbolBuffer
);
57 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)symbolBuffer
;
58 pSymbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
59 pSymbol
->MaxNameLen
= MAX_NAME_LEN
;
61 DWORD64 symDisplacement
= 0;
62 if ( !wxDbgHelpDLL::SymFromAddr
64 ::GetCurrentProcess(),
70 wxDbgHelpDLL::LogError(wxT("SymFromAddr"));
74 m_name
= wxString::FromAscii(pSymbol
->Name
);
75 m_offset
= symDisplacement
;
78 void wxStackFrame::OnGetLocation()
85 // get the source line for this stack frame entry
86 IMAGEHLP_LINE lineInfo
= { sizeof(IMAGEHLP_LINE
) };
87 DWORD dwLineDisplacement
;
88 if ( !wxDbgHelpDLL::SymGetLineFromAddr
90 ::GetCurrentProcess(),
96 // it is normal that we don't have source info for some symbols,
97 // notably all the ones from the system DLLs...
98 //wxDbgHelpDLL::LogError(wxT("SymGetLineFromAddr"));
102 m_filename
= wxString::FromAscii(lineInfo
.FileName
);
103 m_line
= lineInfo
.LineNumber
;
107 wxStackFrame::GetParam(size_t n
,
110 wxString
*value
) const
112 if ( !DoGetParamCount() )
113 ConstCast()->OnGetParam();
115 if ( n
>= DoGetParamCount() )
119 *type
= m_paramTypes
[n
];
121 *name
= m_paramNames
[n
];
123 *value
= m_paramValues
[n
];
128 void wxStackFrame::OnParam(PSYMBOL_INFO pSymInfo
)
130 m_paramTypes
.Add(wxEmptyString
);
132 m_paramNames
.Add(wxString::FromAscii(pSymInfo
->Name
));
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
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...)
145 // as it is a parameter (and not a global var), it is always offset by
147 DWORD_PTR pValue
= m_addrFrame
+ pSymInfo
->Address
;
148 m_paramValues
.Add(wxDbgHelpDLL::DumpSymbol(pSymInfo
, (void *)pValue
));
153 __except ( EXCEPTION_EXECUTE_HANDLER
)
156 m_paramValues
.Add(wxEmptyString
);
161 EnumSymbolsProc(PSYMBOL_INFO pSymInfo
, ULONG
WXUNUSED(SymSize
), PVOID data
)
163 wxStackFrame
*frame
= static_cast<wxStackFrame
*>(data
);
165 // we're only interested in parameters
166 if ( pSymInfo
->Flags
& IMAGEHLP_SYMBOL_INFO_PARAMETER
)
168 frame
->OnParam(pSymInfo
);
171 // return true to continue enumeration, false would have stopped it
175 void wxStackFrame::OnGetParam()
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
183 ::GetCurrentProcess(),
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
)
192 wxDbgHelpDLL::LogError(wxT("SymSetContext"));
198 if ( !wxDbgHelpDLL::SymEnumSymbols
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
207 wxDbgHelpDLL::LogError(wxT("SymEnumSymbols"));
212 // ----------------------------------------------------------------------------
214 // ----------------------------------------------------------------------------
216 void wxStackWalker::WalkFrom(const CONTEXT
*pCtx
, size_t skip
, size_t maxDepth
)
218 if ( !wxDbgHelpDLL::Init() )
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
223 wxLogDebug(wxT("Failed to get stack backtrace: %s"),
224 wxDbgHelpDLL::GetErrorMessage().c_str());
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();
235 if ( !wxDbgHelpDLL::SymInitialize
238 NULL
, // use default symbol search path
239 TRUE
// load symbols for all loaded modules
242 wxDbgHelpDLL::LogError(wxT("SymInitialize"));
247 CONTEXT ctx
= *pCtx
; // will be modified by StackWalk()
251 // initialize the initial frame: currently we can do it for x86 only
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
;
263 dwMachineType
= IMAGE_FILE_MACHINE_AMD64
;
264 #elif defined(_M_IX86)
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
;
272 dwMachineType
= IMAGE_FILE_MACHINE_I386
;
274 #error "Need to initialize STACKFRAME on non x86"
277 // iterate over all stack frames
278 for ( size_t nLevel
= 0; nLevel
< maxDepth
; nLevel
++ )
280 // get the next stack frame
281 if ( !wxDbgHelpDLL::StackWalk
285 ::GetCurrentThread(),
288 NULL
, // read memory function (default)
289 wxDbgHelpDLL::SymFunctionTableAccess
,
290 wxDbgHelpDLL::SymGetModuleBase
,
291 NULL
// address translator for 16 bit
294 if ( ::GetLastError() )
295 wxDbgHelpDLL::LogError(wxT("StackWalk"));
300 // don't show this frame itself in the output
301 if ( nLevel
>= skip
)
303 wxStackFrame
frame(nLevel
- skip
,
304 wxUIntToPtr(sf
.AddrPC
.Offset
),
305 sf
.AddrFrame
.Offset
);
311 if ( !wxDbgHelpDLL::SymCleanup(hProcess
) )
313 wxDbgHelpDLL::LogError(wxT("SymCleanup"));
317 void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS
*ep
, size_t skip
, size_t maxDepth
)
319 WalkFrom(ep
->ContextRecord
, skip
, maxDepth
);
322 #if wxUSE_ON_FATAL_EXCEPTION
324 void wxStackWalker::WalkFromException(size_t maxDepth
)
326 extern EXCEPTION_POINTERS
*wxGlobalSEInformation
;
328 wxCHECK_RET( wxGlobalSEInformation
,
329 wxT("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") );
331 // don't skip any frames, the first one is where we crashed
332 WalkFrom(wxGlobalSEInformation
, 0, maxDepth
);
335 #endif // wxUSE_ON_FATAL_EXCEPTION
337 void wxStackWalker::Walk(size_t skip
, size_t WXUNUSED(maxDepth
))
339 // to get a CONTEXT for the current location, simply force an exception and
340 // get EXCEPTION_POINTERS from it
343 // 1. we additionally skip RaiseException() and WalkFrom() frames
344 // 2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it
345 // shouldn't have been according to the docs
348 RaiseException(0x1976, 0, 0, NULL
);
350 __except( WalkFrom((EXCEPTION_POINTERS
*)GetExceptionInformation(),
351 skip
+ 2), EXCEPTION_CONTINUE_EXECUTION
)
353 // never executed because the above expression always evaluates to
354 // EXCEPTION_CONTINUE_EXECUTION
358 #else // !wxUSE_DBGHELP
360 // ============================================================================
362 // ============================================================================
364 // ----------------------------------------------------------------------------
366 // ----------------------------------------------------------------------------
368 void wxStackFrame::OnGetName()
372 void wxStackFrame::OnGetLocation()
377 wxStackFrame::GetParam(size_t WXUNUSED(n
),
378 wxString
* WXUNUSED(type
),
379 wxString
* WXUNUSED(name
),
380 wxString
* WXUNUSED(value
)) const
385 void wxStackFrame::OnParam(_SYMBOL_INFO
* WXUNUSED(pSymInfo
))
389 void wxStackFrame::OnGetParam()
393 // ----------------------------------------------------------------------------
395 // ----------------------------------------------------------------------------
398 wxStackWalker::WalkFrom(const CONTEXT
* WXUNUSED(pCtx
),
399 size_t WXUNUSED(skip
),
400 size_t WXUNUSED(maxDepth
))
405 wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS
* WXUNUSED(ep
),
406 size_t WXUNUSED(skip
),
407 size_t WXUNUSED(maxDepth
))
411 #if wxUSE_ON_FATAL_EXCEPTION
412 void wxStackWalker::WalkFromException(size_t WXUNUSED(maxDepth
))
415 #endif // wxUSE_ON_FATAL_EXCEPTION
417 void wxStackWalker::Walk(size_t WXUNUSED(skip
), size_t WXUNUSED(maxDepth
))
421 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
423 #endif // wxUSE_STACKWALKER