Files
fs2open.github.com/code/globalincs/mspdb_callstack.cpp
T
2009-08-03 16:15:44 +00:00

295 lines
8.6 KiB
C++

/* This file contains debugging utilities used only under VC2005+
* Maintained by: portej05 (please run patches past him before committing here!)
*/
/* Based on the ATL headers, however, updated to fix one or two bugs in the ATL headers
* and moved to the Sym*64 functions
*/
#if defined(PDB_DEBUGGING)
/* Windows */
#include <windows.h>
#include <dbghelp.h>
/* SCP */
#include "globalincs/pstypes.h"
#include "globalincs/mspdb_callstack.h"
/* Link the library that we need */
#pragma comment( lib, "dbghelp.lib" )
HRESULT SCP_DumpStack( SCP_IDumpHandler* pISH );
BOOL SCP_mspdbcs_ResolveSymbol( HANDLE hProcess, UINT_PTR dwAddress, SCP_mspdbcs_SDumpStackSymbolInfo& siSymbol );
LPVOID __stdcall SCP_mspdbcs_FunctionTableAccess( HANDLE hProcess, DWORD64 dwPCAddress );
DWORD64 __stdcall SCP_mspdbcs_GetModuleBase( HANDLE hProcess, DWORD64 returnAddress );
DWORD WINAPI SCP_mspdbcs_DumpStackThread( LPVOID pv );
BOOL SCP_mspdbcs_ResolveSymbol( HANDLE hProcess, UINT_PTR dwAddress, SCP_mspdbcs_SDumpStackSymbolInfo& siSymbol )
{
BOOL retVal = TRUE;
char szUndec[ SCP_MSPDBCS_MAX_SYMBOL_LENGTH ];
char szWithOffset[ SCP_MSPDBCS_MAX_SYMBOL_LENGTH ];
char* pszSymbol = NULL;
IMAGEHLP_MODULE64 mi;
memset( &siSymbol, 0, sizeof( SCP_mspdbcs_SDumpStackSymbolInfo ) );
mi.SizeOfStruct = sizeof( IMAGEHLP_MODULE64 );
siSymbol.dwAddress = dwAddress;
if ( !SymGetModuleInfo64( hProcess, dwAddress, &mi ) )
{
/* Unneeded */
/*printf("Error: %x\n", HRESULT_FROM_WIN32( GetLastError( ) ));*/
strcpy_s( siSymbol.szModule, "<no module>" );
}
else
{
char* pszLastModule = strchr( mi.ImageName, '\\' );
if ( pszLastModule == NULL )
pszLastModule = mi.ImageName;
else
pszLastModule++; /* move off the backslash */
strncpy_s( siSymbol.szModule, pszLastModule, _TRUNCATE );
}
__try
{
union
{
CHAR rgchSymbol[ sizeof(IMAGEHLP_SYMBOL64) + SCP_MSPDBCS_MAX_SYMBOL_LENGTH ];
IMAGEHLP_SYMBOL64 sym;
} sym;
memset( &sym.sym, 0, sizeof( sym.sym ) );
sym.sym.SizeOfStruct = sizeof( IMAGEHLP_SYMBOL64 );
#ifdef _WIN64
sym.sym.Address = dwAddress;
#else
sym.sym.Address = (DWORD)dwAddress;
#endif
sym.sym.MaxNameLength = SCP_MSPDBCS_MAX_SYMBOL_LENGTH;
#ifdef _WIN64
if ( SymGetSymFromAddr64( hProcess, dwAddress, &(siSymbol.dwOffset), &sym.sym ) )
#else
if ( SymGetSymFromAddr64( hProcess, (DWORD)dwAddress, &(siSymbol.dwOffset), &sym.sym ) )
#endif
{
pszSymbol = sym.sym.Name;
if ( UnDecorateSymbolName( sym.sym.Name, szUndec, sizeof( szUndec )/sizeof( szUndec[0] ),
UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS ) )
{
pszSymbol = szUndec;
}
else if ( SymUnDName64( &sym.sym, szUndec, sizeof( szUndec )/sizeof( szUndec[0] ) ) )
{
pszSymbol = szUndec;
}
if ( siSymbol.dwOffset != 0 )
{
sprintf_s( szWithOffset, SCP_MSPDBCS_MAX_SYMBOL_LENGTH, "%s + %d bytes", pszSymbol, siSymbol.dwOffset );
szWithOffset[ SCP_MSPDBCS_MAX_SYMBOL_LENGTH - 1 ] = '\0'; /* Because sprintf doesn't guarantee NULL terminating */
pszSymbol = szWithOffset;
}
}
else
pszSymbol = "<no symbol>";
}
__except( EXCEPTION_ACCESS_VIOLATION == GetExceptionCode( ) )
{
pszSymbol = "<EX: no symbol>";
siSymbol.dwOffset = dwAddress - mi.BaseOfImage;
}
strncpy_s( siSymbol.szSymbol, pszSymbol, _TRUNCATE );
return retVal;
}
LPVOID __stdcall SCP_mspdbcs_FunctionTableAccess( HANDLE hProcess, DWORD64 dwPCAddress )
{
return SymFunctionTableAccess64( hProcess, dwPCAddress );
}
DWORD64 __stdcall SCP_mspdbcs_GetModuleBase( HANDLE hProcess, DWORD64 returnAddress )
{
IMAGEHLP_MODULE moduleInfo;
moduleInfo.SizeOfStruct = sizeof( IMAGEHLP_MODULE );
/* The ATL headers do it this way */
#ifdef _WIN64
if ( SymGetModuleInfo( hProcess, returnAddress, &moduleInfo ) )
#else
if ( SymGetModuleInfo( hProcess, (ULONG)returnAddress, &moduleInfo ) )
#endif
{
return moduleInfo.BaseOfImage;
}
else
{
MEMORY_BASIC_INFORMATION memoryBasicInfo;
if ( VirtualQueryEx( hProcess, (LPVOID)returnAddress,
&memoryBasicInfo, sizeof( MEMORY_BASIC_INFORMATION ) ) )
{
DWORD cch = 0;
char szFile[ _MAX_PATH ] = {0}; /* Initialise the file path */
cch = GetModuleFileNameA( (HINSTANCE)memoryBasicInfo.AllocationBase, szFile, MAX_PATH );
SymLoadModule( hProcess, NULL, ((cch)?szFile:NULL),
#ifdef _WIN64
NULL, (DWORD_PTR)memoryBasicInfo.AllocationBase, 0 );
#else
NULL, (DWORD)(DWORD_PTR)memoryBasicInfo.AllocationBase, 0 );
#endif
return (DWORD_PTR)memoryBasicInfo.AllocationBase;
}
}
return NULL;
}
DWORD WINAPI SCP_mspdbcs_DumpStackThread( LPVOID pv )
{
CONTEXT context;
memset( &context, 0, sizeof( CONTEXT ) );
context.ContextFlags = CONTEXT_FULL;
SCP_mspdbcs_SDumpStackThreadInfo* pdsti =
reinterpret_cast< SCP_mspdbcs_SDumpStackThreadInfo* >( pv );
/* We're going to walk the stack here */
/* Suspend the running thread */
SuspendThread( pdsti->hThread );
pdsti->pIDS->OnBegin( );
/* Retrieve the context (processor state) of the suspended thread */
GetThreadContext( pdsti->hThread, &context );
/* Initialise symbol handling */
if ( SymInitialize( pdsti->hProcess, NULL, TRUE ) )
{
/* Set name decoration handling */
DWORD dw = SymGetOptions( );
dw = dw & ~SYMOPT_UNDNAME;
SymSetOptions( dw );
/* Initialise the stackframe */
STACKFRAME64 stackFrame;
memset( &stackFrame, 0, sizeof( STACKFRAME ) );
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrStack.Mode = AddrModeFlat;
stackFrame.AddrReturn.Mode = AddrModeFlat;
stackFrame.AddrBStore.Mode = AddrModeFlat;
DWORD dwMachType;
/* Determine the machine type based on the compilation.
* Initialise stackFrame accordingly
* We will only handle the types of _M_IX86 or _M_AMD64
* We're not intending to be compatible with IA64 or any other architectures
* under windows
*/
#if defined(_M_IX86)
dwMachType = IMAGE_FILE_MACHINE_I386;
stackFrame.AddrPC.Offset = context.Eip;
stackFrame.AddrStack.Offset = context.Esp;
stackFrame.AddrFrame.Offset = context.Ebp;
#elif defined(_M_AMD64)
dwMachType = IMAGE_FILE_MACHINE_AMD64;
stackFrame.AddrPC.Offset = context.Rip;
#else
# error UNKNOWN ARCHITECTURE
#endif
/* All the discovered addresses will be stored in an array */
SCP_vector< void* > addresses;
/* Walk the stack */
for ( int currFrame = 0; currFrame < SCP_MSPDBCS_MAX_STACK_FRAMES; currFrame++ )
{
if ( !StackWalk64( dwMachType, pdsti->hProcess, pdsti->hThread,
&stackFrame, &context, NULL,
SCP_mspdbcs_FunctionTableAccess, SCP_mspdbcs_GetModuleBase, NULL ) )
{
break; /* No more stack frames to walk */
}
/* Found a useful address? */
if ( stackFrame.AddrPC.Offset != 0 )
{
addresses.push_back( (void*)(DWORD_PTR)stackFrame.AddrPC.Offset );
}
}
/* Dump the stack information that we have */
for ( size_t i = 0; i < addresses.size( ); i++ )
{
SCP_mspdbcs_SDumpStackSymbolInfo info;
const char* szModule = NULL;
const char* szSymbol = NULL;
if ( SCP_mspdbcs_ResolveSymbol( pdsti->hProcess, (UINT_PTR)addresses[ i ], info ) )
{
szModule = info.szModule;
szSymbol = info.szSymbol;
}
pdsti->pIDS->OnEntry( addresses[ i ], szModule, szSymbol );
}
SymCleanup( pdsti->hProcess );
}
pdsti->pIDS->OnEnd( );
ResumeThread( pdsti->hThread );
return 0;
}
/* Entry point */
HRESULT SCP_DumpStack( SCP_IDumpHandler* pIDH )
{
if ( !pIDH )
return E_POINTER;
/* Retrieve pseudo handles to the current thread and process */
HANDLE hPseudoThread = GetCurrentThread( );
HANDLE hPseudoProcess = GetCurrentProcess( );
/* Retrieve the real handle of this thread */
HANDLE hThread = NULL;
if ( !DuplicateHandle( hPseudoProcess, hPseudoThread, /* Source process and thread */
hPseudoProcess, &hThread, /* Target process and thread */
0, FALSE, DUPLICATE_SAME_ACCESS ) ) /* Non-Inheritable, same access as current process/thread */
return HRESULT_FROM_WIN32( GetLastError( ) ); /* Bugger */
DWORD dwID;
SCP_mspdbcs_SDumpStackThreadInfo info;
info.hProcess = hPseudoProcess;
info.hThread = hThread;
info.pIDS = pIDH;
HANDLE workerThread = CreateThread( NULL, 0, SCP_mspdbcs_DumpStackThread,
&info, 0, &dwID );
if ( workerThread == NULL )
return HRESULT_FROM_WIN32( GetLastError( ) ); /* Bugger */
while ( WaitForSingleObject( workerThread, 0 ) != WAIT_OBJECT_0 );
CloseHandle( workerThread );
return S_OK;
}
#endif // PDB_DEBUGGING