diff options
Diffstat (limited to 'src/shared/WheatyExceptionReport.cpp')
-rw-r--r-- | src/shared/WheatyExceptionReport.cpp | 2028 |
1 files changed, 1014 insertions, 1014 deletions
diff --git a/src/shared/WheatyExceptionReport.cpp b/src/shared/WheatyExceptionReport.cpp index 27fae70e4c1..41d2a5e2727 100644 --- a/src/shared/WheatyExceptionReport.cpp +++ b/src/shared/WheatyExceptionReport.cpp @@ -1,1014 +1,1014 @@ -//==========================================
-// Matt Pietrek
-// MSDN Magazine, 2002
-// FILE: WheatyExceptionReport.CPP
-//==========================================
-#define WIN32_LEAN_AND_MEAN
-#pragma warning(disable:4996)
-#pragma warning(disable:4312)
-#pragma warning(disable:4311)
-#include <windows.h>
-#include <tlhelp32.h>
-#include <stdio.h>
-#include <tchar.h>
-#define _NO_CVCONST_H
-#include <dbghelp.h>
-#include "WheatyExceptionReport.h"
-#include "svn_revision.h"
-#define CrashFolder _T("Crashs")
-//#pragma comment(linker, "/defaultlib:dbghelp.lib")
-
-inline LPTSTR ErrorMessage(DWORD dw)
-{
- LPVOID lpMsgBuf;
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,
- dw,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &lpMsgBuf,
- 0, NULL );
- return (LPTSTR)lpMsgBuf;
-}
-
-//============================== Global Variables =============================
-
-//
-// Declare the static variables of the WheatyExceptionReport class
-//
-TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH];
-LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter;
-HANDLE WheatyExceptionReport::m_hReportFile;
-HANDLE WheatyExceptionReport::m_hProcess;
-
-// Declare global instance of class
-WheatyExceptionReport g_WheatyExceptionReport;
-
-//============================== Class Methods =============================
-
-WheatyExceptionReport::WheatyExceptionReport( ) // Constructor
-{
- // Install the unhandled exception filter function
- m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
- m_hProcess = GetCurrentProcess();
-}
-
-//============
-// Destructor
-//============
-WheatyExceptionReport::~WheatyExceptionReport( )
-{
- if(m_previousFilter)
- SetUnhandledExceptionFilter( m_previousFilter );
-}
-
-//===========================================================
-// Entry point where control comes on an unhandled exception
-//===========================================================
-LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
-PEXCEPTION_POINTERS pExceptionInfo )
-{
- TCHAR module_folder_name[MAX_PATH];
- GetModuleFileName( 0, module_folder_name, MAX_PATH );
- TCHAR* pos = _tcsrchr(module_folder_name, '\\');
- if(!pos)
- return 0;
- pos[0] = '\0';
- ++pos;
-
- TCHAR crash_folder_path[MAX_PATH];
- sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder);
- if(!CreateDirectory(crash_folder_path, NULL))
- {
- if(GetLastError() != ERROR_ALREADY_EXISTS)
- return 0;
- }
-
- SYSTEMTIME systime;
- GetLocalTime(&systime);
- sprintf(m_szLogFileName, "%s\\%s_[%u-%u_%u-%u-%u].txt",
- crash_folder_path, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond
- );
-
- m_hReportFile = CreateFile( m_szLogFileName,
- GENERIC_WRITE,
- 0,
- 0,
- OPEN_ALWAYS,
- FILE_FLAG_WRITE_THROUGH,
- 0 );
-
- if ( m_hReportFile )
- {
- SetFilePointer( m_hReportFile, 0, 0, FILE_END );
-
- GenerateExceptionReport( pExceptionInfo );
-
- CloseHandle( m_hReportFile );
- m_hReportFile = 0;
- }
-
- if ( m_previousFilter )
- return m_previousFilter( pExceptionInfo );
- else
- return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/;
-}
-
-BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount)
-{
- if(!sProcessorName)
- return FALSE;
-
- HKEY hKey;
- LONG lRet;
- lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
- 0, KEY_QUERY_VALUE, &hKey);
- if (lRet != ERROR_SUCCESS)
- return FALSE;
- TCHAR szTmp[2048];
- DWORD cntBytes = sizeof(szTmp);
- lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, NULL,
- (LPBYTE)szTmp, &cntBytes);
- if (lRet != ERROR_SUCCESS)
- return FALSE;
- ::RegCloseKey(hKey);
- sProcessorName[0] = '\0';
- // Skip spaces
- TCHAR* psz = szTmp;
- while (iswspace(*psz))
- ++psz;
- _tcsncpy(sProcessorName, psz, maxcount);
- return TRUE;
-}
-
-BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
-{
- // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
- // If that fails, try using the OSVERSIONINFO structure.
- OSVERSIONINFOEX osvi = { 0 };
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
- BOOL bOsVersionInfoEx;
- bOsVersionInfoEx = ::GetVersionEx((LPOSVERSIONINFO)(&osvi));
- if (!bOsVersionInfoEx)
- {
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- if (!::GetVersionEx((OSVERSIONINFO*)&osvi))
- return FALSE;
- }
- *szVersion = _T('\0');
- TCHAR wszTmp[128];
- switch (osvi.dwPlatformId)
- {
- // Windows NT product family.
- case VER_PLATFORM_WIN32_NT:
- // Test for the specific product family.
- if (osvi.dwMajorVersion == 6)
- _tcsncat(szVersion, _T("Windows Vista or Windows Server 2008 "), cntMax);
- if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
- _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax);
- if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
- _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax);
- if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
- _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax);
- if (osvi.dwMajorVersion <= 4 )
- _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax);
-
- // Test for specific product on Windows NT 4.0 SP6 and later.
- if (bOsVersionInfoEx)
- {
- // Test for the workstation type.
- #if WINVER < 0x0500
- if (osvi.wReserved[1] == VER_NT_WORKSTATION)
- #else
- if (osvi.wProductType == VER_NT_WORKSTATION)
- #endif // WINVER < 0x0500
- {
- if (osvi.dwMajorVersion == 4)
- _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] & VER_SUITE_PERSONAL)
- #else
- else if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
- #endif // WINVER < 0x0500
- _tcsncat(szVersion, _T("Home Edition "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] & VER_SUITE_EMBEDDEDNT)
- #else
- else if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT)
- #endif // WINVER < 0x0500
- _tcsncat(szVersion, _T("Embedded "), cntMax);
- else
- _tcsncat(szVersion, _T("Professional "), cntMax);
- }
- // Test for the server type.
- #if WINVER < 0x0500
- else if (osvi.wReserved[1] == VER_NT_SERVER)
- #else
- else if (osvi.wProductType == VER_NT_SERVER)
- #endif // WINVER < 0x0500
- {
- if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
- {
- #if WINVER < 0x0500
- if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
- #else
- if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
- #endif // WINVER < 0x0500
- _tcsncat(szVersion, _T("Datacenter Edition "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
- #else
- else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
- #endif // WINVER < 0x0500
- _tcsncat(szVersion, _T("Enterprise Edition "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] == VER_SUITE_BLADE)
- #else
- else if (osvi.wSuiteMask == VER_SUITE_BLADE)
- #endif // WINVER < 0x0500
- _tcsncat(szVersion, _T("Web Edition "), cntMax);
- else
- _tcsncat(szVersion, _T("Standard Edition "), cntMax);
- }
- else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
- {
- #if WINVER < 0x0500
- if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
- #else
- if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
- #endif // WINVER < 0x0500
- _tcsncat(szVersion, _T("Datacenter Server "), cntMax);
- #if WINVER < 0x0500
- else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE )
- #else
- else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
- #endif // WINVER < 0x0500
- _tcsncat(szVersion, _T("Advanced Server "), cntMax);
- else
- _tcsncat(szVersion, _T("Server "), cntMax);
- }
- else // Windows NT 4.0
- {
- #if WINVER < 0x0500
- if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
- #else
- if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
- #endif // WINVER < 0x0500
- _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax);
- else
- _tcsncat(szVersion, _T("Server 4.0 "), cntMax);
- }
- }
- }
- // Display service pack (if any) and build number.
- if (osvi.dwMajorVersion == 4 && _tcsicmp(osvi.szCSDVersion, _T("Service Pack 6")) == 0)
- {
- HKEY hKey;
- LONG lRet;
-
- // Test for SP6 versus SP6a.
- lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
- if (lRet == ERROR_SUCCESS)
- {
- _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"),
- osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
- }
- else // Windows NT 4.0 prior to SP6a
- {
- _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
- osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
- }
- ::RegCloseKey(hKey);
- }
- else // Windows NT 3.51 and earlier or Windows 2000 and later
- {
- if (!_tcslen(osvi.szCSDVersion))
- _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"),
- osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- else
- _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
- osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
- }
- break;
- default:
- _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
- osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
- break;
- }
-
- return TRUE;
-}
-
-void WheatyExceptionReport::PrintSystemInfo()
-{
- SYSTEM_INFO SystemInfo;
- ::GetSystemInfo(&SystemInfo);
-
- MEMORYSTATUS MemoryStatus;
- MemoryStatus.dwLength = sizeof (MEMORYSTATUS);
- ::GlobalMemoryStatus(&MemoryStatus);
- TCHAR sString[1024];
- _tprintf(_T("//=====================================================\r\n"));
- if (_GetProcessorName(sString, countof(sString)))
- _tprintf(_T("*** Hardware ***\r\nProcessor: %s\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"),
- sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
- else
- _tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"),
- SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
-
- if(_GetWindowsVersion(sString, countof(sString)))
- _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString);
- else
- _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
-}
-
-//===========================================================================
-void WheatyExceptionReport::printTracesForAllThreads()
-{
- HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
- THREADENTRY32 te32;
-
- DWORD dwOwnerPID = GetCurrentProcessId();
- m_hProcess = GetCurrentProcess();
- // Take a snapshot of all running threads
- hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
- if( hThreadSnap == INVALID_HANDLE_VALUE )
- return;
-
- // Fill in the size of the structure before using it.
- te32.dwSize = sizeof(THREADENTRY32 );
-
- // Retrieve information about the first thread,
- // and exit if unsuccessful
- if( !Thread32First( hThreadSnap, &te32 ) )
- {
- CloseHandle( hThreadSnap ); // Must clean up the
- // snapshot object!
- return;
- }
-
- // Now walk the thread list of the system,
- // and display information about each thread
- // associated with the specified process
- do
- {
- if( te32.th32OwnerProcessID == dwOwnerPID )
- {
- CONTEXT context;
- context.ContextFlags = 0xffffffff;
- HANDLE threadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,false, te32.th32ThreadID);
- if(threadHandle && GetThreadContext(threadHandle, &context))
- {
- WriteStackDetails( &context, false, threadHandle );
- }
- CloseHandle(threadHandle);
- }
- } while( Thread32Next(hThreadSnap, &te32 ) );
-
-// Don't forget to clean up the snapshot object.
- CloseHandle( hThreadSnap );
-}
-
-
-//===========================================================================
-// Open the report file, and write the desired information to it. Called by
-// WheatyUnhandledExceptionFilter
-//===========================================================================
-void WheatyExceptionReport::GenerateExceptionReport(
-PEXCEPTION_POINTERS pExceptionInfo )
-{
- SYSTEMTIME systime;
- GetLocalTime(&systime);
-
- // Start out with a banner
- _tprintf(_T("Revision: %s\r\n"), SVN_REVISION);
- _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute);
- PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
-
- PrintSystemInfo();
- // First print information about the type of fault
- _tprintf(_T("\r\n//=====================================================\r\n"));
- _tprintf( _T("Exception code: %08X %s\r\n"),
- pExceptionRecord->ExceptionCode,
- GetExceptionString(pExceptionRecord->ExceptionCode) );
-
- // Now print information about where the fault occured
- TCHAR szFaultingModule[MAX_PATH];
- DWORD section;
- DWORD_PTR offset;
- GetLogicalAddress( pExceptionRecord->ExceptionAddress,
- szFaultingModule,
- sizeof( szFaultingModule ),
- section, offset );
-
-#ifdef _M_IX86
- _tprintf( _T("Fault address: %08X %02X:%08X %s\r\n"),
- pExceptionRecord->ExceptionAddress,
- section, offset, szFaultingModule );
-#endif
-#ifdef _M_X64
- _tprintf( _T("Fault address: %016I64X %02X:%016I64X %s\r\n"),
- pExceptionRecord->ExceptionAddress,
- section, offset, szFaultingModule );
-#endif
-
- PCONTEXT pCtx = pExceptionInfo->ContextRecord;
-
- // Show the registers
- #ifdef _M_IX86 // X86 Only!
- _tprintf( _T("\r\nRegisters:\r\n") );
-
- _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n")
- ,pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
- pCtx->Esi, pCtx->Edi );
-
- _tprintf( _T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip );
- _tprintf( _T("SS:ESP:%04X:%08X EBP:%08X\r\n"),
- pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
- _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
- pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
- _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags );
- #endif
-
- #ifdef _M_X64
- _tprintf( _T("\r\nRegisters:\r\n") );
- _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n")
- _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n")
- ,pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx,
- pCtx->Rsi, pCtx->Rdi ,pCtx->R9,pCtx->R10,pCtx->R11,pCtx->R12,pCtx->R13,pCtx->R14,pCtx->R15);
- _tprintf( _T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip );
- _tprintf( _T("SS:RSP:%04X:%016X RBP:%08X\r\n"),
- pCtx->SegSs, pCtx->Rsp, pCtx->Rbp );
- _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
- pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
- _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags );
- #endif
-
- SymSetOptions( SYMOPT_DEFERRED_LOADS );
-
- // Initialize DbgHelp
- if ( !SymInitialize( GetCurrentProcess(), 0, TRUE ) )
- {
- _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"),
- ErrorMessage(GetLastError()));
- }
-
- CONTEXT trashableContext = *pCtx;
-
- WriteStackDetails( &trashableContext, false, NULL );
- printTracesForAllThreads();
-
-// #ifdef _M_IX86 // X86 Only!
-
- _tprintf( _T("========================\r\n") );
- _tprintf( _T("Local Variables And Parameters\r\n") );
-
- trashableContext = *pCtx;
- WriteStackDetails( &trashableContext, true, NULL );
-
- _tprintf( _T("========================\r\n") );
- _tprintf( _T("Global Variables\r\n") );
-
- SymEnumSymbols( GetCurrentProcess(),
- (DWORD64)GetModuleHandle(szFaultingModule),
- 0, EnumerateSymbolsCallback, 0 );
- // #endif // X86 Only!
-
- SymCleanup( GetCurrentProcess() );
-
- _tprintf( _T("\r\n") );
-}
-
-//======================================================================
-// Given an exception code, returns a pointer to a static string with a
-// description of the exception
-//======================================================================
-LPTSTR WheatyExceptionReport::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 WheatyExceptionReport::GetLogicalAddress(
-PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset )
-{
- MEMORY_BASIC_INFORMATION mbi;
-
- if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) )
- return FALSE;
-
- DWORD_PTR hMod = (DWORD_PTR)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 + DWORD_PTR(pDosHdr->e_lfanew));
-
- PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
-
- DWORD_PTR rva = (DWORD_PTR)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_PTR sectionStart = pSection->VirtualAddress;
- DWORD_PTR sectionEnd = sectionStart
- + DWORD_PTR(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!
-}
-
-// It contains SYMBOL_INFO structure plus additional
-// space for the name of the symbol
-struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE
-{
- CSymbolInfoPackage()
- {
- si.SizeOfStruct = sizeof(SYMBOL_INFO);
- si.MaxNameLen = sizeof(name);
- }
-};
-
-//============================================================
-// Walks the stack, and writes the results to the report file
-//============================================================
-void WheatyExceptionReport::WriteStackDetails(
-PCONTEXT pContext,
-bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output
-{
- _tprintf( _T("\r\nCall stack:\r\n") );
-
- _tprintf( _T("Address Frame Function SourceFile\r\n") );
-
- DWORD dwMachineType = 0;
- // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
-
- STACKFRAME64 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
-
-#ifdef _M_X64
- sf.AddrPC.Offset = pContext->Rip;
- sf.AddrPC.Mode = AddrModeFlat;
- sf.AddrStack.Offset = pContext->Rsp;
- sf.AddrStack.Mode = AddrModeFlat;
- sf.AddrFrame.Offset = pContext->Rbp;
- sf.AddrFrame.Mode = AddrModeFlat;
- dwMachineType = IMAGE_FILE_MACHINE_AMD64;
-#endif
-
- while ( 1 )
- {
- // Get the next stack frame
- if ( ! StackWalk64( dwMachineType,
- m_hProcess,
- pThreadHandle != NULL ? pThreadHandle : GetCurrentThread(),
- &sf,
- pContext,
- 0,
- SymFunctionTableAccess64,
- SymGetModuleBase64,
- 0 ) )
- break;
- if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
- break; // the frame is OK. Bail if not.
-#ifdef _M_IX86
- _tprintf( _T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset );
-#endif
-#ifdef _M_X64
- _tprintf( _T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset );
-#endif
-
- DWORD64 symDisplacement = 0; // Displacement of the input address,
- // relative to the start of the symbol
-
- // Get the name of the function for this stack frame entry
- CSymbolInfoPackage sip;
- if ( SymFromAddr(
- m_hProcess, // Process handle of the current process
- sf.AddrPC.Offset, // Symbol address
- &symDisplacement, // Address of the variable that will receive the displacement
- &sip.si // Address of the SYMBOL_INFO structure (inside "sip" object)
- ))
- {
- _tprintf( _T("%hs+%I64X"), sip.si.Name, symDisplacement );
-
- }
- else // No symbol found. Print out the logical address instead.
- {
- TCHAR szModule[MAX_PATH] = _T("");
- DWORD section = 0;
- DWORD_PTR offset = 0;
-
- GetLogicalAddress( (PVOID)sf.AddrPC.Offset,
- szModule, sizeof(szModule), section, offset );
-#ifdef _M_IX86
- _tprintf( _T("%04X:%08X %s"), section, offset, szModule );
-#endif
-#ifdef _M_X64
- _tprintf( _T("%04X:%016I64X %s"), section, offset, szModule );
-#endif
- }
-
- // Get the source line for this stack frame entry
- IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE) };
- DWORD dwLineDisplacement;
- if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset,
- &dwLineDisplacement, &lineInfo ) )
- {
- _tprintf(_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber);
- }
-
- _tprintf( _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;
- SymSetContext( m_hProcess, &imagehlpStackFrame, 0 );
-
- // Enumerate the locals/parameters
- SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf );
-
- _tprintf( _T("\r\n") );
- }
- }
-
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// The function invoked by SymEnumSymbols
-//////////////////////////////////////////////////////////////////////////////
-
-BOOL CALLBACK
-WheatyExceptionReport::EnumerateSymbolsCallback(
-PSYMBOL_INFO pSymInfo,
-ULONG SymbolSize,
-PVOID UserContext )
-{
-
- char szBuffer[2048];
-
- __try
- {
- if ( FormatSymbolValue( pSymInfo, (STACKFRAME*)UserContext,
- szBuffer, sizeof(szBuffer) ) )
- _tprintf( _T("\t%s\r\n"), szBuffer );
- }
- __except( 1 )
- {
- _tprintf( _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 WheatyExceptionReport::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;
-
- 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, pSym->Name );
-
- 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 += sprintf( pszCurrBuffer, rgBaseType[basicType]);
-
- // Emit the variable name
- pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", pSym->Name );
-
- 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 * WheatyExceptionReport::DumpTypeIndex(
-char * pszCurrBuffer,
-DWORD64 modBase,
-DWORD dwTypeIndex,
-unsigned nestingLevel,
-DWORD_PTR offset,
-bool & bHandled,
-char* Name)
-{
- 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;
- BasicType basicType = GetBasicType(children.ChildId[i], modBase );
- pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]);
-
- pszCurrBuffer = DumpTypeIndex( pszCurrBuffer, modBase,
- children.ChildId[i], nestingLevel+1,
- offset, bHandled2, ""/*Name */);
-
- // 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 += sprintf( pszCurrBuffer, rgBaseType[basicType]);
- //
- // Emit the variable name
- // pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", Name );
-
- pszCurrBuffer = FormatOutputValue( pszCurrBuffer, basicType,
- length, (PVOID)dwFinalOffset );
-
- pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" );
- }
- }
-
- bHandled = true;
- return pszCurrBuffer;
-}
-
-char * WheatyExceptionReport::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
-WheatyExceptionReport::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;
-}
-
-//============================================================================
-// Helper function that writes to the report file, and allows the user to use
-// printf style formating
-//============================================================================
-int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...)
-{
- TCHAR szBuff[1024];
- 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;
-}
+//========================================== +// Matt Pietrek +// MSDN Magazine, 2002 +// FILE: WheatyExceptionReport.CPP +//========================================== +#define WIN32_LEAN_AND_MEAN +#pragma warning(disable:4996) +#pragma warning(disable:4312) +#pragma warning(disable:4311) +#include <windows.h> +#include <tlhelp32.h> +#include <stdio.h> +#include <tchar.h> +#define _NO_CVCONST_H +#include <dbghelp.h> +#include "WheatyExceptionReport.h" +#include "svn_revision.h" +#define CrashFolder _T("Crashs") +//#pragma comment(linker, "/defaultlib:dbghelp.lib") + +inline LPTSTR ErrorMessage(DWORD dw) +{ + LPVOID lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL ); + return (LPTSTR)lpMsgBuf; +} + +//============================== Global Variables ============================= + +// +// Declare the static variables of the WheatyExceptionReport class +// +TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH]; +LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter; +HANDLE WheatyExceptionReport::m_hReportFile; +HANDLE WheatyExceptionReport::m_hProcess; + +// Declare global instance of class +WheatyExceptionReport g_WheatyExceptionReport; + +//============================== Class Methods ============================= + +WheatyExceptionReport::WheatyExceptionReport( ) // Constructor +{ + // Install the unhandled exception filter function + m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter); + m_hProcess = GetCurrentProcess(); +} + +//============ +// Destructor +//============ +WheatyExceptionReport::~WheatyExceptionReport( ) +{ + if(m_previousFilter) + SetUnhandledExceptionFilter( m_previousFilter ); +} + +//=========================================================== +// Entry point where control comes on an unhandled exception +//=========================================================== +LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter( +PEXCEPTION_POINTERS pExceptionInfo ) +{ + TCHAR module_folder_name[MAX_PATH]; + GetModuleFileName( 0, module_folder_name, MAX_PATH ); + TCHAR* pos = _tcsrchr(module_folder_name, '\\'); + if(!pos) + return 0; + pos[0] = '\0'; + ++pos; + + TCHAR crash_folder_path[MAX_PATH]; + sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder); + if(!CreateDirectory(crash_folder_path, NULL)) + { + if(GetLastError() != ERROR_ALREADY_EXISTS) + return 0; + } + + SYSTEMTIME systime; + GetLocalTime(&systime); + sprintf(m_szLogFileName, "%s\\%s_[%u-%u_%u-%u-%u].txt", + crash_folder_path, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond + ); + + m_hReportFile = CreateFile( m_szLogFileName, + GENERIC_WRITE, + 0, + 0, + OPEN_ALWAYS, + FILE_FLAG_WRITE_THROUGH, + 0 ); + + if ( m_hReportFile ) + { + SetFilePointer( m_hReportFile, 0, 0, FILE_END ); + + GenerateExceptionReport( pExceptionInfo ); + + CloseHandle( m_hReportFile ); + m_hReportFile = 0; + } + + if ( m_previousFilter ) + return m_previousFilter( pExceptionInfo ); + else + return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/; +} + +BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount) +{ + if(!sProcessorName) + return FALSE; + + HKEY hKey; + LONG lRet; + lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), + 0, KEY_QUERY_VALUE, &hKey); + if (lRet != ERROR_SUCCESS) + return FALSE; + TCHAR szTmp[2048]; + DWORD cntBytes = sizeof(szTmp); + lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, NULL, + (LPBYTE)szTmp, &cntBytes); + if (lRet != ERROR_SUCCESS) + return FALSE; + ::RegCloseKey(hKey); + sProcessorName[0] = '\0'; + // Skip spaces + TCHAR* psz = szTmp; + while (iswspace(*psz)) + ++psz; + _tcsncpy(sProcessorName, psz, maxcount); + return TRUE; +} + +BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax) +{ + // Try calling GetVersionEx using the OSVERSIONINFOEX structure. + // If that fails, try using the OSVERSIONINFO structure. + OSVERSIONINFOEX osvi = { 0 }; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + BOOL bOsVersionInfoEx; + bOsVersionInfoEx = ::GetVersionEx((LPOSVERSIONINFO)(&osvi)); + if (!bOsVersionInfoEx) + { + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!::GetVersionEx((OSVERSIONINFO*)&osvi)) + return FALSE; + } + *szVersion = _T('\0'); + TCHAR wszTmp[128]; + switch (osvi.dwPlatformId) + { + // Windows NT product family. + case VER_PLATFORM_WIN32_NT: + // Test for the specific product family. + if (osvi.dwMajorVersion == 6) + _tcsncat(szVersion, _T("Windows Vista or Windows Server 2008 "), cntMax); + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) + _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax); + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) + _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax); + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) + _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax); + if (osvi.dwMajorVersion <= 4 ) + _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax); + + // Test for specific product on Windows NT 4.0 SP6 and later. + if (bOsVersionInfoEx) + { + // Test for the workstation type. + #if WINVER < 0x0500 + if (osvi.wReserved[1] == VER_NT_WORKSTATION) + #else + if (osvi.wProductType == VER_NT_WORKSTATION) + #endif // WINVER < 0x0500 + { + if (osvi.dwMajorVersion == 4) + _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] & VER_SUITE_PERSONAL) + #else + else if (osvi.wSuiteMask & VER_SUITE_PERSONAL) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Home Edition "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] & VER_SUITE_EMBEDDEDNT) + #else + else if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Embedded "), cntMax); + else + _tcsncat(szVersion, _T("Professional "), cntMax); + } + // Test for the server type. + #if WINVER < 0x0500 + else if (osvi.wReserved[1] == VER_NT_SERVER) + #else + else if (osvi.wProductType == VER_NT_SERVER) + #endif // WINVER < 0x0500 + { + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) + { + #if WINVER < 0x0500 + if (osvi.wReserved[0] & VER_SUITE_DATACENTER) + #else + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Datacenter Edition "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE) + #else + else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Enterprise Edition "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] == VER_SUITE_BLADE) + #else + else if (osvi.wSuiteMask == VER_SUITE_BLADE) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Web Edition "), cntMax); + else + _tcsncat(szVersion, _T("Standard Edition "), cntMax); + } + else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) + { + #if WINVER < 0x0500 + if (osvi.wReserved[0] & VER_SUITE_DATACENTER) + #else + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Datacenter Server "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE ) + #else + else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Advanced Server "), cntMax); + else + _tcsncat(szVersion, _T("Server "), cntMax); + } + else // Windows NT 4.0 + { + #if WINVER < 0x0500 + if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE) + #else + if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax); + else + _tcsncat(szVersion, _T("Server 4.0 "), cntMax); + } + } + } + // Display service pack (if any) and build number. + if (osvi.dwMajorVersion == 4 && _tcsicmp(osvi.szCSDVersion, _T("Service Pack 6")) == 0) + { + HKEY hKey; + LONG lRet; + + // Test for SP6 versus SP6a. + lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey); + if (lRet == ERROR_SUCCESS) + { + _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"), + osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + _tcsncat(szVersion, wszTmp, cntMax); + } + else // Windows NT 4.0 prior to SP6a + { + _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), + osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + _tcsncat(szVersion, wszTmp, cntMax); + } + ::RegCloseKey(hKey); + } + else // Windows NT 3.51 and earlier or Windows 2000 and later + { + if (!_tcslen(osvi.szCSDVersion)) + _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"), + osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + else + _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), + osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + _tcsncat(szVersion, wszTmp, cntMax); + } + break; + default: + _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), + osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + _tcsncat(szVersion, wszTmp, cntMax); + break; + } + + return TRUE; +} + +void WheatyExceptionReport::PrintSystemInfo() +{ + SYSTEM_INFO SystemInfo; + ::GetSystemInfo(&SystemInfo); + + MEMORYSTATUS MemoryStatus; + MemoryStatus.dwLength = sizeof (MEMORYSTATUS); + ::GlobalMemoryStatus(&MemoryStatus); + TCHAR sString[1024]; + _tprintf(_T("//=====================================================\r\n")); + if (_GetProcessorName(sString, countof(sString))) + _tprintf(_T("*** Hardware ***\r\nProcessor: %s\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"), + sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400); + else + _tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"), + SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400); + + if(_GetWindowsVersion(sString, countof(sString))) + _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString); + else + _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n")); +} + +//=========================================================================== +void WheatyExceptionReport::printTracesForAllThreads() +{ + HANDLE hThreadSnap = INVALID_HANDLE_VALUE; + THREADENTRY32 te32; + + DWORD dwOwnerPID = GetCurrentProcessId(); + m_hProcess = GetCurrentProcess(); + // Take a snapshot of all running threads + hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); + if( hThreadSnap == INVALID_HANDLE_VALUE ) + return; + + // Fill in the size of the structure before using it. + te32.dwSize = sizeof(THREADENTRY32 ); + + // Retrieve information about the first thread, + // and exit if unsuccessful + if( !Thread32First( hThreadSnap, &te32 ) ) + { + CloseHandle( hThreadSnap ); // Must clean up the + // snapshot object! + return; + } + + // Now walk the thread list of the system, + // and display information about each thread + // associated with the specified process + do + { + if( te32.th32OwnerProcessID == dwOwnerPID ) + { + CONTEXT context; + context.ContextFlags = 0xffffffff; + HANDLE threadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,false, te32.th32ThreadID); + if(threadHandle && GetThreadContext(threadHandle, &context)) + { + WriteStackDetails( &context, false, threadHandle ); + } + CloseHandle(threadHandle); + } + } while( Thread32Next(hThreadSnap, &te32 ) ); + +// Don't forget to clean up the snapshot object. + CloseHandle( hThreadSnap ); +} + + +//=========================================================================== +// Open the report file, and write the desired information to it. Called by +// WheatyUnhandledExceptionFilter +//=========================================================================== +void WheatyExceptionReport::GenerateExceptionReport( +PEXCEPTION_POINTERS pExceptionInfo ) +{ + SYSTEMTIME systime; + GetLocalTime(&systime); + + // Start out with a banner + _tprintf(_T("Revision: %s\r\n"), SVN_REVISION); + _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute); + PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; + + PrintSystemInfo(); + // First print information about the type of fault + _tprintf(_T("\r\n//=====================================================\r\n")); + _tprintf( _T("Exception code: %08X %s\r\n"), + pExceptionRecord->ExceptionCode, + GetExceptionString(pExceptionRecord->ExceptionCode) ); + + // Now print information about where the fault occured + TCHAR szFaultingModule[MAX_PATH]; + DWORD section; + DWORD_PTR offset; + GetLogicalAddress( pExceptionRecord->ExceptionAddress, + szFaultingModule, + sizeof( szFaultingModule ), + section, offset ); + +#ifdef _M_IX86 + _tprintf( _T("Fault address: %08X %02X:%08X %s\r\n"), + pExceptionRecord->ExceptionAddress, + section, offset, szFaultingModule ); +#endif +#ifdef _M_X64 + _tprintf( _T("Fault address: %016I64X %02X:%016I64X %s\r\n"), + pExceptionRecord->ExceptionAddress, + section, offset, szFaultingModule ); +#endif + + PCONTEXT pCtx = pExceptionInfo->ContextRecord; + + // Show the registers + #ifdef _M_IX86 // X86 Only! + _tprintf( _T("\r\nRegisters:\r\n") ); + + _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n") + ,pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, + pCtx->Esi, pCtx->Edi ); + + _tprintf( _T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip ); + _tprintf( _T("SS:ESP:%04X:%08X EBP:%08X\r\n"), + pCtx->SegSs, pCtx->Esp, pCtx->Ebp ); + _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), + pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); + _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags ); + #endif + + #ifdef _M_X64 + _tprintf( _T("\r\nRegisters:\r\n") ); + _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n") + _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n") + ,pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx, + pCtx->Rsi, pCtx->Rdi ,pCtx->R9,pCtx->R10,pCtx->R11,pCtx->R12,pCtx->R13,pCtx->R14,pCtx->R15); + _tprintf( _T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip ); + _tprintf( _T("SS:RSP:%04X:%016X RBP:%08X\r\n"), + pCtx->SegSs, pCtx->Rsp, pCtx->Rbp ); + _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), + pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); + _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags ); + #endif + + SymSetOptions( SYMOPT_DEFERRED_LOADS ); + + // Initialize DbgHelp + if ( !SymInitialize( GetCurrentProcess(), 0, TRUE ) ) + { + _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"), + ErrorMessage(GetLastError())); + } + + CONTEXT trashableContext = *pCtx; + + WriteStackDetails( &trashableContext, false, NULL ); + printTracesForAllThreads(); + +// #ifdef _M_IX86 // X86 Only! + + _tprintf( _T("========================\r\n") ); + _tprintf( _T("Local Variables And Parameters\r\n") ); + + trashableContext = *pCtx; + WriteStackDetails( &trashableContext, true, NULL ); + + _tprintf( _T("========================\r\n") ); + _tprintf( _T("Global Variables\r\n") ); + + SymEnumSymbols( GetCurrentProcess(), + (DWORD64)GetModuleHandle(szFaultingModule), + 0, EnumerateSymbolsCallback, 0 ); + // #endif // X86 Only! + + SymCleanup( GetCurrentProcess() ); + + _tprintf( _T("\r\n") ); +} + +//====================================================================== +// Given an exception code, returns a pointer to a static string with a +// description of the exception +//====================================================================== +LPTSTR WheatyExceptionReport::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 WheatyExceptionReport::GetLogicalAddress( +PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset ) +{ + MEMORY_BASIC_INFORMATION mbi; + + if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) ) + return FALSE; + + DWORD_PTR hMod = (DWORD_PTR)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 + DWORD_PTR(pDosHdr->e_lfanew)); + + PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr ); + + DWORD_PTR rva = (DWORD_PTR)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_PTR sectionStart = pSection->VirtualAddress; + DWORD_PTR sectionEnd = sectionStart + + DWORD_PTR(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! +} + +// It contains SYMBOL_INFO structure plus additional +// space for the name of the symbol +struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE +{ + CSymbolInfoPackage() + { + si.SizeOfStruct = sizeof(SYMBOL_INFO); + si.MaxNameLen = sizeof(name); + } +}; + +//============================================================ +// Walks the stack, and writes the results to the report file +//============================================================ +void WheatyExceptionReport::WriteStackDetails( +PCONTEXT pContext, +bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output +{ + _tprintf( _T("\r\nCall stack:\r\n") ); + + _tprintf( _T("Address Frame Function SourceFile\r\n") ); + + DWORD dwMachineType = 0; + // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag + + STACKFRAME64 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 + +#ifdef _M_X64 + sf.AddrPC.Offset = pContext->Rip; + sf.AddrPC.Mode = AddrModeFlat; + sf.AddrStack.Offset = pContext->Rsp; + sf.AddrStack.Mode = AddrModeFlat; + sf.AddrFrame.Offset = pContext->Rbp; + sf.AddrFrame.Mode = AddrModeFlat; + dwMachineType = IMAGE_FILE_MACHINE_AMD64; +#endif + + while ( 1 ) + { + // Get the next stack frame + if ( ! StackWalk64( dwMachineType, + m_hProcess, + pThreadHandle != NULL ? pThreadHandle : GetCurrentThread(), + &sf, + pContext, + 0, + SymFunctionTableAccess64, + SymGetModuleBase64, + 0 ) ) + break; + if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure + break; // the frame is OK. Bail if not. +#ifdef _M_IX86 + _tprintf( _T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset ); +#endif +#ifdef _M_X64 + _tprintf( _T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset ); +#endif + + DWORD64 symDisplacement = 0; // Displacement of the input address, + // relative to the start of the symbol + + // Get the name of the function for this stack frame entry + CSymbolInfoPackage sip; + if ( SymFromAddr( + m_hProcess, // Process handle of the current process + sf.AddrPC.Offset, // Symbol address + &symDisplacement, // Address of the variable that will receive the displacement + &sip.si // Address of the SYMBOL_INFO structure (inside "sip" object) + )) + { + _tprintf( _T("%hs+%I64X"), sip.si.Name, symDisplacement ); + + } + else // No symbol found. Print out the logical address instead. + { + TCHAR szModule[MAX_PATH] = _T(""); + DWORD section = 0; + DWORD_PTR offset = 0; + + GetLogicalAddress( (PVOID)sf.AddrPC.Offset, + szModule, sizeof(szModule), section, offset ); +#ifdef _M_IX86 + _tprintf( _T("%04X:%08X %s"), section, offset, szModule ); +#endif +#ifdef _M_X64 + _tprintf( _T("%04X:%016I64X %s"), section, offset, szModule ); +#endif + } + + // Get the source line for this stack frame entry + IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE) }; + DWORD dwLineDisplacement; + if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset, + &dwLineDisplacement, &lineInfo ) ) + { + _tprintf(_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber); + } + + _tprintf( _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; + SymSetContext( m_hProcess, &imagehlpStackFrame, 0 ); + + // Enumerate the locals/parameters + SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf ); + + _tprintf( _T("\r\n") ); + } + } + +} + +////////////////////////////////////////////////////////////////////////////// +// The function invoked by SymEnumSymbols +////////////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK +WheatyExceptionReport::EnumerateSymbolsCallback( +PSYMBOL_INFO pSymInfo, +ULONG SymbolSize, +PVOID UserContext ) +{ + + char szBuffer[2048]; + + __try + { + if ( FormatSymbolValue( pSymInfo, (STACKFRAME*)UserContext, + szBuffer, sizeof(szBuffer) ) ) + _tprintf( _T("\t%s\r\n"), szBuffer ); + } + __except( 1 ) + { + _tprintf( _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 WheatyExceptionReport::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; + + 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, pSym->Name ); + + 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 += sprintf( pszCurrBuffer, rgBaseType[basicType]); + + // Emit the variable name + pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", pSym->Name ); + + 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 * WheatyExceptionReport::DumpTypeIndex( +char * pszCurrBuffer, +DWORD64 modBase, +DWORD dwTypeIndex, +unsigned nestingLevel, +DWORD_PTR offset, +bool & bHandled, +char* Name) +{ + 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; + BasicType basicType = GetBasicType(children.ChildId[i], modBase ); + pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); + + pszCurrBuffer = DumpTypeIndex( pszCurrBuffer, modBase, + children.ChildId[i], nestingLevel+1, + offset, bHandled2, ""/*Name */); + + // 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 += sprintf( pszCurrBuffer, rgBaseType[basicType]); + // + // Emit the variable name + // pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", Name ); + + pszCurrBuffer = FormatOutputValue( pszCurrBuffer, basicType, + length, (PVOID)dwFinalOffset ); + + pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" ); + } + } + + bHandled = true; + return pszCurrBuffer; +} + +char * WheatyExceptionReport::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 +WheatyExceptionReport::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; +} + +//============================================================================ +// Helper function that writes to the report file, and allows the user to use +// printf style formating +//============================================================================ +int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...) +{ + TCHAR szBuff[1024]; + 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; +} |