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     // 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 
= 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         // 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(_T("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(_T("SymInitialize")); 
 248     CONTEXT ctx 
= *pCtx
; // will be modified by StackWalk() 
 252     // initialize the initial frame: currently we can do it for x86 only 
 257     sf
.AddrPC
.Offset       
= ctx
.Eip
; 
 258     sf
.AddrPC
.Mode         
= AddrModeFlat
; 
 259     sf
.AddrStack
.Offset    
= ctx
.Esp
; 
 260     sf
.AddrStack
.Mode      
= AddrModeFlat
; 
 261     sf
.AddrFrame
.Offset    
= ctx
.Ebp
; 
 262     sf
.AddrFrame
.Mode      
= AddrModeFlat
; 
 264     dwMachineType 
= IMAGE_FILE_MACHINE_I386
; 
 266     #error "Need to initialize STACKFRAME on non x86" 
 269     // iterate over all stack frames 
 270     for ( size_t nLevel 
= 0; ; nLevel
++ ) 
 272         // get the next stack frame 
 273         if ( !wxDbgHelpDLL::StackWalk
 
 277                                 ::GetCurrentThread(), 
 280                                 NULL
,       // read memory function (default) 
 281                                 wxDbgHelpDLL::SymFunctionTableAccess
, 
 282                                 wxDbgHelpDLL::SymGetModuleBase
, 
 283                                 NULL        
// address translator for 16 bit 
 286             if ( ::GetLastError() ) 
 287                 wxDbgHelpDLL::LogError(_T("StackWalk")); 
 292         // don't show this frame itself in the output 
 293         if ( nLevel 
>= skip 
) 
 295             wxStackFrame 
frame(nLevel 
- skip
, 
 296                                (void *)sf
.AddrPC
.Offset
, 
 297                                sf
.AddrFrame
.Offset
); 
 303     // this results in crashes inside ntdll.dll when called from 
 304     // exception handler ... 
 306     if ( !wxDbgHelpDLL::SymCleanup(hProcess
) ) 
 308         wxDbgHelpDLL::LogError(_T("SymCleanup")); 
 313 void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS 
*ep
, size_t skip
) 
 315     WalkFrom(ep
->ContextRecord
, skip
); 
 318 void wxStackWalker::WalkFromException() 
 320     extern EXCEPTION_POINTERS 
*wxGlobalSEInformation
; 
 322     wxCHECK_RET( wxGlobalSEInformation
, 
 323                  _T("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") ); 
 325     // don't skip any frames, the first one is where we crashed 
 326     WalkFrom(wxGlobalSEInformation
, 0); 
 329 void wxStackWalker::Walk(size_t skip
) 
 331     // to get a CONTEXT for the current location, simply force an exception and 
 332     // get EXCEPTION_POINTERS from it 
 335     //  1. we additionally skip RaiseException() and WalkFromException() frames 
 336     //  2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it 
 337     //     shouldn't have been according to the docs 
 340         RaiseException(0x1976, 0, 0, NULL
); 
 342     __except( WalkFrom((EXCEPTION_POINTERS 
*)GetExceptionInformation(), 
 343                        skip 
+ 2), EXCEPTION_CONTINUE_EXECUTION 
) 
 345         // never executed because of WalkFromException() return value 
 349 #else // !wxUSE_DBGHELP 
 351 // ============================================================================ 
 353 // ============================================================================ 
 355 // ---------------------------------------------------------------------------- 
 357 // ---------------------------------------------------------------------------- 
 359 void wxStackFrame::OnGetName() 
 363 void wxStackFrame::OnGetLocation() 
 368 wxStackFrame::GetParam(size_t WXUNUSED(n
), 
 369                        wxString 
* WXUNUSED(type
), 
 370                        wxString 
* WXUNUSED(name
), 
 371                        wxString 
* WXUNUSED(value
)) const 
 376 void wxStackFrame::OnParam(_SYMBOL_INFO 
* WXUNUSED(pSymInfo
)) 
 380 void wxStackFrame::OnGetParam() 
 384 // ---------------------------------------------------------------------------- 
 386 // ---------------------------------------------------------------------------- 
 389 wxStackWalker::WalkFrom(const CONTEXT 
* WXUNUSED(pCtx
), size_t WXUNUSED(skip
)) 
 394 wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS 
* WXUNUSED(ep
), 
 395                         size_t WXUNUSED(skip
)) 
 399 void wxStackWalker::WalkFromException() 
 403 void wxStackWalker::Walk(size_t WXUNUSED(skip
)) 
 407 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP 
 409 #endif // wxUSE_STACKWALKER