// $Id$ // /*//////////////////////////////////////////////////////////////////////////// * Project: * Memory_and_Exception_Trace * * /////////////////////////////////////////////////////////////////////////// * File: * Stackwalker.cpp * * Remarks: * Dumps the stack of an thread if an exception occurs * * Author: * Jochen Kalmbach, Germany * (c) 2002-2003 (Freeware) * http://www.codeproject.com/tools/leakfinder.asp * * License (The zlib/libpng License, http://www.opensource.org/licenses/zlib-license.php): * * Copyright (c) 2003 Jochen Kalmbach * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from the * use of this software. * * Permission is granted to anyone to use this software for any purpose, including * commercial applications, and to alter it and redistribute it freely, subject to * the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not claim * that you wrote the original software. If you use this software in a product, * an acknowledgment in the product documentation would be appreciated but is * not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * *////////////////////////////////////////////////////////////////////////////// #if defined(_WIN32) #include "boinc_win.h" #endif #include "diagnostics.h" #include "str_replace.h" #include "str_util.h" #include "stackwalker_win.h" #include "stackwalker_imports.h" // Link to dbghelp.dll and version.dll dynamically at runtime so we // can be specific about which version we are getting and where // we are getting it from static tIAV pIAV = NULL; // ImagehlpApiVersion() static tSC pSC = NULL; // SymCleanup() static tSEM pSEM = NULL; // SymEnumerateModules64() static tSFTA pSFTA = NULL; // SymFunctionTableAccess64() static tSGLFA pSGLFA = NULL; // SymGetLineFromAddr64() static tSGMB pSGMB = NULL; // SymGetModuleBase64() static tSGMI pSGMI = NULL; // SymGetModuleInfo64() static tSGO pSGO = NULL; // SymGetOptions() static tSGSP pSGSP = NULL; // SymGetSearchPath() static tSFA pSFA = NULL; // SymFromAddr() static tSI pSI = NULL; // SymInitialize() static tSLM pSLM = NULL; // SymLoadModuleEx() static tSRC pSRC = NULL; // SymRegisterCallback64() static tSSO pSSO = NULL; // SymSetOptions() static tSW pSW = NULL; // StackWalk() static tUDSN pUDSN = NULL; // UnDecorateSymbolName() static tSSSO pSSSO = NULL; // SymbolServerSetOptions static tSDD pSDD = NULL; // SetDllDirectory static tGFVIS pGFVIS = NULL; // GetFileVersionInfoSize static tGFVI pGFVI = NULL; // GetFileVersionInfo static tVQV pVQV = NULL; // VerQueryValue // Forward definitions of functions: static void ShowStackRM(HANDLE hThread, CONTEXT& c); // Global data: static BOOL g_bInitialized = FALSE; static HANDLE g_hProcess = NULL; static HMODULE g_hDbgHelpDll = NULL; static HMODULE g_hSymSrvDll = NULL; static HMODULE g_hSrcSrvDll = NULL; static HMODULE g_hVersionDll = NULL; static CRITICAL_SECTION g_csFileOpenClose = {0}; // ########################################################################################## // ########################################################################################## // ########################################################################################## // ########################################################################################## bool DebuggerLoadLibrary( HINSTANCE* lphInstance, const std::string strBOINCLocation, const std::string strLibrary ) { std::string strTargetLibrary; if (strBOINCLocation.empty()) { strTargetLibrary = strLibrary; } else { strTargetLibrary = strBOINCLocation + "\\" + strLibrary; } *lphInstance = LoadLibraryA( strTargetLibrary.c_str() ); if ( *lphInstance == NULL ) { fprintf(stderr, "LoadLibraryA( %s ): GetLastError = %lu\n", strTargetLibrary.c_str(), gle); strTargetLibrary = strLibrary; *lphInstance = LoadLibraryA( strTargetLibrary.c_str() ); if ( *lphInstance == NULL ) { fprintf(stderr, "LoadLibraryA( %s ): GetLastError = %lu\n", strTargetLibrary.c_str(), gle); return false; } } fprintf(stderr, "Loaded Library : %s\n", strTargetLibrary.c_str()); return true; } BOOL CALLBACK SymbolServerCallbackProc64(UINT_PTR ActionCode, ULONG64 CallbackData, ULONG64 /* UserContext */) { BOOL bRetVal = FALSE; PIMAGEHLP_CBA_EVENT pEvent = NULL; switch(ActionCode) { case SSRVACTION_EVENT: pEvent = (PIMAGEHLP_CBA_EVENT)CallbackData; switch(pEvent->severity) { case sevInfo: fprintf(stderr, "SSRVINFO: %s\n", pEvent->desc); break; case sevProblem: fprintf(stderr, "SSRVPROB: %s\n", pEvent->desc); break; case sevAttn: fprintf(stderr, "SSRVATTN: %s\n", pEvent->desc); break; case sevFatal: fprintf(stderr, "SSRVFATAL: %s\n", pEvent->desc); break; } bRetVal = TRUE; break; case SSRVACTION_TRACE: fprintf(stderr, "SSRVDEBUG: %s\n", (PCTSTR)CallbackData); bRetVal = TRUE; break; } return bRetVal; } BOOL CALLBACK SymRegisterCallbackProc64(HANDLE /* hProcess */, ULONG ActionCode, ULONG64 CallbackData, ULONG64 /* UserContext */) { BOOL bRetVal = FALSE; PIMAGEHLP_CBA_EVENT pEvent = NULL; switch(ActionCode) { case CBA_EVENT: pEvent = (PIMAGEHLP_CBA_EVENT)CallbackData; switch(pEvent->severity) { case sevInfo: fprintf(stderr, "INFO: %s\n", pEvent->desc); break; case sevProblem: fprintf(stderr, "PROB: %s\n", pEvent->desc); break; case sevAttn: fprintf(stderr, "ATTN: %s\n", pEvent->desc); break; case sevFatal: fprintf(stderr, "FATAL: %s\n", pEvent->desc); break; } bRetVal = TRUE; break; case CBA_DEBUG_INFO: fprintf(stderr, "DEBUG: %s\n", (PCTSTR)CallbackData); bRetVal = TRUE; break; } return bRetVal; } BOOL CALLBACK SymEnumerateModulesProc64(LPCSTR /* ModuleName */, DWORD64 BaseOfDll, PVOID /* UserContext */) { IMAGEHLP_MODULE64 Module; char szSymbolType[32]; DWORD dwHandle; LPVOID lpData; DWORD dwSize; char szQuery[256]; LPVOID lpVar; UINT uiVarSize; VS_FIXEDFILEINFO* pFileInfo; char szVersionInfo[24]; char szCompanyName[256]; char szProductName[256]; char szFileVersion[256]; char szProductVersion[256]; bool bFileVersionSupported = false; bool bFileVersionRetrieved = false; struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; } *lpTranslate; memset( &Module, '\0', sizeof(IMAGEHLP_MODULE64) ); Module.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); memset( &szSymbolType, '\0', sizeof(szSymbolType) ); memset( &szQuery, '\0', sizeof(szQuery) ); memset( &szVersionInfo, '\0', sizeof(szVersionInfo) ); memset( &szCompanyName, '\0', sizeof(szCompanyName) ); memset( &szProductName, '\0', sizeof(szProductName) ); memset( &szFileVersion, '\0', sizeof(szFileVersion) ); memset( &szProductVersion, '\0', sizeof(szProductVersion) ); if ( !pSGMI( g_hProcess, BaseOfDll, &Module ) ) { fprintf(stderr, "SymGetModuleInfo(): GetLastError = %lu\n", gle); } else { switch ( Module.SymType ) { case SymNone: safe_strcpy( szSymbolType, "-nosymbols-" ); break; case SymCoff: safe_strcpy( szSymbolType, "COFF" ); break; case SymCv: safe_strcpy( szSymbolType, "CV" ); break; case SymPdb: safe_strcpy( szSymbolType, "PDB" ); break; case SymExport: safe_strcpy( szSymbolType, "-exported-" ); break; case SymDeferred: safe_strcpy( szSymbolType, "-deferred-" ); break; case SymSym: safe_strcpy( szSymbolType, "SYM" ); break; default: snprintf( szSymbolType, sizeof(szSymbolType), "symtype=%ld", (long) Module.SymType ); break; } } // Get File Version Information // bFileVersionSupported = (NULL != pGFVIS) && (NULL != pGFVI) && (NULL != pVQV); if (bFileVersionSupported) { dwSize = pGFVIS(Module.LoadedImageName, &dwHandle); if (dwSize) { lpData = (LPVOID)malloc(dwSize); if(pGFVI(Module.LoadedImageName, dwHandle, dwSize, lpData)) { bFileVersionRetrieved = true; // Which language should be used to lookup the structure? safe_strcpy(szQuery, "\\VarFileInfo\\Translation"); pVQV(lpData, szQuery, (LPVOID*)&lpTranslate, &uiVarSize); // Version specified as part of the root record. if (pVQV(lpData, "\\", (LPVOID*)&pFileInfo, &uiVarSize)) { snprintf(szVersionInfo, sizeof(szVersionInfo), "%d.%d.%d.%d", HIWORD(pFileInfo->dwFileVersionMS), LOWORD(pFileInfo->dwFileVersionMS), HIWORD(pFileInfo->dwFileVersionLS), LOWORD(pFileInfo->dwFileVersionLS) ); } // Company Name. snprintf(szQuery, sizeof(szQuery), "\\StringFileInfo\\%04x%04x\\CompanyName", lpTranslate[0].wLanguage, lpTranslate[0].wCodePage ); if (pVQV(lpData, szQuery, &lpVar, &uiVarSize)) { uiVarSize = snprintf(szCompanyName, sizeof(szCompanyName), "%s", lpVar); if ((sizeof(szCompanyName) == uiVarSize) || (-1 == uiVarSize)) { szCompanyName[255] = '\0'; } } else { fprintf(stderr, "Get Company Name Failed.\n"); } // Product Name. snprintf(szQuery, sizeof(szQuery), "\\StringFileInfo\\%04x%04x\\ProductName", lpTranslate[0].wLanguage, lpTranslate[0].wCodePage ); if (pVQV(lpData, szQuery, &lpVar, &uiVarSize)) { uiVarSize = snprintf(szProductName, sizeof(szProductName), "%s", lpVar); if ((sizeof(szProductName) == uiVarSize) || (-1 == uiVarSize)) { szProductName[255] = '\0'; } } else { fprintf(stderr, "Get Product Name Failed.\n"); } // File Version. snprintf(szQuery, sizeof(szQuery), "\\StringFileInfo\\%04x%04x\\FileVersion", lpTranslate[0].wLanguage, lpTranslate[0].wCodePage ); if (pVQV(lpData, szQuery, &lpVar, &uiVarSize)) { uiVarSize = snprintf(szFileVersion, sizeof(szFileVersion), "%s", lpVar); if ((sizeof(szFileVersion) == uiVarSize) || (-1 == uiVarSize)) { szFileVersion[255] = '\0'; } } // Product Version. snprintf(szQuery, sizeof(szQuery), "\\StringFileInfo\\%04x%04x\\ProductVersion", lpTranslate[0].wLanguage, lpTranslate[0].wCodePage ); if (pVQV(lpData, szQuery, &lpVar, &uiVarSize)) { uiVarSize = snprintf(szProductVersion, sizeof(szProductVersion), "%s", lpVar); if ((sizeof(szProductVersion) == uiVarSize) || (-1 == uiVarSize)) { szProductVersion[255] = '\0'; } } free(lpData); } } } fprintf(stderr, "ModLoad: "); fprintf(stderr, "%.16x " , Module.BaseOfImage); fprintf(stderr, "%.16x " , Module.ImageSize); fprintf(stderr, "%s " , Module.LoadedImageName); if (bFileVersionSupported && bFileVersionRetrieved) { fprintf(stderr, "(%s) " , szVersionInfo); } fprintf(stderr, "(%s Symbols Loaded)" , szSymbolType); fprintf(stderr, "\n"); #ifndef __MINGW32__ fprintf(stderr, " Linked PDB Filename : %s\n" , Module.CVData); #endif if (bFileVersionSupported && bFileVersionRetrieved) { fprintf(stderr, " File Version : %s\n" , szFileVersion); fprintf(stderr, " Company Name : %s\n" , szCompanyName); fprintf(stderr, " Product Name : %s\n" , szProductName); fprintf(stderr, " Product Version : %s\n" , szProductVersion); } fprintf(stderr, "\n"); return TRUE; } int DebuggerInitialize( LPCSTR pszBOINCLocation, LPCSTR pszSymbolStore, BOOL bProxyEnabled, LPCSTR pszProxyServer ) { if (g_bInitialized != FALSE) return 0; // Get a real handle to the current process and store it for future use. DuplicateHandle( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &g_hProcess, 0, false, DUPLICATE_SAME_ACCESS ); if (!DebuggerLoadLibrary(&g_hDbgHelpDll, pszBOINCLocation, "dbghelp.dll")) { g_bInitialized = FALSE; return 1; } DebuggerLoadLibrary(&g_hSymSrvDll, pszBOINCLocation, "symsrv.dll"); DebuggerLoadLibrary(&g_hSrcSrvDll, pszBOINCLocation, "srcsrv.dll"); DebuggerLoadLibrary(&g_hVersionDll, pszBOINCLocation, "version.dll"); if (g_hSymSrvDll) { pSSSO = (tSSSO)GetProcAddress(g_hSymSrvDll, "SymbolServerSetOptions"); if (pSSSO) { if (!pSSSO(SSRVOPT_TRACE, (ULONG64)TRUE)) { fprintf(stderr, "SymbolServerSetOptions(): Register Trace Failed, GetLastError = %lu\n", gle); } if (!pSSSO(SSRVOPT_CALLBACK, (ULONG64)SymbolServerCallbackProc64)) { fprintf(stderr, "SymbolServerSetOptions(): Register Callback Failed, GetLastError = %lu\n", gle); } if (!pSSSO(SSRVOPT_UNATTENDED, (ULONG64)TRUE)) { fprintf(stderr, "SymbolServerSetOptions(): Register Unattended Failed, GetLastError = %lu\n", gle); } if (bProxyEnabled) { if (!pSSSO(SSRVOPT_PROXY, (ULONG64)pszProxyServer)) { fprintf(stderr, "SymbolServerSetOptions(): Register Proxy Failed, GetLastError = %lu\n", gle); } } else { if (!pSSSO(SSRVOPT_PROXY, (ULONG64)NULL)) { fprintf(stderr, "SymbolServerSetOptions(): Register Proxy Failed, GetLastError = %lu\n", gle); } } } } if (g_hVersionDll) { pGFVIS = (tGFVIS)GetProcAddress(g_hVersionDll, "GetFileVersionInfoSizeA"); pGFVI = (tGFVI)GetProcAddress(g_hVersionDll, "GetFileVersionInfoA"); pVQV = (tVQV)GetProcAddress(g_hVersionDll, "VerQueryValueA"); } pIAV = (tIAV) GetProcAddress( g_hDbgHelpDll, "ImagehlpApiVersion" ); pSC = (tSC) GetProcAddress( g_hDbgHelpDll, "SymCleanup" ); pSEM = (tSEM) GetProcAddress( g_hDbgHelpDll, "SymEnumerateModules64" ); pSFTA = (tSFTA) GetProcAddress( g_hDbgHelpDll, "SymFunctionTableAccess64" ); pSGLFA = (tSGLFA) GetProcAddress( g_hDbgHelpDll, "SymGetLineFromAddr64" ); pSGMB = (tSGMB) GetProcAddress( g_hDbgHelpDll, "SymGetModuleBase64" ); pSGMI = (tSGMI) GetProcAddress( g_hDbgHelpDll, "SymGetModuleInfo64" ); pSGO = (tSGO) GetProcAddress( g_hDbgHelpDll, "SymGetOptions" ); pSGSP = (tSGSP) GetProcAddress( g_hDbgHelpDll, "SymGetSearchPath" ); pSFA = (tSFA) GetProcAddress( g_hDbgHelpDll, "SymFromAddr" ); pSI = (tSI) GetProcAddress( g_hDbgHelpDll, "SymInitialize" ); pSRC = (tSRC) GetProcAddress( g_hDbgHelpDll, "SymRegisterCallback64" ); pSSO = (tSSO) GetProcAddress( g_hDbgHelpDll, "SymSetOptions" ); pSW = (tSW) GetProcAddress( g_hDbgHelpDll, "StackWalk64" ); pUDSN = (tUDSN) GetProcAddress( g_hDbgHelpDll, "UnDecorateSymbolName" ); pSLM = (tSLM) GetProcAddress( g_hDbgHelpDll, "SymLoadModuleEx" ); if ( pIAV == NULL || pSC == NULL || pSEM == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || pSGO == NULL || pSFA == NULL || pSI == NULL || pSRC == NULL || pSSO == NULL || pSW == NULL || pUDSN == NULL || pSLM == NULL ) { if (!pIAV) fprintf( stderr, "GetProcAddress(): ImagehlpApiVersion missing.\n" ); if (!pSC) fprintf( stderr, "GetProcAddress(): SymCleanup missing.\n" ); if (!pSEM) fprintf( stderr, "GetProcAddress(): SymEnumerateModules64 missing.\n" ); if (!pSFTA) fprintf( stderr, "GetProcAddress(): SymFunctionTableAccess64 missing.\n" ); if (!pSGLFA) fprintf( stderr, "GetProcAddress(): SymGetLineFromAddr64 missing.\n" ); if (!pSGMB) fprintf( stderr, "GetProcAddress(): SymGetModuleBase64 missing.\n" ); if (!pSGMI) fprintf( stderr, "GetProcAddress(): SymGetModuleInfo64 missing.\n" ); if (!pSGO) fprintf( stderr, "GetProcAddress(): SymGetOptions missing.\n" ); if (!pSGSP) fprintf( stderr, "GetProcAddress(): SymGetSearchPath missing.\n" ); if (!pSFA) fprintf( stderr, "GetProcAddress(): SymFromAddr missing.\n" ); if (!pSI) fprintf( stderr, "GetProcAddress(): SymInitialize missing.\n" ); if (!pSRC) fprintf( stderr, "GetProcAddress(): SymRegisterCallback64 missing.\n" ); if (!pSSO) fprintf( stderr, "GetProcAddress(): SymSetOptions missing.\n" ); if (!pSW) fprintf( stderr, "GetProcAddress(): StackWalk64 missing.\n" ); if (!pUDSN) fprintf( stderr, "GetProcAddress(): UnDecorateSymbolName missing.\n" ); if (!pSLM) fprintf( stderr, "GetProcAddress(): SymLoadModuleEx missing.\n" ); FreeLibrary( g_hDbgHelpDll ); g_bInitialized = FALSE; return 1; } g_bInitialized = TRUE; InitializeCriticalSection(&g_csFileOpenClose); EnterCriticalSection(&g_csFileOpenClose); CHAR* tt; CHAR* p; DWORD symOptions; // symbol handler settings std::string strCurrentDirectory; std::string strExecutableDirectory; std::string strLocalSymbolStore; std::string strSymbolSearchPath; tt = (CHAR*) malloc(sizeof(CHAR) * TTBUFLEN); // Get the temporary buffer if (!tt) goto error; // not enough memory... // build symbol search path from: strCurrentDirectory = ""; strExecutableDirectory = ""; strLocalSymbolStore = ""; strSymbolSearchPath = ""; // Detect Current Directory if ( GetCurrentDirectoryA( TTBUFLEN, tt ) ) { strCurrentDirectory = tt; } // Detect Executable Directory if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) ) { for ( p = tt + strlen( tt ) - 1; p >= tt; -- p ) { // locate the rightmost path separator if ( *p == '\\' || *p == '/' || *p == ':' ) { break; } } // if we found one, p is pointing at it; if not, tt only contains // an exe name (no path), and p points before its first byte if ( p != tt ) // path sep found? { if ( *p == ':' ) { // we leave colons in place ++p; } *p = '\0'; // eliminate the exe name and last path sep strExecutableDirectory += tt; } } // Current Directory if (!strCurrentDirectory.empty()) { strSymbolSearchPath += strCurrentDirectory + std::string( ";" ); } // Executable Directory if (!strExecutableDirectory.empty()) { strSymbolSearchPath += strExecutableDirectory + std::string( ";" ); } // Environment Variable _NT_SYMBOL_PATH if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) ) { strSymbolSearchPath += tt + std::string( ";" ); } // Environment Variable _NT_ALTERNATE_SYMBOL_PATH if ( GetEnvironmentVariableA( "_NT_ALT_SYMBOL_PATH", tt, TTBUFLEN ) ) { strSymbolSearchPath += tt + std::string( ";" ); } // Depending on if we are a BOINC application or a project application // we'll need to store our symbol files in two different locations. // // BOINC: // [DATADIR]\symbols // Project: // [DATADIR]\projects\project_dir\symbols // if (!diagnostics_is_flag_set(BOINC_DIAG_BOINCAPPLICATION)) { strLocalSymbolStore += strCurrentDirectory + std::string("\\symbols"); } else { strLocalSymbolStore += strExecutableDirectory + std::string("\\symbols"); } // Microsoft Public Symbol Server if (!diagnostics_is_flag_set(BOINC_DIAG_BOINCAPPLICATION) || (0 < strlen(pszSymbolStore))) { if (std::string::npos == strSymbolSearchPath.find("http://msdl.microsoft.com/download/symbols")) { strSymbolSearchPath += std::string( "srv*" ) + strLocalSymbolStore + std::string( "*http://msdl.microsoft.com/download/symbols;" ); } } // Project Symbol Server if (diagnostics_is_flag_set(BOINC_DIAG_BOINCAPPLICATION) && (0 < strlen(pszSymbolStore))) { if (std::string::npos == strSymbolSearchPath.find(pszSymbolStore)) { strSymbolSearchPath += std::string( "srv*" ) + strLocalSymbolStore + std::string( "*" ) + std::string( pszSymbolStore ) + std::string( ";" ); } } // BOINC Symbol Server if (!diagnostics_is_flag_set(BOINC_DIAG_BOINCAPPLICATION)) { if (std::string::npos == strSymbolSearchPath.find("http://boinc.berkeley.edu/symstore")) { strSymbolSearchPath += std::string( "srv*" ) + strLocalSymbolStore + std::string( "*http://boinc.berkeley.edu/symstore;" ); } } if ( strSymbolSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon strSymbolSearchPath = strSymbolSearchPath.substr( 0, strSymbolSearchPath.size() - 1 ); free( tt ); // Setting symbol options to the WinDbg defaults. symOptions = (DWORD)NULL; symOptions |= SYMOPT_CASE_INSENSITIVE; symOptions |= SYMOPT_LOAD_LINES; symOptions |= SYMOPT_OMAP_FIND_NEAREST; symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; symOptions |= SYMOPT_AUTO_PUBLICS; symOptions |= SYMOPT_NO_IMAGE_SEARCH; symOptions |= SYMOPT_DEBUG; symOptions |= SYMOPT_NO_PROMPTS; pSSO( symOptions ); // SymSetOptions() // init symbol handler stuff (SymInitialize()) if (!pSI(g_hProcess, strSymbolSearchPath.c_str(), TRUE)) { fprintf(stderr, "SymInitialize(): GetLastError = %lu\n", gle); goto error; } if (!pSRC(g_hProcess, SymRegisterCallbackProc64, (ULONG64)g_hProcess)) { fprintf(stderr, "SymRegisterCallback64(): GetLastError = %lu\n", gle); } LeaveCriticalSection(&g_csFileOpenClose); return 0; error: LeaveCriticalSection(&g_csFileOpenClose); return 1; } int DebuggerDisplayDiagnostics() { LPAPI_VERSION lpDV = NULL; char buf[TTBUFLEN]; if (g_bInitialized == FALSE) { fprintf(stderr, "Stackwalker not initialized (or was not able to initialize)!\n"); return 1; } EnterCriticalSection(&g_csFileOpenClose); lpDV = pIAV(); pSGSP(g_hProcess, (PTSTR)&buf, TTBUFLEN); fprintf(stderr, "Debugger Engine : %d.%d.%d.%d\n", lpDV->MajorVersion, lpDV->MinorVersion, lpDV->Revision, lpDV->Reserved); fprintf(stderr, "Symbol Search Path: %s\n", buf); fprintf(stderr, "\n\n"); if (!pSEM(g_hProcess, (PSYM_ENUMMODULES_CALLBACK64)SymEnumerateModulesProc64, NULL)) { fprintf(stderr, "SymEnumerateModules64(): GetLastError = %lu\n", gle ); } fprintf(stderr, "\n\n"); LeaveCriticalSection(&g_csFileOpenClose); return 0; } // ################################################################################# // ################################################################################# // Here the Stackwalk-Part begins. // Some of the code is from an example from a book // But I couldn't find the reference anymore... sorry... // If someone knowns, please let me know... // ################################################################################# // ################################################################################# // if you use C++ exception handling: install a translator function // with set_se_translator(). In the context of that function (but *not* // afterwards), you can either do your stack dump, or save the CONTEXT // record as a local copy. Note that you must do the stack sump at the // earliest opportunity, to avoid the interesting stackframes being gone // by the time you do the dump. // status: // - EXCEPTION_CONTINUE_SEARCH: exception wird weitergereicht // - EXCEPTION_CONTINUE_EXECUTION: // - EXCEPTION_EXECUTE_HANDLER: DWORD StackwalkFilter(EXCEPTION_POINTERS *ep, DWORD status) { HANDLE hThread; DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS ); StackwalkThread( hThread, ep->ContextRecord ); CloseHandle( hThread ); return status; } // StackwalkFilter void StackwalkThread(HANDLE hThread, CONTEXT* c) { ShowStackRM(hThread, *c); } static void ShowStackRM(HANDLE hThread, CONTEXT& Context) { BOOL bRetVal = FALSE; int frameNum = 0; DWORD64 offsetFromSymbol = 0; DWORD offsetFromLine = 0; char undName[MAX_SYM_NAME]; char szMsgSymFromAddr[256]; char szMsgSymGetLineFromAddr[256]; char szMsgSymGetModuleInfo[256]; IMAGEHLP_MODULE64 Module; IMAGEHLP_LINE64 Line; STACKFRAME64 StackFrame; ULONG64 SymbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME + 1]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)SymbolBuffer; if (g_bInitialized == FALSE) { fprintf(stderr, "Stackwalker not initialized (or was not able to initialize)!\n"); return; } // Critical section begin... EnterCriticalSection(&g_csFileOpenClose); fprintf(stderr, "- Registers -\n"); // Dump the Context data #if defined(_WIN64) && defined(_M_X64) fprintf(stderr, "rax=%.16x rbx=%.16x rcx=%.16x rdx=%.16x rsi=%.16x rdi=%.16x\n", Context.Rax, Context.Rbx, Context.Rcx, Context.Rdx, Context.Rsi, Context.Rdi ); fprintf(stderr, "r8=%.16x r9=%.16x r10=%.16x r11=%.16x r12=%.16x r13=%.16x\n", Context.R8, Context.R9, Context.R10, Context.R11, Context.R12, Context.R13 ); fprintf(stderr, "r14=%.16x r15=%.16x rip=%.16x rsp=%.16x rbp=%.16x\n", Context.R14, Context.R15, Context.Rip, Context.Rsp, Context.Rbp ); fprintf(stderr, "cs=%.4x ss=%.4x ds=%.4x es=%.4x fs=%.4x gs=%.4x efl=%.8x\n\n", Context.SegCs, Context.SegSs, Context.SegDs, Context.SegEs, Context.SegFs, Context.SegGs, Context.EFlags ); #else fprintf(stderr, "eax=%.8x ebx=%.8x ecx=%.8x edx=%.8x esi=%.8x edi=%.8x\n", Context.Eax, Context.Ebx, Context.Ecx, Context.Edx, Context.Esi, Context.Edi ); fprintf(stderr, "eip=%.8x esp=%.8x ebp=%.8x\n", Context.Eip, Context.Esp, Context.Ebp ); fprintf(stderr, "cs=%.4x ss=%.4x ds=%.4x es=%.4x fs=%.4x gs=%.4x efl=%.8x\n\n", Context.SegCs, Context.SegSs, Context.SegDs, Context.SegEs, Context.SegFs, Context.SegGs, Context.EFlags ); #endif // Stack Header fprintf(stderr, "- Callstack -\n"); fprintf(stderr, "ChildEBP RetAddr Args to Child\n"); fflush( stderr ); // init STACKFRAME for first call // Notes: AddrModeFlat is just an assumption. I hate VDM debugging. // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway, // and good riddance. memset( &StackFrame, '\0', sizeof(STACKFRAME64) ); #if defined(_WIN64) && defined(_M_X64) StackFrame.AddrPC.Offset = Context.Rip; StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrFrame.Offset = Context.Rbp; StackFrame.AddrFrame.Mode = AddrModeFlat; #else StackFrame.AddrPC.Offset = Context.Eip; StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrFrame.Offset = Context.Ebp; StackFrame.AddrFrame.Mode = AddrModeFlat; #endif memset( pSymbol, '\0', sizeof(SymbolBuffer) ); pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; memset( &Line, '\0', sizeof(IMAGEHLP_LINE64) ); Line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); memset( &Module, '\0', sizeof(IMAGEHLP_MODULE64) ); Module.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); safe_strcpy(szMsgSymFromAddr, ""); safe_strcpy(szMsgSymGetLineFromAddr, ""); safe_strcpy(szMsgSymGetModuleInfo, ""); for ( frameNum = 0; ; ++frameNum ) { // get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase()) // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can // assume that either you are done, or that the stack is so hosed that the next // deeper frame could not be found. bRetVal = pSW( #if defined(_WIN64) && defined(_M_X64) IMAGE_FILE_MACHINE_AMD64, #else IMAGE_FILE_MACHINE_I386, #endif g_hProcess, hThread, &StackFrame, &Context, NULL, (PFUNCTION_TABLE_ACCESS_ROUTINE64)pSFTA, (PGET_MODULE_BASE_ROUTINE64)pSGMB, NULL ); if (!bRetVal) { break; } if ( StackFrame.AddrPC.Offset == 0 ) { // Special case: If we are here, we have no valid callstack entry! fprintf(stderr, "(-nosymbols- PC == 0)\n"); } else { // show procedure info (SymFromAddr()) undName[0] = 0; offsetFromSymbol = 0; if ( !pSFA( g_hProcess, StackFrame.AddrPC.Offset, &offsetFromSymbol, pSymbol ) ) { if ( gle != 487 ) { snprintf( szMsgSymFromAddr, sizeof(szMsgSymFromAddr), "SymFromAddr(): GetLastError = '%lu'", gle ); } } else { // UnDecorateSymbolName() pUDSN( pSymbol->Name, undName, MAX_SYM_NAME, UNDNAME_NAME_ONLY ); } // show line number info (SymGetLineFromAddr()) offsetFromLine = 0; if ( !pSGLFA( g_hProcess, StackFrame.AddrPC.Offset, &offsetFromLine, &Line ) ) { if ( (gle != 487) && (frameNum > 0) ) { snprintf( szMsgSymGetLineFromAddr, sizeof(szMsgSymGetLineFromAddr), "SymGetLineFromAddr(): GetLastError = '%lu'", gle ); } } // show module info (SymGetModuleInfo()) if ( !pSGMI( g_hProcess, StackFrame.AddrPC.Offset, &Module ) ) { snprintf( szMsgSymGetModuleInfo, sizeof(szMsgSymGetModuleInfo), "SymGetModuleInfo(): GetLastError = '%lu'", gle ); } } // we seem to have a valid PC fprintf(stderr, "%.8x ", StackFrame.AddrFrame.Offset); fprintf(stderr, "%.8x ", StackFrame.AddrReturn.Offset); fprintf(stderr, "%.8x ", StackFrame.Params[0]); fprintf(stderr, "%.8x ", StackFrame.Params[1]); fprintf(stderr, "%.8x ", StackFrame.Params[2]); fprintf(stderr, "%.8x ", StackFrame.Params[3]); fprintf(stderr, "%s", Module.ModuleName); fprintf(stderr, "!%s+", undName); fprintf(stderr, "0x%x ", offsetFromLine); if (Line.LineNumber) { fprintf(stderr, "(%s:%lu) ", Line.FileName, Line.LineNumber); } if (StackFrame.FuncTableEntry) { // FPO Data PFPO_DATA pFPO = (PFPO_DATA)StackFrame.FuncTableEntry; switch(pFPO->cbFrame) { case FRAME_FPO: fprintf(stderr, "FPO: [%d,%d,%d] ", pFPO->cdwParams, pFPO->cdwLocals, pFPO->cbRegs); break; case FRAME_TRAP: fprintf(stderr, "FPO: [%d,%d] TrapFrame @ 0x%.8x ", pFPO->cdwParams, pFPO->cdwLocals, pFPO->ulOffStart); break; case FRAME_TSS: fprintf(stderr, "FPO: TaskGate Segment: 0 "); break; } } if (strlen(szMsgSymFromAddr) || strlen(szMsgSymGetLineFromAddr) || strlen(szMsgSymGetModuleInfo)) { fprintf( stderr, "%s %s %s Address = '%.8x'", szMsgSymFromAddr, szMsgSymGetLineFromAddr, szMsgSymGetModuleInfo, StackFrame.AddrPC.Offset ); } fprintf(stderr, "\n"); // Zero out params so we have fresh parameters through the next interation StackFrame.Params[0] = (DWORD64)NULL; StackFrame.Params[1] = (DWORD64)NULL; StackFrame.Params[2] = (DWORD64)NULL; StackFrame.Params[3] = (DWORD64)NULL; // no return address means no deeper stackframe if ( StackFrame.AddrReturn.Offset == 0 ) { // avoid misunderstandings in the printf() following the loop SetLastError( 0 ); break; } } // for ( frameNum ) switch(gle){ case ERROR_SUCCESS: break; case ERROR_INVALID_ADDRESS: fprintf(stderr, "\nStackWalk(): ERROR_INVALID_ADDRESS (%lu) - Possible stack corruption.\n", gle ); break; case ERROR_NOACCESS: fprintf(stderr, "\nStackWalk(): ERROR_NOACCESS (%lu) - Possible stack corruption.\n", gle ); break; default: fprintf(stderr, "\nStackWalk(): GetLastError = %lu\n", gle ); break; } fflush(stderr); LeaveCriticalSection(&g_csFileOpenClose); }