boinc/lib/stackwalker_win.cpp

960 lines
35 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// $Id$
//
/*////////////////////////////////////////////////////////////////////////////
* Project:
* Memory_and_Exception_Trace
*
* ///////////////////////////////////////////////////////////////////////////
* File:
* Stackwalker.cpp
*
* Remarks:
* Dumps the stack of an thread if an exepction 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.
sprintf(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.
sprintf(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.
sprintf(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.
sprintf(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);
}