Files
Client/Server/RylServerProject/BaseLibrary/Utility/Debug/ExceptionReport.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

1155 lines
33 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.
/* ==========================================================================
File : ExceptionReport.h
Vers.: 0.91
Plat.: Windows 98 or above
Desc.: ExceptionHandle routine (report)
Req. : DbgHelp v5.1 or above
Adapted from MSDN March 2002 - Under the Hood
first made by cpascal.
modified by Sparrowhawk.
========================================================================== */
// #define _WIN32_WINDOWS 0x0410 // Windows 98
// #define _WIN32_WINNT 0x0500 // Windows 2000
#include "stdafx.h"
#include "ExceptionReport.h"
#define CHECK_LENGTH_RETURN(length, total_length, retval) \
if(length < 0) { return retval; } else { total_length += length; }
struct SymEnumSymbols_UserData
{
STACKFRAME* m_lpsf;
char* m_szBuffer_Out;
int m_nBufferSize_In;
int m_nWritten_Out;
};
// stolen from dia sdk 2.0 (cvconst.h)
enum SymTagEnum
{
SymTagNull,
SymTagExe,
SymTagCompiland,
SymTagCompilandDetails,
SymTagCompilandEnv,
SymTagFunction,
SymTagBlock,
SymTagData,
SymTagAnnotation,
SymTagLabel,
SymTagPublicSymbol,
SymTagUDT,
SymTagEnum,
SymTagFunctionType,
SymTagPointerType,
SymTagArrayType,
SymTagBaseType,
SymTagTypedef,
SymTagBaseClass,
SymTagFriend,
SymTagFunctionArgType,
SymTagFuncDebugStart,
SymTagFuncDebugEnd,
SymTagUsingNamespace,
SymTagVTableShape,
SymTagVTable,
SymTagCustom,
SymTagThunk,
SymTagCustomType,
SymTagManagedType,
SymTagDimension,
SymTagMax
};
CExceptionReport& CExceptionReport::GetInstance()
{
static CExceptionReport exceptionReport;
return exceptionReport;
}
CExceptionReport::CExceptionReport()
: m_dwFeaturesFlag(0),
m_OldFilter(0),
m_lpUserFunc(0),
m_logFile(0),
m_eMiniDumpType(MiniDumpNormal)
{
SetProgramName(m_szLogPrefixName, MAX_PATH, 0);
Enable(CATCH_EXCEPTION | USE_MINIDUMP | USE_REPORT);
m_DBGHELP.Init("DBGHELP.DLL");
}
CExceptionReport::~CExceptionReport()
{
m_DBGHELP.Release();
Disable(CATCH_EXCEPTION | USE_MINIDUMP | USE_REPORT);
CloseLogFile();
}
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>̱<EFBFBD> <20><><EFBFBD>ؼ<EFBFBD>(<28><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20>ϱ<EFBFBD> <20><><EFBFBD>ؼ<EFBFBD>) <20>̰<EFBFBD><CCB0><EFBFBD> <20><><EFBFBD><EFBFBD>.
void CExceptionReport::SetProgramName(TCHAR* pszOutBuffer, int nBufferSize, TCHAR* pszProgramName)
{
if(0 == pszProgramName)
{
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, pszOutBuffer, nBufferSize);
PTSTR pszDot = pszOutBuffer;
// Look for the '.' before the "EXE" extension. Replace '.' to '\0'
if((pszDot = _tcsrchr( pszDot, _T('.'))))
{
*pszDot = 0;
}
_tsplitpath(pszOutBuffer, szDrive, szDir, szFilename, szExt);
_tcsncpy(pszOutBuffer, szFilename, nBufferSize);
}
else
{
_tcsncpy(pszOutBuffer, pszProgramName, nBufferSize);
}
}
void CExceptionReport::CloseLogFile()
{
if(m_logFile)
{
fprintf(m_logFile, "Exception occured AGAIN! EXITING NOW!\r\n");
fclose(m_logFile);
m_logFile = 0;
}
}
void CExceptionReport::Enable(unsigned long dwEnableFeature)
{
if(0 == m_OldFilter && (dwEnableFeature & CATCH_EXCEPTION))
{
m_OldFilter = SetUnhandledExceptionFilter(UnhandledExceptionFilter);
}
m_dwFeaturesFlag |= dwEnableFeature;
}
void CExceptionReport::Disable(unsigned long dwDisableFeature)
{
if(0 != m_OldFilter && (dwDisableFeature & CATCH_EXCEPTION))
{
SetUnhandledExceptionFilter(m_OldFilter);
m_OldFilter = 0;
}
m_dwFeaturesFlag &= ~dwDisableFeature;
}
LONG CExceptionReport::ProcessSecondException(PEXCEPTION_POINTERS lpExceptionInfo)
{
CloseLogFile();
return (0 != m_OldFilter) ? m_OldFilter(lpExceptionInfo) : EXCEPTION_CONTINUE_SEARCH;
}
LONG CExceptionReport::ProcessException(PEXCEPTION_POINTERS lpExceptionInfo)
{
SetUnhandledExceptionFilter(UnhandledSecondExceptionFilter);
GetLocalTime(&m_tempSystemTime);
_tcscpy(m_szTempBuffer, m_szLogPrefixName);
wsprintf(m_szTempBuffer + strlen(m_szTempBuffer), " %04d-%02d-%02d %02d,%02d,%02d.dmp",
m_tempSystemTime.wYear, m_tempSystemTime.wMonth, m_tempSystemTime.wDay,
m_tempSystemTime.wHour, m_tempSystemTime.wMinute, m_tempSystemTime.wSecond );
HANDLE hDumpFile = CreateFile(m_szTempBuffer, GENERIC_WRITE, FILE_SHARE_READ,
0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
if(m_DBGHELP.MiniDumpWriteDump && INVALID_HANDLE_VALUE != hDumpFile)
{
memset(&m_miniDumpInfo, 0, sizeof(MINIDUMP_EXCEPTION_INFORMATION));
m_miniDumpInfo.ThreadId = ::GetCurrentThreadId();
m_miniDumpInfo.ExceptionPointers = lpExceptionInfo;
m_miniDumpInfo.ClientPointers = 0;
/*
MiniDump Options
MiniDumpWithDataSegs
MiniDumpWithFullMemory
*/
m_DBGHELP.MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
hDumpFile, m_eMiniDumpType, &m_miniDumpInfo, 0, 0);
CloseHandle(hDumpFile);
}
_tcscpy(m_szTempBuffer, m_szLogPrefixName);
wsprintf(m_szTempBuffer + strlen(m_szTempBuffer), " %04d-%02d-%02d %02d,%02d,%02d.txt",
m_tempSystemTime.wYear, m_tempSystemTime.wMonth, m_tempSystemTime.wDay,
m_tempSystemTime.wHour, m_tempSystemTime.wMinute, m_tempSystemTime.wSecond );
m_logFile = fopen(m_szTempBuffer, "wt");
if(0 != m_logFile)
{
WriteExceptionReport(lpExceptionInfo);
if(m_lpUserFunc)
{
fprintf(m_logFile,
_T( "------------------------------------------------------------------------------\r\n")
_T( " Application-specific log\r\n" )
_T( "------------------------------------------------------------------------------\r\n")
_T( "\r\n" ));
fflush(m_logFile);
__try
{
int nLength = m_lpUserFunc(m_szLogBuffer, MAX_LOG_BUFFER);
if(0 < nLength)
{
m_szLogBuffer[MAX_LOG_BUFFER - 1] = 0;
fprintf(m_logFile, "%s", m_szLogBuffer);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
fprintf(m_logFile, "Exception occured by UserFunc : ExceptionCode : %d - %s",
GetExceptionCode(), GetExceptionString(GetExceptionCode()));
}
fprintf(m_logFile, _T("\r\n\r\n")
_T( "==============================================================================\r\n"));
CloseLogFile();
}
}
return EXCEPTION_EXECUTE_HANDLER;
}
//
// Write informations to Report file. Called by UnhandledExceptionFilter
//
void CExceptionReport::WriteExceptionReport(PEXCEPTION_POINTERS pExceptionInfo)
{
// Start out with a banner
if(0 == m_logFile)
{
return;
}
fprintf(m_logFile,
_T( "==============================================================================\r\n\r\n" ));
if(WriteBasicInfo(m_szLogBuffer, MAX_LOG_BUFFER,
pExceptionInfo->ExceptionRecord) < 0)
{
_sntprintf(m_szLogBuffer, MAX_LOG_BUFFER,
"Failed to write basic info.\r\n\r\n");
}
_fputts(m_szLogBuffer, m_logFile);
fflush(m_logFile);
if(WriteRegistersInfo(m_szLogBuffer, MAX_LOG_BUFFER,
pExceptionInfo->ContextRecord) < 0)
{
_sntprintf(m_szLogBuffer, MAX_LOG_BUFFER,
"Failed to write registers info.\r\n\r\n");
}
_fputts(m_szLogBuffer, m_logFile);
fflush(m_logFile);
if(!m_DBGHELP.SymInitialize ||
!m_DBGHELP.SymInitialize(GetCurrentProcess(), 0, TRUE))
{
return;
}
m_tempContext = *(pExceptionInfo->ContextRecord);
BOOL bHasSymbol = FALSE;
if(WriteStackDetails(m_szLogBuffer, MAX_LOG_BUFFER,
&m_tempContext, FALSE, bHasSymbol) < 0)
{
_sntprintf(m_szLogBuffer, MAX_LOG_BUFFER,
"Failed to write stack details.\r\n\r\n");
}
_fputts(m_szLogBuffer, m_logFile);
fflush(m_logFile);
if(bHasSymbol)
{
/* m_tempContext = *(pExceptionInfo->ContextRecord);
if(WriteStackDetails(m_szLogBuffer, MAX_LOG_BUFFER,
&m_tempContext, FALSE, bHasSymbol) < 0)
{
_sntprintf(m_szLogBuffer, MAX_LOG_BUFFER,
"Failed to write stack details.\r\n\r\n");
}
_fputts(m_szLogBuffer, m_logFile);
fflush(m_logFile);
<09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>α׷<CEB1> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>ƾ. <20><>, <20><EFBFBD><EBB7AE> <20><> <20>ǹǷ<C7B9>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ʴ´<CAB4>.
_fputts(
_T( "------------------------------------------------------------------------------\r\n" )
_T( " Global Variables\r\n" )
_T( "------------------------------------------------------------------------------\r\n" )
_T( "\r\n" ));
if(m_DBGHELP.SymEnumSymbols)
{
m_DBGHELP.SymEnumSymbols( GetCurrentProcess(), (DWORD64) GetModuleHandle( m_szModuleName ), 0, EnumerateSymbolsCallback, 0 );
}
*/
}
if(m_DBGHELP.SymCleanup)
{
m_DBGHELP.SymCleanup(GetCurrentProcess());
}
_fputts(_T("\r\n\r\n\r\n"), m_logFile);
if(WriteMemoryDump(m_szLogBuffer, MAX_LOG_BUFFER,
pExceptionInfo->ContextRecord) < 0)
{
_sntprintf(m_szLogBuffer, MAX_LOG_BUFFER,
"Failed to write memory dump.\r\n\r\n");
}
_fputts(m_szLogBuffer, m_logFile);
fflush(m_logFile);
return;
}
//
// Write basic informations(user, computer name, error type)
//
int CExceptionReport::WriteBasicInfo(TCHAR* szBuffer_Out, const int nBufferSize,
PEXCEPTION_RECORD pExceptionRecord)
{
TCHAR szProgramName[MAX_PATH];
TCHAR szFileName[MAX_PATH];
TCHAR szUserName[MAX_PATH];
TCHAR szComputerName[MAX_PATH];
TCHAR szModuleName[MAX_PATH];
unsigned long dwUserLen = MAX_PATH;
unsigned long dwComputerLen = MAX_PATH;
unsigned long dwSection = 0;
unsigned long dwOffset = 0;
SetProgramName(szProgramName, MAX_PATH, 0);
GetModuleFileName((HMODULE)0, szFileName, MAX_PATH);
GetUserName(szUserName, &dwUserLen);
GetComputerName(szComputerName, &dwComputerLen);
// Print information about the type of fault and where the fault occured
GetLogicalAddress(pExceptionRecord->ExceptionAddress,
szModuleName, sizeof(szModuleName), dwSection, dwOffset);
return _sntprintf(szBuffer_Out, nBufferSize,
_T("------------------------------------------------------------------------------\r\n")
_T(" Basic Information\r\n")
_T("------------------------------------------------------------------------------\r\n")
_T("\r\n")
_T("Program Name : %s\r\n")
_T("EXE : %s\r\n")
_T("User : %s\r\n")
_T("Computer : %s\r\n")
_T("\r\n")
_T("Program : %s\r\n")
_T("Exception : %08x (%s)\r\n")
_T("Fault Address: %08x %02X:%08x\r\n\r\n\r\n"),
szProgramName,
szFileName,
szUserName,
szComputerName,
szModuleName,
pExceptionRecord->ExceptionCode,
GetExceptionString(pExceptionRecord->ExceptionCode),
pExceptionRecord->ExceptionAddress,
dwSection,
dwOffset);
}
//
// Show the registers
//
int CExceptionReport::WriteRegistersInfo(TCHAR* szBuffer_Out, const int nBufferSize, PCONTEXT pContext)
{
return _sntprintf(szBuffer_Out, nBufferSize,
_T("------------------------------------------------------------------------------\r\n")
_T(" x86 Registers\r\n")
_T("------------------------------------------------------------------------------\r\n")
_T("\r\n")
_T("EAX=%08x EBX=%08x ECX=%08x EDX=%08x\r\n")
_T("ESI=%08x EDI=%08x EBP=%08x\r\n")
_T("DS =%04X ES=%04X FS=%04X GS:%04X\r\n")
_T("CS:EIP=%04X:%08x\r\n")
_T("SS:ESP=%04X:%08x\r\n")
_T("Flags=%08x\r\n")
_T("\r\n\r\n"),
pContext->Eax, pContext->Ebx, pContext->Ecx, pContext->Edx,
pContext->Esi, pContext->Edi, pContext->Ebp,
pContext->SegDs, pContext->SegEs, pContext->SegFs, pContext->SegGs,
pContext->SegCs, pContext->Eip,
pContext->SegSs, pContext->Esp,
pContext->EFlags);
}
//
// Show the Memory
//
int CExceptionReport::WriteMemoryDump(TCHAR* szBuffer_Out,
const int nBufferSize,
PCONTEXT pContext,
unsigned int nMaxIPDump,
unsigned int nMaxStackDump)
{
int nTotalLength = 0;
int nLength = _sntprintf(szBuffer_Out, nBufferSize,
_T("------------------------------------------------------------------------------\r\n")
_T(" Memory Dump\r\n")
_T("------------------------------------------------------------------------------\r\n")
_T("\r\n" )
_T("Code: %d bytes starting at (EIP = %08lX)\r\n"), nMaxIPDump, pContext->Eip);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
nLength = Dump(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength,
pContext->Eip, nMaxIPDump, FALSE);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
nLength = _snprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength,
_T("\r\nStack: %d bytes starting at (ESP = %08lX)\r\n"), nMaxStackDump, pContext->Esp);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
nLength = Dump(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength,
pContext->Esp, nMaxStackDump, TRUE);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
nLength = _snprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength, _T("\r\n\r\n"));
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
return nTotalLength;
}
LPTSTR CExceptionReport::GetExceptionString(unsigned long 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);
}
#undef EXCEPTION
// If not one of the "known" exceptions, try to get the string
// from NTDLL.DLL's message table.
__declspec(thread) static TCHAR s_szBuffer[MAX_PATH] = { 0 };
FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle( _T("NTDLL.DLL") ),
dwCode, 0, s_szBuffer, MAX_PATH, 0 );
return s_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 TCHARacters!)
//
BOOL CExceptionReport::GetLogicalAddress(PVOID addr, PTSTR szModule,
unsigned long len,
unsigned long& section,
unsigned long& offset )
{
MEMORY_BASIC_INFORMATION mbi;
if(!VirtualQuery(addr, &mbi, sizeof(mbi)))
{
return FALSE;
}
PVOID hMod = mbi.AllocationBase;
if(NULL == hMod)
{
return FALSE;
}
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++ )
{
unsigned long sectionStart = pSection->VirtualAddress;
unsigned long 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 = (unsigned long) (rva - sectionStart);
return TRUE;
}
}
return FALSE; // Should never get here!
}
int CExceptionReport::WriteStackDetails(TCHAR* szBuffer_Out, const int nBufferSize,
PCONTEXT pContext,
BOOL bWriteVariables, /* TRUE if local/params should be output */
BOOL& bHasSymbol_Out,
const int nStackDepth)
{
int nTotalLength = 0;
int nLength = _sntprintf(szBuffer_Out, nBufferSize,
_T("------------------------------------------------------------------------------\r\n")
_T(" Call Stack (%s)\r\n")
_T("------------------------------------------------------------------------------\r\n")
_T("\r\n" )
_T("Address Frame Function SourceFile\r\n"),
bWriteVariables ? "Detail" : "Short");
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
unsigned long dwMachineType = 0;
// Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
STACKFRAME sf;
memset( &sf, 0, sizeof(sf) );
// 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;
const int MAX_SYMBOL_NAME_LEN = 2048;
BYTE symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYMBOL_NAME_LEN];
TCHAR szModule[MAX_PATH] = _T("");
IMAGEHLP_LINE lineInfo;
IMAGEHLP_STACK_FRAME imagehlpStackFrame;
SymEnumSymbols_UserData symbol_userdata;
CDBGFuncClass& dbgFuncClass = CExceptionReport::GetInstance().GetDBGFuncClass();
if(dbgFuncClass.StackWalk)
{
for(int nCount = 0; nCount < nStackDepth; ++nCount)
{
if(0 == sf.AddrFrame.Offset ||
!dbgFuncClass.StackWalk(dwMachineType, GetCurrentProcess(), GetCurrentThread(), &sf, pContext, 0,
dbgFuncClass.SymFunctionTableAccess, dbgFuncClass.SymGetModuleBase, 0))
{
break;
}
nLength = _sntprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength,
_T("%08x %08x "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
// Get the name of the function for this stack frame entry
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
pSymbol->SizeOfStruct = sizeof(symbolBuffer);
pSymbol->MaxNameLen = MAX_SYMBOL_NAME_LEN;
DWORD64 symDisplacement = 0; // Displacement of the input address,
// relative to the start of the symbol
if(dbgFuncClass.SymFromAddr &&
dbgFuncClass.SymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &symDisplacement, pSymbol))
{
wsprintf(szModule, _T("%-s+0x%p"), pSymbol->Name, (void*)symDisplacement);
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength,
_T( "%-30s" ), szModule);
}
else
{
// No symbol found. Print out the logical address instead.
unsigned long section = 0, offset = 0;
GetLogicalAddress((PVOID)(DWORD64)sf.AddrPC.Offset, szModule, sizeof(szModule), section, offset);
nLength = _sntprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength,
_T("%04X:%08x %s"), section, offset, szModule);
}
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
// Get the source line for this stack frame entry
memset(&lineInfo, 0, sizeof(IMAGEHLP_LINE));
lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE);
unsigned long dwLineDisplacement = 0;
if(dbgFuncClass.SymGetLineFromAddr &&
(dbgFuncClass.SymGetLineFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo)))
{
nLength = _sntprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength,
_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
if(bHasSymbol_Out == FALSE)
{
bHasSymbol_Out = TRUE;
}
}
nLength = _sntprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength, _T("\r\n"));
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
// Write out the variables, if desired
if(bWriteVariables)
{
// Use SymSetContext to get just the locals/params for this frame
memset(&imagehlpStackFrame, 0, sizeof(IMAGEHLP_STACK_FRAME));
imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
if(dbgFuncClass.SymSetContext)
{
dbgFuncClass.SymSetContext(GetCurrentProcess(),
&imagehlpStackFrame, 0);
}
// Enumerate the locals/parameters
if(dbgFuncClass.SymEnumSymbols)
{
symbol_userdata.m_lpsf = &sf;
symbol_userdata.m_szBuffer_Out = szBuffer_Out + nTotalLength;
symbol_userdata.m_nWritten_Out = 0;
symbol_userdata.m_nBufferSize_In = nBufferSize - nTotalLength;
dbgFuncClass.SymEnumSymbols(GetCurrentProcess(),
0, 0, EnumerateSymbolsCallback, &symbol_userdata);
CHECK_LENGTH_RETURN(symbol_userdata.m_nWritten_Out, nTotalLength, -1);
}
nLength = _sntprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength, _T("\r\n"));
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
}
}
nLength = _sntprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength, _T("\r\n\r\n"));
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
return nTotalLength;
}
//
// The function invoked by SymEnumSymbols
//
BOOL CALLBACK CExceptionReport::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext)
{
const int MAX_NAME_BUFFER = 3072;
TCHAR szBuffer[MAX_NAME_BUFFER];
SymEnumSymbols_UserData* lpSymbol_UserData =
reinterpret_cast<SymEnumSymbols_UserData*>(UserContext);
__try
{
if(0 < FormatSymbolValue(pSymInfo, lpSymbol_UserData->m_lpsf, szBuffer, MAX_NAME_BUFFER))
{
lpSymbol_UserData->m_nWritten_Out = _sntprintf(
lpSymbol_UserData->m_szBuffer_Out,
lpSymbol_UserData->m_nBufferSize_In,
_T("\t%s\r\n"), szBuffer);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
lpSymbol_UserData->m_nWritten_Out = _sntprintf(
lpSymbol_UserData->m_szBuffer_Out,
lpSymbol_UserData->m_nBufferSize_In,
_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.
//
int CExceptionReport::FormatSymbolValue(PSYMBOL_INFO pSym, STACKFRAME * sf,
TCHAR* pszBuffer, unsigned int nBufferSize)
{
int nLength = 0;
int nTotalLength = 0;
// If it's a function, don't do anything.
if(pSym->Tag == SymTagFunction)
{
return 0;
}
// Indicate if the variable is a local or parameter
if(pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
{
nLength = _sntprintf(pszBuffer, nBufferSize, "Parameter ");
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
else if(pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
{
nLength = _sntprintf(pszBuffer, nBufferSize, "Local ");
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
// Emit the variable name
nLength = _sntprintf(pszBuffer + nTotalLength, nBufferSize - nTotalLength, "\'%s\'", pSym->Name);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
DWORD_PTR pVariable = 0; // Will point to the variable's data in memory
if(pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE)
{
pVariable = sf->AddrFrame.Offset;
pVariable += (DWORD_PTR)pSym->Address;
}
else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER)
{
// Don't try to report register variable
return 0;
}
else
{
// It must be a global variable
pVariable = (DWORD_PTR)pSym->Address;
}
// Determine if the variable is a user defined type (UDT). IF so, bHandled
// will return TRUE.
BOOL bHandled;
nLength = DumpTypeIndex(pszBuffer + nTotalLength, nBufferSize - nTotalLength,
pSym->ModBase, pSym->TypeIndex, 0, pVariable, bHandled);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
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 TCHAR, WORD, or
// unsigned long.
BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase);
nLength = FormatOutputValue(pszBuffer + nTotalLength, nBufferSize - nTotalLength,
basicType, pSym->Size, (PVOID) pVariable);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
return nTotalLength;
}
//////////////////////////////////////////////////////////////////////////////
// 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.
//////////////////////////////////////////////////////////////////////////////
int CExceptionReport::DumpTypeIndex(TCHAR* szBuffer_Out,
const int nBufferSize,
DWORD64 modBase,
unsigned long dwTypeIndex,
unsigned int nestingLevel,
DWORD_PTR offset,
BOOL & bHandled)
{
bHandled = FALSE;
int nTotalLength = 0;
int nLength = 0;
CDBGFuncClass& dbgFuncClass = CExceptionReport::GetInstance().GetDBGFuncClass();
if(!dbgFuncClass.SymGetTypeInfo)
{
return 0;
}
// Get the name of the symbol. This will either be a Type name (if a UDT), or the structure member name.
WCHAR* pwszTypeName = 0;
if(dbgFuncClass.SymGetTypeInfo(GetCurrentProcess(),
modBase, dwTypeIndex, TI_GET_SYMNAME, &pwszTypeName))
{
nLength += _snprintf(szBuffer_Out, nBufferSize, " %ls", pwszTypeName);
LocalFree(pwszTypeName);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
// Determine how many children this type has.
unsigned long dwChildrenCount = 0;
dbgFuncClass.SymGetTypeInfo(GetCurrentProcess(), modBase,
dwTypeIndex, TI_GET_CHILDRENCOUNT, &dwChildrenCount);
if(!dwChildrenCount) // If no children, we're done
{
return nTotalLength;
}
// 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(!dbgFuncClass.SymGetTypeInfo(GetCurrentProcess(),
modBase, dwTypeIndex, TI_FINDCHILDREN, &children))
{
return nTotalLength;
}
// Append a line feed
nLength += _snprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength, "\r\n");
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
// Iterate through each of the children
for(unsigned int nChildrenCount = 0; nChildrenCount < dwChildrenCount; ++nChildrenCount)
{
// Add appropriate indentation level (since this routine is recursive)
for( unsigned int j = 0; j <= nestingLevel+1; j++ )
{
szBuffer_Out += sprintf( szBuffer_Out, "\t" );
}
// Recurse for each of the child types
BOOL bHandled2;
nLength = DumpTypeIndex(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength,
modBase, children.ChildId[nChildrenCount], nestingLevel+1, offset, bHandled2);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
// If the child wasn't a UDT, format it appropriately
if(!bHandled2)
{
// Get the offset of the child member, relative to its parent
unsigned long dwMemberOffset = 0;
dbgFuncClass.SymGetTypeInfo( GetCurrentProcess(),
modBase, children.ChildId[nChildrenCount], TI_GET_OFFSET, &dwMemberOffset);
// Get the real "TypeId" of the child. We need this for the SymGetTypeInfo( TI_GET_TYPEID ) call below.
unsigned long typeId = 0;
dbgFuncClass.SymGetTypeInfo( GetCurrentProcess(),
modBase, children.ChildId[nChildrenCount], TI_GET_TYPEID, &typeId);
// Get the size of the child member
ULONG64 length = 0;
dbgFuncClass.SymGetTypeInfo(GetCurrentProcess(), modBase,
typeId, TI_GET_LENGTH, &length);
// Calculate the address of the member
DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
BasicType basicType = GetBasicType(children.ChildId[nChildrenCount], modBase );
nLength = FormatOutputValue(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength, basicType, length, (PVOID)dwFinalOffset);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength, "\r\n");
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
}
bHandled = TRUE;
return nTotalLength;
}
int CExceptionReport::FormatOutputValue(TCHAR* szBuffer_Out,
const int nBufferSize,
BasicType basicType,
DWORD64 length, PVOID pAddress)
{
int nLength = 0;
int nTotalLength = 0;
// Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
if(length == 1)
{
nLength = _sntprintf(szBuffer_Out, nBufferSize, " = %X", *(PBYTE)pAddress);
}
else if(length == 2)
{
nLength = _sntprintf(szBuffer_Out, nBufferSize, " = %X", *(PWORD)pAddress);
}
else if(length == 4)
{
if(basicType == btFloat)
{
nLength = _sntprintf(szBuffer_Out, nBufferSize, " = %f", *(PFLOAT)pAddress);
}
else if(basicType == btChar)
{
if(!IsBadStringPtr(*(PSTR*)pAddress, 32))
{
nLength = _sntprintf(szBuffer_Out, nBufferSize, " = \"%.31s\"", *(PDWORD)pAddress);
}
else
{
nLength = _sntprintf(szBuffer_Out, nBufferSize, " = %X", *(PDWORD)pAddress);
}
}
else
{
nLength = _sntprintf(szBuffer_Out, nBufferSize, " = %X", *(PDWORD)pAddress);
}
}
else if (length == 8)
{
if(basicType == btFloat)
{
nLength = _sntprintf(szBuffer_Out, nBufferSize, " = %lf", *(double *)pAddress );
}
else
{
TCHAR szAddress[64] = "";
_ui64toa(*(DWORD64*)pAddress, szAddress, 16);
nLength = _sntprintf(szBuffer_Out, nBufferSize, " = 0x%s", szAddress);
}
}
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
return nTotalLength;
}
BasicType CExceptionReport::GetBasicType(unsigned long typeIndex, DWORD64 modBase)
{
CDBGFuncClass& dbgFuncClass = CExceptionReport::GetInstance().GetDBGFuncClass();
BasicType basicType;
if(!dbgFuncClass.SymGetTypeInfo)
return btNoType;
if(dbgFuncClass.SymGetTypeInfo(GetCurrentProcess(),
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.
unsigned long typeId = 0;
if(dbgFuncClass.SymGetTypeInfo(GetCurrentProcess(),
modBase, typeIndex, TI_GET_TYPEID, &typeId))
{
if(dbgFuncClass.SymGetTypeInfo( GetCurrentProcess(),
modBase, typeId, TI_GET_BASETYPE, &basicType))
{
return basicType;
}
}
return btNoType;
}
//
// Memory Dump function
//
int CExceptionReport::Dump(TCHAR* szBuffer_Out, const int nBufferSize,
DWORD64 dw64Offset, unsigned long dwSize, BOOL bAlign )
{
int nTotalLength = 0;
int nLength = 0;
__try
{
unsigned long dwLoc = 0, dwILoc = 0, dwX = 0;
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;
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength,
_T( "%08lX: " ), (DWORD64) pOut);
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
for(dwX = 0, dwILoc = dwLoc; dwX < BYTES_PER_LINE; dwX++)
{
if(dwX == (BYTES_PER_LINE / 2))
{
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength, _T(" "));
}
if( dwILoc++ > dwSize )
{
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength, _T("?? "));
}
else
{
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength, _T("%02X "), *(pLine++));
}
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
pLine = pOut;
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength, _T(" "));
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
for( dwX = 0, dwILoc = dwLoc; dwX < BYTES_PER_LINE; dwX++ )
{
if( dwILoc++ > dwSize )
{
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength, _T(" "));
}
else
{
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength, _T( "%c" ), isprint(*pLine) ? *pLine : '.');
pLine++;
}
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
nLength = _sntprintf(szBuffer_Out + nTotalLength,
nBufferSize - nTotalLength, _T("\r\n"));
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
nLength = _sntprintf(szBuffer_Out + nTotalLength, nBufferSize - nTotalLength,
_T("\r\n Dump failed by Exception : %d - %s\r\n"),
GetExceptionCode(), GetExceptionString(GetExceptionCode()));
CHECK_LENGTH_RETURN(nLength, nTotalLength, -1);
}
return nTotalLength;
}