1///////////////////////////////////////////////////////////////////////////// 2// Name: msw/stackwalk.cpp 3// Purpose: wxStackWalker implementation for Win32 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 2005-01-08 7// RCS-ID: $Id: stackwalk.cpp 61341 2009-07-07 09:35:56Z VZ $ 8// Copyright: (c) 2003-2005 Vadim Zeitlin <vadim@wxwindows.org> 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12// ============================================================================ 13// declarations 14// ============================================================================ 15 16// ---------------------------------------------------------------------------- 17// headers 18// ---------------------------------------------------------------------------- 19 20#include "wx/wxprec.h" 21 22#ifdef __BORLANDC__ 23 #pragma hdrstop 24#endif 25 26#if wxUSE_STACKWALKER 27 28#ifndef WX_PRECOMP 29 #include "wx/string.h" 30#endif 31 32#include "wx/stackwalk.h" 33 34#include "wx/msw/debughlp.h" 35 36#if wxUSE_DBGHELP 37 38// ============================================================================ 39// implementation 40// ============================================================================ 41 42// ---------------------------------------------------------------------------- 43// wxStackFrame 44// ---------------------------------------------------------------------------- 45 46void wxStackFrame::OnGetName() 47{ 48 if ( m_hasName ) 49 return; 50 51 m_hasName = true; 52 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); 57 58 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer; 59 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); 60 pSymbol->MaxNameLen = MAX_NAME_LEN; 61 62 DWORD64 symDisplacement = 0; 63 if ( !wxDbgHelpDLL::SymFromAddr 64 ( 65 ::GetCurrentProcess(), 66 GetSymAddr(), 67 &symDisplacement, 68 pSymbol 69 ) ) 70 { 71 wxDbgHelpDLL::LogError(_T("SymFromAddr")); 72 return; 73 } 74 75 m_name = wxString::FromAscii(pSymbol->Name); 76 m_offset = symDisplacement; 77} 78 79void wxStackFrame::OnGetLocation() 80{ 81 if ( m_hasLocation ) 82 return; 83 84 m_hasLocation = true; 85 86 // get the source line for this stack frame entry 87 IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) }; 88 DWORD dwLineDisplacement; 89 if ( !wxDbgHelpDLL::SymGetLineFromAddr 90 ( 91 ::GetCurrentProcess(), 92 GetSymAddr(), 93 &dwLineDisplacement, 94 &lineInfo 95 ) ) 96 { 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")); 100 return; 101 } 102 103 m_filename = wxString::FromAscii(lineInfo.FileName); 104 m_line = lineInfo.LineNumber; 105} 106 107bool 108wxStackFrame::GetParam(size_t n, 109 wxString *type, 110 wxString *name, 111 wxString *value) const 112{ 113 if ( !DoGetParamCount() ) 114 ConstCast()->OnGetParam(); 115 116 if ( n >= DoGetParamCount() ) 117 return false; 118 119 if ( type ) 120 *type = m_paramTypes[n]; 121 if ( name ) 122 *name = m_paramNames[n]; 123 if ( value ) 124 *value = m_paramValues[n]; 125 126 return true; 127} 128 129void wxStackFrame::OnParam(PSYMBOL_INFO pSymInfo) 130{ 131 m_paramTypes.Add(wxEmptyString); 132 133 m_paramNames.Add(wxString::FromAscii(pSymInfo->Name)); 134 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...) 140#ifdef _CPPUNWIND 141 try 142#else 143 __try 144#endif 145 { 146 // as it is a parameter (and not a global var), it is always offset by 147 // the frame address 148 DWORD_PTR pValue = m_addrFrame + pSymInfo->Address; 149 m_paramValues.Add(wxDbgHelpDLL::DumpSymbol(pSymInfo, (void *)pValue)); 150 } 151#ifdef _CPPUNWIND 152 catch ( ... ) 153#else 154 __except ( EXCEPTION_EXECUTE_HANDLER ) 155#endif 156 { 157 m_paramValues.Add(wxEmptyString); 158 } 159} 160 161BOOL CALLBACK 162EnumSymbolsProc(PSYMBOL_INFO pSymInfo, ULONG WXUNUSED(SymSize), PVOID data) 163{ 164 wxStackFrame *frame = wx_static_cast(wxStackFrame *, data); 165 166 // we're only interested in parameters 167 if ( pSymInfo->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER ) 168 { 169 frame->OnParam(pSymInfo); 170 } 171 172 // return true to continue enumeration, false would have stopped it 173 return TRUE; 174} 175 176void wxStackFrame::OnGetParam() 177{ 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 183 ( 184 ::GetCurrentProcess(), 185 &imagehlpStackFrame, 186 0 // unused 187 ) ) 188 { 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 ) 192 { 193 wxDbgHelpDLL::LogError(_T("SymSetContext")); 194 } 195 196 return; 197 } 198 199 if ( !wxDbgHelpDLL::SymEnumSymbols 200 ( 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 206 ) ) 207 { 208 wxDbgHelpDLL::LogError(_T("SymEnumSymbols")); 209 } 210} 211 212 213// ---------------------------------------------------------------------------- 214// wxStackWalker 215// ---------------------------------------------------------------------------- 216 217void wxStackWalker::WalkFrom(const CONTEXT *pCtx, size_t skip) 218{ 219 if ( !wxDbgHelpDLL::Init() ) 220 { 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()); 226 return; 227 } 228 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(); 235 236 if ( !wxDbgHelpDLL::SymInitialize 237 ( 238 hProcess, 239 NULL, // use default symbol search path 240 TRUE // load symbols for all loaded modules 241 ) ) 242 { 243 wxDbgHelpDLL::LogError(_T("SymInitialize")); 244 245 return; 246 } 247 248 CONTEXT ctx = *pCtx; // will be modified by StackWalk() 249 250 DWORD dwMachineType; 251 252 // initialize the initial frame: currently we can do it for x86 only 253 STACKFRAME sf; 254 wxZeroMemory(sf); 255 256#ifdef _M_IX86 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; 263 264 dwMachineType = IMAGE_FILE_MACHINE_I386; 265#else 266 #error "Need to initialize STACKFRAME on non x86" 267#endif // _M_IX86 268 269 // iterate over all stack frames (but stop after 200 to avoid entering 270 // infinite loop if the stack is corrupted) 271 for ( size_t nLevel = 0; nLevel < 200; nLevel++ ) 272 { 273 // get the next stack frame 274 if ( !wxDbgHelpDLL::StackWalk 275 ( 276 dwMachineType, 277 hProcess, 278 ::GetCurrentThread(), 279 &sf, 280 &ctx, 281 NULL, // read memory function (default) 282 wxDbgHelpDLL::SymFunctionTableAccess, 283 wxDbgHelpDLL::SymGetModuleBase, 284 NULL // address translator for 16 bit 285 ) ) 286 { 287 if ( ::GetLastError() ) 288 wxDbgHelpDLL::LogError(_T("StackWalk")); 289 290 break; 291 } 292 293 // don't show this frame itself in the output 294 if ( nLevel >= skip ) 295 { 296 wxStackFrame frame(nLevel - skip, 297 (void *)sf.AddrPC.Offset, 298 sf.AddrFrame.Offset); 299 300 OnStackFrame(frame); 301 } 302 } 303 304 // this results in crashes inside ntdll.dll when called from 305 // exception handler ... 306#if 0 307 if ( !wxDbgHelpDLL::SymCleanup(hProcess) ) 308 { 309 wxDbgHelpDLL::LogError(_T("SymCleanup")); 310 } 311#endif 312} 313 314void wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS *ep, size_t skip) 315{ 316 WalkFrom(ep->ContextRecord, skip); 317} 318 319void wxStackWalker::WalkFromException() 320{ 321 // wxGlobalSEInformation is unavailable if wxUSE_ON_FATAL_EXCEPTION==0 322#if wxUSE_ON_FATAL_EXCEPTION 323 extern EXCEPTION_POINTERS *wxGlobalSEInformation; 324 325 wxCHECK_RET( wxGlobalSEInformation, 326 _T("wxStackWalker::WalkFromException() can only be called from wxApp::OnFatalException()") ); 327 328 // don't skip any frames, the first one is where we crashed 329 WalkFrom(wxGlobalSEInformation, 0); 330#endif // wxUSE_ON_FATAL_EXCEPTION 331} 332 333void wxStackWalker::Walk(size_t skip, size_t WXUNUSED(maxDepth)) 334{ 335 // to get a CONTEXT for the current location, simply force an exception and 336 // get EXCEPTION_POINTERS from it 337 // 338 // note: 339 // 1. we additionally skip RaiseException() and WalkFromException() frames 340 // 2. explicit cast to EXCEPTION_POINTERS is needed with VC7.1 even if it 341 // shouldn't have been according to the docs 342 __try 343 { 344 RaiseException(0x1976, 0, 0, NULL); 345 } 346 __except( WalkFrom((EXCEPTION_POINTERS *)GetExceptionInformation(), 347 skip + 2), EXCEPTION_CONTINUE_EXECUTION ) 348 { 349 // never executed because of WalkFromException() return value 350 } 351} 352 353#else // !wxUSE_DBGHELP 354 355// ============================================================================ 356// stubs 357// ============================================================================ 358 359// ---------------------------------------------------------------------------- 360// wxStackFrame 361// ---------------------------------------------------------------------------- 362 363void wxStackFrame::OnGetName() 364{ 365} 366 367void wxStackFrame::OnGetLocation() 368{ 369} 370 371bool 372wxStackFrame::GetParam(size_t WXUNUSED(n), 373 wxString * WXUNUSED(type), 374 wxString * WXUNUSED(name), 375 wxString * WXUNUSED(value)) const 376{ 377 return false; 378} 379 380void wxStackFrame::OnParam(_SYMBOL_INFO * WXUNUSED(pSymInfo)) 381{ 382} 383 384void wxStackFrame::OnGetParam() 385{ 386} 387 388// ---------------------------------------------------------------------------- 389// wxStackWalker 390// ---------------------------------------------------------------------------- 391 392void 393wxStackWalker::WalkFrom(const CONTEXT * WXUNUSED(pCtx), size_t WXUNUSED(skip)) 394{ 395} 396 397void 398wxStackWalker::WalkFrom(const _EXCEPTION_POINTERS * WXUNUSED(ep), 399 size_t WXUNUSED(skip)) 400{ 401} 402 403void wxStackWalker::WalkFromException() 404{ 405} 406 407void wxStackWalker::Walk(size_t WXUNUSED(skip), size_t WXUNUSED(maxDepth)) 408{ 409} 410 411#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP 412 413#endif // wxUSE_STACKWALKER 414 415