mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@1871 3b4a5df2-a311-0410-b5c6-a8a6f20db521
559 lines
17 KiB
C++
559 lines
17 KiB
C++
#ifdef JA2_PRECOMPILED_HEADERS
|
|
#include "JA2 SGP ALL.H"
|
|
#elif defined( WIZ8_PRECOMPILED_HEADERS )
|
|
#include "WIZ8 SGP ALL.H"
|
|
#else
|
|
#include "Types.h"
|
|
#include <stdlib.h>
|
|
#include <malloc.h>
|
|
#include <stdio.h>
|
|
#include "FileMan.h"
|
|
#endif
|
|
|
|
#include "ExceptionHandling.h"
|
|
|
|
|
|
#ifdef JA2
|
|
#include "GameVersion.h"
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//If we are to use exception handling
|
|
#ifdef ENABLE_EXCEPTION_HANDLING
|
|
|
|
|
|
|
|
|
|
const int NumCodeBytes = 16; // Number of code bytes to record.
|
|
const int MaxStackDump = 2048; // Maximum number of DWORDS in stack dumps.
|
|
const int StackColumns = 8; // Number of columns in stack dump.
|
|
|
|
#define ONEK 1024
|
|
#define SIXTYFOURK (64*ONEK)
|
|
#define ONEM (ONEK*ONEK)
|
|
#define ONEG (ONEK*ONEK*ONEK)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//ppp
|
|
void ErrorLog(HWFILE LogFile, STR8 Format, ...);
|
|
STR GetExceptionString( DWORD uiExceptionCode );
|
|
void DisplayRegisters( HWFILE hFile, CONTEXT *pContext );
|
|
BOOLEAN GetAndDisplayModuleAndSystemInfo( HWFILE hFile, CONTEXT *pContext );
|
|
BOOLEAN DisplayStack( HWFILE hFile, CONTEXT *pContext );
|
|
void RecordModuleList(HWFILE hFile );
|
|
void PrintTime(STR8 output, FILETIME TimeToPrint);
|
|
static void ShowModuleInfo(HWFILE hFile, HINSTANCE ModuleHandle);
|
|
|
|
|
|
|
|
INT32 RecordExceptionInfo( EXCEPTION_POINTERS *pExceptInfo )
|
|
{
|
|
EXCEPTION_RECORD Record;
|
|
CONTEXT Context;
|
|
CHAR8 zFileName[512];
|
|
CHAR8 zDate[512];
|
|
CHAR8 zTime[512];
|
|
HWFILE hFile;
|
|
CHAR8 zString[2048];
|
|
SYSTEMTIME SysTime;
|
|
CHAR8 zNewLine[] = "\r\n";
|
|
|
|
|
|
//create local copies of the exception info
|
|
memcpy( &Record, pExceptInfo->ExceptionRecord , sizeof( EXCEPTION_RECORD ) );
|
|
memcpy( &Context, pExceptInfo->ContextRecord, sizeof( CONTEXT ) );
|
|
|
|
|
|
//
|
|
// Open a file to output the current state of the game
|
|
//
|
|
|
|
|
|
// Get the current time
|
|
GetLocalTime( &SysTime );
|
|
|
|
//create a date string
|
|
sprintf( zDate, "%02d_%02d_%d", SysTime.wDay, SysTime.wMonth, SysTime.wYear );
|
|
|
|
//create a time string
|
|
sprintf( zTime, "%02d_%02d", SysTime.wHour, SysTime.wMinute );
|
|
|
|
//create the crash file
|
|
sprintf( zFileName, "Crash Report_%s___%s.txt", zDate, zTime );
|
|
|
|
// create the save game file
|
|
hFile = FileOpen( zFileName, FILE_ACCESS_WRITE | FILE_OPEN_ALWAYS, FALSE );
|
|
if( !hFile )
|
|
{
|
|
FileClose( hFile );
|
|
return( 0 );
|
|
}
|
|
|
|
//
|
|
// Display the version number
|
|
//
|
|
#ifdef JA2
|
|
|
|
//Dispay Ja's version number
|
|
ErrorLog( hFile, "%S: %s. %S",zVersionLabel, czVersionNumber, zTrackingNumber );
|
|
|
|
//Insert a new line
|
|
ErrorLog( hFile, zNewLine );
|
|
|
|
//Insert a new line
|
|
ErrorLog( hFile, zNewLine );
|
|
#endif
|
|
|
|
|
|
|
|
//
|
|
// Write out the current state of the system
|
|
//
|
|
|
|
GetAndDisplayModuleAndSystemInfo( hFile, &Context );
|
|
|
|
|
|
//Insert a new line
|
|
ErrorLog( hFile, zNewLine );
|
|
|
|
//Display the address of where the exception occured
|
|
sprintf( zString, "Exception occured at address: 0x%08x\r\n", Record.ExceptionAddress );
|
|
ErrorLog( hFile, zString );
|
|
|
|
|
|
//if the exception was an access violation, display the offending address
|
|
if( Record.ExceptionCode == EXCEPTION_ACCESS_VIOLATION && Record.NumberParameters != 0 )
|
|
{
|
|
if( Record.ExceptionInformation[0] != 0 )
|
|
{
|
|
//Display the address of where the access violation occured
|
|
sprintf( zString, "\tWrite Access Violation at: 0x%08x\r\n", Record.ExceptionInformation[1] );
|
|
}
|
|
else
|
|
{
|
|
//Display the address of where the access violation occured
|
|
sprintf( zString, "\tWrite Access Violation at: 0x%08x\r\n", Record.ExceptionInformation[1] );
|
|
}
|
|
|
|
ErrorLog( hFile, zString );
|
|
}
|
|
|
|
|
|
//Insert a new line
|
|
ErrorLog( hFile, zNewLine );
|
|
|
|
//Display the exception that caused this
|
|
sprintf( zString, "Exact Error Message \r\n \"%s\"\r\n", GetExceptionString( Record.ExceptionCode ) );
|
|
ErrorLog( hFile, zString );
|
|
|
|
//Insert a new line
|
|
ErrorLog( hFile, zNewLine );
|
|
|
|
//Dispay if the code 'could' continue
|
|
if( Record.ExceptionFlags != 0 )
|
|
sprintf( zString, "%s\r\n", "The game 'can NOT' continue" );
|
|
else
|
|
sprintf( zString, "%s\r\n", "The game 'CAN' continue" );
|
|
ErrorLog( hFile, zString );
|
|
|
|
|
|
//Insert a new line
|
|
ErrorLog( hFile, zNewLine );
|
|
ErrorLog( hFile, zNewLine );
|
|
|
|
|
|
//
|
|
// Display the current context information
|
|
//
|
|
DisplayRegisters( hFile, &Context );
|
|
|
|
ErrorLog( hFile, zNewLine );
|
|
ErrorLog( hFile, zNewLine );
|
|
|
|
//display the stack ( call stack + local variables )
|
|
DisplayStack( hFile, &Context);
|
|
|
|
//Display some spaces
|
|
ErrorLog( hFile, zNewLine );
|
|
ErrorLog( hFile, zNewLine );
|
|
|
|
//Display all modules currently loaded
|
|
RecordModuleList(hFile );
|
|
|
|
|
|
//eee
|
|
|
|
FileClose( hFile );
|
|
|
|
return( EXCEPTION_EXECUTE_HANDLER );
|
|
}
|
|
|
|
|
|
|
|
void ErrorLog( HWFILE hFile, STR8 Format, ...)
|
|
{
|
|
char buffer[2000]; // wvsprintf never prints more than one K.
|
|
UINT32 uiNumBytesWritten=0;
|
|
UINT32 uiStringWidth;
|
|
va_list arglist;
|
|
va_start( arglist, Format);
|
|
wvsprintf(buffer, Format, arglist);
|
|
va_end( arglist);
|
|
|
|
//WriteFile(LogFile, buffer, lstrlen(buffer), &NumBytes, 0);
|
|
|
|
|
|
uiStringWidth = lstrlen(buffer);
|
|
|
|
//write out the string
|
|
FileWrite( hFile, buffer, uiStringWidth, &uiNumBytesWritten );
|
|
if( uiNumBytesWritten != uiStringWidth )
|
|
{
|
|
FileClose( hFile );
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
STR GetExceptionString( DWORD uiExceptionCode )
|
|
{
|
|
switch( uiExceptionCode )
|
|
{
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
return( "The thread tried to read from or write to a virtual address for which it does not have the appropriate access.");
|
|
break;
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
return( "The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.");
|
|
case EXCEPTION_BREAKPOINT:
|
|
return( "A breakpoint was encountered.");
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
|
return( "The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on.");
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
|
return( "One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.");
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
return( "The thread tried to divide a floating-point value by a floating-point divisor of zero.");
|
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
|
return( "The result of a floating-point operation cannot be represented exactly as a decimal fraction.");
|
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
|
return( "This exception represents any floating-point exception not included in this list.");
|
|
case EXCEPTION_FLT_OVERFLOW:
|
|
return( "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.");
|
|
case EXCEPTION_FLT_STACK_CHECK:
|
|
return( "The stack overflowed or underflowed as the result of a floating-point operation.");
|
|
case EXCEPTION_FLT_UNDERFLOW:
|
|
return( "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.");
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
return( "The thread tried to execute an invalid instruction.");
|
|
case EXCEPTION_IN_PAGE_ERROR:
|
|
return( "The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network.");
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
|
return( "The thread tried to divide an integer value by an integer divisor of zero.");
|
|
case EXCEPTION_INT_OVERFLOW:
|
|
return( "The result of an integer operation caused a carry out of the most significant bit of the result.");
|
|
case EXCEPTION_INVALID_DISPOSITION:
|
|
return( "An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception.");
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
|
return( "The thread tried to continue execution after a noncontinuable exception occurred.");
|
|
case EXCEPTION_PRIV_INSTRUCTION:
|
|
return( "The thread tried to execute an instruction whose operation is not allowed in the current machine mode.");
|
|
case EXCEPTION_SINGLE_STEP:
|
|
return( "A trace trap or other single-instruction mechanism signaled that one instruction has been executed.");
|
|
case EXCEPTION_STACK_OVERFLOW:
|
|
return( "The thread used up its stack.");
|
|
|
|
default:
|
|
return("Exception not in case ");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void DisplayRegisters( HWFILE hFile, CONTEXT *pContext )
|
|
{
|
|
ErrorLog( hFile, "Registers:\r\n");
|
|
ErrorLog( hFile, "\tEAX=%08x CS=%04x EIP=%08x EFLGS=%08x\r\n",
|
|
pContext->Eax, pContext->SegCs, pContext->Eip, pContext->EFlags);
|
|
ErrorLog( hFile, "\tEBX=%08x SS=%04x ESP=%08x EBP=%08x\r\n",
|
|
pContext->Ebx, pContext->SegSs, pContext->Esp, pContext->Ebp);
|
|
ErrorLog( hFile, "\tECX=%08x DS=%04x ESI=%08x FS=%04x\r\n",
|
|
pContext->Ecx, pContext->SegDs, pContext->Esi, pContext->SegFs);
|
|
ErrorLog( hFile, "\tEDX=%08x ES=%04x EDI=%08x GS=%04x\r\n",
|
|
pContext->Edx, pContext->SegEs, pContext->Edi, pContext->SegGs);
|
|
// ErrorLog( hFile, "Bytes at CS:EIP:\r\n");
|
|
}
|
|
|
|
BOOLEAN GetAndDisplayModuleAndSystemInfo( HWFILE hFile, CONTEXT *pContext )
|
|
{
|
|
char zFileName[2048];
|
|
char zString[2048];
|
|
SYSTEM_INFO SystemInfo;
|
|
MEMORYSTATUS MemInfo;
|
|
// MEMORY_BASIC_INFORMATION MemBasicInfo;
|
|
size_t PageSize;
|
|
size_t pageNum = 0;
|
|
FILETIME LastWriteTime;
|
|
|
|
if( GetModuleFileName(0, zFileName, sizeof(zFileName) ) == 0)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
MemInfo.dwLength = sizeof(MemInfo);
|
|
GlobalMemoryStatus(&MemInfo);
|
|
|
|
//Display the filename
|
|
ErrorLog( hFile, "File:\r\n\t%s\r\n", zFileName);
|
|
|
|
|
|
//Get the time the file was created
|
|
if (GetFileTime(GetRealFileHandleFromFileManFileHandle( hFile ), 0, 0, &LastWriteTime))
|
|
{
|
|
PrintTime( zString, LastWriteTime);
|
|
|
|
ErrorLog( hFile, "\tFile created on: %s\r\n", zString );
|
|
}
|
|
|
|
|
|
//Get cpu type and number
|
|
GetSystemInfo(&SystemInfo);
|
|
ErrorLog( hFile, "\t%d type %d processor.\r\n", SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType );
|
|
|
|
//Get free ram
|
|
ErrorLog( hFile, "\tTotal Physical Memory: %d Megs.\r\n", MemInfo.dwTotalPhys/(1024*1024) );
|
|
|
|
|
|
PageSize = SystemInfo.dwPageSize;
|
|
|
|
pageNum = 0;
|
|
|
|
/*
|
|
//Get current instruction pointer
|
|
if( VirtualQuery((void *)(pageNum * PageSize), &MemBasicInfo, sizeof(MemBasicInfo) ) )
|
|
{
|
|
if (MemBasicInfo.RegionSize > 0)
|
|
{
|
|
ErrorLog( hFile, "\r\n\r\nJagged is loaded into memory at: 0x%08d.\r\n", MemBasicInfo.AllocationBase );
|
|
}
|
|
}
|
|
*/
|
|
|
|
ErrorLog( hFile, "Segment( CS:EIP ):\t%04x:%08x.\r\n", pContext->SegCs, pContext->Eip );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// This code for this function is based ( stolen ) from Bruce Dawson's article in Game Developer Magazine Jan 99
|
|
//
|
|
BOOLEAN DisplayStack( HWFILE hFile, CONTEXT *pContext )
|
|
{
|
|
int Count = 0;
|
|
char buffer[1000] = "";
|
|
const int safetyzone = 50;
|
|
STR8 nearend = buffer + sizeof(buffer) - safetyzone;
|
|
STR8 output = buffer;
|
|
|
|
// Time to print part or all of the stack to the error log. This allows
|
|
// us to figure out the call stack, parameters, local variables, etc.
|
|
ErrorLog( hFile, "Stack dump:\r\n" );
|
|
|
|
__try
|
|
{
|
|
// Esp contains the bottom of the stack, or at least the bottom of
|
|
// the currently used area.
|
|
DWORD* pStack = (DWORD *)pContext->Esp;
|
|
DWORD* pStackTop;
|
|
__asm
|
|
{
|
|
// Load the top (highest address) of the stack from the
|
|
// thread information block. It will be found there in
|
|
// Win9x and Windows NT.
|
|
mov eax, fs:[4]
|
|
mov pStackTop, eax
|
|
}
|
|
if (pStackTop > pStack + MaxStackDump)
|
|
pStackTop = pStack + MaxStackDump;
|
|
|
|
|
|
// Too many calls to WriteFile can take a long time, causing
|
|
// confusing delays when programs crash. Therefore I implemented
|
|
// simple buffering for the stack dumping code instead of calling
|
|
// hprintf directly.
|
|
|
|
while (pStack + 1 <= pStackTop)
|
|
{
|
|
STR8 Suffix = " ";
|
|
|
|
if ((Count % StackColumns) == 0)
|
|
output += wsprintf(output, "%08x: ", pStack);
|
|
|
|
if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop)
|
|
Suffix = "\r\n";
|
|
output += wsprintf(output, "%08x%s", *pStack, Suffix);
|
|
pStack++;
|
|
|
|
// Check for when the buffer is almost full, and flush it to disk.
|
|
if (output > nearend)
|
|
{
|
|
ErrorLog( hFile, "%s", buffer);
|
|
buffer[0] = 0;
|
|
output = buffer;
|
|
}
|
|
}
|
|
// Print out any final characters from the cache.
|
|
ErrorLog( hFile, "%s", buffer);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ErrorLog( hFile, "Exception encountered during stack dump.\r\n");
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
//
|
|
// This code for this function is ( stolen :) from Bruce Dawson's article in Game Developer Magazine Jan 99
|
|
//
|
|
|
|
// Print the specified FILETIME to output in a human readable format,
|
|
// without using the C run time.
|
|
|
|
void PrintTime(STR8 output, FILETIME TimeToPrint)
|
|
{
|
|
WORD Date, Time;
|
|
if (FileTimeToLocalFileTime(&TimeToPrint, &TimeToPrint) &&
|
|
FileTimeToDosDateTime(&TimeToPrint, &Date, &Time))
|
|
{
|
|
// What a silly way to print out the file date/time. Oh well,
|
|
// it works, and I'm not aware of a cleaner way to do it.
|
|
wsprintf(output, "%02d/%02d/%d %02d:%02d:%02d",
|
|
(Date / 32) & 15, Date & 31, (Date / 512) + 1980,
|
|
(Time / 2048), (Time / 32) & 63, (Time & 31) * 2);
|
|
}
|
|
else
|
|
output[0] = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// This code for this function is ( stolen :) from Bruce Dawson's article in Game Developer Magazine Jan 99
|
|
//
|
|
|
|
// Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used
|
|
// to find all the blocks of address space that were reserved or committed,
|
|
// and ShowModuleInfo will display module information if they are code
|
|
// modules.
|
|
|
|
|
|
void RecordModuleList(HWFILE hFile )
|
|
{
|
|
ErrorLog( hFile, "\r\n"
|
|
"Module list: names, addresses, sizes, time stamps "
|
|
"and file times:\r\n");
|
|
SYSTEM_INFO SystemInfo;
|
|
GetSystemInfo(&SystemInfo);
|
|
const size_t PageSize = SystemInfo.dwPageSize;
|
|
// Set NumPages to the number of pages in the 4GByte address space,
|
|
// while being careful to avoid overflowing ints.
|
|
const size_t NumPages = 4 * size_t(ONEG / PageSize);
|
|
size_t pageNum = 0;
|
|
void *LastAllocationBase = 0;
|
|
while (pageNum < NumPages)
|
|
{
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo,
|
|
sizeof(MemInfo)))
|
|
{
|
|
if (MemInfo.RegionSize > 0)
|
|
{
|
|
// Adjust the page number to skip over this block of memory.
|
|
pageNum += MemInfo.RegionSize / PageSize;
|
|
if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase >
|
|
LastAllocationBase)
|
|
{
|
|
// Look for new blocks of committed memory, and try
|
|
// recording their module names - this will fail
|
|
// gracefully if they aren't code modules.
|
|
LastAllocationBase = MemInfo.AllocationBase;
|
|
ShowModuleInfo(hFile, (HINSTANCE)LastAllocationBase);
|
|
}
|
|
}
|
|
else
|
|
pageNum += SIXTYFOURK / PageSize;
|
|
}
|
|
else
|
|
pageNum += SIXTYFOURK / PageSize;
|
|
// If VirtualQuery fails we advance by 64K because that is the
|
|
// granularity of address space doled out by VirtualAlloc().
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// This code for this function is ( stolen :) from Bruce Dawson's article in Game Developer Magazine Jan 99
|
|
//
|
|
static void ShowModuleInfo(HWFILE hFile, HINSTANCE ModuleHandle)
|
|
{
|
|
char ModName[MAX_PATH];
|
|
__try
|
|
{
|
|
if (GetModuleFileName(ModuleHandle, ModName, sizeof(ModName)) > 0)
|
|
{
|
|
// If GetModuleFileName returns greater than zero then this must
|
|
// be a valid code module address. Therefore we can try to walk
|
|
// our way through its structures to find the link time stamp.
|
|
IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle;
|
|
if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic)
|
|
return;
|
|
IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS*)((STR8 )DosHeader
|
|
+ DosHeader->e_lfanew);
|
|
if (IMAGE_NT_SIGNATURE != NTHeader->Signature)
|
|
return;
|
|
// Open the code module file so that we can get its file date
|
|
// and size.
|
|
HANDLE ModuleFile = CreateFile(ModName, GENERIC_READ,
|
|
FILE_SHARE_READ, 0, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0);
|
|
char TimeBuffer[100] = "";
|
|
DWORD FileSize = 0;
|
|
if (ModuleFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
FileSize = GetFileSize(ModuleFile, 0);
|
|
FILETIME LastWriteTime;
|
|
if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime))
|
|
{
|
|
wsprintf(TimeBuffer, " - file date is ");
|
|
PrintTime(TimeBuffer + lstrlen(TimeBuffer), LastWriteTime);
|
|
}
|
|
CloseHandle(ModuleFile);
|
|
}
|
|
ErrorLog( hFile, "%-35s, loaded at 0x%08x - %7d bytes - %08x%s\r\n",
|
|
ModName, ModuleHandle, FileSize,
|
|
NTHeader->FileHeader.TimeDateStamp, TimeBuffer);
|
|
}
|
|
}
|
|
// Handle any exceptions by continuing from this point.
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
#endif |