814 lines
25 KiB
C++
814 lines
25 KiB
C++
// ExceptionHandler.cpp Version 1.4
|
|
//
|
|
// Copyright © 1998 Bruce Dawson
|
|
//
|
|
// This source file contains the exception handler for recording error
|
|
// information after crashes. See ExceptionHandler.h for information
|
|
// on how to hook it in.
|
|
//
|
|
// Author: Bruce Dawson
|
|
// brucedawson@cygnus-software.com
|
|
//
|
|
// Modified by: Hans Dietrich
|
|
// hdietrich2@hotmail.com
|
|
//
|
|
// Version 1.4: - Added invocation of XCrashReport.exe
|
|
//
|
|
// Version 1.3: - Added minidump output
|
|
//
|
|
// Version 1.1: - reformatted output for XP-like error report
|
|
// - added ascii output to stack dump
|
|
//
|
|
// A paper by the original author can be found at:
|
|
// http://www.cygnus-software.com/papers/release_debugging.html
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Disable warnings generated by the Windows header files.
|
|
#pragma warning(disable : 4514)
|
|
#pragma warning(disable : 4201)
|
|
|
|
#define _WIN32_WINDOWS 0x0500 // for IsDebuggerPresent
|
|
|
|
#include "windows.h"
|
|
#include <tchar.h>
|
|
#include "GetWinVer.h"
|
|
#include "miniversion.h"
|
|
#include "../nu/ns_wc.h"
|
|
|
|
#include "minidump.h"
|
|
#include ".\settings.h"
|
|
#include "api__gen_crasher.h"
|
|
|
|
extern char *winampVersion;
|
|
extern Settings settings;
|
|
|
|
#ifndef _countof
|
|
#define _countof(array) (sizeof(array)/sizeof(array[0]))
|
|
#endif
|
|
|
|
const int NumCodeBytes = 16; // Number of code bytes to record.
|
|
const int MaxStackDump = 3072; // Maximum number of DWORDS in stack dumps.
|
|
const int StackColumns = 4; // Number of columns in stack dump.
|
|
|
|
#define ONEK 1024
|
|
#define SIXTYFOURK (64*ONEK)
|
|
#define ONEM (ONEK*ONEK)
|
|
#define ONEG (ONEK*ONEK*ONEK)
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// lstrrchr (avoid the C Runtime )
|
|
static TCHAR * lstrrchr(LPCTSTR string, int ch)
|
|
{
|
|
TCHAR *start = (TCHAR *)string;
|
|
|
|
while (string && *string++) /* find end of string */
|
|
;
|
|
/* search towards front */
|
|
while (--string != start && *string != (TCHAR) ch)
|
|
;
|
|
|
|
if (*string == (TCHAR) ch) /* char found ? */
|
|
return (TCHAR *)string;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// hprintf behaves similarly to printf, with a few vital differences.
|
|
// It uses wvsprintf to do the formatting, which is a system routine,
|
|
// thus avoiding C run time interactions. For similar reasons it
|
|
// uses WriteFile rather than fwrite.
|
|
// The one limitation that this imposes is that wvsprintf, and
|
|
// therefore hprintf, cannot handle floating point numbers.
|
|
|
|
// Too many calls to WriteFile can take a long time, causing
|
|
// confusing delays when programs crash. Therefore I implemented
|
|
// a simple buffering scheme for hprintf
|
|
|
|
#define HPRINTF_BUFFER_SIZE (8*1024) // must be at least 2048
|
|
static wchar_t hprintf_buffer[HPRINTF_BUFFER_SIZE]; // wvsprintf never prints more than one K.
|
|
static int hprintf_index = 0;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// hflush
|
|
static void hflush(HANDLE LogFile)
|
|
{
|
|
if (hprintf_index > 0)
|
|
{
|
|
DWORD NumBytes = 0;
|
|
WriteFile(LogFile, hprintf_buffer, lstrlenW(hprintf_buffer)*2, &NumBytes, 0);
|
|
hprintf_index = 0;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// hprintf
|
|
static void hprintf(HANDLE LogFile, const wchar_t *Format, ...)
|
|
{
|
|
if (hprintf_index > (HPRINTF_BUFFER_SIZE-1024))
|
|
{
|
|
DWORD NumBytes = 0;
|
|
WriteFile(LogFile, hprintf_buffer, lstrlen(hprintf_buffer)*2, &NumBytes, 0);
|
|
hprintf_index = 0;
|
|
}
|
|
|
|
va_list arglist;
|
|
va_start( arglist, Format);
|
|
hprintf_index += vswprintf(&hprintf_buffer[hprintf_index], Format, arglist);
|
|
va_end( arglist);
|
|
}
|
|
|
|
#include <strsafe.h>
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// DumpMiniDump
|
|
static BOOL DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo)
|
|
{
|
|
if (excpInfo == NULL)
|
|
{
|
|
// Generate exception to get proper context in dump
|
|
__try
|
|
{
|
|
//OutputDebugString(_T("raising exception\r\n"));
|
|
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
|
|
}
|
|
__except(DumpMiniDump(hFile, GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION)
|
|
{
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//OutputDebugString(_T("writing minidump\r\n"));
|
|
MINIDUMP_EXCEPTION_INFORMATION eInfo = {0};
|
|
eInfo.ThreadId = GetCurrentThreadId();
|
|
eInfo.ExceptionPointers = excpInfo;
|
|
eInfo.ClientPointers = FALSE;
|
|
|
|
// try to load dbghelpdll
|
|
HMODULE hm = NULL;
|
|
// first from app folder
|
|
wchar_t szDbgHelpPath[_MAX_PATH] = {0};
|
|
|
|
if (GetModuleFileNameW( NULL, szDbgHelpPath, _MAX_PATH ))
|
|
{
|
|
wchar_t *pSlash = wcsrchr( szDbgHelpPath, L'\\' );
|
|
if (pSlash)
|
|
{
|
|
StringCchCopy( pSlash+1, _MAX_PATH, L"dbghelp.dll");
|
|
hm = LoadLibraryW( szDbgHelpPath );
|
|
}
|
|
}
|
|
if (!hm)
|
|
{
|
|
// load any version we can
|
|
hm = LoadLibraryW(L"dbghelp.dll");
|
|
}
|
|
|
|
if (hm)
|
|
{
|
|
BOOL (WINAPI* MiniDumpWriteDump)(
|
|
HANDLE hProcess,
|
|
DWORD ProcessId,
|
|
HANDLE hFile,
|
|
MINIDUMP_TYPE DumpType,
|
|
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
|
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
|
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
|
) = NULL;
|
|
//OutputDebugString(_T("Found dbghelp.dll, searching for MiniDumpWriteDump\r\n"));
|
|
*(FARPROC*)&MiniDumpWriteDump = GetProcAddress(hm, "MiniDumpWriteDump");
|
|
if (MiniDumpWriteDump)
|
|
{
|
|
//OutputDebugString(_T("Calling MiniDumpWriteDump\r\n"));
|
|
BOOL ret = MiniDumpWriteDump(
|
|
GetCurrentProcess(),
|
|
GetCurrentProcessId(),
|
|
hFile,
|
|
(MINIDUMP_TYPE)settings.dumpType,
|
|
excpInfo ? &eInfo : NULL,
|
|
NULL,
|
|
NULL);
|
|
//OutputDebugString(_T("MiniDumpWriteDump finished\r\n"));
|
|
if (!ret)
|
|
{
|
|
DWORD le = GetLastError();
|
|
wchar_t tmp[256] = {0};
|
|
StringCchPrintfW(tmp, 256, L"call failed with error code: %d", le);
|
|
//OutputDebugString(tmp);
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// FormatTime
|
|
//
|
|
// Format the specified FILETIME to output in a human readable format,
|
|
// without using the C run time.
|
|
static void FormatTime(LPTSTR output, FILETIME TimeToPrint)
|
|
{
|
|
output[0] = _T('\0');
|
|
WORD Date, Time;
|
|
if (FileTimeToLocalFileTime(&TimeToPrint, &TimeToPrint) &&
|
|
FileTimeToDosDateTime(&TimeToPrint, &Date, &Time))
|
|
{
|
|
StringCchPrintf(output, 100, _T("%d/%d/%d %02d:%02d:%02d"),
|
|
(Date / 32) & 15, Date & 31, (Date / 512) + 1980,
|
|
(Time >> 11), (Time >> 5) & 0x3F, (Time & 0x1F) * 2);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// DumpModuleInfo
|
|
//
|
|
// Print information about a code module (DLL or EXE) such as its size,
|
|
// location, time stamp, etc.
|
|
static bool DumpModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle, int nModuleNo)
|
|
{
|
|
bool rc = false;
|
|
wchar_t szModName[MAX_PATH*2] = {0};
|
|
__try
|
|
{
|
|
if (GetModuleFileName(ModuleHandle, szModName, MAX_PATH*2) > 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 false;
|
|
|
|
IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS*)((char *)DosHeader
|
|
+ DosHeader->e_lfanew);
|
|
if (IMAGE_NT_SIGNATURE != NTHeader->Signature)
|
|
return false;
|
|
|
|
// open the code module file so that we can get its file date and size
|
|
HANDLE ModuleFile = CreateFile(szModName, GENERIC_READ,
|
|
FILE_SHARE_READ, 0, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
TCHAR TimeBuffer[100] = {0};
|
|
DWORD FileSize = 0;
|
|
if (ModuleFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
FileSize = GetFileSize(ModuleFile, 0);
|
|
FILETIME LastWriteTime;
|
|
if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime))
|
|
{
|
|
FormatTime(TimeBuffer, LastWriteTime);
|
|
}
|
|
CloseHandle(ModuleFile);
|
|
}
|
|
hprintf(LogFile, _T("Module %d\r\n"), nModuleNo);
|
|
hprintf(LogFile, _T("%s\r\n"), szModName);
|
|
hprintf(LogFile, _T("Image Base: 0x%08x Image Size: 0x%08x\r\n"),
|
|
NTHeader->OptionalHeader.ImageBase,
|
|
NTHeader->OptionalHeader.SizeOfImage),
|
|
|
|
hprintf(LogFile, _T("Checksum: 0x%08x Time Stamp: 0x%08x\r\n"),
|
|
NTHeader->OptionalHeader.CheckSum,
|
|
NTHeader->FileHeader.TimeDateStamp);
|
|
|
|
hprintf(LogFile, _T("File Size: %-10d File Time: %s\r\n"),
|
|
FileSize, TimeBuffer);
|
|
|
|
hprintf(LogFile, _T("Version Information:\r\n"));
|
|
|
|
CMiniVersion ver(szModName);
|
|
TCHAR szBuf[200] = {0};
|
|
WORD dwBuf[4] = {0};
|
|
|
|
ver.GetCompanyName(szBuf, _countof(szBuf)-1);
|
|
hprintf(LogFile, _T(" Company: %s\r\n"), szBuf);
|
|
|
|
ver.GetProductName(szBuf, _countof(szBuf)-1);
|
|
hprintf(LogFile, _T(" Product: %s\r\n"), szBuf);
|
|
|
|
ver.GetFileDescription(szBuf, _countof(szBuf)-1);
|
|
hprintf(LogFile, _T(" FileDesc: %s\r\n"), szBuf);
|
|
|
|
ver.GetFileVersion(dwBuf);
|
|
hprintf(LogFile, _T(" FileVer: %d.%d.%d.%d\r\n"),
|
|
dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]);
|
|
|
|
ver.GetProductVersion(dwBuf);
|
|
hprintf(LogFile, _T(" ProdVer: %d.%d.%d.%d\r\n"),
|
|
dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]);
|
|
|
|
ver.Release();
|
|
|
|
hprintf(LogFile, _T("\r\n"));
|
|
|
|
rc = true;
|
|
}
|
|
}
|
|
// Handle any exceptions by continuing from this point.
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
//OutputDebugString(L"DumpModuleInfo exception");
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// DumpModuleList
|
|
//
|
|
// 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 DumpModuleList(HANDLE LogFile)
|
|
{
|
|
SYSTEM_INFO SystemInfo;
|
|
GetSystemInfo(&SystemInfo);
|
|
|
|
//OutputDebugString(L"Dumping modules list");
|
|
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;
|
|
|
|
int nModuleNo = 1;
|
|
|
|
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;
|
|
|
|
if (DumpModuleInfo(LogFile, (HINSTANCE)LastAllocationBase, nModuleNo))
|
|
{
|
|
nModuleNo++;
|
|
}
|
|
}
|
|
}
|
|
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()
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// DumpSystemInformation
|
|
//
|
|
// Record information about the user's system, such as processor type, amount
|
|
// of memory, etc.
|
|
static void DumpSystemInformation(HANDLE LogFile)
|
|
{
|
|
FILETIME CurrentTime;
|
|
GetSystemTimeAsFileTime(&CurrentTime);
|
|
TCHAR szTimeBuffer[100] = {0};
|
|
FormatTime(szTimeBuffer, CurrentTime);
|
|
|
|
hprintf(LogFile, _T("Error occurred at %s.\r\n"), szTimeBuffer);
|
|
|
|
TCHAR szModuleName[MAX_PATH*2] = {0};
|
|
if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0)
|
|
StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown"));
|
|
|
|
TCHAR szUserName[200] = {0};
|
|
DWORD UserNameSize = _countof(szUserName)-2;
|
|
if (!GetUserName(szUserName, &UserNameSize))
|
|
StringCbCopy(szUserName, sizeof(szUserName), _T("Unknown"));
|
|
|
|
hprintf(LogFile, _T("%s, run by %s.\r\n"), szModuleName, szUserName);
|
|
|
|
// print out operating system
|
|
TCHAR szWinVer[50] = {0}, szMajorMinorBuild[50] = {0};
|
|
int nWinVer = 0;
|
|
GetWinVer(szWinVer, &nWinVer, szMajorMinorBuild);
|
|
hprintf(LogFile, _T("Operating system: %s (%s).\r\n"),
|
|
szWinVer, szMajorMinorBuild);
|
|
|
|
SYSTEM_INFO SystemInfo;
|
|
GetSystemInfo(&SystemInfo);
|
|
hprintf(LogFile, _T("%d processor(s), type %d.\r\n"),
|
|
SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType);
|
|
|
|
MEMORYSTATUS MemInfo;
|
|
MemInfo.dwLength = sizeof(MemInfo);
|
|
GlobalMemoryStatus(&MemInfo);
|
|
|
|
// Print out info on memory, rounded up.
|
|
hprintf(LogFile, _T("%d%% memory in use.\r\n"), MemInfo.dwMemoryLoad);
|
|
hprintf(LogFile, _T("%d MBytes physical memory.\r\n"), (MemInfo.dwTotalPhys +
|
|
ONEM - 1) / ONEM);
|
|
hprintf(LogFile, _T("%d MBytes physical memory free.\r\n"),
|
|
(MemInfo.dwAvailPhys + ONEM - 1) / ONEM);
|
|
hprintf(LogFile, _T("%d MBytes paging file.\r\n"), (MemInfo.dwTotalPageFile +
|
|
ONEM - 1) / ONEM);
|
|
hprintf(LogFile, _T("%d MBytes paging file free.\r\n"),
|
|
(MemInfo.dwAvailPageFile + ONEM - 1) / ONEM);
|
|
hprintf(LogFile, _T("%d MBytes user address space.\r\n"),
|
|
(MemInfo.dwTotalVirtual + ONEM - 1) / ONEM);
|
|
hprintf(LogFile, _T("%d MBytes user address space free.\r\n"),
|
|
(MemInfo.dwAvailVirtual + ONEM - 1) / ONEM);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// GetExceptionDescription
|
|
//
|
|
// Translate the exception code into something human readable
|
|
static const TCHAR *GetExceptionDescription(DWORD ExceptionCode)
|
|
{
|
|
struct ExceptionNames
|
|
{
|
|
DWORD ExceptionCode;
|
|
TCHAR * ExceptionName;
|
|
};
|
|
|
|
#if 0 // from winnt.h
|
|
#define STATUS_WAIT_0 ((DWORD )0x00000000L)
|
|
#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
|
|
#define STATUS_USER_APC ((DWORD )0x000000C0L)
|
|
#define STATUS_TIMEOUT ((DWORD )0x00000102L)
|
|
#define STATUS_PENDING ((DWORD )0x00000103L)
|
|
#define STATUS_SEGMENT_NOTIFICATION ((DWORD )0x40000005L)
|
|
#define STATUS_GUARD_PAGE_VIOLATION ((DWORD )0x80000001L)
|
|
#define STATUS_DATATYPE_MISALIGNMENT ((DWORD )0x80000002L)
|
|
#define STATUS_BREAKPOINT ((DWORD )0x80000003L)
|
|
#define STATUS_SINGLE_STEP ((DWORD )0x80000004L)
|
|
#define STATUS_ACCESS_VIOLATION ((DWORD )0xC0000005L)
|
|
#define STATUS_IN_PAGE_ERROR ((DWORD )0xC0000006L)
|
|
#define STATUS_INVALID_HANDLE ((DWORD )0xC0000008L)
|
|
#define STATUS_NO_MEMORY ((DWORD )0xC0000017L)
|
|
#define STATUS_ILLEGAL_INSTRUCTION ((DWORD )0xC000001DL)
|
|
#define STATUS_NONCONTINUABLE_EXCEPTION ((DWORD )0xC0000025L)
|
|
#define STATUS_INVALID_DISPOSITION ((DWORD )0xC0000026L)
|
|
#define STATUS_ARRAY_BOUNDS_EXCEEDED ((DWORD )0xC000008CL)
|
|
#define STATUS_FLOAT_DENORMAL_OPERAND ((DWORD )0xC000008DL)
|
|
#define STATUS_FLOAT_DIVIDE_BY_ZERO ((DWORD )0xC000008EL)
|
|
#define STATUS_FLOAT_INEXACT_RESULT ((DWORD )0xC000008FL)
|
|
#define STATUS_FLOAT_INVALID_OPERATION ((DWORD )0xC0000090L)
|
|
#define STATUS_FLOAT_OVERFLOW ((DWORD )0xC0000091L)
|
|
#define STATUS_FLOAT_STACK_CHECK ((DWORD )0xC0000092L)
|
|
#define STATUS_FLOAT_UNDERFLOW ((DWORD )0xC0000093L)
|
|
#define STATUS_INTEGER_DIVIDE_BY_ZERO ((DWORD )0xC0000094L)
|
|
#define STATUS_INTEGER_OVERFLOW ((DWORD )0xC0000095L)
|
|
#define STATUS_PRIVILEGED_INSTRUCTION ((DWORD )0xC0000096L)
|
|
#define STATUS_STACK_OVERFLOW ((DWORD )0xC00000FDL)
|
|
#define STATUS_CONTROL_C_EXIT ((DWORD )0xC000013AL)
|
|
#define STATUS_FLOAT_MULTIPLE_FAULTS ((DWORD )0xC00002B4L)
|
|
#define STATUS_FLOAT_MULTIPLE_TRAPS ((DWORD )0xC00002B5L)
|
|
#define STATUS_ILLEGAL_VLM_REFERENCE ((DWORD )0xC00002C0L)
|
|
#endif
|
|
|
|
ExceptionNames ExceptionMap[] =
|
|
{
|
|
{0x40010005, _T("a Control-C")},
|
|
{0x40010008, _T("a Control-Break")},
|
|
{0x80000002, _T("a Datatype Misalignment")},
|
|
{0x80000003, _T("a Breakpoint")},
|
|
{0xc0000005, _T("an Access Violation")},
|
|
{0xc0000006, _T("an In Page Error")},
|
|
{0xc0000017, _T("a No Memory")},
|
|
{0xc000001d, _T("an Illegal Instruction")},
|
|
{0xc0000025, _T("a Noncontinuable Exception")},
|
|
{0xc0000026, _T("an Invalid Disposition")},
|
|
{0xc000008c, _T("a Array Bounds Exceeded")},
|
|
{0xc000008d, _T("a Float Denormal Operand")},
|
|
{0xc000008e, _T("a Float Divide by Zero")},
|
|
{0xc000008f, _T("a Float Inexact Result")},
|
|
{0xc0000090, _T("a Float Invalid Operation")},
|
|
{0xc0000091, _T("a Float Overflow")},
|
|
{0xc0000092, _T("a Float Stack Check")},
|
|
{0xc0000093, _T("a Float Underflow")},
|
|
{0xc0000094, _T("an Integer Divide by Zero")},
|
|
{0xc0000095, _T("an Integer Overflow")},
|
|
{0xc0000096, _T("a Privileged Instruction")},
|
|
{0xc00000fD, _T("a Stack Overflow")},
|
|
{0xc0000142, _T("a DLL Initialization Failed")},
|
|
{0xe06d7363, _T("a Microsoft C++ Exception")},
|
|
};
|
|
|
|
for (int i = 0; i < _countof(ExceptionMap); i++)
|
|
if (ExceptionCode == ExceptionMap[i].ExceptionCode)
|
|
return ExceptionMap[i].ExceptionName;
|
|
|
|
return _T("an Unknown exception type");
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// GetFilePart
|
|
static TCHAR * GetFilePart(LPCTSTR source)
|
|
{
|
|
TCHAR *result = lstrrchr(source, _T('\\'));
|
|
if (result)
|
|
result++;
|
|
else
|
|
result = (TCHAR *)source;
|
|
return result;
|
|
}
|
|
|
|
#ifdef _M_IX86
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// DumpStack
|
|
static void DumpStack(HANDLE LogFile, DWORD *pStack)
|
|
{
|
|
hprintf(LogFile, _T("\r\n\r\nStack:\r\n"));
|
|
|
|
__try
|
|
{
|
|
// Esp contains the bottom of the stack, or at least the bottom of
|
|
// the currently used area.
|
|
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;
|
|
|
|
int Count = 0;
|
|
|
|
DWORD* pStackStart = pStack;
|
|
|
|
int nDwordsPrinted = 0;
|
|
|
|
while (pStack + 1 <= pStackTop)
|
|
{
|
|
if ((Count % StackColumns) == 0)
|
|
{
|
|
pStackStart = pStack;
|
|
nDwordsPrinted = 0;
|
|
hprintf(LogFile, _T("0x%08x: "), pStack);
|
|
}
|
|
hprintf(LogFile, _T("%08x "), pStack);
|
|
if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop)
|
|
{
|
|
nDwordsPrinted++;
|
|
int n = nDwordsPrinted;
|
|
while (n < 4)
|
|
{
|
|
hprintf(LogFile, _T(" "));
|
|
n++;
|
|
}
|
|
|
|
for (int i = 0; i < nDwordsPrinted; i++)
|
|
{
|
|
DWORD dwStack = *pStackStart;
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
char c = (char)(dwStack & 0xFF);
|
|
if (c < 0x20 || c > 0x7E)
|
|
c = '.';
|
|
#ifdef _UNICODE
|
|
WCHAR w = (WCHAR)c;
|
|
hprintf(LogFile, _T("%c"), w);
|
|
#else
|
|
hprintf(LogFile, _T("%c"), c);
|
|
#endif
|
|
dwStack = dwStack >> 8;
|
|
}
|
|
pStackStart++;
|
|
}
|
|
|
|
hprintf(LogFile, _T("\r\n"));
|
|
}
|
|
else
|
|
{
|
|
// hprintf(LogFile, _T("%08x "), *pStack);
|
|
nDwordsPrinted++;
|
|
}
|
|
pStack++;
|
|
}
|
|
hprintf(LogFile, _T("\r\n"));
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
hprintf(LogFile, _T("Exception encountered during stack dump.\r\n"));
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// DumpRegisters
|
|
static void DumpRegisters(HANDLE LogFile, PCONTEXT Context)
|
|
{
|
|
// Print out the register values in an XP error window compatible format.
|
|
hprintf(LogFile, _T("\r\n"));
|
|
hprintf(LogFile, _T("Context:\r\n"));
|
|
hprintf(LogFile, _T("EDI: 0x%08x ESI: 0x%08x EAX: 0x%08x\r\n"),
|
|
Context->Edi, Context->Esi, Context->Eax);
|
|
hprintf(LogFile, _T("EBX: 0x%08x ECX: 0x%08x EDX: 0x%08x\r\n"),
|
|
Context->Ebx, Context->Ecx, Context->Edx);
|
|
hprintf(LogFile, _T("EIP: 0x%08x EBP: 0x%08x SegCs: 0x%08x\r\n"),
|
|
Context->Eip, Context->Ebp, Context->SegCs);
|
|
hprintf(LogFile, _T("EFlags: 0x%08x ESP: 0x%08x SegSs: 0x%08x\r\n"),
|
|
Context->EFlags, Context->Esp, Context->SegSs);
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL CreateLog(PEXCEPTION_POINTERS pExceptPtrs, LPCWSTR lpszMessage)
|
|
{
|
|
HANDLE hLogFile = CreateFile(settings.logPath, GENERIC_WRITE, 0, 0,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);
|
|
|
|
if (hLogFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
//OutputDebugString(_T("Error creating exception report\r\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
// add BOM
|
|
WORD wBOM = 0xFEFF;
|
|
DWORD num = 0;
|
|
WriteFile(hLogFile, &wBOM, sizeof(WORD), &num, NULL);
|
|
// Append to the error log
|
|
SetFilePointer(hLogFile, 0, 0, FILE_END);
|
|
|
|
wchar_t line[1024] = {0};
|
|
wchar_t msgBody[4*1024] = {0};
|
|
wchar_t winampVersionWide[1024] = {0};
|
|
MultiByteToWideCharSZ(CP_ACP, 0, winampVersion, -1, winampVersionWide, 1024);
|
|
StringCchPrintf(line, 1024, L"Winamp client version: %s\r\n", winampVersionWide);
|
|
StringCchCopy(msgBody, 4*1024, line);
|
|
|
|
PEXCEPTION_RECORD Exception = pExceptPtrs->ExceptionRecord;
|
|
PCONTEXT Context = pExceptPtrs->ContextRecord;
|
|
|
|
TCHAR szCrashModulePathName[MAX_PATH*2] = {0};
|
|
|
|
TCHAR *pszCrashModuleFileName = _T("Unknown");
|
|
|
|
#ifdef _M_IX86
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
|
|
// 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 (VirtualQuery((void*)Context->Eip, &MemInfo, sizeof(MemInfo)) &&
|
|
(GetModuleFileName((HINSTANCE)MemInfo.AllocationBase,
|
|
szCrashModulePathName,
|
|
sizeof(szCrashModulePathName)-2) > 0))
|
|
{
|
|
//OutputDebugString(szCrashModulePathName);
|
|
pszCrashModuleFileName = GetFilePart(szCrashModulePathName);
|
|
}
|
|
#endif
|
|
|
|
// Print out the beginning of the error log in a Win95 error window
|
|
// compatible format.
|
|
TCHAR szModuleName[MAX_PATH*2] = {0};
|
|
if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0)
|
|
StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown"));
|
|
|
|
TCHAR *pszFilePart = GetFilePart(szModuleName);
|
|
|
|
// Extract the file name portion and remove it's file extension
|
|
TCHAR szFileName[MAX_PATH*2] = {0};
|
|
StringCbCopy(szFileName, sizeof(szFileName), pszFilePart);
|
|
TCHAR *lastperiod = lstrrchr(szFileName, _T('.'));
|
|
if (lastperiod)
|
|
lastperiod[0] = 0;
|
|
|
|
#ifdef _M_IX86
|
|
StringCchPrintf(line, 1024, L"%s caused %s (0x%08x) \r\nin module %s at %04x:%08x.\r\n\r\n",
|
|
szFileName, GetExceptionDescription(Exception->ExceptionCode),
|
|
Exception->ExceptionCode,
|
|
pszCrashModuleFileName, Context->SegCs, Context->Eip);
|
|
#endif
|
|
StringCchCat(msgBody, 4*1024, line);
|
|
|
|
StringCchPrintf(line, 1024, L"Exception handler called in %s.\r\n", lpszMessage);
|
|
StringCchCat(msgBody, 4*1024, line);
|
|
|
|
hprintf(hLogFile, L"%s", msgBody);
|
|
wchar_t *p = msgBody, *end = msgBody + wcslen(msgBody);
|
|
while(p != end)
|
|
{
|
|
if (*p == L'\r') *p = 1;
|
|
if (*p == L'\n') *p = 2;
|
|
p++;
|
|
|
|
}
|
|
settings.WriteBody(msgBody);
|
|
|
|
if (settings.logSystem)
|
|
{
|
|
DumpSystemInformation(hLogFile);
|
|
|
|
// 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 szDebugMessage[1000] = {0};
|
|
const TCHAR* readwrite = _T("Read from");
|
|
if (Exception->ExceptionInformation[0])
|
|
readwrite = _T("Write to");
|
|
StringCchPrintf(szDebugMessage, 1000, _T("%s location %08x caused an access violation.\r\n"),
|
|
readwrite, Exception->ExceptionInformation[1]);
|
|
hprintf(hLogFile, _T("%s"), szDebugMessage);
|
|
}
|
|
}
|
|
if (settings.logRegistry)
|
|
{
|
|
#ifdef _M_IX86
|
|
DumpRegisters(hLogFile, Context);
|
|
#endif
|
|
|
|
// 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 '??'.
|
|
#ifdef _M_IX86
|
|
hprintf(hLogFile, _T("\r\nBytes at CS:EIP:\r\n"));
|
|
BYTE * code = (BYTE *)Context->Eip;
|
|
for (int codebyte = 0; codebyte < NumCodeBytes; codebyte++)
|
|
{
|
|
__try
|
|
{
|
|
hprintf(hLogFile, _T("%02x "), code[codebyte]);
|
|
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
hprintf(hLogFile, _T("?? "));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (settings.logStack)
|
|
{
|
|
// 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.
|
|
|
|
// Esp contains the bottom of the stack, or at least the bottom of
|
|
// the currently used area
|
|
|
|
#ifdef _M_IX86
|
|
DWORD* pStack = (DWORD *)Context->Esp;
|
|
DumpStack(hLogFile, pStack);
|
|
#endif
|
|
}
|
|
if (settings.logModule)
|
|
{
|
|
DumpModuleList(hLogFile);
|
|
}
|
|
|
|
hprintf(hLogFile, _T("\r\n===== [end of log file] =====\r\n"));
|
|
hflush(hLogFile);
|
|
CloseHandle(hLogFile);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CreateDump(PEXCEPTION_POINTERS pExceptPtrs)
|
|
{
|
|
BOOL retCode = FALSE;
|
|
|
|
// Create the file
|
|
//OutputDebugString(_T("CreateFile: "));
|
|
//OutputDebugString(settings.dumpPath);
|
|
HANDLE hMiniDumpFile = CreateFile(
|
|
settings.dumpPath,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
|
|
NULL);
|
|
|
|
// Write the minidump to the file
|
|
if (hMiniDumpFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
retCode = DumpMiniDump(hMiniDumpFile, pExceptPtrs);
|
|
// Close file
|
|
CloseHandle(hMiniDumpFile);
|
|
if (!retCode) DeleteFile(settings.dumpPath);
|
|
}
|
|
return retCode;
|
|
} |