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" 
  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(symbolBuffer
); 
  60     pSymbol
->MaxNameLen 
= MAX_NAME_LEN
; 
  62     DWORD64 symDisplacement 
= 0; 
  63     if ( !wxDbgHelpDLL::SymFromAddr
 
  65                             ::GetCurrentProcess(), 
  71         wxDbgHelpDLL::LogError(_T("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(_T("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     // except handler there returning EXCEPTION_CONTINUE_EXECUTION, but we'd be 
 138     // left in an inconsistent state, so deal with it explicitely here (even if 
 139     // 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 
= wx_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(_T("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(_T("SymEnumSymbols")); 
 213 // ---------------------------------------------------------------------------- 
 215 // ---------------------------------------------------------------------------- 
 217 void wxStackWalker::WalkFrom(const CONTEXT 
*pCtx
, size_t skip
) 
 219     if ( !wxDbgHelpDLL::Init() ) 
 221         wxLogError(_("Failed to get stack backtrace:\n%s"), 
 222                    wxDbgHelpDLL::GetErrorMessage().c_str()); 
 225     // according to MSDN, the first parameter should be just a unique value and 
 226     // not process handle (although the parameter is prototyped as "HANDLE 
 227     // hProcess") and actually it advises to use the process id and not handle 
 228     // for Win9x, but then we need to use the same value in StackWalk() call 
 229     // below which should be a real handle... so this is what we use 
 230     const HANDLE hProcess 
= ::GetCurrentProcess(); 
 232     if ( !wxDbgHelpDLL::SymInitialize
 
 235                             NULL
,   // use default symbol search path 
 236                             TRUE    
// load symbols for all loaded modules 
 239         wxDbgHelpDLL::LogError(_T("SymInitialize")); 
 244     CONTEXT ctx 
= *pCtx
; // will be modified by StackWalk() 
 248     // initialize the initial frame: currently we can do it for x86 only 
 253     sf
.AddrPC
.Offset       
= ctx
.Eip
; 
 254     sf
.AddrPC
.Mode         
= AddrModeFlat
; 
 255     sf
.AddrStack
.Offset    
= ctx
.Esp
; 
 256     sf
.AddrStack
.Mode      
= AddrModeFlat
; 
 257     sf
.AddrFrame
.Offset    
= ctx
.Ebp
; 
 258     sf
.AddrFrame
.Mode      
= AddrModeFlat
; 
 260     dwMachineType 
= IMAGE_FILE_MACHINE_I386
; 
 262     #error "Need to initialize STACKFRAME on non x86" 
 265     // iterate over all stack frames 
 266     for ( size_t nLevel 
= 0; ; nLevel
++ ) 
 268         // get the next stack frame 
 269         if ( !wxDbgHelpDLL::StackWalk
 
 273                                 ::GetCurrentThread(), 
 276                                 NULL
,       // read memory function (default) 
 277                                 wxDbgHelpDLL::SymFunctionTableAccess
, 
 278                                 wxDbgHelpDLL::SymGetModuleBase
, 
 279                                 NULL        
// address translator for 16 bit 
 282             if ( ::GetLastError() ) 
 283                 wxDbgHelpDLL::LogError(_T("StackWalk")); 
 288         // don't show this frame itself in the output 
 289         if ( nLevel 
>= skip 
) 
 291             wxStackFrame 
frame(nLevel 
- skip
, 
 292                                (void *)sf
.AddrPC
.Offset
, 
 293                                sf
.AddrFrame
.Offset
); 
 299     // this results in crashes inside ntdll.dll when called from 
 300     // exception handler ... 
 302     if ( !wxDbgHelpDLL::SymCleanup(hProcess
) ) 
 304         wxDbgHelpDLL::LogError(_T("SymCleanup")); 
 309 void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS 
*ep
, size_t skip
) 
 311     WalkFrom(ep
->ContextRecord
, skip
); 
 314 void wxStackWalker::WalkFromException() 
 316     extern EXCEPTION_POINTERS 
*wxGlobalSEInformation
; 
 318     wxCHECK_RET( wxGlobalSEInformation
, 
 319                  _T("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") ); 
 321     // don't skip any frames, the first one is where we crashed 
 322     WalkFrom(wxGlobalSEInformation
, 0); 
 325 void wxStackWalker::Walk(size_t skip
) 
 327     // to get a CONTEXT for the current location, simply force an exception and 
 328     // get EXCEPTION_POINTERS from it 
 331     //  1. we additionally skip RaiseException() and WalkFromException() frames 
 332     //  2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it 
 333     //     shouldn't have been according to the docs 
 336         RaiseException(0x1976, 0, 0, NULL
); 
 338     __except( WalkFrom((EXCEPTION_POINTERS 
*)GetExceptionInformation(), 
 339                        skip 
+ 2), EXCEPTION_CONTINUE_EXECUTION 
) 
 341         // never executed because of WalkFromException() return value 
 345 #else // !wxUSE_DBGHELP 
 347 // ============================================================================ 
 349 // ============================================================================ 
 351 // ---------------------------------------------------------------------------- 
 353 // ---------------------------------------------------------------------------- 
 355 void wxStackFrame::OnGetName() 
 359 void wxStackFrame::OnGetLocation() 
 364 wxStackFrame::GetParam(size_t WXUNUSED(n
), 
 365                        wxString 
* WXUNUSED(type
), 
 366                        wxString 
* WXUNUSED(name
), 
 367                        wxString 
* WXUNUSED(value
)) const 
 372 void wxStackFrame::OnParam(_SYMBOL_INFO 
* WXUNUSED(pSymInfo
)) 
 376 void wxStackFrame::OnGetParam() 
 380 // ---------------------------------------------------------------------------- 
 382 // ---------------------------------------------------------------------------- 
 385 wxStackWalker::WalkFrom(const CONTEXT 
* WXUNUSED(pCtx
), size_t WXUNUSED(skip
)) 
 390 wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS 
* WXUNUSED(ep
), 
 391                         size_t WXUNUSED(skip
)) 
 395 void wxStackWalker::WalkFromException() 
 399 void wxStackWalker::Walk(size_t WXUNUSED(skip
)) 
 403 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP 
 405 #endif // wxUSE_STACKWALKER