1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/stackwalk.cpp
3 // Purpose: wxStackWalker implementation for Win32
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
28 #include "wx/stackwalk.h"
30 #include "wx/msw/debughlp.h"
34 // ============================================================================
36 // ============================================================================
38 // ----------------------------------------------------------------------------
40 // ----------------------------------------------------------------------------
42 void wxStackFrame::OnGetName()
49 // get the name of the function for this stack frame entry
50 static const size_t MAX_NAME_LEN
= 1024;
51 BYTE symbolBuffer
[sizeof(SYMBOL_INFO
) + MAX_NAME_LEN
];
52 wxZeroMemory(symbolBuffer
);
54 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)symbolBuffer
;
55 pSymbol
->SizeOfStruct
= sizeof(symbolBuffer
);
56 pSymbol
->MaxNameLen
= MAX_NAME_LEN
;
58 DWORD64 symDisplacement
= 0;
59 if ( !wxDbgHelpDLL::SymFromAddr
61 ::GetCurrentProcess(),
67 wxDbgHelpDLL::LogError(_T("SymFromAddr"));
71 m_name
= wxString::FromAscii(pSymbol
->Name
);
72 m_offset
= symDisplacement
;
75 void wxStackFrame::OnGetLocation()
82 // get the source line for this stack frame entry
83 IMAGEHLP_LINE lineInfo
= { sizeof(IMAGEHLP_LINE
) };
84 DWORD dwLineDisplacement
;
85 if ( !wxDbgHelpDLL::SymGetLineFromAddr
87 ::GetCurrentProcess(),
93 // it is normal that we don't have source info for some symbols,
94 // notably all the ones from the system DLLs...
95 //wxDbgHelpDLL::LogError(_T("SymGetLineFromAddr"));
99 m_filename
= wxString::FromAscii(lineInfo
.FileName
);
100 m_line
= lineInfo
.LineNumber
;
104 wxStackFrame::GetParam(size_t n
,
107 wxString
*value
) const
109 if ( !DoGetParamCount() )
110 ConstCast()->OnGetParam();
112 if ( n
>= DoGetParamCount() )
116 *type
= m_paramTypes
[n
];
118 *name
= m_paramNames
[n
];
120 *value
= m_paramValues
[n
];
125 void wxStackFrame::OnParam(PSYMBOL_INFO pSymInfo
)
127 m_paramTypes
.Add(_T(""));
129 m_paramNames
.Add(wxString::FromAscii(pSymInfo
->Name
));
131 // if symbol information is corrupted and we crash, the exception is going
132 // to be ignored when we're called from WalkFromException() because of the
133 // except handler there returning EXCEPTION_CONTINUE_EXECUTION, but we'd be
134 // left in an inconsistent state, so deal with it explicitely here (even if
135 // normally we should never crash, of course...)
142 // as it is a parameter (and not a global var), it is always offset by
144 DWORD_PTR pValue
= m_addrFrame
+ pSymInfo
->Address
;
145 m_paramValues
.Add(wxDbgHelpDLL::DumpSymbol(pSymInfo
, (void *)pValue
));
150 __except ( EXCEPTION_EXECUTE_HANDLER
)
153 m_paramValues
.Add(_T(""));
158 EnumSymbolsProc(PSYMBOL_INFO pSymInfo
, ULONG
WXUNUSED(SymSize
), PVOID data
)
160 wxStackFrame
*frame
= wx_static_cast(wxStackFrame
*, data
);
162 // we're only interested in parameters
163 if ( pSymInfo
->Flags
& IMAGEHLP_SYMBOL_INFO_PARAMETER
)
165 frame
->OnParam(pSymInfo
);
168 // return true to continue enumeration, false would have stopped it
172 void wxStackFrame::OnGetParam()
174 // use SymSetContext to get just the locals/params for this frame
175 IMAGEHLP_STACK_FRAME imagehlpStackFrame
;
176 wxZeroMemory(imagehlpStackFrame
);
177 imagehlpStackFrame
.InstructionOffset
= GetSymAddr();
178 if ( !wxDbgHelpDLL::SymSetContext
180 ::GetCurrentProcess(),
185 // for symbols from kernel DLL we might not have access to their
186 // address, this is not a real error
187 if ( ::GetLastError() != ERROR_INVALID_ADDRESS
)
189 wxDbgHelpDLL::LogError(_T("SymSetContext"));
195 if ( !wxDbgHelpDLL::SymEnumSymbols
197 ::GetCurrentProcess(),
198 NULL
, // DLL base: use current context
199 NULL
, // no mask, get all symbols
200 EnumSymbolsProc
, // callback
201 this // data to pass to it
204 wxDbgHelpDLL::LogError(_T("SymEnumSymbols"));
209 // ----------------------------------------------------------------------------
211 // ----------------------------------------------------------------------------
213 void wxStackWalker::WalkFrom(const CONTEXT
*pCtx
, size_t skip
)
215 if ( !wxDbgHelpDLL::Init() )
217 wxLogError(_("Failed to get stack backtrace:\n%s"),
218 wxDbgHelpDLL::GetErrorMessage().c_str());
221 // according to MSDN, the first parameter should be just a unique value and
222 // not process handle (although the parameter is prototyped as "HANDLE
223 // hProcess") and actually it advises to use the process id and not handle
224 // for Win9x, but then we need to use the same value in StackWalk() call
225 // below which should be a real handle... so this is what we use
226 const HANDLE hProcess
= ::GetCurrentProcess();
228 if ( !wxDbgHelpDLL::SymInitialize
231 NULL
, // use default symbol search path
232 TRUE
// load symbols for all loaded modules
235 wxDbgHelpDLL::LogError(_T("SymInitialize"));
240 CONTEXT ctx
= *pCtx
; // will be modified by StackWalk()
244 // initialize the initial frame: currently we can do it for x86 only
249 sf
.AddrPC
.Offset
= ctx
.Eip
;
250 sf
.AddrPC
.Mode
= AddrModeFlat
;
251 sf
.AddrStack
.Offset
= ctx
.Esp
;
252 sf
.AddrStack
.Mode
= AddrModeFlat
;
253 sf
.AddrFrame
.Offset
= ctx
.Ebp
;
254 sf
.AddrFrame
.Mode
= AddrModeFlat
;
256 dwMachineType
= IMAGE_FILE_MACHINE_I386
;
258 #error "Need to initialize STACKFRAME on non x86"
261 // iterate over all stack frames
262 for ( size_t nLevel
= 0; ; nLevel
++ )
264 // get the next stack frame
265 if ( !wxDbgHelpDLL::StackWalk
269 ::GetCurrentThread(),
272 NULL
, // read memory function (default)
273 wxDbgHelpDLL::SymFunctionTableAccess
,
274 wxDbgHelpDLL::SymGetModuleBase
,
275 NULL
// address translator for 16 bit
278 if ( ::GetLastError() )
279 wxDbgHelpDLL::LogError(_T("StackWalk"));
284 // don't show this frame itself in the output
285 if ( nLevel
>= skip
)
287 wxStackFrame
frame(nLevel
- skip
,
288 (void *)sf
.AddrPC
.Offset
,
289 sf
.AddrFrame
.Offset
);
295 // this results in crashes inside ntdll.dll when called from
296 // exception handler ...
298 if ( !wxDbgHelpDLL::SymCleanup(hProcess
) )
300 wxDbgHelpDLL::LogError(_T("SymCleanup"));
305 void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS
*ep
, size_t skip
)
307 WalkFrom(ep
->ContextRecord
, skip
);
310 void wxStackWalker::WalkFromException()
312 extern EXCEPTION_POINTERS
*wxGlobalSEInformation
;
314 wxCHECK_RET( wxGlobalSEInformation
,
315 _T("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") );
317 // don't skip any frames, the first one is where we crashed
318 WalkFrom(wxGlobalSEInformation
, 0);
321 void wxStackWalker::Walk(size_t skip
)
323 // to get a CONTEXT for the current location, simply force an exception and
324 // get EXCEPTION_POINTERS from it
327 // 1. we additionally skip RaiseException() and WalkFromException() frames
328 // 2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it
329 // shouldn't have been according to the docs
332 RaiseException(0x1976, 0, 0, NULL
);
334 __except( WalkFrom((EXCEPTION_POINTERS
*)GetExceptionInformation(),
335 skip
+ 2), EXCEPTION_CONTINUE_EXECUTION
)
337 // never executed because of WalkFromException() return value
341 #else // !wxUSE_DBGHELP
343 // TODO: implement stubs
345 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
347 #endif // wxUSE_STACKWALKER