mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
1099 lines
30 KiB
C++
1099 lines
30 KiB
C++
#include "CrashCatcher.h"
|
|
#include "../util/CritSect.h"
|
|
#include "../util/Dictionary.h"
|
|
#include <commdlg.h>
|
|
#include <time.h>
|
|
|
|
USING_NS_BF;
|
|
|
|
#pragma comment(lib, "version.lib")
|
|
#pragma comment(lib, "comdlg32.lib")
|
|
|
|
#pragma warning(disable:4091)
|
|
#pragma warning(disable:4996)
|
|
#include <imagehlp.h>
|
|
|
|
#ifdef BF64
|
|
typedef BOOL(__stdcall * SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL);
|
|
typedef DWORD(__stdcall *SYMSETOPTIONSPROC)(DWORD);
|
|
typedef BOOL(__stdcall *SYMCLEANUPPROC)(HANDLE);
|
|
typedef LPCSTR(__stdcall *UNDECORATESYMBOLNAMEPROC)(LPCSTR, LPSTR, DWORD, DWORD);
|
|
typedef BOOL(__stdcall * STACKWALKPROC)
|
|
(DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID,
|
|
PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE,
|
|
PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
|
|
typedef LPVOID(__stdcall *SYMFUNCTIONTABLEACCESSPROC)(HANDLE, DWORD64);
|
|
typedef DWORD64 (__stdcall *SYMGETMODULEBASEPROC)(HANDLE, DWORD64);
|
|
typedef BOOL(__stdcall *SYMGETSYMFROMADDRPROC)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
|
|
typedef BOOL(__stdcall *SYMGETLINEFROMADDR)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64);
|
|
#else
|
|
typedef BOOL(__stdcall * SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL);
|
|
typedef DWORD(__stdcall *SYMSETOPTIONSPROC)(DWORD);
|
|
typedef BOOL(__stdcall *SYMCLEANUPPROC)(HANDLE);
|
|
typedef LPCSTR(__stdcall *UNDECORATESYMBOLNAMEPROC)(LPCSTR, LPSTR, DWORD, DWORD);
|
|
typedef BOOL(__stdcall * STACKWALKPROC)
|
|
(DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID,
|
|
PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE,
|
|
PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
|
|
typedef LPVOID(__stdcall *SYMFUNCTIONTABLEACCESSPROC)(HANDLE, DWORD);
|
|
typedef DWORD(__stdcall *SYMGETMODULEBASEPROC)(HANDLE, DWORD);
|
|
typedef BOOL(__stdcall *SYMGETSYMFROMADDRPROC)(HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL);
|
|
typedef BOOL(__stdcall *SYMGETLINEFROMADDR)(HANDLE hProcess, DWORD qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line64);
|
|
#endif
|
|
|
|
static HMODULE gImageHelpLib = NULL;
|
|
static SYMINITIALIZEPROC gSymInitialize = NULL;
|
|
static SYMSETOPTIONSPROC gSymSetOptions = NULL;
|
|
static UNDECORATESYMBOLNAMEPROC gUnDecorateSymbolName = NULL;
|
|
static SYMCLEANUPPROC gSymCleanup = NULL;
|
|
static STACKWALKPROC gStackWalk = NULL;
|
|
static SYMFUNCTIONTABLEACCESSPROC gSymFunctionTableAccess = NULL;
|
|
static SYMGETMODULEBASEPROC gSymGetModuleBase = NULL;
|
|
static SYMGETSYMFROMADDRPROC gSymGetSymFromAddr = NULL;
|
|
static SYMGETLINEFROMADDR gSymGetLineFromAddr = NULL;
|
|
static Array<CrashInfoFunc> gCrashInfoFuncs;
|
|
static StringT<0> gCrashInfo;
|
|
static bool gCrashed = false;
|
|
extern CritSect gBfpCritSect;
|
|
|
|
static EXCEPTION_POINTERS* gExceptionPointers = NULL;
|
|
static LPTOP_LEVEL_EXCEPTION_FILTER gPreviousFilter = NULL;
|
|
|
|
static bool gDebugError = false;
|
|
|
|
static bool CreateMiniDump(EXCEPTION_POINTERS* pep, const StringImpl& filePath);
|
|
|
|
static bool LoadImageHelp()
|
|
{
|
|
gImageHelpLib = LoadLibraryA("IMAGEHLP.DLL");
|
|
if (!gImageHelpLib)
|
|
return false;
|
|
|
|
gSymInitialize = (SYMINITIALIZEPROC)GetProcAddress(gImageHelpLib, "SymInitialize");
|
|
if (!gSymInitialize)
|
|
return false;
|
|
|
|
gSymSetOptions = (SYMSETOPTIONSPROC)GetProcAddress(gImageHelpLib, "SymSetOptions");
|
|
if (!gSymSetOptions)
|
|
return false;
|
|
|
|
gSymCleanup = (SYMCLEANUPPROC)GetProcAddress(gImageHelpLib, "SymCleanup");
|
|
if (!gSymCleanup)
|
|
return false;
|
|
|
|
gUnDecorateSymbolName = (UNDECORATESYMBOLNAMEPROC)GetProcAddress(gImageHelpLib, "UnDecorateSymbolName");
|
|
if (!gUnDecorateSymbolName)
|
|
return false;
|
|
|
|
gStackWalk = (STACKWALKPROC)GetProcAddress(gImageHelpLib, "StackWalk");
|
|
if (!gStackWalk)
|
|
return false;
|
|
|
|
gSymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC)GetProcAddress(gImageHelpLib, "SymFunctionTableAccess");
|
|
if (!gSymFunctionTableAccess)
|
|
return false;
|
|
|
|
gSymGetModuleBase = (SYMGETMODULEBASEPROC)GetProcAddress(gImageHelpLib, "SymGetModuleBase");
|
|
if (!gSymGetModuleBase)
|
|
return false;
|
|
|
|
gSymGetSymFromAddr = (SYMGETSYMFROMADDRPROC)GetProcAddress(gImageHelpLib, "SymGetSymFromAddr");
|
|
if (!gSymGetSymFromAddr)
|
|
return false;
|
|
|
|
gSymGetLineFromAddr = (SYMGETLINEFROMADDR)GetProcAddress(gImageHelpLib, "SymGetLineFromAddr64");
|
|
if (!gSymGetLineFromAddr)
|
|
return false;
|
|
|
|
gSymSetOptions(SYMOPT_DEFERRED_LOADS);
|
|
|
|
// Get image filename of the main executable
|
|
char filepath[MAX_PATH], *lastdir, *pPath;
|
|
DWORD filepathlen = GetModuleFileNameA(NULL, filepath, sizeof(filepath));
|
|
|
|
lastdir = strrchr(filepath, '/');
|
|
if (lastdir == NULL) lastdir = strrchr(filepath, '\\');
|
|
if (lastdir != NULL) lastdir[0] = '\0';
|
|
|
|
// Initialize the symbol table routines, supplying a pointer to the path
|
|
pPath = filepath;
|
|
if (filepath[0] == 0) pPath = NULL;
|
|
|
|
if (!gSymInitialize(GetCurrentProcess(), pPath, TRUE))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
struct
|
|
{
|
|
DWORD dwExceptionCode;
|
|
char *szMessage;
|
|
} gMsgTable[] = {
|
|
{ STATUS_SEGMENT_NOTIFICATION, "Segment Notification" },
|
|
{ STATUS_BREAKPOINT, "Breakpoint" },
|
|
{ STATUS_SINGLE_STEP, "Single step" },
|
|
{ STATUS_WAIT_0, "Wait 0" },
|
|
{ STATUS_ABANDONED_WAIT_0, "Abandoned Wait 0" },
|
|
{ STATUS_USER_APC, "User APC" },
|
|
{ STATUS_TIMEOUT, "Timeout" },
|
|
{ STATUS_PENDING, "Pending" },
|
|
{ STATUS_GUARD_PAGE_VIOLATION, "Guard Page Violation" },
|
|
{ STATUS_DATATYPE_MISALIGNMENT, "Data Type Misalignment" },
|
|
{ STATUS_ACCESS_VIOLATION, "Access Violation" },
|
|
{ STATUS_IN_PAGE_ERROR, "In Page Error" },
|
|
{ STATUS_NO_MEMORY, "No Memory" },
|
|
{ STATUS_ILLEGAL_INSTRUCTION, "Illegal Instruction" },
|
|
{ STATUS_NONCONTINUABLE_EXCEPTION, "Noncontinuable Exception" },
|
|
{ STATUS_INVALID_DISPOSITION, "Invalid Disposition" },
|
|
{ STATUS_ARRAY_BOUNDS_EXCEEDED, "Array Bounds Exceeded" },
|
|
{ STATUS_FLOAT_DENORMAL_OPERAND, "Float Denormal Operand" },
|
|
{ STATUS_FLOAT_DIVIDE_BY_ZERO, "Divide by Zero" },
|
|
{ STATUS_FLOAT_INEXACT_RESULT, "Float Inexact Result" },
|
|
{ STATUS_FLOAT_INVALID_OPERATION, "Float Invalid Operation" },
|
|
{ STATUS_FLOAT_OVERFLOW, "Float Overflow" },
|
|
{ STATUS_FLOAT_STACK_CHECK, "Float Stack Check" },
|
|
{ STATUS_FLOAT_UNDERFLOW, "Float Underflow" },
|
|
{ STATUS_INTEGER_DIVIDE_BY_ZERO, "Integer Divide by Zero" },
|
|
{ STATUS_INTEGER_OVERFLOW, "Integer Overflow" },
|
|
{ STATUS_PRIVILEGED_INSTRUCTION, "Privileged Instruction" },
|
|
{ STATUS_STACK_OVERFLOW, "Stack Overflow" },
|
|
{ STATUS_CONTROL_C_EXIT, "Ctrl+C Exit" },
|
|
{ 0xFFFFFFFF, "" }
|
|
};
|
|
|
|
static bool gUseDefaultFonts;
|
|
static HFONT gDialogFont;
|
|
static HFONT gBoldFont;
|
|
static String gErrorTitle;
|
|
static String gErrorText;
|
|
static HWND gDebugButtonWindow = NULL;
|
|
static HWND gYesButtonWindow = NULL;
|
|
static HWND gNoButtonWindow = NULL;
|
|
static bool gExiting = false;
|
|
static BfpCrashReportKind gCrashReportKind = BfpCrashReportKind_Default;
|
|
|
|
static LRESULT CALLBACK SEHWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_COMMAND:
|
|
{
|
|
HWND hwndCtl = (HWND)lParam;
|
|
if (hwndCtl == gYesButtonWindow)
|
|
{
|
|
WCHAR fileName[MAX_PATH];
|
|
fileName[0] = 0;
|
|
|
|
OPENFILENAMEW openFileName = { 0 };
|
|
openFileName.hInstance = ::GetModuleHandle(NULL);
|
|
openFileName.hwndOwner = hWnd;
|
|
openFileName.lStructSize = sizeof(OPENFILENAMEW);
|
|
openFileName.lpstrDefExt = L".dmp";
|
|
openFileName.lpstrFilter = L"Crash Dump (*.dmp)\0*.dmp\0All files (*.*)\0*.*\0\0";
|
|
openFileName.lpstrFile = fileName;
|
|
openFileName.nMaxFile = MAX_PATH;
|
|
openFileName.Flags = OFN_EXPLORER | OFN_ENABLESIZING;
|
|
openFileName.lpstrTitle = L"Save Crash Dump";
|
|
|
|
if (::GetSaveFileNameW(&openFileName))
|
|
{
|
|
CreateMiniDump(gExceptionPointers, UTF8Encode(fileName));
|
|
}
|
|
}
|
|
else if (hwndCtl == gNoButtonWindow)
|
|
{
|
|
gExiting = true;
|
|
}
|
|
else if (hwndCtl == gDebugButtonWindow)
|
|
{
|
|
gDebugError = true;
|
|
gExiting = true;
|
|
}
|
|
}
|
|
break;
|
|
case WM_CLOSE:
|
|
gExiting = true;
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
static void ShowErrorDialog(const StringImpl& errorTitle, const StringImpl& errorText)
|
|
{
|
|
HINSTANCE gHInstance = ::GetModuleHandle(NULL);
|
|
|
|
OSVERSIONINFO aVersionInfo;
|
|
aVersionInfo.dwOSVersionInfoSize = sizeof(aVersionInfo);
|
|
GetVersionEx(&aVersionInfo);
|
|
|
|
// Setting fonts on 98 causes weirdo crash things in GDI upon the second crash.
|
|
// That's no good.
|
|
gUseDefaultFonts = aVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT;
|
|
|
|
int aHeight = -MulDiv(8, 96, 72);
|
|
gDialogFont = ::CreateFontA(aHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
|
|
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
|
|
DEFAULT_PITCH | FF_DONTCARE, "Tahoma");
|
|
|
|
aHeight = -MulDiv(10, 96, 72);
|
|
gBoldFont = ::CreateFontA(aHeight, 0, 0, 0, FW_BOLD, FALSE, FALSE,
|
|
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
|
|
DEFAULT_PITCH | FF_DONTCARE, "Tahoma");
|
|
|
|
::SetCursor(::LoadCursor(NULL, IDC_ARROW));
|
|
|
|
gErrorTitle = errorTitle;
|
|
gErrorText = errorText;
|
|
|
|
|
|
WNDCLASSW wc;
|
|
wc.style = 0;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE);
|
|
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
|
|
wc.hIcon = ::LoadIcon(NULL, IDI_ERROR);
|
|
wc.hInstance = gHInstance;
|
|
wc.lpfnWndProc = SEHWindowProc;
|
|
wc.lpszClassName = L"SEHWindow";
|
|
wc.lpszMenuName = NULL;
|
|
RegisterClassW(&wc);
|
|
|
|
RECT aRect;
|
|
aRect.left = 0;
|
|
aRect.top = 0;
|
|
aRect.right = 500;
|
|
aRect.bottom = 400;
|
|
|
|
DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
|
|
|
RECT windowRect = aRect;
|
|
BOOL worked = AdjustWindowRect(&windowRect, aWindowStyle, FALSE);
|
|
|
|
HWND aHWnd = ::CreateWindowExW(0, L"SEHWindow", L"Fatal Error!",
|
|
aWindowStyle,
|
|
64, 64,
|
|
windowRect.right - windowRect.left,
|
|
windowRect.bottom - windowRect.top,
|
|
NULL,
|
|
NULL,
|
|
gHInstance,
|
|
0);
|
|
|
|
int textHeight = 30;
|
|
|
|
HWND aLabelWindow = ::CreateWindowW(L"EDIT",
|
|
L"An unexpected error has occured!",
|
|
WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY,
|
|
8, 8,
|
|
aRect.right - 8 - 8,
|
|
textHeight,
|
|
aHWnd,
|
|
NULL,
|
|
gHInstance,
|
|
0);
|
|
|
|
int aFontHeight = -MulDiv(9, 96, 72);
|
|
HFONT aBoldArialFont = CreateFontA(aFontHeight, 0, 0, 0, FW_BOLD, 0, 0,
|
|
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
|
|
DEFAULT_PITCH | FF_DONTCARE, "Arial");
|
|
|
|
if (!gUseDefaultFonts)
|
|
SendMessage(aLabelWindow, WM_SETFONT, (WPARAM)aBoldArialFont, 0);
|
|
|
|
HWND anEditWindow = CreateWindowA("EDIT", errorText.c_str(),
|
|
WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER | WS_HSCROLL | WS_VSCROLL | ES_READONLY,
|
|
8, textHeight + 8,
|
|
aRect.right - 8 - 8,
|
|
aRect.bottom - textHeight - 24 - 8 - 8 - 8,
|
|
aHWnd,
|
|
NULL,
|
|
gHInstance,
|
|
0);
|
|
|
|
aFontHeight = -MulDiv(8, 96, 72);
|
|
HFONT aCourierNewFont = CreateFontA(aFontHeight, 0, 0, 0, FW_NORMAL, 0, 0,
|
|
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
|
|
DEFAULT_PITCH | FF_DONTCARE, "Courier New");
|
|
if (!gUseDefaultFonts)
|
|
SendMessage(anEditWindow, WM_SETFONT, (WPARAM)aCourierNewFont, 0);
|
|
|
|
aWindowStyle = WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON;
|
|
|
|
//if (mApp == NULL)
|
|
//aWindowStyle |= WS_DISABLED;
|
|
|
|
//#ifdef _DEBUG
|
|
bool doDebugButton = true;
|
|
// #else
|
|
// bool doDebugButton = false;
|
|
// #endif
|
|
|
|
//bool canSubmit = mAllowSubmit && !mSubmitHost.empty();
|
|
bool canSubmit = true;
|
|
int aNumButtons = 1 + (doDebugButton ? 1 : 0) + (canSubmit ? 1 : 0);
|
|
|
|
int aButtonWidth = (aRect.right - 8 - 8 - (aNumButtons - 1) * 8) / aNumButtons;
|
|
|
|
int aCurX = 8;
|
|
|
|
if (canSubmit)
|
|
{
|
|
gYesButtonWindow = CreateWindowA("BUTTON", "Save Crash Dump...",
|
|
aWindowStyle,
|
|
aCurX, aRect.bottom - 24 - 8,
|
|
aButtonWidth,
|
|
24,
|
|
aHWnd,
|
|
NULL,
|
|
gHInstance,
|
|
0);
|
|
if (!gUseDefaultFonts)
|
|
SendMessage(gYesButtonWindow, WM_SETFONT, (WPARAM)aBoldArialFont, 0);
|
|
|
|
aCurX += aButtonWidth + 8;
|
|
}
|
|
|
|
if (doDebugButton)
|
|
{
|
|
gDebugButtonWindow = CreateWindowA("BUTTON", "Debug",
|
|
aWindowStyle,
|
|
aCurX, aRect.bottom - 24 - 8,
|
|
aButtonWidth,
|
|
24,
|
|
aHWnd,
|
|
NULL,
|
|
gHInstance,
|
|
0);
|
|
if (!gUseDefaultFonts)
|
|
SendMessage(gDebugButtonWindow, WM_SETFONT, (WPARAM)aBoldArialFont, 0);
|
|
|
|
aCurX += aButtonWidth + 8;
|
|
}
|
|
|
|
gNoButtonWindow = CreateWindowA("BUTTON", "Close Now",
|
|
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON,
|
|
aCurX, aRect.bottom - 24 - 8,
|
|
aButtonWidth,
|
|
24,
|
|
aHWnd,
|
|
NULL,
|
|
gHInstance,
|
|
0);
|
|
|
|
if (!gUseDefaultFonts)
|
|
SendMessage(gNoButtonWindow, WM_SETFONT, (WPARAM)aBoldArialFont, 0);
|
|
|
|
ShowWindow(aHWnd, SW_NORMAL);
|
|
|
|
MSG msg;
|
|
while ((GetMessage(&msg, NULL, 0, 0) > 0) && (!gExiting))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
DestroyWindow(aHWnd);
|
|
|
|
DeleteObject(gDialogFont);
|
|
DeleteObject(gBoldFont);
|
|
DeleteObject(aBoldArialFont);
|
|
DeleteObject(aCourierNewFont);
|
|
}
|
|
|
|
static bool GetLogicalAddress(void* addr, char* szModule, DWORD len, uintptr& section, uintptr& offset)
|
|
{
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
|
|
if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
|
|
return false;
|
|
|
|
uintptr hMod = (uintptr)mbi.AllocationBase;
|
|
|
|
if (hMod == NULL)
|
|
{
|
|
szModule[0] = 0;
|
|
section = 0;
|
|
offset = 0;
|
|
return false;
|
|
}
|
|
|
|
if (!GetModuleFileNameA((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)((uint8*)hMod + pDosHdr->e_lfanew);
|
|
|
|
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);
|
|
|
|
uintptr rva = (uintptr)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++)
|
|
{
|
|
uintptr sectionStart = pSection->VirtualAddress;
|
|
uintptr sectionEnd = sectionStart + BF_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!
|
|
}
|
|
|
|
static BOOL CALLBACK MyMiniDumpCallback(
|
|
PVOID pParam,
|
|
const PMINIDUMP_CALLBACK_INPUT pInput,
|
|
PMINIDUMP_CALLBACK_OUTPUT pOutput
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
// Check parameters
|
|
|
|
if (pInput == 0)
|
|
return FALSE;
|
|
|
|
if (pOutput == 0)
|
|
return FALSE;
|
|
|
|
|
|
// Process the callbacks
|
|
|
|
switch (pInput->CallbackType)
|
|
{
|
|
case IncludeModuleCallback:
|
|
{
|
|
// Include the module into the dump
|
|
bRet = TRUE;
|
|
}
|
|
break;
|
|
case IncludeThreadCallback:
|
|
{
|
|
// Include the thread into the dump
|
|
bRet = TRUE;
|
|
}
|
|
break;
|
|
case ModuleCallback:
|
|
{
|
|
// Does the module have ModuleReferencedByMemory flag set ?
|
|
|
|
if (!(pOutput->ModuleWriteFlags & ModuleReferencedByMemory))
|
|
{
|
|
// No, it does not - exclude it
|
|
|
|
//wprintf(L"Excluding module: %s \n", pInput->Module.FullPath);
|
|
|
|
pOutput->ModuleWriteFlags &= (~ModuleWriteModule);
|
|
}
|
|
|
|
bRet = TRUE;
|
|
}
|
|
break;
|
|
case ThreadCallback:
|
|
{
|
|
// Include all thread information into the minidump
|
|
bRet = TRUE;
|
|
}
|
|
break;
|
|
case ThreadExCallback:
|
|
{
|
|
// Include this information
|
|
bRet = TRUE;
|
|
}
|
|
break;
|
|
case MemoryCallback:
|
|
{
|
|
// We do not include any information here -> return FALSE
|
|
bRet = FALSE;
|
|
}
|
|
break;
|
|
case CancelCallback:
|
|
break;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
static bool CreateMiniDump(EXCEPTION_POINTERS* pep, const StringImpl& filePath)
|
|
{
|
|
// Open the file
|
|
typedef BOOL(*PDUMPFN)(
|
|
HANDLE hProcess,
|
|
DWORD ProcessId,
|
|
HANDLE hFile,
|
|
MINIDUMP_TYPE DumpType,
|
|
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
|
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
|
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
|
);
|
|
|
|
|
|
HANDLE hFile = CreateFileW(UTF8Decode(filePath).c_str(), GENERIC_READ | GENERIC_WRITE,
|
|
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
HMODULE h = ::LoadLibrary(L"DbgHelp.dll");
|
|
PDUMPFN pFn = (PDUMPFN)GetProcAddress(h, "MiniDumpWriteDump");
|
|
if (pFn == NULL)
|
|
return false;
|
|
|
|
if ((hFile == NULL) || (hFile == INVALID_HANDLE_VALUE))
|
|
return false;
|
|
|
|
// Create the minidump
|
|
|
|
MINIDUMP_EXCEPTION_INFORMATION mdei;
|
|
|
|
mdei.ThreadId = GetCurrentThreadId();
|
|
mdei.ExceptionPointers = pep;
|
|
mdei.ClientPointers = TRUE;
|
|
|
|
MINIDUMP_CALLBACK_INFORMATION mci;
|
|
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback;
|
|
mci.CallbackParam = 0;
|
|
|
|
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory);
|
|
|
|
BOOL rv = (*pFn)(GetCurrentProcess(), GetCurrentProcessId(),
|
|
hFile, mdt, (pep != 0) ? &mdei : 0, 0, &mci);
|
|
|
|
// Close the file
|
|
CloseHandle(hFile);
|
|
return true;
|
|
}
|
|
|
|
//
|
|
//LONG WINAPI MyUnhandledExceptionFilter(
|
|
// struct _EXCEPTION_POINTERS *ExceptionInfo
|
|
//)
|
|
//{
|
|
// CreateMiniDump(ExceptionInfo);
|
|
// return EXCEPTION_EXECUTE_HANDLER;
|
|
//}
|
|
|
|
static String ImageHelpWalk(PCONTEXT theContext, int theSkipCount)
|
|
{
|
|
//char aBuffer[2048];
|
|
String aDebugDump;
|
|
|
|
STACKFRAME sf;
|
|
memset(&sf, 0, sizeof(sf));
|
|
|
|
// Initialize the STACKFRAME structure for the first call. This is only
|
|
// necessary for Intel CPUs, and isn't mentioned in the documentation.
|
|
#ifdef BF64
|
|
sf.AddrPC.Offset = theContext->Rip;
|
|
sf.AddrPC.Mode = AddrModeFlat;
|
|
sf.AddrStack.Offset = theContext->Rsp;
|
|
sf.AddrStack.Mode = AddrModeFlat;
|
|
sf.AddrFrame.Offset = theContext->Rbp;
|
|
sf.AddrFrame.Mode = AddrModeFlat;
|
|
#else
|
|
sf.AddrPC.Offset = theContext->Eip;
|
|
sf.AddrPC.Mode = AddrModeFlat;
|
|
sf.AddrStack.Offset = theContext->Esp;
|
|
sf.AddrStack.Mode = AddrModeFlat;
|
|
sf.AddrFrame.Offset = theContext->Ebp;
|
|
sf.AddrFrame.Mode = AddrModeFlat;
|
|
#endif
|
|
|
|
int aLevelCount = 0;
|
|
|
|
CONTEXT ctx = *theContext;
|
|
|
|
struct ModuleInfo
|
|
{
|
|
|
|
};
|
|
|
|
Dictionary<String, ModuleInfo> moduleInfoMap;
|
|
|
|
for (;;)
|
|
{
|
|
#ifdef BF64
|
|
DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
|
|
PCONTEXT ctxPtr = &ctx;
|
|
#else
|
|
DWORD machineType = IMAGE_FILE_MACHINE_I386;
|
|
PCONTEXT ctxPtr = NULL;
|
|
#endif
|
|
|
|
if (!gStackWalk(machineType, GetCurrentProcess(), GetCurrentThread(),
|
|
&sf, ctxPtr, NULL, gSymFunctionTableAccess, gSymGetModuleBase, 0))
|
|
{
|
|
//DWORD lastErr = GetLastError();
|
|
//sprintf(aBuffer, "StackWalk failed (error %d)\r\n", lastErr);
|
|
//aDebugDump += aBuffer;
|
|
break;
|
|
}
|
|
|
|
if ((aLevelCount > 0) && ((sf.AddrFrame.Offset == 0) || (sf.AddrPC.Offset == 0)))
|
|
break;
|
|
|
|
if (theSkipCount > 0)
|
|
{
|
|
theSkipCount--;
|
|
continue;
|
|
}
|
|
|
|
BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
|
|
PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
|
|
pSymbol->SizeOfStruct = sizeof(symbolBuffer);
|
|
pSymbol->MaxNameLength = 512;
|
|
|
|
// Displacement of the input address, relative to the start of the symbol
|
|
#ifdef BF64
|
|
DWORD64 symDisplacement = 0;
|
|
#else
|
|
DWORD symDisplacement = 0;
|
|
#endif
|
|
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
|
|
bool forceFail = false;
|
|
if ((gSymGetSymFromAddr(hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol)) && (!forceFail))
|
|
{
|
|
char aUDName[256];
|
|
gUnDecorateSymbolName(pSymbol->Name, aUDName, 256,
|
|
UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ALLOCATION_LANGUAGE |
|
|
UNDNAME_NO_MS_THISTYPE | UNDNAME_NO_ACCESS_SPECIFIERS |
|
|
UNDNAME_NO_THISTYPE | UNDNAME_NO_MEMBER_TYPE |
|
|
UNDNAME_NO_RETURN_UDT_MODEL | UNDNAME_NO_THROW_SIGNATURES |
|
|
UNDNAME_NO_SPECIAL_SYMS);
|
|
|
|
String dispName = aUDName;
|
|
|
|
if (dispName.StartsWith("_bf::"))
|
|
{
|
|
dispName.Remove(0, 5);
|
|
dispName.Replace("::", ".");
|
|
}
|
|
|
|
aDebugDump += StrFormat("%@ %@ %hs+%X\r\n",
|
|
sf.AddrFrame.Offset, sf.AddrPC.Offset, dispName.c_str(), symDisplacement);
|
|
|
|
DWORD displacement = 0;
|
|
#ifdef BF64
|
|
IMAGEHLP_LINE64 lineInfo = { 0 };
|
|
lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
#else
|
|
IMAGEHLP_LINE lineInfo = { 0 };
|
|
lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE);
|
|
#endif
|
|
if (gSymGetLineFromAddr(hProcess, sf.AddrPC.Offset, &displacement, &lineInfo))
|
|
{
|
|
aDebugDump += StrFormat(" at %s:%d\r\n", lineInfo.FileName, lineInfo.LineNumber);
|
|
}
|
|
}
|
|
else // No symbol found. Print out the logical address instead.
|
|
{
|
|
char szModule[MAX_PATH];
|
|
szModule[0] = 0;
|
|
uintptr section = 0, offset = 0;
|
|
|
|
GetLogicalAddress((PVOID)sf.AddrPC.Offset, szModule, sizeof(szModule), section, offset);
|
|
|
|
// ModuleInfo* moduleInfo = NULL;
|
|
// if (moduleInfoMap.TryAdd(szModule, NULL, &moduleInfo))
|
|
// {
|
|
//
|
|
// }
|
|
|
|
aDebugDump += StrFormat("%@ %@ %04X:%@ %s\r\n", sf.AddrFrame.Offset, sf.AddrPC.Offset, section, offset, GetFileName(szModule).c_str());
|
|
}
|
|
|
|
aDebugDump += StrFormat(" Params: %@ %@ %@ %@\r\n", sf.Params[0], sf.Params[1], sf.Params[2], sf.Params[3]);
|
|
aDebugDump += "\r\n";
|
|
|
|
aLevelCount++;
|
|
}
|
|
|
|
return aDebugDump;
|
|
}
|
|
|
|
static String GetSysInfo()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
static String GetVersion(const StringImpl& fileName)
|
|
{
|
|
String verStr = "";
|
|
|
|
bool bReturn = false;
|
|
DWORD dwReserved = 0;
|
|
DWORD dwBufferSize = GetFileVersionInfoSizeA(fileName.c_str(), &dwReserved);
|
|
|
|
struct TRANSARRAY
|
|
{
|
|
WORD wLanguageID;
|
|
WORD wCharacterSet;
|
|
};
|
|
|
|
if (dwBufferSize > 0)
|
|
{
|
|
LPVOID pBuffer = (void*)malloc(dwBufferSize);
|
|
|
|
if (pBuffer != (void*)NULL)
|
|
{
|
|
UINT nInfoSize = 0,
|
|
nFixedLength = 0;
|
|
LPSTR lpVersion = NULL;
|
|
void* lpFixedPointer;
|
|
TRANSARRAY* lpTransArray;
|
|
|
|
GetFileVersionInfoA(fileName.c_str(),
|
|
dwReserved,
|
|
dwBufferSize,
|
|
pBuffer);
|
|
|
|
VerQueryValueA(pBuffer,
|
|
"\\VarFileInfo\\Translation",
|
|
&lpFixedPointer,
|
|
&nFixedLength);
|
|
lpTransArray = (TRANSARRAY*)lpFixedPointer;
|
|
|
|
String langStr = StrFormat(
|
|
"\\StringFileInfo\\%04x%04x\\FileVersion",
|
|
lpTransArray[0].wLanguageID,
|
|
lpTransArray[0].wCharacterSet);
|
|
VerQueryValueA(pBuffer,
|
|
langStr.c_str(),
|
|
(void**)&lpVersion,
|
|
&nInfoSize);
|
|
if (nInfoSize != 0)
|
|
{
|
|
verStr += "File Version: ";
|
|
verStr += lpVersion;
|
|
verStr += "\r\n";
|
|
}
|
|
|
|
langStr = StrFormat(
|
|
"\\StringFileInfo\\%04x%04x\\ProductVersion",
|
|
lpTransArray[0].wLanguageID,
|
|
lpTransArray[0].wCharacterSet);
|
|
VerQueryValueA(pBuffer,
|
|
langStr.c_str(),
|
|
(void**)&lpVersion,
|
|
&nInfoSize);
|
|
if (nInfoSize != 0)
|
|
{
|
|
verStr += "Product Version: ";
|
|
verStr += lpVersion;
|
|
verStr += "\r\n";
|
|
}
|
|
|
|
free(pBuffer);
|
|
}
|
|
}
|
|
|
|
return verStr;
|
|
}
|
|
|
|
static void DoHandleDebugEvent(LPEXCEPTION_POINTERS lpEP)
|
|
{
|
|
if (gCrashed)
|
|
return;
|
|
gCrashed = true;
|
|
|
|
HMODULE hMod = GetModuleHandleA(NULL);
|
|
|
|
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
|
|
PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)((uint8*)hMod + pDosHdr->e_lfanew);
|
|
bool isCLI = pNtHdr->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI;
|
|
if (gCrashReportKind == BfpCrashReportKind_GUI)
|
|
isCLI = false;
|
|
else if ((gCrashReportKind == BfpCrashReportKind_Console) || (gCrashReportKind == BfpCrashReportKind_PrintOnly))
|
|
isCLI = true;
|
|
|
|
bool hasImageHelp = LoadImageHelp();
|
|
|
|
String anErrorTitle;
|
|
String aDebugDump;
|
|
|
|
char aBuffer[2048];
|
|
|
|
if (isCLI)
|
|
aDebugDump += "**** FATAL APPLICATION ERROR ****\n";
|
|
|
|
WCHAR exeFilePathW[MAX_PATH];
|
|
exeFilePathW[0] = 0;
|
|
::GetModuleFileNameW(hMod, exeFilePathW, MAX_PATH);
|
|
String exeFilePath = UTF8Encode(exeFilePathW);
|
|
String exeDir = GetFileDir(exeFilePath);
|
|
String crashPath = exeDir + "\\CrashDumps";
|
|
if (BfpDirectory_Exists(crashPath.c_str()))
|
|
{
|
|
crashPath += "\\" + GetFileName(exeFilePath);
|
|
crashPath.RemoveToEnd((int)crashPath.length() - 4);
|
|
crashPath += "_";
|
|
|
|
time_t curTime = time(NULL);
|
|
auto time_info = localtime(&curTime);
|
|
crashPath += StrFormat("%4d%02d%02d_%02d%02d%02d",
|
|
time_info->tm_year + 1900, time_info->tm_mon + 1, time_info->tm_mday,
|
|
time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
|
|
crashPath += ".dmp";
|
|
|
|
if (CreateMiniDump(lpEP, crashPath))
|
|
{
|
|
aDebugDump += StrFormat("Crash minidump saved as %s\n", crashPath.c_str());
|
|
}
|
|
}
|
|
|
|
for (auto func : gCrashInfoFuncs)
|
|
func();
|
|
|
|
aDebugDump.Append(gCrashInfo);
|
|
|
|
for (int i = 0; i < (int)aDebugDump.length(); i++)
|
|
{
|
|
char c = aDebugDump[i];
|
|
if (c == '\n')
|
|
{
|
|
aDebugDump.Insert(i, '\r');
|
|
i++;
|
|
}
|
|
else if (c == '\t')
|
|
{
|
|
aDebugDump[i] = ' ';
|
|
aDebugDump.Insert(i, ' ');
|
|
}
|
|
}
|
|
|
|
// aDebugDump.Replace("\n", "\r\n");
|
|
// aDebugDump.Replace("\t", " ");
|
|
|
|
if (!aDebugDump.IsEmpty())
|
|
{
|
|
if (!aDebugDump.EndsWith("\n"))
|
|
aDebugDump += "\r\n";
|
|
|
|
aDebugDump += "\r\n";
|
|
}
|
|
|
|
///////////////////////////
|
|
// first name the exception
|
|
char *szName = NULL;
|
|
for (int i = 0; gMsgTable[i].dwExceptionCode != 0xFFFFFFFF; i++)
|
|
{
|
|
if (gMsgTable[i].dwExceptionCode == lpEP->ExceptionRecord->ExceptionCode)
|
|
{
|
|
szName = gMsgTable[i].szMessage;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (szName != NULL)
|
|
{
|
|
aDebugDump += StrFormat("Exception: %s (code 0x%x) at address %@ in thread %X\r\n",
|
|
szName, lpEP->ExceptionRecord->ExceptionCode,
|
|
lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId());
|
|
}
|
|
else
|
|
{
|
|
aDebugDump += StrFormat("Unknown exception: (code 0x%x) at address %@ in thread %X\r\n",
|
|
lpEP->ExceptionRecord->ExceptionCode,
|
|
lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId());
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// Get logical address of the module where exception occurs
|
|
uintptr section, offset;
|
|
GetLogicalAddress(lpEP->ExceptionRecord->ExceptionAddress, aBuffer, sizeof(aBuffer), section, offset);
|
|
|
|
CHAR path[MAX_PATH];
|
|
GetModuleFileNameA(NULL, path, MAX_PATH);
|
|
aDebugDump += "Executable: ";
|
|
aDebugDump += path;
|
|
aDebugDump += "\r\n";
|
|
aDebugDump += GetVersion(path);
|
|
aDebugDump += "Module: " + GetFileName(aBuffer) + "\r\n";
|
|
|
|
aDebugDump += StrFormat("Logical Address: %04X:%@\r\n", section, offset);
|
|
|
|
aDebugDump += "\r\n";
|
|
|
|
anErrorTitle = StrFormat("Exception at %04X:%08X", section, offset);
|
|
|
|
String aWalkString;
|
|
|
|
if (hasImageHelp)
|
|
aWalkString = ImageHelpWalk(lpEP->ContextRecord, 0);
|
|
|
|
/*if (aWalkString.length() == 0)
|
|
aWalkString = IntelWalk(lpEP->ContextRecord, 0);*/
|
|
|
|
aDebugDump += aWalkString;
|
|
|
|
aDebugDump += "\r\n";
|
|
|
|
#ifdef BF64
|
|
aDebugDump += StrFormat("RAX:%@ RBX:%@ RCX:%@ RDX:%@ RSI:%@ RDI:%@\r\n",
|
|
lpEP->ContextRecord->Rax, lpEP->ContextRecord->Rbx, lpEP->ContextRecord->Rcx, lpEP->ContextRecord->Rdx, lpEP->ContextRecord->Rsi, lpEP->ContextRecord->Rdi);
|
|
aDebugDump += StrFormat("R8:%@ R9:%@ R10:%@ R11:%@\r\nR12:%@ R13:%@ R14:%@ R15:%@\r\n",
|
|
lpEP->ContextRecord->R8, lpEP->ContextRecord->R9, lpEP->ContextRecord->R10, lpEP->ContextRecord->R11, lpEP->ContextRecord->R12, lpEP->ContextRecord->R13, lpEP->ContextRecord->R14, lpEP->ContextRecord->R15);
|
|
aDebugDump += StrFormat("EIP:%@ ESP:%@ EBP:%@\r\n", lpEP->ContextRecord->Rip, lpEP->ContextRecord->Rsp, lpEP->ContextRecord->Rbp);
|
|
aDebugDump += StrFormat("CS:%04X SS:%04X DS:%04X ES:%04X FS:%04X GS:%04X\r\n", lpEP->ContextRecord->SegCs, lpEP->ContextRecord->SegSs, lpEP->ContextRecord->SegDs, lpEP->ContextRecord->SegEs, lpEP->ContextRecord->SegFs, lpEP->ContextRecord->SegGs);
|
|
aDebugDump += StrFormat("Flags:%@\r\n", lpEP->ContextRecord->EFlags);
|
|
#else
|
|
aDebugDump += StrFormat("EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X\r\n",
|
|
lpEP->ContextRecord->Eax, lpEP->ContextRecord->Ebx, lpEP->ContextRecord->Ecx, lpEP->ContextRecord->Edx, lpEP->ContextRecord->Esi, lpEP->ContextRecord->Edi);
|
|
aDebugDump += StrFormat("EIP:%08X ESP:%08X EBP:%08X\r\n", lpEP->ContextRecord->Eip, lpEP->ContextRecord->Esp, lpEP->ContextRecord->Ebp);
|
|
aDebugDump += StrFormat("CS:%04X SS:%04X DS:%04X ES:%04X FS:%04X GS:%04X\r\n", lpEP->ContextRecord->SegCs, lpEP->ContextRecord->SegSs, lpEP->ContextRecord->SegDs, lpEP->ContextRecord->SegEs, lpEP->ContextRecord->SegFs, lpEP->ContextRecord->SegGs);
|
|
aDebugDump += StrFormat("Flags:%08X\r\n", lpEP->ContextRecord->EFlags);
|
|
#endif
|
|
|
|
aDebugDump += "\r\n";
|
|
aDebugDump += GetSysInfo();
|
|
|
|
/*if (mApp != NULL)
|
|
{
|
|
String aGameSEHInfo = mApp->GetGameSEHInfo();
|
|
if (aGameSEHInfo.length() > 0)
|
|
{
|
|
aDebugDump += "\r\n";
|
|
aDebugDump += aGameSEHInfo;
|
|
}
|
|
|
|
mApp->CopyToClipboard(aDebugDump);
|
|
}
|
|
|
|
if (hasImageHelp)
|
|
GetSymbolsFromMapFile(aDebugDump);*/
|
|
|
|
if (isCLI)
|
|
{
|
|
aDebugDump += "\n";
|
|
//fwrite(aDebugDump.c_str(), 1, aDebugDump.length(), stderr);
|
|
//fflush(stderr);
|
|
DWORD bytesWritten;
|
|
::WriteFile(::GetStdHandle(STD_ERROR_HANDLE), aDebugDump.c_str(), (DWORD)aDebugDump.length(), &bytesWritten, NULL);
|
|
}
|
|
else
|
|
ShowErrorDialog(anErrorTitle, aDebugDump);
|
|
}
|
|
|
|
CrashCatcher::CrashCatcher()
|
|
{
|
|
|
|
}
|
|
|
|
static long __stdcall SEHFilter(LPEXCEPTION_POINTERS lpExceptPtr)
|
|
{
|
|
//OutputDebugStrF("SEH Filter! CraskReportKind:%d\n", gCrashReportKind);
|
|
|
|
if (gCrashReportKind == BfpCrashReportKind_None)
|
|
{
|
|
OutputDebugStrF("Silent Exiting\n");
|
|
::TerminateProcess(GetCurrentProcess(), lpExceptPtr->ExceptionRecord->ExceptionCode);
|
|
}
|
|
|
|
AutoCrit autoCrit(gBfpCritSect);
|
|
//::ExitProcess();
|
|
//quick_exit(1);
|
|
|
|
if (!gCrashed)
|
|
{
|
|
gExceptionPointers = lpExceptPtr;
|
|
//CreateMiniDump(lpExceptPtr);
|
|
DoHandleDebugEvent(lpExceptPtr);
|
|
}
|
|
|
|
//if (!gDebugError)
|
|
//SetErrorMode(SEM_NOGPFAULTERRORBOX);
|
|
|
|
if (gCrashReportKind == BfpCrashReportKind_PrintOnly)
|
|
{
|
|
::TerminateProcess(GetCurrentProcess(), lpExceptPtr->ExceptionRecord->ExceptionCode);
|
|
}
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
void CrashCatcher::Init()
|
|
{
|
|
gPreviousFilter = SetUnhandledExceptionFilter(SEHFilter);
|
|
}
|
|
|
|
void CrashCatcher::Test()
|
|
{
|
|
__try
|
|
{
|
|
// all of code normally inside of main or WinMain here...
|
|
int a = 123;
|
|
int b = 0;
|
|
a /= b;
|
|
}
|
|
__except (SEHFilter(GetExceptionInformation()))
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void CrashCatcher::AddCrashInfoFunc(CrashInfoFunc crashInfoFunc)
|
|
{
|
|
AutoCrit autoCrit(gBfpCritSect);
|
|
gCrashInfoFuncs.Add(crashInfoFunc);
|
|
}
|
|
|
|
void CrashCatcher::AddInfo(const StringImpl& str)
|
|
{
|
|
AutoCrit autoCrit(gBfpCritSect);
|
|
gCrashInfo.Append(str);
|
|
}
|
|
|
|
void CrashCatcher::Crash(const StringImpl& str)
|
|
{
|
|
OutputDebugStrF("CrashCatcher::Crash\n");
|
|
|
|
gBfpCritSect.Lock();
|
|
gCrashInfo.Append(str);
|
|
|
|
if (gPreviousFilter == NULL)
|
|
{
|
|
// A little late, but install handler now so we can catch this crash
|
|
Init();
|
|
}
|
|
|
|
gBfpCritSect.Unlock();
|
|
|
|
|
|
__debugbreak();
|
|
|
|
// When we catch the exception information like this, it displays the dump correctly but
|
|
// the minidump doesn't contain a valid callstack, so we need to rely on SetUnhandledExceptionFilter
|
|
/*__try
|
|
{
|
|
::MessageBoxA(NULL, "A", "B", MB_ICONERROR);
|
|
__debugbreak();
|
|
}
|
|
__except (SEHFilter(GetExceptionInformation()))
|
|
{
|
|
|
|
}*/
|
|
|
|
exit(1);
|
|
}
|
|
|
|
void CrashCatcher::SetCrashReportKind(BfpCrashReportKind crashReportKind)
|
|
{
|
|
gCrashReportKind = crashReportKind;
|
|
}
|