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,
6 // Suzumizaki-kimitaka 2013-04-09
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(wxSYMBOL_INFO
) + MAX_NAME_LEN
*sizeof(TCHAR
)];
56 wxZeroMemory(symbolBuffer
);
58 wxPSYMBOL_INFO pSymbol
= (wxPSYMBOL_INFO
)symbolBuffer
;
59 pSymbol
->SizeOfStruct
= sizeof(wxSYMBOL_INFO
);
60 pSymbol
->MaxNameLen
= MAX_NAME_LEN
;
62 DWORD64 symDisplacement
= 0;
63 if ( !wxDbgHelpDLL::SymFromAddrT
65 ::GetCurrentProcess(),
71 wxDbgHelpDLL::LogError(wxT("SymFromAddrT"));
75 m_name
= pSymbol
->Name
;
77 m_name
= wxString(pSymbol
->Name
, wxConvLocal
);
79 m_offset
= symDisplacement
;
82 void wxStackFrame::OnGetLocation()
89 // get the source line for this stack frame entry
90 wxIMAGEHLP_LINE lineInfo
= { sizeof(wxIMAGEHLP_LINE
) };
91 DWORD dwLineDisplacement
;
92 if ( !wxDbgHelpDLL::SymGetLineFromAddrT
94 ::GetCurrentProcess(),
100 // it is normal that we don't have source info for some symbols,
101 // notably all the ones from the system DLLs...
102 //wxDbgHelpDLL::LogError(wxT("SymGetLineFromAddr64"));
106 m_filename
= lineInfo
.FileName
;
108 m_filename
= wxString(lineInfo
.FileName
, wxConvLocal
);
110 m_line
= lineInfo
.LineNumber
;
114 wxStackFrame::GetParam(size_t n
,
117 wxString
*value
) const
119 if ( !DoGetParamCount() )
120 ConstCast()->OnGetParam();
122 if ( n
>= DoGetParamCount() )
126 *type
= m_paramTypes
[n
];
128 *name
= m_paramNames
[n
];
130 *value
= m_paramValues
[n
];
135 void wxStackFrame::OnParam(wxPSYMBOL_INFO pSymInfo
)
137 m_paramTypes
.Add(wxEmptyString
);
140 m_paramNames
.Add(pSymInfo
->Name
);
142 m_paramNames
.Add(wxString(pSymInfo
->Name
, wxConvLocal
));
145 // if symbol information is corrupted and we crash, the exception is going
146 // to be ignored when we're called from WalkFromException() because of the
147 // exception handler there returning EXCEPTION_CONTINUE_EXECUTION, but we'd
148 // be left in an inconsistent state, so deal with it explicitly here (even
149 // if normally we should never crash, of course...)
156 // as it is a parameter (and not a global var), it is always offset by
158 DWORD_PTR pValue
= m_addrFrame
+ pSymInfo
->Address
;
159 m_paramValues
.Add(wxDbgHelpDLL::DumpSymbol(pSymInfo
, (void *)pValue
));
164 __except ( EXCEPTION_EXECUTE_HANDLER
)
167 m_paramValues
.Add(wxEmptyString
);
172 EnumSymbolsProc(wxPSYMBOL_INFO pSymInfo
, ULONG
WXUNUSED(SymSize
), PVOID data
)
174 wxStackFrame
*frame
= static_cast<wxStackFrame
*>(data
);
176 // we're only interested in parameters
177 if ( pSymInfo
->Flags
& IMAGEHLP_SYMBOL_INFO_PARAMETER
)
179 frame
->OnParam(pSymInfo
);
182 // return true to continue enumeration, false would have stopped it
186 void wxStackFrame::OnGetParam()
188 // use SymSetContext to get just the locals/params for this frame
189 IMAGEHLP_STACK_FRAME imagehlpStackFrame
;
190 wxZeroMemory(imagehlpStackFrame
);
191 imagehlpStackFrame
.InstructionOffset
= GetSymAddr();
192 if ( !wxDbgHelpDLL::SymSetContext
194 ::GetCurrentProcess(),
199 // for symbols from kernel DLL we might not have access to their
200 // address, this is not a real error
201 if ( ::GetLastError() != ERROR_INVALID_ADDRESS
)
203 wxDbgHelpDLL::LogError(wxT("SymSetContext"));
209 if ( !wxDbgHelpDLL::SymEnumSymbolsT
211 ::GetCurrentProcess(),
212 NULL
, // DLL base: use current context
213 NULL
, // no mask, get all symbols
214 EnumSymbolsProc
, // callback
215 this // data to pass to it
218 wxDbgHelpDLL::LogError(wxT("SymEnumSymbolsT"));
223 // ----------------------------------------------------------------------------
225 // ----------------------------------------------------------------------------
227 void wxStackWalker::WalkFrom(const CONTEXT
*pCtx
, size_t skip
, size_t maxDepth
)
229 if ( !wxDbgHelpDLL::Init() )
231 // don't log a user-visible error message here because the stack trace
232 // is only needed for debugging/diagnostics anyhow and we shouldn't
233 // confuse the user by complaining that we couldn't generate it
234 wxLogDebug(wxT("Failed to get stack backtrace: %s"),
235 wxDbgHelpDLL::GetErrorMessage().c_str());
239 // according to MSDN, the first parameter should be just a unique value and
240 // not process handle (although the parameter is prototyped as "HANDLE
241 // hProcess") and actually it advises to use the process id and not handle
242 // for Win9x, but then we need to use the same value in StackWalk() call
243 // below which should be a real handle... so this is what we use
244 const HANDLE hProcess
= ::GetCurrentProcess();
246 if ( !wxDbgHelpDLL::SymInitializeT
249 NULL
, // use default symbol search path
250 TRUE
// load symbols for all loaded modules
253 wxDbgHelpDLL::LogError(wxT("SymInitializeT"));
258 CONTEXT ctx
= *pCtx
; // will be modified by StackWalk()
262 // initialize the initial frame: currently we can do it for x86 only
266 #if defined(_M_AMD64)
267 sf
.AddrPC
.Offset
= ctx
.Rip
;
268 sf
.AddrPC
.Mode
= AddrModeFlat
;
269 sf
.AddrStack
.Offset
= ctx
.Rsp
;
270 sf
.AddrStack
.Mode
= AddrModeFlat
;
271 sf
.AddrFrame
.Offset
= ctx
.Rbp
;
272 sf
.AddrFrame
.Mode
= AddrModeFlat
;
274 dwMachineType
= IMAGE_FILE_MACHINE_AMD64
;
275 #elif defined(_M_IX86)
276 sf
.AddrPC
.Offset
= ctx
.Eip
;
277 sf
.AddrPC
.Mode
= AddrModeFlat
;
278 sf
.AddrStack
.Offset
= ctx
.Esp
;
279 sf
.AddrStack
.Mode
= AddrModeFlat
;
280 sf
.AddrFrame
.Offset
= ctx
.Ebp
;
281 sf
.AddrFrame
.Mode
= AddrModeFlat
;
283 dwMachineType
= IMAGE_FILE_MACHINE_I386
;
285 #error "Need to initialize STACKFRAME on non x86"
288 // iterate over all stack frames
289 for ( size_t nLevel
= 0; nLevel
< maxDepth
; nLevel
++ )
291 // get the next stack frame
292 if ( !wxDbgHelpDLL::StackWalk
296 ::GetCurrentThread(),
299 NULL
, // read memory function (default)
300 wxDbgHelpDLL::SymFunctionTableAccess
,
301 wxDbgHelpDLL::SymGetModuleBase
,
302 NULL
// address translator for 16 bit
305 if ( ::GetLastError() )
306 wxDbgHelpDLL::LogError(wxT("StackWalk"));
311 // don't show this frame itself in the output
312 if ( nLevel
>= skip
)
314 wxStackFrame
frame(nLevel
- skip
,
315 wxUIntToPtr(sf
.AddrPC
.Offset
),
316 sf
.AddrFrame
.Offset
);
322 if ( !wxDbgHelpDLL::SymCleanup(hProcess
) )
324 wxDbgHelpDLL::LogError(wxT("SymCleanup"));
328 void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS
*ep
, size_t skip
, size_t maxDepth
)
330 WalkFrom(ep
->ContextRecord
, skip
, maxDepth
);
333 #if wxUSE_ON_FATAL_EXCEPTION
335 void wxStackWalker::WalkFromException(size_t maxDepth
)
337 extern EXCEPTION_POINTERS
*wxGlobalSEInformation
;
339 wxCHECK_RET( wxGlobalSEInformation
,
340 wxT("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") );
342 // don't skip any frames, the first one is where we crashed
343 WalkFrom(wxGlobalSEInformation
, 0, maxDepth
);
346 #endif // wxUSE_ON_FATAL_EXCEPTION
348 void wxStackWalker::Walk(size_t skip
, size_t WXUNUSED(maxDepth
))
350 // to get a CONTEXT for the current location, simply force an exception and
351 // get EXCEPTION_POINTERS from it
354 // 1. we additionally skip RaiseException() and WalkFrom() frames
355 // 2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it
356 // shouldn't have been according to the docs
359 RaiseException(0x1976, 0, 0, NULL
);
361 __except( WalkFrom((EXCEPTION_POINTERS
*)GetExceptionInformation(),
362 skip
+ 2), EXCEPTION_CONTINUE_EXECUTION
)
364 // never executed because the above expression always evaluates to
365 // EXCEPTION_CONTINUE_EXECUTION
369 #else // !wxUSE_DBGHELP
371 // ============================================================================
373 // ============================================================================
375 // ----------------------------------------------------------------------------
377 // ----------------------------------------------------------------------------
379 void wxStackFrame::OnGetName()
383 void wxStackFrame::OnGetLocation()
388 wxStackFrame::GetParam(size_t WXUNUSED(n
),
389 wxString
* WXUNUSED(type
),
390 wxString
* WXUNUSED(name
),
391 wxString
* WXUNUSED(value
)) const
396 void wxStackFrame::OnParam(wxPSYMBOL_INFO
WXUNUSED(pSymInfo
))
400 void wxStackFrame::OnGetParam()
404 // ----------------------------------------------------------------------------
406 // ----------------------------------------------------------------------------
409 wxStackWalker::WalkFrom(const CONTEXT
* WXUNUSED(pCtx
),
410 size_t WXUNUSED(skip
),
411 size_t WXUNUSED(maxDepth
))
416 wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS
* WXUNUSED(ep
),
417 size_t WXUNUSED(skip
),
418 size_t WXUNUSED(maxDepth
))
422 #if wxUSE_ON_FATAL_EXCEPTION
423 void wxStackWalker::WalkFromException(size_t WXUNUSED(maxDepth
))
426 #endif // wxUSE_ON_FATAL_EXCEPTION
428 void wxStackWalker::Walk(size_t WXUNUSED(skip
), size_t WXUNUSED(maxDepth
))
432 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
434 #endif // wxUSE_STACKWALKER