mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-12-22 18:51:33 +00:00
675 lines
23 KiB
C
675 lines
23 KiB
C
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file
|
|
/// \brief Sources from GameDeveloper magazine article, January 1998, by Bruce Dawson.
|
|
/// this source file contains the exception handler for recording error
|
|
/// information after crashes.
|
|
|
|
|
|
#include <tchar.h>
|
|
#ifndef HAVE_SDL
|
|
#include "win_main.h"
|
|
#endif
|
|
#include "../doomdef.h" //just for VERSION
|
|
#include "win_dbg.h"
|
|
#include "../m_argv.h" //print the parameter in the log
|
|
|
|
LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionFilter = NULL;
|
|
|
|
#ifdef BUGTRAP
|
|
|
|
|
|
typedef void (APIENTRY *BT_SETSUPPORTURL)(LPCTSTR pszSupportURL);
|
|
typedef void (APIENTRY *BT_SETFLAGS)(DWORD dwFlags);
|
|
typedef void (APIENTRY *BT_SETAPPNAME)(LPCTSTR pszAppName);
|
|
typedef void (APIENTRY *BT_SETAPPVERSION)(LPCTSTR pszAppVersion);
|
|
typedef void (APIENTRY *BT_SETSUPPORTSERVER)(LPCTSTR pszSupportHost, SHORT nSupportPort);
|
|
|
|
// BT constant definitions that we use, as given in the docs.
|
|
#define BTF_DETAILEDMODE 0x01
|
|
#define BTF_ATTACHREPORT 0x04
|
|
|
|
|
|
static HMODULE g_hmodBugTrap;
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Initialises the Bug Trap exception-handling library. Returns true iff
|
|
// successful.
|
|
// --------------------------------------------------------------------------
|
|
BOOL InitBugTrap(void)
|
|
{
|
|
BT_SETFLAGS lpfnBT_SetFlags;
|
|
BT_SETSUPPORTURL lpfnBT_SetSupportURL;
|
|
BT_SETAPPNAME lpfnBT_SetAppName;
|
|
BT_SETAPPVERSION lpfnBT_SetAppVersion;
|
|
BT_SETSUPPORTSERVER lpfnBT_SetSupportServer;
|
|
|
|
// Loading the library installs the exception handler.
|
|
#ifdef UNICODE
|
|
g_hmodBugTrap = LoadLibrary(L"BugTrapU.dll");
|
|
#else
|
|
g_hmodBugTrap = LoadLibrary("BugTrap.dll");
|
|
#endif
|
|
|
|
// Get the functions.
|
|
lpfnBT_SetFlags = (BT_SETFLAGS)GetProcAddress(g_hmodBugTrap, "BT_SetFlags");
|
|
lpfnBT_SetSupportURL = (BT_SETSUPPORTURL)GetProcAddress(g_hmodBugTrap, "BT_SetSupportURL");
|
|
lpfnBT_SetAppName = (BT_SETAPPNAME)GetProcAddress(g_hmodBugTrap, "BT_SetAppName");
|
|
lpfnBT_SetAppVersion = (BT_SETAPPVERSION)GetProcAddress(g_hmodBugTrap, "BT_SetAppVersion");
|
|
lpfnBT_SetSupportServer = (BT_SETSUPPORTSERVER)GetProcAddress(g_hmodBugTrap, "BT_SetSupportServer");
|
|
|
|
if (g_hmodBugTrap)
|
|
{
|
|
lpfnBT_SetAppName(TEXT("Sonic Robo Blast 2"));
|
|
lpfnBT_SetAppVersion(TEXT(VERSIONSTRING));
|
|
lpfnBT_SetFlags(BTF_DETAILEDMODE | BTF_ATTACHREPORT);
|
|
lpfnBT_SetSupportURL(TEXT("http://www.srb2.org/"));
|
|
lpfnBT_SetSupportServer(TEXT("srb2.org"), 9999);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Removes the BugTrap exception handler. Safe to call even if BT was never
|
|
// initialized.
|
|
// --------------------------------------------------------------------------
|
|
void ShutdownBugTrap(void)
|
|
{
|
|
if (g_hmodBugTrap) FreeLibrary(g_hmodBugTrap);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Simple test to check whether BugTrap is loaded without exposing its
|
|
// handle.
|
|
// --------------------------------------------------------------------------
|
|
BOOL IsBugTrapLoaded(void)
|
|
{
|
|
return !!g_hmodBugTrap;
|
|
}
|
|
|
|
#endif // (defined BUGTRAP)
|
|
|
|
|
|
#define NumCodeBytes 16 // Number of code bytes to record.
|
|
#define MaxStackDump 2048 // Maximum number of DWORDS in stack dumps.
|
|
#define StackColumns 8 // Number of columns in stack dump.
|
|
|
|
#define ONEK 1024
|
|
#define SIXTYFOURK (64*ONEK)
|
|
#define ONEM (ONEK*ONEK)
|
|
#define ONEG (ONEK*ONEK*ONEK)
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// return a description for an ExceptionCode
|
|
// --------------------------------------------------------------------------
|
|
static inline LPCSTR GetExceptionDescription(DWORD ExceptionCode)
|
|
{
|
|
size_t i;
|
|
|
|
struct ExceptionNames
|
|
{
|
|
DWORD ExceptionCode;
|
|
LPCSTR ExceptionName;
|
|
};
|
|
|
|
struct ExceptionNames ExceptionMap[] =
|
|
{
|
|
{EXCEPTION_ACCESS_VIOLATION, "an Access Violation"},
|
|
{EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "a Array Bounds Exceeded"},
|
|
{EXCEPTION_BREAKPOINT, "a Breakpoint"},
|
|
{EXCEPTION_DATATYPE_MISALIGNMENT, "a Datatype Misalignment"},
|
|
{EXCEPTION_FLT_DENORMAL_OPERAND, "a Float Denormal Operand"},
|
|
{EXCEPTION_FLT_DIVIDE_BY_ZERO, "a Float Divide By Zero"},
|
|
{EXCEPTION_FLT_INEXACT_RESULT, "a Float Inexact Result"},
|
|
{EXCEPTION_FLT_INVALID_OPERATION, "a Float Invalid Operation"},
|
|
{EXCEPTION_FLT_OVERFLOW, "a Float Overflow"},
|
|
{EXCEPTION_FLT_STACK_CHECK, "a Float Stack Check"},
|
|
{EXCEPTION_FLT_UNDERFLOW, "a Float Underflow"},
|
|
{EXCEPTION_ILLEGAL_INSTRUCTION, "an Illegal Instruction"},
|
|
{EXCEPTION_IN_PAGE_ERROR, "an In Page Error"},
|
|
{EXCEPTION_INT_DIVIDE_BY_ZERO, "an Integer Divide By Zero"},
|
|
{EXCEPTION_INT_OVERFLOW, "an Integer Overflow"},
|
|
{EXCEPTION_INVALID_DISPOSITION, "an Invalid Disposition"},
|
|
{EXCEPTION_NONCONTINUABLE_EXCEPTION, "Noncontinuable Exception"},
|
|
{EXCEPTION_PRIV_INSTRUCTION, "a Privileged Instruction"},
|
|
{EXCEPTION_SINGLE_STEP, "a Single Step"},
|
|
{EXCEPTION_STACK_OVERFLOW, "a Stack Overflow"},
|
|
{0x40010005, "a Control-C"},
|
|
{0x40010008, "a Control-Break"},
|
|
{0xc0000006, "an In Page Error"},
|
|
{0xc0000017, "a No Memory"},
|
|
{0xc000001d, "an Illegal Instruction"},
|
|
{0xc0000025, "a Noncontinuable Exception"},
|
|
{0xc0000142, "a DLL Initialization Failed"},
|
|
{0xe06d7363, "a Microsoft C++ Exception"},
|
|
};
|
|
|
|
for (i = 0; i < (sizeof(ExceptionMap) / sizeof(ExceptionMap[0])); i++)
|
|
if (ExceptionCode == ExceptionMap[i].ExceptionCode)
|
|
return ExceptionMap[i].ExceptionName;
|
|
|
|
return "Unknown exception type";
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Directly output a formatted string to the errorlog file, using win32 funcs
|
|
// --------------------------------------------------------------------------
|
|
static VOID FPrintf(HANDLE fileHandle, LPCSTR lpFmt, ...)
|
|
{
|
|
CHAR str[1999];
|
|
va_list arglist;
|
|
DWORD bytesWritten;
|
|
|
|
va_start(arglist, lpFmt);
|
|
vsprintf(str, lpFmt, arglist);
|
|
va_end(arglist);
|
|
|
|
WriteFile(fileHandle, str, (DWORD)strlen(str), &bytesWritten, NULL);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Print the specified FILETIME to output in a human readable format,
|
|
// without using the C run time.
|
|
// --------------------------------------------------------------------------
|
|
static VOID PrintTime(LPSTR 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.
|
|
wsprintfA(output, "%d/%d/%d %02d:%02d:%02d",
|
|
(Date / 32) & 15, Date & 31, (Date / 512) + 1980,
|
|
(Time / 2048), (Time / 32) & 63, (Time & 31) * 2);
|
|
}
|
|
else
|
|
output[0] = 0;
|
|
}
|
|
|
|
|
|
static LPTSTR GetFilePart(LPTSTR source)
|
|
{
|
|
LPTSTR result = _tcsrchr(source, '\\');
|
|
if (result)
|
|
result++;
|
|
else
|
|
result = source;
|
|
return result;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Print information about a code module (DLL or EXE) such as its size,
|
|
// location, time stamp, etc.
|
|
// --------------------------------------------------------------------------
|
|
static VOID ShowModuleInfo(HANDLE LogFile, HMODULE ModuleHandle)
|
|
{
|
|
CHAR ModName[MAX_PATH];
|
|
IMAGE_DOS_HEADER *DosHeader;
|
|
IMAGE_NT_HEADERS *NTHeader;
|
|
HANDLE ModuleFile;
|
|
CHAR TimeBuffer[100] = "";
|
|
DWORD FileSize = 0;
|
|
#ifdef NO_SEH_MINGW
|
|
__try1(EXCEPTION_EXECUTE_HANDLER)
|
|
#else
|
|
__try
|
|
#endif
|
|
{
|
|
if (GetModuleFileNameA(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.
|
|
DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle;
|
|
if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic)
|
|
return;
|
|
NTHeader = (IMAGE_NT_HEADERS*)((char *)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.
|
|
ModuleFile = CreateFileA(ModName, GENERIC_READ,
|
|
FILE_SHARE_READ, 0, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0);
|
|
if (ModuleFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
FILETIME LastWriteTime;
|
|
FileSize = GetFileSize(ModuleFile, 0);
|
|
if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime))
|
|
{
|
|
wsprintfA(TimeBuffer, " - file date is ");
|
|
PrintTime(TimeBuffer + strlen(TimeBuffer), LastWriteTime);
|
|
}
|
|
CloseHandle(ModuleFile);
|
|
}
|
|
FPrintf(LogFile, "%s, loaded at 0x%08x - %d bytes - %08x%s\r\n",
|
|
ModName, ModuleHandle, FileSize,
|
|
NTHeader->FileHeader.TimeDateStamp, TimeBuffer);
|
|
}
|
|
}
|
|
// Handle any exceptions by continuing from this point.
|
|
#ifdef NO_SEH_MINGW
|
|
__except1
|
|
#else
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
#endif
|
|
{}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// 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.
|
|
// --------------------------------------------------------------------------
|
|
static VOID RecordModuleList(HANDLE LogFile)
|
|
{
|
|
SYSTEM_INFO SystemInfo;
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
size_t PageSize;
|
|
size_t NumPages;
|
|
size_t pageNum = 0;
|
|
LPVOID LastAllocationBase = 0;
|
|
|
|
FPrintf(LogFile, "\r\n"
|
|
"\tModule list: names, addresses, sizes, time stamps "
|
|
"and file times:\r\n");
|
|
|
|
// Set NumPages to the number of pages in the 4GByte address space,
|
|
// while being careful to avoid overflowing ints.
|
|
GetSystemInfo(&SystemInfo);
|
|
PageSize = SystemInfo.dwPageSize;
|
|
NumPages = 4 * (size_t)(ONEG / PageSize);
|
|
while(pageNum < NumPages)
|
|
{
|
|
if (VirtualQuery((LPVOID)(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(LogFile, (HMODULE)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().
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Record information about the user's system, such as processor type, amount
|
|
// of memory, etc.
|
|
// --------------------------------------------------------------------------
|
|
static VOID RecordSystemInformation(HANDLE fileHandle)
|
|
{
|
|
FILETIME CurrentTime;
|
|
CHAR TimeBuffer[100];
|
|
CHAR ModuleName[MAX_PATH];
|
|
CHAR UserName[200];
|
|
DWORD UserNameSize;
|
|
SYSTEM_INFO SystemInfo;
|
|
MEMORYSTATUS MemInfo;
|
|
|
|
GetSystemTimeAsFileTime(&CurrentTime);
|
|
PrintTime(TimeBuffer, CurrentTime);
|
|
FPrintf(fileHandle, "Error occurred at %s.\r\n", TimeBuffer);
|
|
|
|
if (GetModuleFileNameA(NULL, ModuleName, sizeof(ModuleName)) <= 0)
|
|
strcpy(ModuleName, "Unknown");
|
|
UserNameSize = sizeof(UserName);
|
|
if (!GetUserNameA(UserName, &UserNameSize))
|
|
strcpy(UserName, "Unknown");
|
|
FPrintf(fileHandle, "%s, run by %s.\r\n", ModuleName, UserName);
|
|
|
|
GetSystemInfo(&SystemInfo);
|
|
FPrintf(fileHandle, "%d processor(s), type %d %d.%d.\r\n"
|
|
"Program Memory from 0x%p to 0x%p\r\n",
|
|
SystemInfo.dwNumberOfProcessors,
|
|
SystemInfo.dwProcessorType,
|
|
SystemInfo.wProcessorLevel,
|
|
SystemInfo.wProcessorRevision,
|
|
SystemInfo.lpMinimumApplicationAddress,
|
|
SystemInfo.lpMaximumApplicationAddress);
|
|
|
|
MemInfo.dwLength = sizeof(MemInfo);
|
|
GlobalMemoryStatus(&MemInfo);
|
|
// Print out the amount of physical memory, rounded up.
|
|
FPrintf(fileHandle, "%d MBytes physical memory.\r\n", (MemInfo.dwTotalPhys +
|
|
ONEM - 1) / ONEM);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// What we do here is trivial : open a file, write out the register information
|
|
// from the PEXCEPTION_POINTERS structure, then return EXCEPTION_CONTINUE_SEARCH
|
|
// whose magic value tells Win32 to proceed with its normal error handling
|
|
// mechanism. This is important : an error dialog will popup if possible and
|
|
// the debugger will hopefully coexist peacefully with the structured exception
|
|
// handler.
|
|
// --------------------------------------------------------------------------
|
|
LONG WINAPI RecordExceptionInfo(PEXCEPTION_POINTERS data/*, LPCSTR Message, LPSTR lpCmdLine*/)
|
|
{
|
|
PEXCEPTION_RECORD Exception = NULL;
|
|
PCONTEXT Context = NULL;
|
|
TCHAR ModuleName[MAX_PATH];
|
|
TCHAR FileName[MAX_PATH] = TEXT("Unknown");
|
|
LPTSTR FilePart, lastperiod;
|
|
TCHAR CrashModulePathName[MAX_PATH];
|
|
LPCTSTR CrashModuleFileName = TEXT("Unknown");
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
static BOOL BeenHere = FALSE;
|
|
HANDLE fileHandle;
|
|
LPBYTE code = NULL;
|
|
int codebyte,i;
|
|
|
|
if (data)
|
|
{
|
|
Exception = data->ExceptionRecord;
|
|
Context = data->ContextRecord;
|
|
}
|
|
else if (prevExceptionFilter)
|
|
prevExceptionFilter(data);
|
|
else
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
if (BeenHere) // Going recursive! That must mean this routine crashed!
|
|
{
|
|
if (prevExceptionFilter)
|
|
return prevExceptionFilter(data);
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
BeenHere = TRUE;
|
|
|
|
if (Context)
|
|
{
|
|
#ifdef _X86_
|
|
code = (LPBYTE)(size_t)Context->Eip;
|
|
#elif defined (_AMD64_)
|
|
code = (LPBYTE)(size_t)Context->Rip;
|
|
#endif // || defined (_IA64_)
|
|
}
|
|
|
|
// Create a filename to record the error information to.
|
|
// Store it in the executable directory.
|
|
if (GetModuleFileName(NULL, ModuleName, sizeof(ModuleName)) <= 0)
|
|
ModuleName[0] = 0;
|
|
FilePart = GetFilePart(ModuleName);
|
|
|
|
// Extract the file name portion and remove it's file extension. We'll
|
|
// use that name shortly.
|
|
lstrcpy(FileName, FilePart);
|
|
lastperiod = _tcsrchr(FileName, '.');
|
|
if (lastperiod)
|
|
lastperiod[0] = 0;
|
|
// Replace the executable filename with our error log file name.
|
|
lstrcpy(FilePart, TEXT("errorlog.txt"));
|
|
fileHandle = CreateFile(ModuleName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
|
|
if (fileHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
OutputDebugString(TEXT("Error creating exception report"));
|
|
if (prevExceptionFilter)
|
|
prevExceptionFilter(data);
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
// Append to the error log.
|
|
SetFilePointer(fileHandle, 0, 0, FILE_END);
|
|
|
|
// Print out some blank lines to separate this error log from any previous ones.
|
|
FPrintf(fileHandle, "Email Sonic Team Junior so we can fix the bugs\r\n"); // Tails
|
|
FPrintf(fileHandle, "Make sure you tell us what you were doing to cause the crash, and if possible, record a demo!\r\n"); // Tails
|
|
FPrintf(fileHandle, "\r\n\r\n\r\n\r\n");
|
|
FPrintf(fileHandle, "SRB2 %s -ERROR LOG-\r\n\r\n", VERSIONSTRING);
|
|
FPrintf(fileHandle, "\r\n");
|
|
// VirtualQuery can be used to get the allocation base associated with a
|
|
// code address, which is the same as the ModuleHandle. This can be used
|
|
// to get the filename of the module that the crash happened in.
|
|
if (code && VirtualQuery(code, &MemInfo, sizeof(MemInfo)) &&
|
|
GetModuleFileName((HMODULE)MemInfo.AllocationBase,
|
|
CrashModulePathName,
|
|
sizeof(CrashModulePathName)) > 0)
|
|
CrashModuleFileName = GetFilePart(CrashModulePathName);
|
|
|
|
// Print out the beginning of the error log in a Win95 error window
|
|
// compatible format.
|
|
#ifdef _X86_
|
|
FPrintf(fileHandle, "%s caused %s in module %s at %04x:%08x.\r\n",
|
|
FileName, GetExceptionDescription(Exception->ExceptionCode),
|
|
CrashModuleFileName, Context->SegCs, Context->Eip);
|
|
#elif defined (_AMD64_)
|
|
FPrintf(fileHandle, "%s caused %s in module %s at %08x:%016x.\r\n",
|
|
FileName, GetExceptionDescription(Exception->ExceptionCode),
|
|
CrashModuleFileName, Context->SegCs, Context->Rip);
|
|
#else //defined (_IA64_)
|
|
FPrintf(fileHandle, "%s caused %s in module %s at ????.\r\n",
|
|
FileName, GetExceptionDescription(Exception->ExceptionCode),
|
|
CrashModuleFileName);
|
|
#endif
|
|
//if (&Message = Null)
|
|
FPrintf(fileHandle, "Exception handler called in %s.\r\n", "main thread");
|
|
//else
|
|
//FPrintf(fileHandle, "Exception handler called in %s.\r\n", Message);
|
|
|
|
RecordSystemInformation(fileHandle);
|
|
|
|
// If the exception was an access violation, print out some additional
|
|
// information, to the error log and the debugger.
|
|
if (Exception->ExceptionCode == STATUS_ACCESS_VIOLATION &&
|
|
Exception->NumberParameters >= 2)
|
|
{
|
|
TCHAR DebugMessage[1000];
|
|
LPCTSTR readwrite = TEXT("Read from");
|
|
if (Exception->ExceptionInformation[0])
|
|
readwrite = TEXT("Write to");
|
|
wsprintf(DebugMessage, TEXT("%s location %08x caused an access violation.\r\n"),
|
|
readwrite, Exception->ExceptionInformation[1]);
|
|
// The VisualC++ debugger doesn't actually tell you whether a read
|
|
// or a write caused the access violation, nor does it tell what
|
|
// address was being read or written. So I fixed that.
|
|
OutputDebugString(TEXT("Exception handler: "));
|
|
OutputDebugString(DebugMessage);
|
|
FPrintf(fileHandle, "%s", DebugMessage);
|
|
}
|
|
|
|
FPrintf(fileHandle, "\r\n");
|
|
|
|
// Print out the register values in a Win95 error window compatible format.
|
|
if ((Context->ContextFlags & CONTEXT_FULL) == CONTEXT_FULL)
|
|
{
|
|
FPrintf(fileHandle, "Registers:\r\n");
|
|
#ifdef _X86_
|
|
FPrintf(fileHandle, "EAX=%.8lx CS=%.4x EIP=%.8lx EFLGS=%.8lx\r\n",
|
|
Context->Eax,Context->SegCs,Context->Eip,Context->EFlags);
|
|
FPrintf(fileHandle, "EBX=%.8lx SS=%.4x ESP=%.8lx EBP=%.8lx\r\n",
|
|
Context->Ebx,Context->SegSs,Context->Esp,Context->Ebp);
|
|
FPrintf(fileHandle, "ECX=%.8lx DS=%.4x ESI=%.8lx FS=%.4x\r\n",
|
|
Context->Ecx,Context->SegDs,Context->Esi,Context->SegFs);
|
|
FPrintf(fileHandle, "EDX=%.8lx ES=%.4x EDI=%.8lx GS=%.4x\r\n",
|
|
Context->Edx,Context->SegEs,Context->Edi,Context->SegGs);
|
|
#elif defined (_AMD64_)
|
|
FPrintf(fileHandle, "RAX=%.16lx CS=%.8x RIP=%.16lx EFLGS=%.16lx\r\n",
|
|
Context->Rax,Context->SegCs,Context->Rip,Context->EFlags);
|
|
FPrintf(fileHandle, "RBX=%.16lx SS=%.8x RSP=%.16lx EBP=%.16lx\r\n",
|
|
Context->Rbx,Context->SegSs,Context->Rsp,Context->Rbp);
|
|
FPrintf(fileHandle, "RCX=%.16lx DS=%.8x RSI=%.16lx FS=%.8x\r\n",
|
|
Context->Rcx,Context->SegDs,Context->Rsi,Context->SegFs);
|
|
FPrintf(fileHandle, "RDX=%.16lx ES=%.8x RDI=%.16lx GS=%.8x\r\n",
|
|
Context->Rdx,Context->SegEs,Context->Rdi,Context->SegGs);
|
|
#else //defined (_IA64_)
|
|
FPrintf(fileHandle, "Unknown CPU type\r\n");
|
|
#endif
|
|
}
|
|
|
|
// moved down because it was causing the printout to stop
|
|
FPrintf(fileHandle, "Command Line parameters: ");
|
|
for(i = 1;i < myargc;i++)
|
|
FPrintf(fileHandle, "%s ", myargv[i]);
|
|
|
|
FPrintf(fileHandle, "Bytes at CS : EIP:\r\n");
|
|
|
|
// Print out the bytes of code at the instruction pointer. Since the
|
|
// crash may have been caused by an instruction pointer that was bad,
|
|
// this code needs to be wrapped in an exception handler, in case there
|
|
// is no memory to read. If the dereferencing of code[] fails, the
|
|
// exception handler will print '??'.
|
|
if (code)
|
|
for(codebyte = 0; codebyte < NumCodeBytes; codebyte++)
|
|
{
|
|
#ifdef NO_SEH_MINGW
|
|
__try1(EXCEPTION_EXECUTE_HANDLER)
|
|
#else
|
|
__try
|
|
#endif
|
|
{
|
|
FPrintf(fileHandle, "%02x ", code[codebyte]);
|
|
}
|
|
#ifdef NO_SEH_MINGW
|
|
__except1
|
|
#else
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
#endif
|
|
{
|
|
FPrintf(fileHandle, "?? ");
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
FPrintf(fileHandle, "\r\n"
|
|
"Stack dump:\r\n");
|
|
#ifdef NO_SEH_MINGW
|
|
__try1(EXCEPTION_EXECUTE_HANDLER)
|
|
#else
|
|
__try
|
|
#endif
|
|
{
|
|
// Esp contains the bottom of the stack, or at least the bottom of
|
|
// the currently used area.
|
|
LPDWORD pStack = NULL;
|
|
LPDWORD pStackTop = NULL;
|
|
size_t Count = 0;
|
|
TCHAR buffer[1000] = TEXT("");
|
|
const int safetyzone = 50;
|
|
LPTSTR nearend = buffer + sizeof(buffer) - safetyzone*sizeof(TCHAR);
|
|
LPTSTR output = buffer;
|
|
LPCVOID Suffix;
|
|
|
|
#ifdef _X86_
|
|
pStack = (LPDWORD)(size_t)Context->Esp;
|
|
#elif defined (_AMD64_)
|
|
pStack = (LPDWORD)(size_t)Context->Rsp;
|
|
#endif // defined (_IA64_)
|
|
|
|
|
|
// Load the top (highest address) of the stack from the
|
|
// thread information block. It will be found there in
|
|
// Win9x and Windows NT.
|
|
#ifdef _X86_
|
|
#ifdef __GNUC__
|
|
__asm__("movl %%fs : 4, %%eax": "=a"(pStackTop));
|
|
#elif defined (_MSC_VER)
|
|
__asm
|
|
{
|
|
mov eax, fs:[4]
|
|
mov pStackTop, eax
|
|
}
|
|
#endif
|
|
#elif defined (_AMD64_)
|
|
#ifdef __GNUC__
|
|
__asm__("mov %%gs : 4, %%rax": "=a"(pStackTop));
|
|
#elif defined (_MSC_VER)
|
|
/*
|
|
__asm
|
|
{
|
|
mov rax, fs:[4]
|
|
mov pStackTop, rax
|
|
}
|
|
*/
|
|
#endif
|
|
#endif
|
|
if (pStackTop == NULL)
|
|
goto StackSkip;
|
|
else 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
|
|
// FPrintf directly.
|
|
while(pStack + 1 <= pStackTop)
|
|
{
|
|
if ((Count % StackColumns) == 0)
|
|
output += wsprintf(output, TEXT("%p: "), pStack);
|
|
if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop)
|
|
Suffix = TEXT("\r\n");
|
|
else
|
|
Suffix = TEXT(" ");
|
|
output += wsprintf(output, TEXT("%p%s"), *pStack, Suffix);
|
|
pStack++;
|
|
// Check for when the buffer is almost full, and flush it to disk.
|
|
if ( output > nearend)
|
|
{
|
|
FPrintf(fileHandle, "%s", buffer);
|
|
buffer[0] = 0;
|
|
output = buffer;
|
|
}
|
|
}
|
|
// Print out any final characters from the cache.
|
|
StackSkip:
|
|
FPrintf(fileHandle, "%s", buffer);
|
|
}
|
|
#ifdef NO_SEH_MINGW
|
|
__except1
|
|
#else
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
#endif
|
|
{
|
|
FPrintf(fileHandle, "Exception encountered during stack dump.\r\n");
|
|
}
|
|
|
|
RecordModuleList(fileHandle);
|
|
|
|
CloseHandle(fileHandle);
|
|
|
|
// Return the magic value which tells Win32 that this handler didn't
|
|
// actually handle the exception - so that things will proceed as per
|
|
// normal.
|
|
//BP: should put message for end user to send this file to fix any bug
|
|
if (prevExceptionFilter)
|
|
return prevExceptionFilter(data);
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|