void DoStackTrace ( LPTSTR szString , DWORD dwSize , DWORD dwNumSkip ) { HANDLE hProcess = GetCurrentProcess ( ) ; // If the symbol engine is not initialized, do it now. if ( FALSE == g_bSymIsInit ) { DWORD dwOpts = SymGetOptions ( ) ; // Turn on load lines. SymSetOptions ( dwOpts | SYMOPT_LOAD_LINES ) ; if ( FALSE == g_cSym.SymInitialize ( hProcess , NULL , FALSE ) ) { //dsi_PrintToConsole("DoStackTrace : Unable to initialize the symbol engine!!!"); } else { g_bSymIsInit = TRUE ; } } // The symbol engine is initialized so do the stack walk. // The array of addresses. ADDRVECTOR vAddrs ; // The thread information. CONTEXT stCtx ; stCtx.ContextFlags = CONTEXT_FULL ; if ( GetThreadContext ( GetCurrentThread ( ) , &stCtx ) ) { STACKFRAME stFrame ; DWORD dwMachine ; ZeroMemory ( &stFrame , sizeof ( STACKFRAME ) ) ; stFrame.AddrPC.Mode = AddrModeFlat ; #ifdef _M_IX86 dwMachine = IMAGE_FILE_MACHINE_I386 ; #else dwMachine = IMAGE_FILE_MACHINE_AMD64; #endif #ifdef _M_IX86 stFrame.AddrPC.Offset = stCtx.Eip ; stFrame.AddrStack.Offset = stCtx.Esp ; #else stFrame.AddrPC.Offset = stCtx.Rip ; stFrame.AddrStack.Offset = stCtx.Rsp ; #endif stFrame.AddrStack.Mode = AddrModeFlat ; #ifdef _M_IX86 stFrame.AddrFrame.Offset = stCtx.Ebp ; #else stFrame.AddrFrame.Offset = stCtx.Rbp ; #endif stFrame.AddrFrame.Mode = AddrModeFlat ; // Loop for the first 512 stack elements. for ( DWORD i = 0 ; i < 512 ; i++ ) { if ( FALSE == StackWalk ( dwMachine , hProcess , hProcess , &stFrame , &stCtx , NULL , SymFunctionTableAccess , GetModBase , NULL ) ) { // [KLS 4/5/02] The optimization bug doesn't occur if you touch the // variable "i" here (e.g., call dsi_PrintToConsole("i = %d", i); ) // ...I love compiler bugs ;) break ; } if ( i > dwNumSkip ) { // Also check that the address is not zero. Sometimes // StackWalk returns TRUE with a frame of zero. if ( 0 != stFrame.AddrPC.Offset ) { vAddrs.push_back ( stFrame.AddrPC.Offset ) ; } } } // Now start converting the addresses. DWORD dwSizeLeft = dwSize ; DWORD dwSymSize ; TCHAR szSym [ MAX_PATH * 2 ] ; LPTSTR szCurrPos = szString ; ADDRVECTOR::iterator loop ; for ( loop = vAddrs.begin ( ) ; loop != vAddrs.end ( ) ; loop++ ) { dwSymSize = ConvertAddress ( *loop , szSym ) ; if ( dwSizeLeft < dwSymSize ) { break ; } _tcscpy ( szCurrPos , szSym ) ; szCurrPos += dwSymSize ; dwSizeLeft -= dwSymSize ; } } }
/** Print out a stacktrace. */ static void Stacktrace(LPEXCEPTION_POINTERS e, HANDLE hThread = INVALID_HANDLE_VALUE) { PIMAGEHLP_SYMBOL pSym; STACKFRAME sf; HANDLE process, thread; DWORD dwModBase, Disp; BOOL more = FALSE; int count = 0; char modname[MAX_PATH]; pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc(GMEM_FIXED, 16384); BOOL suspended = FALSE; CONTEXT c; if (e) { c = *e->ContextRecord; thread = GetCurrentThread(); } else { SuspendThread(hThread); suspended = TRUE; memset(&c, 0, sizeof(CONTEXT)); c.ContextFlags = CONTEXT_FULL; // FIXME: This does not work if you want to dump the current thread's stack if (!GetThreadContext(hThread, &c)) { ResumeThread(hThread); return; } thread = hThread; } ZeroMemory(&sf, sizeof(sf)); sf.AddrPC.Offset = c.Eip; sf.AddrStack.Offset = c.Esp; sf.AddrFrame.Offset = c.Ebp; sf.AddrPC.Mode = AddrModeFlat; sf.AddrStack.Mode = AddrModeFlat; sf.AddrFrame.Mode = AddrModeFlat; process = GetCurrentProcess(); // use globalalloc to reduce risk for allocator related deadlock char* printstrings = (char*)GlobalAlloc(GMEM_FIXED, 0); bool containsOglDll = false; while (true) { more = StackWalk( IMAGE_FILE_MACHINE_I386, // TODO: fix this for 64 bit windows? process, thread, &sf, &c, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL ); if (!more || sf.AddrFrame.Offset == 0) { break; } dwModBase = SymGetModuleBase(process, sf.AddrPC.Offset); if (dwModBase) { GetModuleFileName((HINSTANCE)dwModBase, modname, MAX_PATH); } else { strcpy(modname, "Unknown"); } pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); pSym->MaxNameLength = MAX_PATH; char *printstringsnew = (char *)GlobalAlloc(GMEM_FIXED, (count + 1) * BUFFER_SIZE); memcpy(printstringsnew, printstrings, count * BUFFER_SIZE); GlobalFree(printstrings); printstrings = printstringsnew; if (SymGetSymFromAddr(process, sf.AddrPC.Offset, &Disp, pSym)) { // This is the code path taken on VC if debugging syms are found. SNPRINTF(printstrings + count * BUFFER_SIZE, BUFFER_SIZE, "(%d) %s(%s+%#0lx) [0x%08lX]", count, modname, pSym->Name, Disp, sf.AddrPC.Offset); } else { // This is the code path taken on MinGW, and VC if no debugging syms are found. SNPRINTF(printstrings + count * BUFFER_SIZE, BUFFER_SIZE, "(%d) %s [0x%08lX]", count, modname, sf.AddrPC.Offset); } // OpenGL lib names (ATI): "atioglxx.dll" "atioglx2.dll" containsOglDll = containsOglDll || strstr(modname, "atiogl"); // OpenGL lib names (Nvidia): "nvoglnt.dll" "nvoglv32.dll" "nvoglv64.dll" (last one is a guess) containsOglDll = containsOglDll || strstr(modname, "nvogl"); ++count; } if (containsOglDll) { PRINT("This stack trace indicates a problem with your graphic card driver. " "Please try upgrading or downgrading it. " "Specifically recommended is the latest driver, and one that is as old as your graphic card. " "Make sure to use a driver removal utility, before installing other drivers."); } if (suspended) { ResumeThread(hThread); } for (int i = 0; i < count; ++i) { PRINT("%s", printstrings + i * BUFFER_SIZE); } GlobalFree(printstrings); GlobalFree(pSym); }