1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00
Beef/BeefySysLib/platform/win/CrashCatcher.cpp

1294 lines
35 KiB
C++
Raw Normal View History

2019-08-23 11:56:54 -07:00
#include "CrashCatcher.h"
#include "../util/Dictionary.h"
2019-09-21 14:54:02 -07:00
#include <commdlg.h>
#include <time.h>
2020-09-17 14:15:38 -07:00
#include <shellapi.h>
2019-08-23 11:56:54 -07:00
USING_NS_BF;
2019-09-12 09:47:19 -07:00
#pragma comment(lib, "version.lib")
2019-09-21 16:01:55 -07:00
#pragma comment(lib, "comdlg32.lib")
2019-09-12 09:47:19 -07:00
2019-08-23 11:56:54 -07:00
#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;
2019-09-21 14:54:02 -07:00
static bool CreateMiniDump(EXCEPTION_POINTERS* pep, const StringImpl& filePath);
2019-08-23 11:56:54 -07:00
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;
}
static HWND gDebugButtonWindow = NULL;
static HWND gYesButtonWindow = NULL;
static HWND gNoButtonWindow = NULL;
static bool gExiting = false;
static LRESULT CALLBACK SEHWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
2019-08-23 11:56:54 -07:00
switch (uMsg)
{
case WM_COMMAND:
{
HWND hwndCtl = (HWND)lParam;
if (hwndCtl == gYesButtonWindow)
{
2019-09-21 14:54:02 -07:00
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(CrashCatcher::Get()->mExceptionPointers, UTF8Encode(fileName));
}
2019-08-23 11:56:54 -07:00
}
else if (hwndCtl == gNoButtonWindow)
{
2020-09-17 14:15:38 -07:00
if (!CrashCatcher::Get()->mRelaunchCmd.IsEmpty())
{
SHELLEXECUTEINFOW shellExecuteInfo = { 0 };
shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
shellExecuteInfo.nShow = SW_SHOWNORMAL;
2020-09-17 14:15:38 -07:00
String cmd = CrashCatcher::Get()->mRelaunchCmd;
String file;
2020-09-17 14:15:38 -07:00
bool nameQuoted = cmd[0] == '\"';
2020-09-17 14:15:38 -07:00
int i;
for (i = (nameQuoted ? 1 : 0); cmd[i] != 0; i++)
{
wchar_t c = cmd[i];
if (((nameQuoted) && (c == '"')) ||
((!nameQuoted) && (c == ' ')))
{
i++;
break;
}
file += cmd[i];
}
const char* useParamsPtr = cmd.c_str();
useParamsPtr += i;
while (*useParamsPtr == L' ')
useParamsPtr++;
auto fileW = UTF8Decode(file);
shellExecuteInfo.lpFile = fileW.c_str();
2020-09-17 14:15:38 -07:00
auto paramsW = UTF8Decode(useParamsPtr);
shellExecuteInfo.lpParameters = paramsW.c_str();
BOOL success = ::ShellExecuteExW(&shellExecuteInfo);
2020-09-17 14:15:38 -07:00
}
CrashCatcher::Get()->mCloseRequested = true;
2019-08-23 11:56:54 -07:00
gExiting = true;
}
else if (hwndCtl == gDebugButtonWindow)
{
CrashCatcher::Get()->mDebugError = true;
2019-08-23 11:56:54 -07:00
gExiting = true;
}
}
break;
case WM_CLOSE:
2020-09-17 14:15:38 -07:00
CrashCatcher::Get()->mCloseRequested = true;
2019-08-23 11:56:54 -07:00
gExiting = true;
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
2020-09-17 14:15:38 -07:00
static void ShowErrorDialog(const StringImpl& errorTitle, const StringImpl& errorText, const StringImpl& relaunchCmd)
2019-08-23 11:56:54 -07:00
{
bool gUseDefaultFonts;
2019-08-23 11:56:54 -07:00
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.
2019-08-23 11:56:54 -07:00
gUseDefaultFonts = aVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT;
int aHeight = -MulDiv(8, 96, 72);
HFONT gDialogFont = ::CreateFontA(aHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
2019-08-23 11:56:54 -07:00
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Tahoma");
aHeight = -MulDiv(10, 96, 72);
HFONT gBoldFont = ::CreateFontA(aHeight, 0, 0, 0, FW_BOLD, FALSE, FALSE,
2019-08-23 11:56:54 -07:00
false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Tahoma");
::SetCursor(::LoadCursor(NULL, IDC_ARROW));
String gErrorTitle = errorTitle;
String gErrorText = errorText;
2019-08-23 11:56:54 -07:00
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);
2019-08-23 11:56:54 -07:00
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);
2019-08-23 11:56:54 -07:00
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();
2019-09-21 14:54:02 -07:00
bool canSubmit = true;
2019-08-23 11:56:54 -07:00
int aNumButtons = 1 + (doDebugButton ? 1 : 0) + (canSubmit ? 1 : 0);
int aButtonWidth = (aRect.right - 8 - 8 - (aNumButtons - 1) * 8) / aNumButtons;
int aCurX = 8;
if (canSubmit)
{
2019-09-21 14:54:02 -07:00
gYesButtonWindow = CreateWindowA("BUTTON", "Save Crash Dump...",
2019-08-23 11:56:54 -07:00
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;
}
2020-09-17 14:15:38 -07:00
gNoButtonWindow = CreateWindowA("BUTTON", relaunchCmd.IsEmpty() ? "Close Now" : "Relaunch",
2019-08-23 11:56:54 -07:00
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;
2019-08-23 11:56:54 -07:00
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
2019-08-23 11:56:54 -07:00
if (pInput == 0)
return FALSE;
if (pOutput == 0)
return FALSE;
// Process the callbacks
2019-08-23 11:56:54 -07:00
switch (pInput->CallbackType)
{
case IncludeModuleCallback:
{
// Include the module into the dump
2019-08-23 11:56:54 -07:00
bRet = TRUE;
}
break;
case IncludeThreadCallback:
{
// Include the thread into the dump
2019-08-23 11:56:54 -07:00
bRet = TRUE;
}
break;
case ModuleCallback:
{
// Does the module have ModuleReferencedByMemory flag set ?
2019-08-23 11:56:54 -07:00
if (!(pOutput->ModuleWriteFlags & ModuleReferencedByMemory))
{
// No, it does not - exclude it
2019-08-23 11:56:54 -07:00
//wprintf(L"Excluding module: %s \n", pInput->Module.FullPath);
pOutput->ModuleWriteFlags &= (~ModuleWriteModule);
}
bRet = TRUE;
}
break;
case ThreadCallback:
{
// Include all thread information into the minidump
2019-08-23 11:56:54 -07:00
bRet = TRUE;
}
break;
case ThreadExCallback:
{
// Include this information
2019-08-23 11:56:54 -07:00
bRet = TRUE;
}
break;
case MemoryCallback:
{
// We do not include any information here -> return FALSE
2019-08-23 11:56:54 -07:00
bRet = FALSE;
}
break;
case CancelCallback:
break;
}
return bRet;
}
2019-09-21 14:54:02 -07:00
static bool CreateMiniDump(EXCEPTION_POINTERS* pep, const StringImpl& filePath)
2019-08-23 11:56:54 -07:00
{
// Open the file
2019-08-23 11:56:54 -07:00
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
);
2019-09-21 14:54:02 -07:00
HANDLE hFile = CreateFileW(UTF8Decode(filePath).c_str(), GENERIC_READ | GENERIC_WRITE,
2019-08-23 11:56:54 -07:00
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
HMODULE h = ::LoadLibrary(L"DbgHelp.dll");
PDUMPFN pFn = (PDUMPFN)GetProcAddress(h, "MiniDumpWriteDump");
2019-09-21 14:54:02 -07:00
if (pFn == NULL)
return false;
2019-08-23 11:56:54 -07:00
2019-09-21 14:54:02 -07:00
if ((hFile == NULL) || (hFile == INVALID_HANDLE_VALUE))
return false;
// Create the minidump
2019-08-23 11:56:54 -07:00
2019-09-21 14:54:02 -07:00
MINIDUMP_EXCEPTION_INFORMATION mdei;
2019-08-23 11:56:54 -07:00
2019-09-21 14:54:02 -07:00
mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pep;
mdei.ClientPointers = TRUE;
2019-08-23 11:56:54 -07:00
2019-09-21 14:54:02 -07:00
MINIDUMP_CALLBACK_INFORMATION mci;
mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback;
mci.CallbackParam = 0;
2019-08-23 11:56:54 -07:00
2019-09-21 14:54:02 -07:00
MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory);
2019-08-23 11:56:54 -07:00
2020-05-29 16:10:47 -07:00
CrashCatcher* crashCatcher = CrashCatcher::Get();
MINIDUMP_USER_STREAM user_info_stream = {
0xBEEF00,
(ULONG)crashCatcher->mCrashInfo.length(),
(void*)crashCatcher->mCrashInfo.c_str()
};
MINIDUMP_USER_STREAM_INFORMATION user_stream_info = {
1,
&user_info_stream
};
2019-09-21 14:54:02 -07:00
BOOL rv = (*pFn)(GetCurrentProcess(), GetCurrentProcessId(),
2020-05-29 16:10:47 -07:00
hFile, mdt, (pep != 0) ? &mdei : 0, &user_stream_info, &mci);
// Close the file
CloseHandle(hFile);
2019-09-21 14:54:02 -07:00
return true;
2019-08-23 11:56:54 -07:00
}
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
2019-08-23 11:56:54 -07:00
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;
2019-08-23 11:56:54 -07:00
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;
}
2019-08-23 11:56:54 -07:00
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;
2019-08-23 11:56:54 -07:00
#else
DWORD symDisplacement = 0;
#endif
HANDLE hProcess = GetCurrentProcess();
2019-09-26 08:25:54 -07:00
char szModule[MAX_PATH];
szModule[0] = 0;
uintptr section = 0, offset = 0;
GetLogicalAddress((PVOID)sf.AddrPC.Offset, szModule, sizeof(szModule), section, offset);
bool forceFail = false;
if ((gSymGetSymFromAddr(hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol)) && (!forceFail))
2019-08-23 11:56:54 -07:00
{
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);
2019-09-12 09:47:19 -07:00
String dispName = aUDName;
if (dispName.StartsWith("_bf::"))
{
dispName.Remove(0, 5);
dispName.Replace("::", ".");
}
2019-09-26 08:25:54 -07:00
aDebugDump += StrFormat("%@ %@ %s %hs+%X\r\n",
sf.AddrFrame.Offset, sf.AddrPC.Offset, GetFileName(szModule).c_str(), dispName.c_str(), symDisplacement);
2019-08-23 11:56:54 -07:00
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
2019-08-23 11:56:54 -07:00
if (gSymGetLineFromAddr(hProcess, sf.AddrPC.Offset, &displacement, &lineInfo))
{
2019-08-23 11:56:54 -07:00
aDebugDump += StrFormat(" at %s:%d\r\n", lineInfo.FileName, lineInfo.LineNumber);
}
}
else // No symbol found. Print out the logical address instead.
{
2019-09-22 09:34:07 -07:00
// ModuleInfo* moduleInfo = NULL;
// if (moduleInfoMap.TryAdd(szModule, NULL, &moduleInfo))
// {
//
2019-09-22 09:34:07 -07:00
// }
2019-08-23 11:56:54 -07:00
aDebugDump += StrFormat("%@ %@ %04X:%@ %s\r\n", sf.AddrFrame.Offset, sf.AddrPC.Offset, section, offset, GetFileName(szModule).c_str());
}
2019-08-23 11:56:54 -07:00
aDebugDump += StrFormat(" Params: %@ %@ %@ %@\r\n", sf.Params[0], sf.Params[1], sf.Params[2], sf.Params[3]);
2019-08-23 11:56:54 -07:00
aDebugDump += "\r\n";
aLevelCount++;
}
return aDebugDump;
}
static String GetSysInfo()
{
return "";
}
2019-09-12 09:47:19 -07:00
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;
2019-09-12 09:47:19 -07:00
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;
}
2019-08-23 11:56:54 -07:00
static void DoHandleDebugEvent(LPEXCEPTION_POINTERS lpEP)
{
2020-09-17 14:15:38 -07:00
auto crashCatcher = CrashCatcher::Get();
if (crashCatcher->mCrashed)
2019-08-23 11:56:54 -07:00
return;
2020-09-17 14:15:38 -07:00
crashCatcher->mCrashed = true;
2019-08-23 11:56:54 -07:00
HMODULE hMod = GetModuleHandleA(NULL);
2019-08-23 11:56:54 -07:00
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 (CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_GUI)
2019-08-23 11:56:54 -07:00
isCLI = false;
else if ((CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_Console) || (CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_PrintOnly))
2019-08-23 11:56:54 -07:00
isCLI = true;
2019-08-23 11:56:54 -07:00
bool hasImageHelp = LoadImageHelp();
String anErrorTitle;
String aDebugDump;
2020-05-29 16:10:47 -07:00
String& crashInfo = CrashCatcher::Get()->mCrashInfo;
2019-08-23 11:56:54 -07:00
char aBuffer[2048];
if (isCLI)
aDebugDump += "**** FATAL APPLICATION ERROR ****\n";
for (auto func : CrashCatcher::Get()->mCrashInfoFuncs)
2019-08-23 11:56:54 -07:00
func();
2020-05-29 16:10:47 -07:00
CHAR path[MAX_PATH];
GetModuleFileNameA(NULL, path, MAX_PATH);
crashInfo += "\nExecutable: ";
crashInfo += path;
crashInfo += "\r\n";
crashInfo += GetVersion(path);
2020-05-29 16:10:47 -07:00
aDebugDump.Append(crashInfo);
2019-08-23 11:56:54 -07:00
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", " ");
2019-08-23 11:56:54 -07:00
if (!aDebugDump.IsEmpty())
{
if (!aDebugDump.EndsWith("\n"))
aDebugDump += "\r\n";
aDebugDump += "\r\n";
}
2020-05-29 16:10:47 -07:00
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());
}
}
2019-08-23 11:56:54 -07:00
///////////////////////////
// first name the exception
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, "" }
};
2019-08-23 11:56:54 -07:00
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);
2020-05-29 16:10:47 -07:00
aDebugDump += StrFormat("Logical Address: %04X:%@\r\n", section, offset);
2019-08-23 11:56:54 -07:00
aDebugDump += "\r\n";
anErrorTitle = StrFormat("Exception at %04X:%08X", section, offset);
String aWalkString;
2019-08-23 11:56:54 -07:00
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);
2019-08-23 11:56:54 -07:00
#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);*/
2019-08-23 11:56:54 -07:00
if (isCLI)
{
2019-08-23 11:56:54 -07:00
aDebugDump += "\n";
//fwrite(aDebugDump.c_str(), 1, aDebugDump.length(), stderr);
//fflush(stderr);
2019-08-23 11:56:54 -07:00
DWORD bytesWritten;
::WriteFile(::GetStdHandle(STD_ERROR_HANDLE), aDebugDump.c_str(), (DWORD)aDebugDump.length(), &bytesWritten, NULL);
}
else
2020-09-17 14:15:38 -07:00
ShowErrorDialog(anErrorTitle, aDebugDump, crashCatcher->mRelaunchCmd);
2019-08-23 11:56:54 -07:00
}
CrashCatcher::CrashCatcher()
{
mCrashed = false;
mInitialized = false;
mExceptionPointers = NULL;
mPreviousFilter = NULL;
mDebugError = false;
mCrashReportKind = BfpCrashReportKind_Default;
2020-09-17 14:15:38 -07:00
mCloseRequested = false;
2019-08-23 11:56:54 -07:00
}
static long __stdcall SEHFilter(LPEXCEPTION_POINTERS lpExceptPtr)
{
OutputDebugStrF("SEH Filter! CraskReportKind:%d\n", CrashCatcher::Get()->mCrashReportKind);
2019-08-23 11:56:54 -07:00
if (CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_System)
return EXCEPTION_CONTINUE_SEARCH;
if (CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_None)
{
2019-08-23 11:56:54 -07:00
OutputDebugStrF("Silent Exiting\n");
::TerminateProcess(GetCurrentProcess(), lpExceptPtr->ExceptionRecord->ExceptionCode);
}
AutoCrit autoCrit(CrashCatcher::Get()->mBfpCritSect);
2019-08-23 11:56:54 -07:00
//::ExitProcess();
//quick_exit(1);
if (!CrashCatcher::Get()->mCrashed)
2019-08-23 11:56:54 -07:00
{
CrashCatcher::Get()->mExceptionPointers = lpExceptPtr;
2019-09-12 09:47:19 -07:00
//CreateMiniDump(lpExceptPtr);
2019-08-23 11:56:54 -07:00
DoHandleDebugEvent(lpExceptPtr);
}
2019-08-23 11:56:54 -07:00
//if (!gDebugError)
//SetErrorMode(SEM_NOGPFAULTERRORBOX);
if (CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_PrintOnly)
2019-08-23 11:56:54 -07:00
{
::TerminateProcess(GetCurrentProcess(), lpExceptPtr->ExceptionRecord->ExceptionCode);
}
2020-09-17 14:15:38 -07:00
//return EXCEPTION_CONTINUE_SEARCH;
return (CrashCatcher::Get()->mCloseRequested) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
2019-08-23 11:56:54 -07:00
}
2019-09-26 08:25:54 -07:00
//PVECTORED_EXCEPTION_HANDLER(
static long __stdcall VectorExceptionHandler(LPEXCEPTION_POINTERS lpExceptPtr)
{
OutputDebugStrF("VectorExceptionHandler\n");
return EXCEPTION_CONTINUE_SEARCH;
}
2019-08-23 11:56:54 -07:00
void CrashCatcher::Init()
{
if (mInitialized)
return;
mPreviousFilter = SetUnhandledExceptionFilter(SEHFilter);
OutputDebugStrF("Setting SEH filter %p\n", mPreviousFilter);
mInitialized = true;
2019-09-26 08:25:54 -07:00
// OutputDebugStrF("AddVectoredExceptionHandler 2\n");
// AddVectoredExceptionHandler(0, VectorExceptionHandler);
2019-08-23 11:56:54 -07:00
}
2019-09-12 09:47:19 -07:00
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()))
{
2019-09-12 09:47:19 -07:00
}
}
2019-08-23 11:56:54 -07:00
void CrashCatcher::AddCrashInfoFunc(CrashInfoFunc crashInfoFunc)
{
AutoCrit autoCrit(mBfpCritSect);
mCrashInfoFuncs.Add(crashInfoFunc);
2019-08-23 11:56:54 -07:00
}
void CrashCatcher::AddInfo(const StringImpl& str)
{
AutoCrit autoCrit(mBfpCritSect);
mCrashInfo.Append(str);
if (!str.EndsWith('\n'))
mCrashInfo.Append('\n');
2019-08-23 11:56:54 -07:00
}
void CrashCatcher::Crash(const StringImpl& str)
{
2019-08-23 11:56:54 -07:00
OutputDebugStrF("CrashCatcher::Crash\n");
mBfpCritSect.Lock();
mCrashInfo.Append(str);
mCrashInfo.Append("\n");
2019-08-23 11:56:54 -07:00
if (mPreviousFilter == NULL)
2019-08-23 11:56:54 -07:00
{
// A little late, but install handler now so we can catch this crash
Init();
}
2020-06-25 05:21:03 -07:00
OutputDebugStr(str);
mBfpCritSect.Unlock();
2019-08-23 11:56:54 -07:00
__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
{
2019-08-23 11:56:54 -07:00
::MessageBoxA(NULL, "A", "B", MB_ICONERROR);
__debugbreak();
2019-08-23 11:56:54 -07:00
}
__except (SEHFilter(GetExceptionInformation()))
{
}*/
2020-12-17 04:51:05 -08:00
for (auto func : CrashCatcher::Get()->mCrashInfoFuncs)
func();
2019-08-23 11:56:54 -07:00
exit(1);
}
void CrashCatcher::SetCrashReportKind(BfpCrashReportKind crashReportKind)
{
mCrashReportKind = crashReportKind;
2019-08-23 11:56:54 -07:00
}
2020-09-17 14:15:38 -07:00
void CrashCatcher::SetRelaunchCmd(const StringImpl& relaunchCmd)
{
mRelaunchCmd = relaunchCmd;
}
struct CrashCatchMemory
{
public:
CrashCatcher* mBpManager;
int mABIVersion;
int mCounter;
};
#define CRASHCATCH_ABI_VERSION 1
static CrashCatcher* sCrashCatcher = NULL;
CrashCatcher* CrashCatcher::Get()
{
if (sCrashCatcher != NULL)
return sCrashCatcher;
char mutexName[128];
sprintf(mutexName, "BfCrashCatch_mutex_%d", GetCurrentProcessId());
char memName[128];
sprintf(memName, "BfCrashCatch_mem_%d", GetCurrentProcessId());
auto mutex = ::CreateMutexA(NULL, TRUE, mutexName);
if (mutex != NULL)
{
HANDLE fileMapping = ::OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, memName);
if (fileMapping != NULL)
{
CrashCatchMemory* sharedMem = (CrashCatchMemory*)MapViewOfFile(fileMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashCatchMemory));
if (sharedMem != NULL)
{
if (sharedMem->mABIVersion == 0 && sharedMem->mBpManager == NULL && sharedMem->mCounter == 0)
{
sCrashCatcher = new CrashCatcher();
sharedMem->mBpManager = sCrashCatcher;
sharedMem->mABIVersion = CRASHCATCH_ABI_VERSION;
sharedMem->mCounter = 1;
}
else if (sharedMem->mABIVersion == CRASHCATCH_ABI_VERSION)
{
sharedMem->mCounter++;
sCrashCatcher = sharedMem->mBpManager;
}
::UnmapViewOfFile(sharedMem);
}
::CloseHandle(fileMapping);
}
else
{
fileMapping = ::CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(CrashCatchMemory), memName);
if (fileMapping != NULL)
{
CrashCatchMemory* sharedMem = (CrashCatchMemory*)MapViewOfFile(fileMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashCatchMemory));
if (sharedMem != NULL)
{
sCrashCatcher = new CrashCatcher();
sharedMem->mBpManager = sCrashCatcher;
sharedMem->mABIVersion = CRASHCATCH_ABI_VERSION;
sharedMem->mCounter = 1;
::UnmapViewOfFile(sharedMem);
::ReleaseMutex(mutex);
}
else
{
::CloseHandle(fileMapping);
::CloseHandle(mutex);
}
}
}
}
if (sCrashCatcher == NULL)
sCrashCatcher = new CrashCatcher();
return sCrashCatcher;
}
int CrashCatcher::Shutdown()
{
if (sCrashCatcher == NULL)
return 0;
char mutexName[128];
sprintf(mutexName, "BfCrashCatch_mutex_%d", GetCurrentProcessId());
char memName[128];
sprintf(memName, "BfCrashCatch_mem_%d", GetCurrentProcessId());
auto mutex = ::CreateMutexA(NULL, TRUE, mutexName);
if (mutex != NULL)
{
HANDLE fileMapping = ::OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, memName);
if (fileMapping != NULL)
{
CrashCatchMemory* sharedMem = (CrashCatchMemory*)MapViewOfFile(fileMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashCatchMemory));
if (sharedMem != NULL)
{
if (sharedMem->mABIVersion == CRASHCATCH_ABI_VERSION && sharedMem->mBpManager != NULL && sharedMem->mCounter > 0)
{
sharedMem->mCounter--;
if (sharedMem->mCounter <= 0)
{
delete sharedMem->mBpManager;
sharedMem->mBpManager = NULL;
sharedMem->mCounter = 0;
sharedMem->mABIVersion = 0;
}
}
::UnmapViewOfFile(sharedMem);
}
::CloseHandle(fileMapping);
}
::CloseHandle(mutex);
}
sCrashCatcher = NULL;
return 1;
}