Files
Client/GameTools/CaldronBase/ExceptionMgr.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
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>
2025-11-29 20:17:20 +09:00

557 lines
19 KiB
C++

#include "./exceptionmgr.h"
#include "./LogMgr.h"
#pragma comment(linker, "/defaultlib:dbghelp.lib")
namespace Caldron { namespace Base {
LPTOP_LEVEL_EXCEPTION_FILTER CExceptionMgr::m_lpPrevFilter = NULL;
HANDLE CExceptionMgr::m_hProcess = NULL;
CExceptionMgr::CExceptionMgr(void)
{
m_lpPrevFilter = SetUnhandledExceptionFilter(Caldron::Base::CExceptionMgr::ExceptionFilter);
m_hProcess = GetCurrentProcess();
}
CExceptionMgr::~CExceptionMgr(void)
{
SetUnhandledExceptionFilter(m_lpPrevFilter);
}
LONG WINAPI CExceptionMgr::ExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo )
{
WriteExceptionReport(pExceptionInfo);
if(m_lpPrevFilter)
return m_lpPrevFilter(pExceptionInfo);
else
return EXCEPTION_CONTINUE_SEARCH;
}
void CExceptionMgr::WriteExceptionReport(PEXCEPTION_POINTERS pExceptionInfo )
{
// Start out with a banner
CLogMgr::_LogNoHeader("========= Exception Report ============\n");
PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
// First print information about the type of fault
CLogMgr::_LogNoHeader("Exception Code : %08X %s\n",pExceptionRecord->ExceptionCode,GetExceptionString(pExceptionRecord->ExceptionCode) );
// Now print information about where the fault occured
TCHAR szFaultingModule[MAX_PATH];
DWORD section, offset;
GetLogicalAddress( pExceptionRecord->ExceptionAddress,
szFaultingModule,
sizeof( szFaultingModule ),
section, offset );
CLogMgr::_LogNoHeader("Fault address: %08X %02X:%08X %s\n",
pExceptionRecord->ExceptionAddress,
section, offset, szFaultingModule );
PCONTEXT pCtx = pExceptionInfo->ContextRecord;
// Show the registers
#ifdef _M_IX86 // X86 Only!
CLogMgr::_LogNoHeader("\nRegisters :\n");
CLogMgr::_LogNoHeader("EAX:%08X\nEBX:%08X\nECX:%08X\nEDX:%08X\nESI:%08X\nEDI:%08X\n",
pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
pCtx->Esi, pCtx->Edi );
CLogMgr::_LogNoHeader("CS:EIP:%04X:%08X\n", pCtx->SegCs, pCtx->Eip );
CLogMgr::_LogNoHeader("SS:ESP:%04X:%08X EBP:%08X\n",
pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
CLogMgr::_LogNoHeader("DS:%04X ES:%04X FS:%04X GS:%04X\n",
pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
CLogMgr::_LogNoHeader("Flags:%08X\n",pCtx->EFlags );
#endif
SymSetOptions( SYMOPT_DEFERRED_LOADS );
// Initialize DbgHelp
if ( !SymInitialize( GetCurrentProcess(), 0, TRUE ) )
return;
CONTEXT trashableContext = *pCtx;
WriteCallStack( &trashableContext, false );
#ifdef _M_IX86 // X86 Only!
CLogMgr::_LogNoHeader("======================================\n");
CLogMgr::_LogNoHeader("Local Variable & Parameters\n");
trashableContext = *pCtx;
WriteCallStack( &trashableContext, true );
CLogMgr::_LogNoHeader("======================================\n");
CLogMgr::_LogNoHeader("Global Variable\n");
SymEnumSymbols( GetCurrentProcess(),
(DWORD64)GetModuleHandle(szFaultingModule),
0, EnumerateSymbolsCallback, 0 );
#endif // X86 Only!
SymCleanup( GetCurrentProcess() );
CLogMgr::_LogNoHeader("\n");
}
LPSTR CExceptionMgr::GetExceptionString(DWORD dwCode)
{
#define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x);
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;
}
//=============================================================================
// 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 CExceptionMgr::GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len,DWORD& section, DWORD& offset)
{
MEMORY_BASIC_INFORMATION mbi;
if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) )
return FALSE;
DWORD hMod = (DWORD)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)(hMod + pDosHdr->e_lfanew);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
DWORD rva = (DWORD)addr - 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 = rva - sectionStart;
return TRUE;
}
}
return FALSE; // Should never get here!
}
void CExceptionMgr::WriteCallStack(PCONTEXT pContext,bool bWriteVariables )
{
CLogMgr::_LogNoHeader("==========Call Stack==========\n");
CLogMgr::_LogNoHeader("Address Frame Function SourceFile\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 ( ! StackWalk( dwMachineType,
m_hProcess,
GetCurrentThread(),
&sf,
pContext,
0,
SymFunctionTableAccess,
SymGetModuleBase,
0 ) )
break;
if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
break; // the frame is OK. Bail if not.
CLogMgr::_LogNoHeader("%08X %08X ",sf.AddrPC.Offset, sf.AddrFrame.Offset );
// Get the name of the function for this stack frame entry
BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
pSymbol->SizeOfStruct = sizeof(symbolBuffer);
pSymbol->MaxNameLen = 1024;
DWORD64 symDisplacement = 0; // Displacement of the input address,
// relative to the start of the symbol
if ( SymFromAddr(m_hProcess,sf.AddrPC.Offset,&symDisplacement,pSymbol))
{
CLogMgr::_LogNoHeader("%hs+%I64X",pSymbol->Name, symDisplacement );
}
else // No symbol found. Print out the logical address instead.
{
TCHAR szModule[MAX_PATH] = _T("");
DWORD section = 0, offset = 0;
GetLogicalAddress( (PVOID)sf.AddrPC.Offset,
szModule, sizeof(szModule), section, offset );
CLogMgr::_LogNoHeader("%04X:%08X %s",section, offset, szModule );
}
// Get the source line for this stack frame entry
IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
DWORD dwLineDisplacement;
if ( SymGetLineFromAddr( m_hProcess, sf.AddrPC.Offset,
&dwLineDisplacement, &lineInfo ) )
{
CLogMgr::_LogNoHeader(" %s line %u",lineInfo.FileName,lineInfo.LineNumber);
}
CLogMgr::_LogNoHeader("\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;
SymSetContext( m_hProcess, &imagehlpStackFrame, 0 );
// Enumerate the locals/parameters
SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf );
CLogMgr::_LogNoHeader("\n");
}
}
}
BOOL CALLBACK CExceptionMgr::EnumerateSymbolsCallback( PSYMBOL_INFO pSymInfo, ULONG SymbolSize,PVOID UserContext )
{
char szBuffer[2048];
__try
{
if ( FormatSymbolValue( pSymInfo, (STACKFRAME*)UserContext,
szBuffer, sizeof(szBuffer) ) )
CLogMgr::_LogNoHeader("\t%s\n",szBuffer);
}
__except( 1 )
{
CLogMgr::_LogNoHeader("punting on symbol %s\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 CExceptionMgr::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 * CExceptionMgr::DumpTypeIndex(
char * pszCurrBuffer,
DWORD64 modBase,
DWORD dwTypeIndex,
unsigned nestingLevel,
DWORD_PTR offset,
bool & bHandled )
{
bHandled = false;
// Get the name of the symbol. This will either be a Type name (if a UDT),
// or the structure member name.
WCHAR * pwszTypeName;
if ( 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;
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 ( !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;
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;
SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i],
TI_GET_TYPEID, &typeId );
// Get the size of the child member
ULONG64 length;
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 * CExceptionMgr::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 CExceptionMgr::GetBasicType( DWORD typeIndex, DWORD64 modBase )
{
BasicType basicType;
if ( 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 (SymGetTypeInfo(m_hProcess,modBase, typeIndex, TI_GET_TYPEID, &typeId))
{
if ( SymGetTypeInfo( m_hProcess, modBase, typeId, TI_GET_BASETYPE,
&basicType ) )
{
return basicType;
}
}
return btNoType;
}
}}