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
8 // Copyright: (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
29 #include "wx/string.h"
32 #include "wx/stackwalk.h"
34 #include "wx/msw/debughlp.h"
38 // ============================================================================
40 // ============================================================================
42 // ----------------------------------------------------------------------------
44 // ----------------------------------------------------------------------------
46 void wxStackFrame::OnGetName()
53 // get the name of the function for this stack frame entry
54 static const size_t MAX_NAME_LEN
= 1024;
55 BYTE symbolBuffer
[sizeof(SYMBOL_INFO
) + MAX_NAME_LEN
];
56 wxZeroMemory(symbolBuffer
);
58 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)symbolBuffer
;
59 pSymbol
->SizeOfStruct
= sizeof(SYMBOL_INFO
);
60 pSymbol
->MaxNameLen
= MAX_NAME_LEN
;
62 DWORD64 symDisplacement
= 0;
63 if ( !wxDbgHelpDLL::SymFromAddr
65 ::GetCurrentProcess(),
71 wxDbgHelpDLL::LogError(wxT("SymFromAddr"));
75 m_name
= wxString::FromAscii(pSymbol
->Name
);
76 m_offset
= symDisplacement
;
79 void wxStackFrame::OnGetLocation()
86 // get the source line for this stack frame entry
87 IMAGEHLP_LINE lineInfo
= { sizeof(IMAGEHLP_LINE
) };
88 DWORD dwLineDisplacement
;
89 if ( !wxDbgHelpDLL::SymGetLineFromAddr
91 ::GetCurrentProcess(),
97 // it is normal that we don't have source info for some symbols,
98 // notably all the ones from the system DLLs...
99 //wxDbgHelpDLL::LogError(wxT("SymGetLineFromAddr"));
103 m_filename
= wxString::FromAscii(lineInfo
.FileName
);
104 m_line
= lineInfo
.LineNumber
;
108 wxStackFrame::GetParam(size_t n
,
111 wxString
*value
) const
113 if ( !DoGetParamCount() )
114 ConstCast()->OnGetParam();
116 if ( n
>= DoGetParamCount() )
120 *type
= m_paramTypes
[n
];
122 *name
= m_paramNames
[n
];
124 *value
= m_paramValues
[n
];
129 void wxStackFrame::OnParam(PSYMBOL_INFO pSymInfo
)
131 m_paramTypes
.Add(wxEmptyString
);
133 m_paramNames
.Add(wxString::FromAscii(pSymInfo
->Name
));
135 // if symbol information is corrupted and we crash, the exception is going
136 // to be ignored when we're called from WalkFromException() because of the
137 // exception handler there returning EXCEPTION_CONTINUE_EXECUTION, but we'd
138 // be left in an inconsistent state, so deal with it explicitly here (even
139 // if normally we should never crash, of course...)
146 // as it is a parameter (and not a global var), it is always offset by
148 DWORD_PTR pValue
= m_addrFrame
+ pSymInfo
->Address
;
149 m_paramValues
.Add(wxDbgHelpDLL::DumpSymbol(pSymInfo
, (void *)pValue
));
154 __except ( EXCEPTION_EXECUTE_HANDLER
)
157 m_paramValues
.Add(wxEmptyString
);
162 EnumSymbolsProc(PSYMBOL_INFO pSymInfo
, ULONG
WXUNUSED(SymSize
), PVOID data
)
164 wxStackFrame
*frame
= static_cast<wxStackFrame
*>(data
);
166 // we're only interested in parameters
167 if ( pSymInfo
->Flags
& IMAGEHLP_SYMBOL_INFO_PARAMETER
)
169 frame
->OnParam(pSymInfo
);
172 // return true to continue enumeration, false would have stopped it
176 void wxStackFrame::OnGetParam()
178 // use SymSetContext to get just the locals/params for this frame
179 IMAGEHLP_STACK_FRAME imagehlpStackFrame
;
180 wxZeroMemory(imagehlpStackFrame
);
181 imagehlpStackFrame
.InstructionOffset
= GetSymAddr();
182 if ( !wxDbgHelpDLL::SymSetContext
184 ::GetCurrentProcess(),
189 // for symbols from kernel DLL we might not have access to their
190 // address, this is not a real error
191 if ( ::GetLastError() != ERROR_INVALID_ADDRESS
)
193 wxDbgHelpDLL::LogError(wxT("SymSetContext"));
199 if ( !wxDbgHelpDLL::SymEnumSymbols
201 ::GetCurrentProcess(),
202 NULL
, // DLL base: use current context
203 NULL
, // no mask, get all symbols
204 EnumSymbolsProc
, // callback
205 this // data to pass to it
208 wxDbgHelpDLL::LogError(wxT("SymEnumSymbols"));
213 // ----------------------------------------------------------------------------
215 // ----------------------------------------------------------------------------
217 void wxStackWalker::WalkFrom(const CONTEXT
*pCtx
, size_t skip
, size_t maxDepth
)
219 if ( !wxDbgHelpDLL::Init() )
221 // don't log a user-visible error message here because the stack trace
222 // is only needed for debugging/diagnostics anyhow and we shouldn't
223 // confuse the user by complaining that we couldn't generate it
224 wxLogDebug(wxT("Failed to get stack backtrace: %s"),
225 wxDbgHelpDLL::GetErrorMessage().c_str());
229 // according to MSDN, the first parameter should be just a unique value and
230 // not process handle (although the parameter is prototyped as "HANDLE
231 // hProcess") and actually it advises to use the process id and not handle
232 // for Win9x, but then we need to use the same value in StackWalk() call
233 // below which should be a real handle... so this is what we use
234 const HANDLE hProcess
= ::GetCurrentProcess();
236 if ( !wxDbgHelpDLL::SymInitialize
239 NULL
, // use default symbol search path
240 TRUE
// load symbols for all loaded modules
243 wxDbgHelpDLL::LogError(wxT("SymInitialize"));
248 CONTEXT ctx
= *pCtx
; // will be modified by StackWalk()
252 // initialize the initial frame: currently we can do it for x86 only
256 #if defined(_M_AMD64)
257 sf
.AddrPC
.Offset
= ctx
.Rip
;
258 sf
.AddrPC
.Mode
= AddrModeFlat
;
259 sf
.AddrStack
.Offset
= ctx
.Rsp
;
260 sf
.AddrStack
.Mode
= AddrModeFlat
;
261 sf
.AddrFrame
.Offset
= ctx
.Rbp
;
262 sf
.AddrFrame
.Mode
= AddrModeFlat
;
264 dwMachineType
= IMAGE_FILE_MACHINE_AMD64
;
265 #elif defined(_M_IX86)
266 sf
.AddrPC
.Offset
= ctx
.Eip
;
267 sf
.AddrPC
.Mode
= AddrModeFlat
;
268 sf
.AddrStack
.Offset
= ctx
.Esp
;
269 sf
.AddrStack
.Mode
= AddrModeFlat
;
270 sf
.AddrFrame
.Offset
= ctx
.Ebp
;
271 sf
.AddrFrame
.Mode
= AddrModeFlat
;
273 dwMachineType
= IMAGE_FILE_MACHINE_I386
;
275 #error "Need to initialize STACKFRAME on non x86"
278 // iterate over all stack frames
279 for ( size_t nLevel
= 0; nLevel
< maxDepth
; nLevel
++ )
281 // get the next stack frame
282 if ( !wxDbgHelpDLL::StackWalk
286 ::GetCurrentThread(),
289 NULL
, // read memory function (default)
290 wxDbgHelpDLL::SymFunctionTableAccess
,
291 wxDbgHelpDLL::SymGetModuleBase
,
292 NULL
// address translator for 16 bit
295 if ( ::GetLastError() )
296 wxDbgHelpDLL::LogError(wxT("StackWalk"));
301 // don't show this frame itself in the output
302 if ( nLevel
>= skip
)
304 wxStackFrame
frame(nLevel
- skip
,
305 wxUIntToPtr(sf
.AddrPC
.Offset
),
306 sf
.AddrFrame
.Offset
);
312 if ( !wxDbgHelpDLL::SymCleanup(hProcess
) )
314 wxDbgHelpDLL::LogError(wxT("SymCleanup"));
318 void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS
*ep
, size_t skip
, size_t maxDepth
)
320 WalkFrom(ep
->ContextRecord
, skip
, maxDepth
);
323 #if wxUSE_ON_FATAL_EXCEPTION
325 void wxStackWalker::WalkFromException(size_t maxDepth
)
327 extern EXCEPTION_POINTERS
*wxGlobalSEInformation
;
329 wxCHECK_RET( wxGlobalSEInformation
,
330 wxT("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") );
332 // don't skip any frames, the first one is where we crashed
333 WalkFrom(wxGlobalSEInformation
, 0, maxDepth
);
336 #endif // wxUSE_ON_FATAL_EXCEPTION
338 void wxStackWalker::Walk(size_t skip
, size_t WXUNUSED(maxDepth
))
340 // to get a CONTEXT for the current location, simply force an exception and
341 // get EXCEPTION_POINTERS from it
344 // 1. we additionally skip RaiseException() and WalkFrom() frames
345 // 2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it
346 // shouldn't have been according to the docs
349 RaiseException(0x1976, 0, 0, NULL
);
351 __except( WalkFrom((EXCEPTION_POINTERS
*)GetExceptionInformation(),
352 skip
+ 2), EXCEPTION_CONTINUE_EXECUTION
)
354 // never executed because the above expression always evaluates to
355 // EXCEPTION_CONTINUE_EXECUTION
359 #else // !wxUSE_DBGHELP
361 // ============================================================================
363 // ============================================================================
365 // ----------------------------------------------------------------------------
367 // ----------------------------------------------------------------------------
369 void wxStackFrame::OnGetName()
373 void wxStackFrame::OnGetLocation()
378 wxStackFrame::GetParam(size_t WXUNUSED(n
),
379 wxString
* WXUNUSED(type
),
380 wxString
* WXUNUSED(name
),
381 wxString
* WXUNUSED(value
)) const
386 void wxStackFrame::OnParam(_SYMBOL_INFO
* WXUNUSED(pSymInfo
))
390 void wxStackFrame::OnGetParam()
394 // ----------------------------------------------------------------------------
396 // ----------------------------------------------------------------------------
399 wxStackWalker::WalkFrom(const CONTEXT
* WXUNUSED(pCtx
),
400 size_t WXUNUSED(skip
),
401 size_t WXUNUSED(maxDepth
))
406 wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS
* WXUNUSED(ep
),
407 size_t WXUNUSED(skip
),
408 size_t WXUNUSED(maxDepth
))
412 #if wxUSE_ON_FATAL_EXCEPTION
413 void wxStackWalker::WalkFromException(size_t WXUNUSED(maxDepth
))
416 #endif // wxUSE_ON_FATAL_EXCEPTION
418 void wxStackWalker::Walk(size_t WXUNUSED(skip
), size_t WXUNUSED(maxDepth
))
422 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
424 #endif // wxUSE_STACKWALKER