From b4383478d184bff605108f7b5fbc7400ce46112b Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Mon, 26 Dec 2022 20:11:37 +0100 Subject: [PATCH] Initial refactor of the CCrashHandler class * Fixed recursive call. * Format file similarly to Respawn's. * Reduced output code size. TODO: * Call crashmsg.exe. * Add build and system information. --- r5dev/public/utility/crashhandler.cpp | 460 +++++++++++++++++--------- r5dev/public/utility/crashhandler.h | 57 ++++ r5dev/vproj/clientsdk.vcxproj | 1 + r5dev/vproj/clientsdk.vcxproj.filters | 3 + r5dev/vproj/dedicated.vcxproj | 1 + r5dev/vproj/dedicated.vcxproj.filters | 3 + r5dev/vproj/gamesdk.vcxproj | 1 + r5dev/vproj/gamesdk.vcxproj.filters | 3 + 8 files changed, 375 insertions(+), 154 deletions(-) create mode 100644 r5dev/public/utility/crashhandler.h diff --git a/r5dev/public/utility/crashhandler.cpp b/r5dev/public/utility/crashhandler.cpp index 6e93b737..e28f3083 100644 --- a/r5dev/public/utility/crashhandler.cpp +++ b/r5dev/public/utility/crashhandler.cpp @@ -1,6 +1,6 @@ //=============================================================================// // -// Purpose: Crash handling, it handles crashes! +// Purpose: Crash handler (overrides the game's implementation!) // //=============================================================================// #include "core/stdafx.h" @@ -8,6 +8,7 @@ #ifndef _DEBUG +#include "crashhandler.h" #include "tier1/cvar.h" #include "vpc/keyvalues.h" #include "rtech/rtech_utils.h" @@ -16,206 +17,357 @@ #include "materialsystem/cmaterialsystem.h" #include "bsplib/bsplib.h" -// Class is just for DLL init and DLL close, so we can actually define it here fine. -class CCrashHandler -{ -public: - CCrashHandler(); - ~CCrashHandler(); +CCrashHandler* g_CrashHandler = new CCrashHandler(); -private: - void* m_hExceptionHandler; -}; - -static const std::map<DWORD, string> g_ExceptionToString = +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char* CCrashHandler::ExceptionToString(DWORD nExceptionCode) { - { EXCEPTION_ACCESS_VIOLATION, "Access Violation" }, - { EXCEPTION_IN_PAGE_ERROR, "Access Violation" }, - { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array bounds exceeded" }, - { EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal instruction" }, - { EXCEPTION_INVALID_DISPOSITION, "Invalid disposition" }, - { EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-continuable exception" }, - { EXCEPTION_PRIV_INSTRUCTION, "Privileged instruction" }, - { EXCEPTION_STACK_OVERFLOW, "Stack overflow" }, - { EXCEPTION_DATATYPE_MISALIGNMENT, "Datatype misalignment" }, - { EXCEPTION_FLT_DENORMAL_OPERAND, "Denormal operand [FLT]" }, - { EXCEPTION_FLT_DIVIDE_BY_ZERO, "Divide by zero [FLT]" }, - { EXCEPTION_FLT_INEXACT_RESULT, "Inexact float result [FLT]" }, - { EXCEPTION_FLT_INVALID_OPERATION, "Invalid operation [FLT]" }, - { EXCEPTION_FLT_OVERFLOW, "Numeric overflow [FLT]" }, - { EXCEPTION_FLT_STACK_CHECK, "Stack check [FLT]" }, - { EXCEPTION_FLT_UNDERFLOW, "Numeric underflow [FLT]" }, - { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero [INT]" }, - { EXCEPTION_INT_OVERFLOW, "Numeric overflow [INT]" } -}; - -// Borrowed from the R2 project -template<> struct fmt::formatter<M128A> : fmt::formatter<string_view> -{ - template <typename FormatContext> auto format(const M128A& obj, FormatContext& ctx) + switch (nExceptionCode) { - int v1 = obj.Low & INT_MAX; - int v2 = obj.Low >> 32; - int v3 = obj.High & INT_MAX; - int v4 = obj.High >> 32; - return fmt::format_to( ctx.out(), - "[ [{:G}, {:G}, {:G}, {:G}], [{:#x}, {:#x}, {:#x}, {:#x}] ]", - *reinterpret_cast<float*>(&v1), - *reinterpret_cast<float*>(&v2), - *reinterpret_cast<float*>(&v3), - *reinterpret_cast<float*>(&v4), - v1, - v2, - v3, - v4); + case EXCEPTION_GUARD_PAGE: { return "\tEXCEPTION_GUARD_PAGE" ": 0x{:08X}\n"; }; + case EXCEPTION_BREAKPOINT: { return "\tEXCEPTION_BREAKPOINT" ": 0x{:08X}\n"; }; + case EXCEPTION_SINGLE_STEP: { return "\tEXCEPTION_SINGLE_STEP" ": 0x{:08X}\n"; }; + case EXCEPTION_ACCESS_VIOLATION: { return "\tEXCEPTION_ACCESS_VIOLATION" ": 0x{:08X}\n"; }; + case EXCEPTION_IN_PAGE_ERROR: { return "\tEXCEPTION_IN_PAGE_ERROR" ": 0x{:08X}\n"; }; + case EXCEPTION_INVALID_HANDLE: { return "\tEXCEPTION_INVALID_HANDLE" ": 0x{:08X}\n"; }; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: { return "\tEXCEPTION_ARRAY_BOUNDS_EXCEEDED" ": 0x{:08X}\n"; }; + case EXCEPTION_ILLEGAL_INSTRUCTION: { return "\tEXCEPTION_ILLEGAL_INSTRUCTION" ": 0x{:08X}\n"; }; + case EXCEPTION_INVALID_DISPOSITION: { return "\tEXCEPTION_INVALID_DISPOSITION" ": 0x{:08X}\n"; }; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: { return "\tEXCEPTION_NONCONTINUABLE_EXCEPTION" ": 0x{:08X}\n"; }; + case EXCEPTION_PRIV_INSTRUCTION: { return "\tEXCEPTION_PRIV_INSTRUCTION" ": 0x{:08X}\n"; }; + case EXCEPTION_STACK_OVERFLOW: { return "\tEXCEPTION_STACK_OVERFLOW" ": 0x{:08X}\n"; }; + case EXCEPTION_DATATYPE_MISALIGNMENT: { return "\tEXCEPTION_DATATYPE_MISALIGNMENT" ": 0x{:08X}\n"; }; + case EXCEPTION_FLT_DENORMAL_OPERAND: { return "\tEXCEPTION_FLT_DENORMAL_OPERAND" ": 0x{:08X}\n"; }; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: { return "\tEXCEPTION_FLT_DIVIDE_BY_ZERO" ": 0x{:08X}\n"; }; + case EXCEPTION_FLT_INEXACT_RESULT: { return "\tEXCEPTION_FLT_INEXACT_RESULT" ": 0x{:08X}\n"; }; + case EXCEPTION_FLT_INVALID_OPERATION: { return "\tEXCEPTION_FLT_INVALID_OPERATION" ": 0x{:08X}\n"; }; + case EXCEPTION_FLT_OVERFLOW: { return "\tEXCEPTION_FLT_OVERFLOW" ": 0x{:08X}\n"; }; + case EXCEPTION_FLT_STACK_CHECK: { return "\tEXCEPTION_FLT_STACK_CHECK" ": 0x{:08X}\n"; }; + case EXCEPTION_FLT_UNDERFLOW: { return "\tEXCEPTION_FLT_UNDERFLOW" ": 0x{:08X}\n"; }; + case EXCEPTION_INT_DIVIDE_BY_ZERO: { return "\tEXCEPTION_INT_DIVIDE_BY_ZERO" ": 0x{:08X}\n"; }; + case EXCEPTION_INT_OVERFLOW: { return "\tEXCEPTION_INT_OVERFLOW" ": 0x{:08X}\n"; }; + default: { return "\tUNKNOWN_EXCEPTION" ": 0x{:08X}\n"; }; } -}; +} -string GetModuleCrashOffsetAndName(uintptr_t address) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrashHandler::FormatExceptionAddress(LPCSTR pExceptionAddress) { - HMODULE crashedModuleHandle; - if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>((void*)address), &crashedModuleHandle)) - return {}; + HMODULE hCrashedModule; + if (!pExceptionAddress) + { + pExceptionAddress = static_cast<LPCSTR>(m_pExceptionPointers->ExceptionRecord->ExceptionAddress); + } - MODULEINFO crashedModuleInfo; - if (!GetModuleInformation(GetCurrentProcess(), crashedModuleHandle, &crashedModuleInfo, sizeof(crashedModuleInfo))) - return {}; + if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, pExceptionAddress, &hCrashedModule)) + { + m_svBuffer.append(fmt::format("\t!!!unknown-module!!!: 0x{:016X}\n", reinterpret_cast<uintptr_t>(pExceptionAddress))); + return; + } - char szCrashedModuleFullName[MAX_PATH]; - if (!GetModuleFileNameExA(GetCurrentProcess(), crashedModuleHandle, szCrashedModuleFullName, MAX_PATH)) - return {}; + LPCSTR pModuleBase = reinterpret_cast<LPCSTR>(pExceptionAddress - reinterpret_cast<LPCSTR>(hCrashedModule)); + char szCrashedModuleFullName[512]; + if (GetModuleFileNameExA(GetCurrentProcess(), hCrashedModule, szCrashedModuleFullName, sizeof(szCrashedModuleFullName)) - 1 > 0x1FE) + { + m_svBuffer.append(fmt::format("\tmodule@{:016X}: 0x{:016X}\n", (void*)hCrashedModule, reinterpret_cast<uintptr_t>(pModuleBase))); + return; + } + + // TODO: REMOVE EXT. const char* szCrashedModuleName = strrchr(szCrashedModuleFullName, '\\') + 1; - - uintptr_t crashedModuleOffset = (address - reinterpret_cast<uintptr_t>(crashedModuleInfo.lpBaseOfDll)); - - return fmt::format("{0} + 0x{1:x}", szCrashedModuleName, crashedModuleOffset); + m_svBuffer.append(fmt::format("\t{:s}: 0x{:016X}\n", szCrashedModuleName, reinterpret_cast<uintptr_t>(pModuleBase))); } - -string GetExceptionReason(EXCEPTION_POINTERS* exceptionInfo) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCrashHandler::IsPageAccessible() { - const string& svException = g_ExceptionToString.at(exceptionInfo->ExceptionRecord->ExceptionCode); - stringstream ss; - - ss << "Cause: " << svException << std::endl; + PCONTEXT pContextRecord = m_pExceptionPointers->ContextRecord; + MEMORY_BASIC_INFORMATION pMemory = { 0 }; - switch (exceptionInfo->ExceptionRecord->ExceptionCode) + SIZE_T t = VirtualQuery((LPCVOID)pContextRecord->Rsp, &pMemory, sizeof(LPCVOID)); + if (t < sizeof(pMemory) || (pMemory.Protect & PAGE_NOACCESS) || !(pMemory.Protect & PAGE_NOACCESS | PAGE_READWRITE)) { - case EXCEPTION_ACCESS_VIOLATION: - case EXCEPTION_IN_PAGE_ERROR: + return false; + } + else { - ULONG_PTR uExceptionInfo0 = exceptionInfo->ExceptionRecord->ExceptionInformation[0]; - ULONG_PTR uExceptionInfo1 = exceptionInfo->ExceptionRecord->ExceptionInformation[1]; + return !(pMemory.State & MEM_COMMIT); + } - // I don't remember why this has been done like this, but I trust my old self for once on this. - if (!uExceptionInfo0) - ss << "Attempted to read from: 0x" << std::setw(8) << std::setfill('0') << std::hex << uExceptionInfo1 << std::endl; - else if (uExceptionInfo0 == 1) - ss << "Attempted to write to: 0x" << std::setw(8) << std::setfill('0') << std::hex << uExceptionInfo1 << std::endl; - else if (uExceptionInfo0 == 8) - ss << "Data Execution Prevention (DEP) at: 0x" << std::setw(8) << std::setfill('0') << std::hex << uExceptionInfo1 << std::endl; - else - ss << "Unknown access violation at: 0x" << std::setw(8) << std::setfill('0') << std::hex << uExceptionInfo1 << std::endl; - - return ss.str(); - } - default: - { - return ss.str(); - } - } + return false; } -// TODO: PDB PARSE AND RUNTIME EXCP. -long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrashHandler::FormatExceptionCode() { - // Avoid recursive calls. - static bool bLogged = false; - if (bLogged) - return EXCEPTION_CONTINUE_SEARCH; - - // If you are debugging you don't need this. - if (IsDebuggerPresent()) - return EXCEPTION_CONTINUE_SEARCH; - - // Goodluck on you if you somehow hit a guard page. - if (g_ExceptionToString.find(exceptionInfo->ExceptionRecord->ExceptionCode) == g_ExceptionToString.end()) - return EXCEPTION_CONTINUE_SEARCH; - - // Now get the callstack.. - constexpr DWORD NUM_FRAMES_TO_CAPTURE = 60; - void* pStackTrace[NUM_FRAMES_TO_CAPTURE] = { 0 }; - WORD nCapturedFrames = RtlCaptureStackBackTrace(0, NUM_FRAMES_TO_CAPTURE, pStackTrace, NULL); - -#ifndef _DEBUG - // THIS WONT WORK ON DEBUG!!! - // THIS IS DUE TO A JMP TABLE CREATED BY MSVC!! - static auto find_IMI_ref = CMemory(IsMaterialInternal).FindAllCallReferences(reinterpret_cast<uintptr_t>(BuildPropStaticFrustumCullMap), 1000); - if (!find_IMI_ref.empty()) + DWORD nExceptionCode = m_pExceptionPointers->ExceptionRecord->ExceptionCode; + if (nExceptionCode > EXCEPTION_IN_PAGE_ERROR) { - const void* imiRetAddr = find_IMI_ref.at(0).Offset(0x5).RCast<void*>(); - for (WORD i = 0; i < 7; i++) + m_svBuffer.append(fmt::format(ExceptionToString(nExceptionCode), nExceptionCode)); + } + else if (nExceptionCode >= EXCEPTION_ACCESS_VIOLATION) + { + const char* pszException = "EXCEPTION_IN_PAGE_ERROR"; + if (nExceptionCode == EXCEPTION_ACCESS_VIOLATION) { - if (imiRetAddr == pStackTrace[i]) - return EXCEPTION_CONTINUE_SEARCH; + pszException = "EXCEPTION_ACCESS_VIOLATION"; + } + + ULONG_PTR uExceptionInfo0 = m_pExceptionPointers->ExceptionRecord->ExceptionInformation[0]; + ULONG_PTR uExceptionInfo1 = m_pExceptionPointers->ExceptionRecord->ExceptionInformation[1]; + + if (uExceptionInfo0) + { + if (uExceptionInfo0 == 1) + { + m_svBuffer.append(fmt::format("\t{:s}(write): 0x{:016X}\n", pszException, uExceptionInfo1)); + } + else if (uExceptionInfo0 == 8) + { + m_svBuffer.append(fmt::format("\t{:s}(execute): 0x{:016X}\n", pszException, uExceptionInfo1)); + } + else + { + m_svBuffer.append(fmt::format("\t{:s}(unknown): 0x{:016X}\n", pszException, uExceptionInfo1)); + } + } + else + { + m_svBuffer.append(fmt::format("\t{:s}(read): 0x{:016X}\n", pszException, uExceptionInfo1)); + } + + if (uExceptionInfo0 != 8) + { + if (IsPageAccessible()) + { + FormatExceptionAddress(); + } } } -#endif // _DEBUG - - CONTEXT* pExceptionContext = exceptionInfo->ContextRecord; - - // Setup message. - string svMessage = fmt::format("{0}Module: {1}\r\n", GetExceptionReason(exceptionInfo), GetModuleCrashOffsetAndName(reinterpret_cast<uintptr_t>(exceptionInfo->ExceptionRecord->ExceptionAddress))); - - constexpr const char* stdRegs[] = { "RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", "RIP" }; - constexpr const char* xmmRegs[] = { "XMM0", "XMM1", "XMM2", "XMM3", "XMM4", "XMM5", "XMM6", "XMM7", "XMM8", "XMM9", "XMM10", "XMM11", "XMM12", "XMM13", "XMM14", "XMM15" }; - - // Normal registers now. - for (int i = 0; i < SDK_ARRAYSIZE(stdRegs); i++) + else { - svMessage += fmt::format("{0}: 0x{1:X}\r\n", stdRegs[i], *(DWORD64*)((std::uintptr_t)pExceptionContext + offsetof(CONTEXT, Rax) + (sizeof(DWORD64) * i))); + m_svBuffer.append(fmt::format(ExceptionToString(nExceptionCode), nExceptionCode)); } +} - // FPU Time! - for (int i = 0; i < SDK_ARRAYSIZE(xmmRegs); i++) - { - svMessage += fmt::format("{0}: {1}\r\n", xmmRegs[i], *(M128A*)((std::uintptr_t)pExceptionContext + offsetof(CONTEXT, Xmm0) + (sizeof(M128A) * i))); - } - - svMessage += "\r\nStacktrace:\r\n\r\n"; - - for (WORD i = 0; i < nCapturedFrames; i++) - { - svMessage += GetModuleCrashOffsetAndName(reinterpret_cast<uintptr_t>(pStackTrace[i])) + "\n"; - } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrashHandler::GetCallStack() +{ + m_nCapturedFrames = RtlCaptureStackBackTrace(0, NUM_FRAMES_TO_CAPTURE, m_ppStackTrace, NULL); +} +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrashHandler::WriteFile() +{ std::time_t time = std::time(nullptr); stringstream ss; ss << "platform\\logs\\" << "apex_crash_" << std::put_time(std::localtime(&time), "%Y-%m-%d %H-%M-%S.txt"); - // Not using CBaseFileSystem here cause if that would crash we'd have a problem. CIOStream ioLogFile = CIOStream(ss.str(), CIOStream::Mode_t::WRITE); - ioLogFile.WriteString(svMessage); - ioLogFile.Close(); + ioLogFile.WriteString(m_svBuffer); +} - MessageBoxA(0, svMessage.c_str(), "R5R Crashed :/", MB_ICONERROR | MB_OK); +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrashHandler::FormatCrash() +{ + m_svBuffer.append("crash:\n{\n"); + FormatExceptionAddress(); + FormatExceptionCode(); + + m_svBuffer.append("}\n"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrashHandler::FormatCallstack() +{ + m_svBuffer.append("callstack:\n{\n"); + for (WORD i = 0; i < m_nCapturedFrames; i++) + { + FormatExceptionAddress(reinterpret_cast<LPCSTR>(m_ppStackTrace[i])); + } + m_svBuffer.append("}\n"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrashHandler::FormatAPU(const char* pszRegister, DWORD64 nContent) +{ + if (abs64(nContent) >= 1000000) + { + if (nContent > 0xFFFFFFFF) + { + m_svBuffer.append(fmt::format("\t{:s} = 0x{:016X}\n", pszRegister, nContent)); + } + else + { + m_svBuffer.append(fmt::format("\t{:s} = 0x{:08X}\n", pszRegister, nContent)); + } + } + else if (nContent < 0xFFFFFF80 || nContent > 0xFF) + { + m_svBuffer.append(fmt::format("\t{:s} = {:<15d} // 0x{:08X}\n", pszRegister, nContent, nContent)); + } + else + { + m_svBuffer.append(fmt::format("\t{:s} = {:<10d}\n", pszRegister, nContent)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrashHandler::FormatFPU(const char* pszRegister, M128A* pxContent) +{ + int nVec[4] = + { + pxContent->Low& INT_MAX, + pxContent->Low >> 32, + pxContent->High& INT_MAX, + pxContent->High >> 32, + }; + + m_svBuffer.append(fmt::format("\t{:s} = [ [{:g}, {:g}, {:g}, {:g}]", pszRegister, + *reinterpret_cast<float*>(&nVec[0]), + *reinterpret_cast<float*>(&nVec[1]), + *reinterpret_cast<float*>(&nVec[2]), + *reinterpret_cast<float*>(&nVec[3]))); + + const char* pszVectorFormat = ", [{:d}, {:d}, {:d}, {:d}] ]\n"; + int nHighest = *std::max_element(nVec, nVec +4); + + if (nHighest >= 1000000) + { + pszVectorFormat = ", [0x{:08X}, 0x{:08X}, 0x{:08X}, 0x{:08X}] ]\n"; + } + + m_svBuffer.append(fmt::format(pszVectorFormat, nVec[0], nVec[1], nVec[2], nVec[3])); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrashHandler::FormatRegisters() +{ + m_svBuffer.append("registers:\n{\n"); + + PCONTEXT pContextRecord = m_pExceptionPointers->ContextRecord; + FormatAPU("rax", pContextRecord->Rax); + FormatAPU("rbx", pContextRecord->Rbx); + FormatAPU("rcx", pContextRecord->Rcx); + FormatAPU("rdx", pContextRecord->Rdx); + FormatAPU("rsp", pContextRecord->Rsp); + FormatAPU("rbp", pContextRecord->Rbp); + FormatAPU("rsi", pContextRecord->Rsi); + FormatAPU("rdi", pContextRecord->Rdi); + FormatAPU("r8 ", pContextRecord->R8); + FormatAPU("r9 ", pContextRecord->R9); + FormatAPU("r10", pContextRecord->R10); + FormatAPU("r11", pContextRecord->R11); + FormatAPU("r12", pContextRecord->R12); + FormatAPU("r13", pContextRecord->R13); + FormatAPU("r14", pContextRecord->R14); + FormatAPU("r15", pContextRecord->R15); + FormatAPU("rip", pContextRecord->Rip); + FormatFPU("xmm0 ", &pContextRecord->Xmm0); + FormatFPU("xmm1 ", &pContextRecord->Xmm1); + FormatFPU("xmm2 ", &pContextRecord->Xmm2); + FormatFPU("xmm3 ", &pContextRecord->Xmm3); + FormatFPU("xmm4 ", &pContextRecord->Xmm4); + FormatFPU("xmm5 ", &pContextRecord->Xmm5); + FormatFPU("xmm6 ", &pContextRecord->Xmm6); + FormatFPU("xmm7 ", &pContextRecord->Xmm7); + FormatFPU("xmm8 ", &pContextRecord->Xmm8); + FormatFPU("xmm9 ", &pContextRecord->Xmm9); + FormatFPU("xmm10", &pContextRecord->Xmm10); + FormatFPU("xmm11", &pContextRecord->Xmm11); + FormatFPU("xmm12", &pContextRecord->Xmm12); + FormatFPU("xmm13", &pContextRecord->Xmm13); + FormatFPU("xmm14", &pContextRecord->Xmm14); + FormatFPU("xmm15", &pContextRecord->Xmm15); + + m_svBuffer.append("}\n"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) +{ + g_CrashHandler->Lock(); + + // Kill on recursive call. + static bool bLogged = false; + if (bLogged) + ExitProcess(1u); bLogged = true; + g_CrashHandler->GetCallStack(); + g_CrashHandler->SetExceptionPointers(exceptionInfo); + + + g_CrashHandler->FormatCrash(); + g_CrashHandler->FormatCallstack(); + g_CrashHandler->FormatRegisters(); + + + + g_CrashHandler->WriteFile(); + g_CrashHandler->Unlock(); + + +//#ifndef _DEBUG +// // THIS WONT WORK ON DEBUG!!! +// // THIS IS DUE TO A JMP TABLE CREATED BY MSVC!! +// static auto find_IMI_ref = CMemory(IsMaterialInternal).FindAllCallReferences(reinterpret_cast<uintptr_t>(BuildPropStaticFrustumCullMap), 1000); +// if (!find_IMI_ref.empty()) +// { +// const void* imiRetAddr = find_IMI_ref.at(0).Offset(0x5).RCast<void*>(); +// for (WORD i = 0; i < 7; i++) +// { +// if (imiRetAddr == pStackTrace[i]) +// return EXCEPTION_CONTINUE_SEARCH; +// } +// } +//#endif // _DEBUG + return EXCEPTION_EXECUTE_HANDLER; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- CCrashHandler::CCrashHandler() + : m_ppStackTrace() + , m_nCapturedFrames(0) + { m_hExceptionHandler = AddVectoredExceptionHandler(TRUE, ExceptionFilter); } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- CCrashHandler::~CCrashHandler() { RemoveVectoredExceptionHandler(m_hExceptionHandler); } -// Init on DLL init! -CCrashHandler* g_CrashHandler = new CCrashHandler(); - #endif // _DEBUG \ No newline at end of file diff --git a/r5dev/public/utility/crashhandler.h b/r5dev/public/utility/crashhandler.h new file mode 100644 index 00000000..01dcbed3 --- /dev/null +++ b/r5dev/public/utility/crashhandler.h @@ -0,0 +1,57 @@ +#ifndef CRASHHANDLER_H +#define CRASHHANDLER_H + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CCrashHandler +{ +public: + CCrashHandler(); + ~CCrashHandler(); + + + static const char* ExceptionToString(DWORD nExceptionCode); + + void FormatCrash(); + void FormatCallstack(); + void FormatRegisters(); + + void FormatAPU(const char* pszRegister, DWORD64 nContent); + void FormatFPU(const char* pszRegister, M128A* pxContent); + + + void FormatExceptionAddress(LPCSTR pExceptionAddress = nullptr); + void FormatExceptionCode(); + + + void GetCallStack(); + + + bool IsPageAccessible(); + + + void SetExceptionPointers(EXCEPTION_POINTERS* pExceptionPointers) { m_pExceptionPointers = pExceptionPointers; }; + + + void WriteFile(); + + + void Lock() const { m_Mutex.lock(); }; + void Unlock() const { m_Mutex.unlock(); }; + +private: + enum + { + NUM_FRAMES_TO_CAPTURE = 60 + }; + + void* m_hExceptionHandler; + PVOID m_ppStackTrace[NUM_FRAMES_TO_CAPTURE]; + EXCEPTION_POINTERS* m_pExceptionPointers; + WORD m_nCapturedFrames; + string m_svBuffer; + mutable std::mutex m_Mutex; +}; + +#endif // CRASHHANDLER_H \ No newline at end of file diff --git a/r5dev/vproj/clientsdk.vcxproj b/r5dev/vproj/clientsdk.vcxproj index 644b0a79..47c54ccf 100644 --- a/r5dev/vproj/clientsdk.vcxproj +++ b/r5dev/vproj/clientsdk.vcxproj @@ -312,6 +312,7 @@ <ClInclude Include="..\public\studio.h" /> <ClInclude Include="..\core\resource.h" /> <ClInclude Include="..\public\utility\binstream.h" /> + <ClInclude Include="..\public\utility\crashhandler.h" /> <ClInclude Include="..\public\utility\httplib.h" /> <ClInclude Include="..\public\utility\memaddr.h" /> <ClInclude Include="..\public\utility\module.h" /> diff --git a/r5dev/vproj/clientsdk.vcxproj.filters b/r5dev/vproj/clientsdk.vcxproj.filters index 2e05582c..0e46eed9 100644 --- a/r5dev/vproj/clientsdk.vcxproj.filters +++ b/r5dev/vproj/clientsdk.vcxproj.filters @@ -1883,6 +1883,9 @@ <ClInclude Include="..\public\utility\vtable.h"> <Filter>sdk\public\utility</Filter> </ClInclude> + <ClInclude Include="..\public\utility\crashhandler.h"> + <Filter>sdk\public\utility</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Image Include="..\shared\resource\lockedserver.png"> diff --git a/r5dev/vproj/dedicated.vcxproj b/r5dev/vproj/dedicated.vcxproj index cbabb438..0c8dbb1d 100644 --- a/r5dev/vproj/dedicated.vcxproj +++ b/r5dev/vproj/dedicated.vcxproj @@ -243,6 +243,7 @@ <ClInclude Include="..\public\server_class.h" /> <ClInclude Include="..\public\studio.h" /> <ClInclude Include="..\public\utility\binstream.h" /> + <ClInclude Include="..\public\utility\crashhandler.h" /> <ClInclude Include="..\public\utility\httplib.h" /> <ClInclude Include="..\public\utility\memaddr.h" /> <ClInclude Include="..\public\utility\module.h" /> diff --git a/r5dev/vproj/dedicated.vcxproj.filters b/r5dev/vproj/dedicated.vcxproj.filters index 8a5b8145..2f492f76 100644 --- a/r5dev/vproj/dedicated.vcxproj.filters +++ b/r5dev/vproj/dedicated.vcxproj.filters @@ -1287,6 +1287,9 @@ <ClInclude Include="..\public\utility\vtable.h"> <Filter>sdk\public\utility</Filter> </ClInclude> + <ClInclude Include="..\public\utility\crashhandler.h"> + <Filter>sdk\public\utility</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="..\common\opcodes.cpp"> diff --git a/r5dev/vproj/gamesdk.vcxproj b/r5dev/vproj/gamesdk.vcxproj index fa13a626..6186b865 100644 --- a/r5dev/vproj/gamesdk.vcxproj +++ b/r5dev/vproj/gamesdk.vcxproj @@ -343,6 +343,7 @@ <ClInclude Include="..\public\studio.h" /> <ClInclude Include="..\core\resource.h" /> <ClInclude Include="..\public\utility\binstream.h" /> + <ClInclude Include="..\public\utility\crashhandler.h" /> <ClInclude Include="..\public\utility\httplib.h" /> <ClInclude Include="..\public\utility\memaddr.h" /> <ClInclude Include="..\public\utility\module.h" /> diff --git a/r5dev/vproj/gamesdk.vcxproj.filters b/r5dev/vproj/gamesdk.vcxproj.filters index 567db253..b6c6a329 100644 --- a/r5dev/vproj/gamesdk.vcxproj.filters +++ b/r5dev/vproj/gamesdk.vcxproj.filters @@ -1985,6 +1985,9 @@ <ClInclude Include="..\public\utility\vtable.h"> <Filter>sdk\public\utility</Filter> </ClInclude> + <ClInclude Include="..\public\utility\crashhandler.h"> + <Filter>sdk\public\utility</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Image Include="..\shared\resource\lockedserver.png">