Move git root from Client/ to src/ to track all source code: - Client: Game client source (moved to Client/Client/) - Server: Game server source - GameTools: Development tools - CryptoSource: Encryption utilities - database: Database scripts - Script: Game scripts - rylCoder_16.02.2008_src: Legacy coder tools - GMFont, Game: Additional resources 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
758 lines
28 KiB
C++
758 lines
28 KiB
C++
/* ==========================================================================
|
|
File : ExceptionReport.h
|
|
Vers.: 0.9
|
|
Plat.: Windows 98 or above
|
|
Desc.: ExceptionHandle routine (report)
|
|
Req. : DbgHelp v5.1 or above
|
|
|
|
Adapted from MSDN March 2002 - Under the Hood
|
|
========================================================================== */
|
|
|
|
// #define _WIN32_WINDOWS 0x0410 // Windows 98
|
|
// #define _WIN32_WINNT 0x0500 // Windows 2000
|
|
|
|
#define USE_DBGHELPS 0
|
|
|
|
#include <windows.h>
|
|
#include <tchar.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "ExceptionReport.h"
|
|
#if USE_DBGHELPS == 1
|
|
#include "DBGHELPS.H"
|
|
#endif
|
|
|
|
|
|
CExceptionReport g_CExceptionReport;
|
|
|
|
TCHAR CExceptionReport::m_szLogPrefixName[ MAX_PATH ];
|
|
TCHAR CExceptionReport::m_szModuleName[ MAX_PATH ];
|
|
LPTOP_LEVEL_EXCEPTION_FILTER CExceptionReport::m_OldFilter;
|
|
HANDLE CExceptionReport::m_hReportFile;
|
|
HANDLE CExceptionReport::m_hProcess;
|
|
BOOL CExceptionReport::m_bHasSymbol;
|
|
BOOL CExceptionReport::m_bWriteInfo;
|
|
LPCERUserFunc CExceptionReport::m_lpUserFunc;
|
|
CDBGFuncClass CExceptionReport::m_DBGHELP;
|
|
|
|
// #pragma comment(linker, "/defaultlib:dbghelp.lib")
|
|
|
|
|
|
//
|
|
// Constructor
|
|
//
|
|
CExceptionReport::CExceptionReport()
|
|
{
|
|
m_bHasSymbol = FALSE;
|
|
m_bWriteInfo = TRUE;
|
|
SetProgramName();
|
|
SetUserFunc( NULL );
|
|
|
|
#if USE_DBGHELPS == 1
|
|
DBGHELP_DllMain( GetModuleHandle( NULL ), DLL_PROCESS_ATTACH, NULL );
|
|
#else
|
|
if( m_DBGHELP.Init( "DBGHELP.DLL" ) == TRUE )
|
|
#endif
|
|
// Install the unhandled exception filter function
|
|
m_OldFilter = SetUnhandledExceptionFilter( UnhandledExceptionFilter );
|
|
m_hProcess = GetCurrentProcess();
|
|
}
|
|
|
|
//
|
|
// Destructor
|
|
//
|
|
CExceptionReport::~CExceptionReport()
|
|
{
|
|
SetUnhandledExceptionFilter( m_OldFilter );
|
|
#if USE_DBGHELPS == 1
|
|
DBGHELP_DllMain( GetModuleHandle( NULL ), DLL_PROCESS_DETACH, NULL );
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Set application-specific function
|
|
//
|
|
void CExceptionReport::SetUserFunc( LPCERUserFunc lpUserFunc )
|
|
{
|
|
m_lpUserFunc = lpUserFunc;
|
|
}
|
|
|
|
//
|
|
// Set application-name for prefix of log filename
|
|
//
|
|
void CExceptionReport::SetProgramName( PTSTR pszProgramName )
|
|
{
|
|
if( pszProgramName == NULL ) {
|
|
TCHAR szDrive[MAX_PATH], szDir[MAX_PATH], szFilename[MAX_PATH], szExt[MAX_PATH];
|
|
|
|
// Figure out what the report file will be named, and store it away
|
|
GetModuleFileName( 0, m_szLogPrefixName, MAX_PATH );
|
|
|
|
PTSTR pszDot = m_szLogPrefixName;
|
|
// Look for the '.' before the "EXE" extension. Replace '.' to '\0'
|
|
if( (pszDot = _tcsrchr( pszDot, _T('.') )) )
|
|
*pszDot = 0;
|
|
_tsplitpath( m_szLogPrefixName, szDrive, szDir, szFilename, szExt );
|
|
_tcscpy( m_szLogPrefixName, szFilename );
|
|
} else {
|
|
_tcscpy( m_szLogPrefixName, pszProgramName );
|
|
}
|
|
}
|
|
|
|
LONG WINAPI CExceptionReport::UnhandledExceptionFilterTwo( PEXCEPTION_POINTERS pExceptionInfo )
|
|
{
|
|
if( m_hReportFile ) {
|
|
Printf( "Exception occured AGAIN! EXITING NOW!\r\n" );
|
|
CloseHandle( m_hReportFile );
|
|
m_hReportFile = NULL;
|
|
}
|
|
if( m_OldFilter )
|
|
return m_OldFilter( pExceptionInfo );
|
|
else
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
//
|
|
// This is called when unhandled exception occurs
|
|
//
|
|
LONG WINAPI CExceptionReport::UnhandledExceptionFilter( PEXCEPTION_POINTERS pExceptionInfo )
|
|
{
|
|
TCHAR szLogFileName[ MAX_PATH ];
|
|
|
|
SetUnhandledExceptionFilter( UnhandledExceptionFilterTwo );
|
|
|
|
if(!m_bWriteInfo) return EXCEPTION_EXECUTE_HANDLER;
|
|
|
|
_tcscpy( szLogFileName, m_szLogPrefixName );
|
|
|
|
SYSTEMTIME stSystemTime;
|
|
GetLocalTime( &stSystemTime );
|
|
|
|
wsprintf( szLogFileName + strlen( szLogFileName ), " %04d-%02d-%02d %02d,%02d,%02d.TXT",
|
|
stSystemTime.wYear, stSystemTime.wMonth, stSystemTime.wDay,
|
|
stSystemTime.wHour, stSystemTime.wMinute, stSystemTime.wSecond );
|
|
|
|
if( (m_hReportFile = CreateFile( szLogFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0 )) != INVALID_HANDLE_VALUE ) {
|
|
SetFilePointer( m_hReportFile, 0, 0, FILE_END );
|
|
WriteExceptionReport( pExceptionInfo );
|
|
CloseHandle( m_hReportFile );
|
|
m_hReportFile = NULL;
|
|
}
|
|
|
|
#if USE_MINIDUMP == 1
|
|
TCHAR szDumpFileName[ MAX_PATH ];
|
|
|
|
_tcscpy( szDumpFileName, m_szLogPrefixName );
|
|
wsprintf( szDumpFileName + strlen( szDumpFileName ), " %04d-%02d-%02d %02d,%02d,%02d.DMP",
|
|
stSystemTime.wYear, stSystemTime.wMonth, stSystemTime.wDay,
|
|
stSystemTime.wHour, stSystemTime.wMinute, stSystemTime.wSecond );
|
|
|
|
HANDLE hDumpFile;
|
|
|
|
if( m_DBGHELP.MiniDumpWriteDump &&(hDumpFile = CreateFile( szDumpFileName, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 )) != INVALID_HANDLE_VALUE ) {
|
|
MINIDUMP_EXCEPTION_INFORMATION ExInfo;
|
|
|
|
ExInfo.ThreadId = ::GetCurrentThreadId();
|
|
ExInfo.ExceptionPointers = pExceptionInfo;
|
|
ExInfo.ClientPointers = NULL;
|
|
|
|
m_DBGHELP.MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &ExInfo, NULL, NULL );
|
|
CloseHandle( hDumpFile );
|
|
}
|
|
#endif
|
|
|
|
if( m_lpUserFunc && (m_hReportFile = CreateFile( szLogFileName, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0 )) != INVALID_HANDLE_VALUE ) {
|
|
SetFilePointer( m_hReportFile, 0, 0, FILE_END );
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( " Application-specific log\r\n" ) );
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
m_lpUserFunc( m_hReportFile );
|
|
Printf( _T( "\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
Printf( _T( "==============================================================================\r\n" ) );
|
|
CloseHandle( m_hReportFile );
|
|
m_hReportFile = NULL;
|
|
}
|
|
|
|
// if( m_OldFilter )
|
|
// return m_OldFilter( pExceptionInfo );
|
|
// else
|
|
// return EXCEPTION_CONTINUE_SEARCH;
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
//
|
|
// Write informations to Report file. Called by UnhandledExceptionFilter
|
|
//
|
|
void CExceptionReport::WriteExceptionReport( PEXCEPTION_POINTERS pExceptionInfo )
|
|
{
|
|
// Start out with a banner
|
|
Printf( _T( "==============================================================================\r\n\r\n" ) );
|
|
WriteBasicInfo( pExceptionInfo->ExceptionRecord );
|
|
WriteRegistersInfo( pExceptionInfo->ContextRecord );
|
|
|
|
if( !m_DBGHELP.SymInitialize )
|
|
return;
|
|
|
|
// if( m_DBGHELP.SymSetOptions )
|
|
// m_DBGHELP.SymSetOptions( SYMOPT_DEFERRED_LOADS );
|
|
// Initialize DbgHelp
|
|
if( !m_DBGHELP.SymInitialize( GetCurrentProcess(), 0, TRUE ) )
|
|
return;
|
|
CONTEXT trashableContext = *(pExceptionInfo->ContextRecord);
|
|
WriteStackDetails( &trashableContext, FALSE );
|
|
#ifdef _M_IX86 // X86 Only!
|
|
if( m_bHasSymbol ) {
|
|
trashableContext = *(pExceptionInfo->ContextRecord);
|
|
WriteStackDetails( &trashableContext, TRUE );
|
|
// Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
// Printf( _T( " Global Variables\r\n" ) );
|
|
// Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
// Printf( _T( "\r\n" ) );
|
|
// if( m_DBGHELP.SymEnumSymbols)
|
|
// m_DBGHELP.SymEnumSymbols( GetCurrentProcess(), (DWORD64) GetModuleHandle( m_szModuleName ), 0, EnumerateSymbolsCallback, 0 );
|
|
}
|
|
#endif // X86 Only!
|
|
if( m_DBGHELP.SymCleanup )
|
|
m_DBGHELP.SymCleanup( GetCurrentProcess() );
|
|
|
|
Printf( _T("\r\n") );
|
|
Printf( _T("\r\n") );
|
|
|
|
WriteMemoryDump( pExceptionInfo->ContextRecord );
|
|
|
|
if( m_lpUserFunc ) {
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( " Application-specific log\r\n" ) );
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
|
|
m_lpUserFunc( m_hReportFile );
|
|
Printf( _T( "\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
}
|
|
|
|
if( !m_lpUserFunc )
|
|
Printf( _T( "==============================================================================\r\n" ) );
|
|
}
|
|
|
|
//
|
|
// Write basic informations(user, computer name, error type)
|
|
//
|
|
void CExceptionReport::WriteBasicInfo( PEXCEPTION_RECORD pExceptionRecord )
|
|
{
|
|
TCHAR szFileName[MAX_PATH] = _T( "" ), szUserName[MAX_PATH] = _T( "" ), szComputerName[MAX_PATH] = _T( "" );
|
|
DWORD dwUserLen = MAX_PATH, dwComputerLen = MAX_PATH;
|
|
|
|
SYSTEMTIME stSystemTime;
|
|
GetLocalTime( &stSystemTime );
|
|
|
|
GetModuleFileName( (HMODULE) NULL, szFileName, MAX_PATH );
|
|
GetUserName( szUserName, &dwUserLen );
|
|
GetComputerName( szComputerName, &dwComputerLen );
|
|
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( " Basic Information\r\n" ) );
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
|
|
Printf( _T( "Program Name : %s\r\n" ), m_szLogPrefixName );
|
|
Printf( _T( "EXE : %s\r\n" ), szFileName );
|
|
Printf( _T( "User : %s\r\n" ), szUserName );
|
|
Printf( _T( "Computer : %s\r\n" ), szComputerName );
|
|
Printf( _T( "\r\n" ) );
|
|
|
|
// Print information about the type of fault and where the fault occured
|
|
DWORD section, offset;
|
|
GetLogicalAddress( pExceptionRecord->ExceptionAddress, m_szModuleName, sizeof( m_szModuleName ), section, offset );
|
|
Printf( _T( "Program : %s\r\n" ), m_szModuleName );
|
|
Printf( _T( "Exception : %08X (%s)\r\n"), pExceptionRecord->ExceptionCode, GetExceptionString( pExceptionRecord->ExceptionCode ) );
|
|
Printf( _T( "Fault Address: %08X %02X:%08X\r\n"), pExceptionRecord->ExceptionAddress, section, offset );
|
|
Printf( _T( "\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
|
|
// TODO: System Information!
|
|
}
|
|
|
|
//
|
|
// Show the registers
|
|
//
|
|
void CExceptionReport::WriteRegistersInfo( PCONTEXT pContext )
|
|
{
|
|
#ifdef _M_IX86 // X86 Only!
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( " x86 Registers\r\n" ) );
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
|
|
Printf( _T("EAX=%08X EBX=%08X ECX=%08X EDX=%08X\r\n"), pContext->Eax, pContext->Ebx, pContext->Ecx, pContext->Edx );
|
|
Printf( _T("ESI=%08X EDI=%08X EBP=%08X\r\n"), pContext->Esi, pContext->Edi, pContext->Ebp );
|
|
Printf( _T("DS =%04X ES=%04X FS=%04X GS:%04X\r\n"), pContext->SegDs, pContext->SegEs, pContext->SegFs, pContext->SegGs );
|
|
Printf( _T("CS:EIP=%04X:%08X\r\n"), pContext->SegCs, pContext->Eip );
|
|
Printf( _T("SS:ESP=%04X:%08X\r\n"), pContext->SegSs, pContext->Esp );
|
|
Printf( _T("Flags=%08X\r\n"), pContext->EFlags );
|
|
Printf( _T("\r\n") );
|
|
Printf( _T("\r\n") );
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Show the Memory
|
|
//
|
|
void CExceptionReport::WriteMemoryDump( PCONTEXT pContext )
|
|
{
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( " Memory Dump\r\n" ) );
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
|
|
Printf( _T( "Code: %d bytes starting at (EIP = %08lX)\r\n" ), 16, pContext->Eip );
|
|
Dump( pContext->Eip, 16, FALSE );
|
|
Printf( _T("\r\n") );
|
|
|
|
Printf( _T( "Stack: %d bytes starting at (ESP = %08lX)\r\n" ), 1024, pContext->Esp );
|
|
Dump( pContext->Esp, 1024, TRUE );
|
|
Printf( _T("\r\n") );
|
|
Printf( _T("\r\n") );
|
|
}
|
|
|
|
//
|
|
// Given an exception code, returns a pointer to a static string with a description of the exception
|
|
//
|
|
#define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x);
|
|
|
|
LPTSTR CExceptionReport::GetExceptionString( DWORD dwCode )
|
|
{
|
|
switch ( dwCode ) {
|
|
EXCEPTION( ACCESS_VIOLATION )
|
|
EXCEPTION( DATATYPE_MISALIGNMENT )
|
|
EXCEPTION( BREAKPOINT )
|
|
EXCEPTION( SINGLE_STEP )
|
|
EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
|
|
EXCEPTION( FLT_DENORMAL_OPERAND )
|
|
EXCEPTION( FLT_DIVIDE_BY_ZERO )
|
|
EXCEPTION( FLT_INEXACT_RESULT )
|
|
EXCEPTION( FLT_INVALID_OPERATION )
|
|
EXCEPTION( FLT_OVERFLOW )
|
|
EXCEPTION( FLT_STACK_CHECK )
|
|
EXCEPTION( FLT_UNDERFLOW )
|
|
EXCEPTION( INT_DIVIDE_BY_ZERO )
|
|
EXCEPTION( INT_OVERFLOW )
|
|
EXCEPTION( PRIV_INSTRUCTION )
|
|
EXCEPTION( IN_PAGE_ERROR )
|
|
EXCEPTION( ILLEGAL_INSTRUCTION )
|
|
EXCEPTION( NONCONTINUABLE_EXCEPTION )
|
|
EXCEPTION( STACK_OVERFLOW )
|
|
EXCEPTION( INVALID_DISPOSITION )
|
|
EXCEPTION( GUARD_PAGE )
|
|
EXCEPTION( INVALID_HANDLE )
|
|
}
|
|
|
|
// If not one of the "known" exceptions, try to get the string
|
|
// from NTDLL.DLL's message table.
|
|
|
|
static TCHAR szBuffer[512] = { 0 };
|
|
|
|
FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
|
|
GetModuleHandle( _T("NTDLL.DLL") ),
|
|
dwCode, 0, szBuffer, sizeof( szBuffer ), 0 );
|
|
|
|
return szBuffer;
|
|
}
|
|
|
|
#undef EXCEPTION
|
|
|
|
//
|
|
// Given a linear address, locates the module, section, and offset containing that address.
|
|
// Note: the szModule paramater buffer is an output buffer of length specified by the len parameter (in characters!)
|
|
//
|
|
BOOL CExceptionReport::GetLogicalAddress( PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD& offset )
|
|
{
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
|
|
if( !VirtualQuery( addr, &mbi, sizeof( mbi ) ) )
|
|
return FALSE;
|
|
|
|
PVOID hMod = mbi.AllocationBase;
|
|
|
|
if( !GetModuleFileName( (HMODULE) hMod, szModule, len ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Point to the DOS header in memory
|
|
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER) hMod;
|
|
|
|
// From the DOS header, find the NT (PE) header
|
|
PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(((DWORD64) hMod) + pDosHdr->e_lfanew);
|
|
|
|
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
|
|
|
|
DWORD64 rva = (DWORD64)addr - (DWORD64)hMod; // RVA is offset from module load address
|
|
|
|
// Iterate through the section table, looking for the one that encompasses
|
|
// the linear address.
|
|
for( unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++ ) {
|
|
DWORD sectionStart = pSection->VirtualAddress;
|
|
DWORD sectionEnd = sectionStart + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
|
|
|
|
// Is the address in this section???
|
|
if ( (rva >= sectionStart) && (rva <= sectionEnd) ) {
|
|
// Yes, address is in the section. Calculate section and offset,
|
|
// and store in the "section" & "offset" params, which were
|
|
// passed by reference.
|
|
section = i+1;
|
|
offset = (DWORD) (rva - sectionStart);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE; // Should never get here!
|
|
}
|
|
|
|
//
|
|
// Walks the stack, and writes the results to the report file
|
|
//
|
|
void CExceptionReport::WriteStackDetails( PCONTEXT pContext, BOOL bWriteVariables ) // TRUE if local/params should be output
|
|
{
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( " Call Stack (%s)\r\n" ), bWriteVariables ? "Detail" : "Short" );
|
|
Printf( _T( "------------------------------------------------------------------------------\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
Printf( _T("Address Frame Function SourceFile\r\n") );
|
|
DWORD dwMachineType = 0;
|
|
// Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
|
|
STACKFRAME sf;
|
|
|
|
memset( &sf, 0, sizeof(sf) );
|
|
|
|
#ifdef _M_IX86
|
|
// Initialize the STACKFRAME structure for the first call. This is only
|
|
// necessary for Intel CPUs, and isn't mentioned in the documentation.
|
|
sf.AddrPC.Offset = pContext->Eip;
|
|
sf.AddrPC.Mode = AddrModeFlat;
|
|
sf.AddrStack.Offset = pContext->Esp;
|
|
sf.AddrStack.Mode = AddrModeFlat;
|
|
sf.AddrFrame.Offset = pContext->Ebp;
|
|
sf.AddrFrame.Mode = AddrModeFlat;
|
|
|
|
dwMachineType = IMAGE_FILE_MACHINE_I386;
|
|
#endif
|
|
while( 1 ) {
|
|
// Get the next stack frame
|
|
if( !m_DBGHELP.StackWalk )
|
|
break;
|
|
if( !m_DBGHELP.StackWalk( dwMachineType, m_hProcess, GetCurrentThread(), &sf, pContext, 0, m_DBGHELP.SymFunctionTableAccess, m_DBGHELP.SymGetModuleBase, 0 ) )
|
|
break;
|
|
if( sf.AddrFrame.Offset == 0 ) // Basic sanity check to make sure
|
|
break; // the frame is OK. Bail if not.
|
|
Printf( _T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset );
|
|
|
|
// Get the name of the function for this stack frame entry
|
|
BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 2048 ];
|
|
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
|
|
pSymbol->SizeOfStruct = sizeof(symbolBuffer);
|
|
pSymbol->MaxNameLen = 2048;
|
|
|
|
DWORD64 symDisplacement = 0; // Displacement of the input address,
|
|
// relative to the start of the symbol
|
|
if( m_DBGHELP.SymFromAddr && m_DBGHELP.SymFromAddr( m_hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol ) ) {
|
|
TCHAR szModule[MAX_PATH] = _T("");
|
|
|
|
wsprintf( szModule, _T("%-s+%I64X"), pSymbol->Name, symDisplacement );
|
|
Printf( _T( "%-30s" ), szModule );
|
|
} else {
|
|
// No symbol found. Print out the logical address instead.
|
|
TCHAR szModule[MAX_PATH] = _T("");
|
|
DWORD section = 0, offset = 0;
|
|
|
|
GetLogicalAddress( (PVOID) (DWORD64) sf.AddrPC.Offset, szModule, sizeof( szModule ), section, offset );
|
|
Printf( _T("%04X:%08X %s"), section, offset, szModule );
|
|
}
|
|
|
|
// Get the source line for this stack frame entry
|
|
IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
|
|
DWORD dwLineDisplacement;
|
|
if ( m_DBGHELP.SymGetLineFromAddr && (m_DBGHELP.SymGetLineFromAddr( m_hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo )) ) {
|
|
Printf(_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber);
|
|
if( m_bHasSymbol == FALSE )
|
|
m_bHasSymbol = TRUE;
|
|
}
|
|
Printf( _T("\r\n") );
|
|
|
|
// Write out the variables, if desired
|
|
if( bWriteVariables ) {
|
|
// Use SymSetContext to get just the locals/params for this frame
|
|
IMAGEHLP_STACK_FRAME imagehlpStackFrame;
|
|
|
|
imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
|
|
if( m_DBGHELP.SymSetContext )
|
|
m_DBGHELP.SymSetContext( m_hProcess, &imagehlpStackFrame, 0 );
|
|
|
|
// Enumerate the locals/parameters
|
|
if( m_DBGHELP.SymEnumSymbols)
|
|
m_DBGHELP.SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf );
|
|
|
|
Printf( _T("\r\n") );
|
|
}
|
|
}
|
|
Printf( _T( "\r\n" ) );
|
|
Printf( _T( "\r\n" ) );
|
|
}
|
|
|
|
//
|
|
// The function invoked by SymEnumSymbols
|
|
//
|
|
BOOL CALLBACK CExceptionReport::EnumerateSymbolsCallback( PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext )
|
|
{
|
|
char szBuffer[2048];
|
|
|
|
__try {
|
|
if( FormatSymbolValue( pSymInfo, (STACKFRAME *) UserContext, szBuffer, sizeof( szBuffer ) ) )
|
|
Printf( _T("\t%s\r\n"), szBuffer );
|
|
}
|
|
__except( 1 ) {
|
|
Printf( _T("punting on symbol %s\r\n"), pSymInfo->Name );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Given a SYMBOL_INFO representing a particular variable, displays its contents.
|
|
// If it's a user defined type, display the members and their values.
|
|
//
|
|
BOOL CExceptionReport::FormatSymbolValue( PSYMBOL_INFO pSym, STACKFRAME * sf, char * pszBuffer, unsigned cbBuffer )
|
|
{
|
|
char * pszCurrBuffer = pszBuffer;
|
|
|
|
// Indicate if the variable is a local or parameter
|
|
if( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, "Parameter " );
|
|
else if( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL )
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, "Local " );
|
|
|
|
// If it's a function, don't do anything.
|
|
if ( pSym->Tag == 5 ) // SymTagFunction from CVCONST.H from the DIA SDK
|
|
return FALSE;
|
|
|
|
// Emit the variable name
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", pSym->Name );
|
|
|
|
DWORD_PTR pVariable = 0; // Will point to the variable's data in memory
|
|
|
|
if( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE ) {
|
|
// if ( pSym->Register == 8 ) // EBP is the value 8 (in DBGHELP 5.1)
|
|
{ // This may change!!!
|
|
pVariable = sf->AddrFrame.Offset;
|
|
pVariable += (DWORD_PTR)pSym->Address;
|
|
}
|
|
// else
|
|
// return FALSE;
|
|
}
|
|
else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER ) {
|
|
return FALSE; // Don't try to report register variable
|
|
} else {
|
|
pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable
|
|
}
|
|
|
|
// Determine if the variable is a user defined type (UDT). IF so, bHandled
|
|
// will return TRUE.
|
|
BOOL bHandled;
|
|
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer,pSym->ModBase, pSym->TypeIndex, 0, pVariable, bHandled );
|
|
if( !bHandled ) {
|
|
// The symbol wasn't a UDT, so do basic, stupid formatting of the
|
|
// variable. Based on the size, we're assuming it's a char, WORD, or
|
|
// DWORD.
|
|
BasicType basicType = GetBasicType( pSym->TypeIndex, pSym->ModBase );
|
|
pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size, (PVOID) pVariable );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// If it's a user defined type (UDT), recurse through its members until we're
|
|
// at fundamental types. When he hit fundamental types, return
|
|
// bHandled = FALSE, so that FormatSymbolValue() will format them.
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
char *CExceptionReport::DumpTypeIndex( char *pszCurrBuffer, DWORD64 modBase, DWORD dwTypeIndex, unsigned nestingLevel, DWORD_PTR offset, BOOL & bHandled )
|
|
{
|
|
bHandled = FALSE;
|
|
|
|
if( !m_DBGHELP.SymGetTypeInfo )
|
|
return NULL;
|
|
|
|
// Get the name of the symbol. This will either be a Type name (if a UDT), or the structure member name.
|
|
WCHAR * pwszTypeName;
|
|
if( m_DBGHELP.SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME, &pwszTypeName ) ) {
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, " %ls", pwszTypeName );
|
|
LocalFree( pwszTypeName );
|
|
}
|
|
|
|
// Determine how many children this type has.
|
|
DWORD dwChildrenCount = 0;
|
|
m_DBGHELP.SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, &dwChildrenCount );
|
|
|
|
if( !dwChildrenCount ) // If no children, we're done
|
|
return pszCurrBuffer;
|
|
|
|
// Prepare to get an array of "TypeIds", representing each of the children.
|
|
// SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
|
|
// TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this.
|
|
struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS {
|
|
ULONG MoreChildIds[1024];
|
|
FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);}
|
|
} children;
|
|
|
|
children.Count = dwChildrenCount;
|
|
children.Start= 0;
|
|
// Get the array of TypeIds, one for each child type
|
|
if( !m_DBGHELP.SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN, &children ) ) {
|
|
return pszCurrBuffer;
|
|
}
|
|
|
|
// Append a line feed
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" );
|
|
|
|
// Iterate through each of the children
|
|
for( unsigned i = 0; i < dwChildrenCount; i++ ) {
|
|
// Add appropriate indentation level (since this routine is recursive)
|
|
for( unsigned j = 0; j <= nestingLevel+1; j++ )
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, "\t" );
|
|
|
|
// Recurse for each of the child types
|
|
BOOL bHandled2;
|
|
pszCurrBuffer = DumpTypeIndex( pszCurrBuffer, modBase, children.ChildId[i], nestingLevel+1, offset, bHandled2 );
|
|
|
|
// If the child wasn't a UDT, format it appropriately
|
|
if( !bHandled2 ) {
|
|
// Get the offset of the child member, relative to its parent
|
|
DWORD dwMemberOffset;
|
|
m_DBGHELP.SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i], TI_GET_OFFSET, &dwMemberOffset );
|
|
|
|
// Get the real "TypeId" of the child. We need this for the SymGetTypeInfo( TI_GET_TYPEID ) call below.
|
|
DWORD typeId;
|
|
m_DBGHELP.SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i], TI_GET_TYPEID, &typeId );
|
|
|
|
// Get the size of the child member
|
|
ULONG64 length;
|
|
m_DBGHELP.SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH,&length);
|
|
|
|
// Calculate the address of the member
|
|
DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
|
|
|
|
BasicType basicType = GetBasicType(children.ChildId[i], modBase );
|
|
|
|
pszCurrBuffer = FormatOutputValue( pszCurrBuffer, basicType, length, (PVOID)dwFinalOffset );
|
|
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" );
|
|
}
|
|
}
|
|
|
|
bHandled = TRUE;
|
|
return pszCurrBuffer;
|
|
}
|
|
|
|
char *CExceptionReport::FormatOutputValue( char *pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress )
|
|
{
|
|
// Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
|
|
if( length == 1 )
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PBYTE)pAddress );
|
|
else if ( length == 2 )
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PWORD)pAddress );
|
|
else if ( length == 4 ) {
|
|
if ( basicType == btFloat ) {
|
|
pszCurrBuffer += sprintf(pszCurrBuffer," = %f", *(PFLOAT)pAddress);
|
|
} else if ( basicType == btChar ) {
|
|
if( !IsBadStringPtr( *(PSTR*)pAddress, 32) ) {
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, " = \"%.31s\"", *(PDWORD)pAddress );
|
|
} else
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PDWORD)pAddress );
|
|
} else
|
|
pszCurrBuffer += sprintf(pszCurrBuffer," = %X", *(PDWORD)pAddress);
|
|
} else if ( length == 8 ) {
|
|
if( basicType == btFloat ) {
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, " = %lf", *(double *)pAddress );
|
|
} else
|
|
pszCurrBuffer += sprintf( pszCurrBuffer, " = %I64X", *(DWORD64*)pAddress );
|
|
}
|
|
return pszCurrBuffer;
|
|
}
|
|
|
|
BasicType CExceptionReport::GetBasicType( DWORD typeIndex, DWORD64 modBase )
|
|
{
|
|
BasicType basicType;
|
|
|
|
if( !m_DBGHELP.SymGetTypeInfo )
|
|
return btNoType;
|
|
if( m_DBGHELP.SymGetTypeInfo( m_hProcess, modBase, typeIndex, TI_GET_BASETYPE, &basicType ) ) {
|
|
return basicType;
|
|
}
|
|
// Get the real "TypeId" of the child. We need this for the
|
|
// SymGetTypeInfo( TI_GET_TYPEID ) call below.
|
|
DWORD typeId;
|
|
if (m_DBGHELP.SymGetTypeInfo(m_hProcess,modBase, typeIndex, TI_GET_TYPEID, &typeId)) {
|
|
if ( m_DBGHELP.SymGetTypeInfo( m_hProcess, modBase, typeId, TI_GET_BASETYPE, &basicType ) ) {
|
|
return basicType;
|
|
}
|
|
}
|
|
return btNoType;
|
|
}
|
|
|
|
#define BYTES_PER_LINE 16
|
|
#define SHIFT_NUM 4
|
|
|
|
#define IsPrint(c) ( isprint(c) || ((c) >= 0xA1) && ((c) <= 0xFE) )
|
|
//
|
|
// Memory Dump function
|
|
//
|
|
void CExceptionReport::Dump( DWORD64 dw64Offset, DWORD dwSize, BOOL bAlign )
|
|
{
|
|
DWORD dwLoc, dwILoc, dwX;
|
|
LPBYTE pOut = (LPBYTE) dw64Offset;
|
|
|
|
if( bAlign == TRUE )
|
|
pOut = (LPBYTE) ((dw64Offset >> SHIFT_NUM) << SHIFT_NUM);
|
|
for( dwLoc = 0; dwLoc < dwSize; dwLoc += 16, pOut += BYTES_PER_LINE ) {
|
|
LPBYTE pLine = pOut;
|
|
|
|
Printf( _T( "%08lX: " ), (DWORD64) pOut );
|
|
for( dwX = 0, dwILoc = dwLoc; dwX < BYTES_PER_LINE; dwX++ ) {
|
|
if( dwX == (BYTES_PER_LINE / 2) )
|
|
Printf( _T( " " ) );
|
|
if( dwILoc++ > dwSize ) {
|
|
Printf( _T( "?? " ) );
|
|
} else {
|
|
Printf( _T( "%02X " ), *(pLine++) );
|
|
}
|
|
}
|
|
pLine = pOut;
|
|
Printf( " " );
|
|
for( dwX = 0, dwILoc = dwLoc; dwX < BYTES_PER_LINE; dwX++ ) {
|
|
if( dwILoc++ > dwSize ) {
|
|
Printf( _T( " " ) );
|
|
} else {
|
|
Printf( _T( "%c" ), isprint( *pLine ) ? *pLine : '.');
|
|
pLine++;
|
|
}
|
|
}
|
|
Printf( "\r\n" );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Helper function that writes to the report file, and allows the user to use printf style formating
|
|
//
|
|
int __cdecl CExceptionReport::Printf( const TCHAR * format, ... )
|
|
{
|
|
TCHAR szBuff[2048];
|
|
int retValue;
|
|
DWORD cbWritten;
|
|
va_list argptr;
|
|
|
|
va_start( argptr, format );
|
|
retValue = vsprintf( szBuff, format, argptr );
|
|
va_end( argptr );
|
|
WriteFile( m_hReportFile, szBuff, retValue * sizeof( TCHAR ), &cbWritten, 0 );
|
|
return retValue;
|
|
}
|