mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
VectoredExceptionHandler init.
* Only gets compiled in for release builds. * Attaches on gamesdk init, so should catch everything on dll setup also. * Fake PDB parsing still needs to be done.
This commit is contained in:
parent
c4d23ebcc8
commit
37a50c92b8
197
r5dev/public/utility/crashhandler.cpp
Normal file
197
r5dev/public/utility/crashhandler.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
//=============================================================================//
|
||||
//
|
||||
// Purpose: Crash handling, it handles crashes!
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "core/stdafx.h"
|
||||
#include "public/utility/binstream.h"
|
||||
|
||||
#ifndef _DEBUG
|
||||
|
||||
// Class is just for DLL init and DLL close, so we can actually define it here fine.
|
||||
class CCrashHandler
|
||||
{
|
||||
public:
|
||||
CCrashHandler();
|
||||
~CCrashHandler();
|
||||
|
||||
private:
|
||||
void* m_hExceptionHandler;
|
||||
};
|
||||
|
||||
static std::map<DWORD, string> g_ExceptionToString =
|
||||
{
|
||||
{ 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, "Priviledged 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 overfloat [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)
|
||||
{
|
||||
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}], [ 0x{:x}, 0x{:x}, 0x{:x}, 0x{:x} ]",
|
||||
*reinterpret_cast<float*>(&v1),
|
||||
*reinterpret_cast<float*>(&v2),
|
||||
*reinterpret_cast<float*>(&v3),
|
||||
*reinterpret_cast<float*>(&v4),
|
||||
v1,
|
||||
v2,
|
||||
v3,
|
||||
v4);
|
||||
}
|
||||
};
|
||||
|
||||
string GetModuleCrashOffsetAndName(uintptr_t address)
|
||||
{
|
||||
HMODULE crashedModuleHandle;
|
||||
if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>((void*)address), &crashedModuleHandle))
|
||||
return {};
|
||||
|
||||
MODULEINFO crashedModuleInfo;
|
||||
if (!GetModuleInformation(GetCurrentProcess(), crashedModuleHandle, &crashedModuleInfo, sizeof(crashedModuleInfo)))
|
||||
return {};
|
||||
|
||||
char szCrashedModuleFullName[MAX_PATH];
|
||||
if (!GetModuleFileNameExA(GetCurrentProcess(), crashedModuleHandle, szCrashedModuleFullName, MAX_PATH))
|
||||
return {};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
string GetExceptionReason(EXCEPTION_POINTERS* exceptionInfo)
|
||||
{
|
||||
const string& svException = g_ExceptionToString.at(exceptionInfo->ExceptionRecord->ExceptionCode);
|
||||
stringstream ss;
|
||||
|
||||
ss << "Cause: " << svException << std::endl;
|
||||
|
||||
switch (exceptionInfo->ExceptionRecord->ExceptionCode)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
{
|
||||
ULONG_PTR uExceptionInfo0 = exceptionInfo->ExceptionRecord->ExceptionInformation[0];
|
||||
ULONG_PTR uExceptionInfo1 = exceptionInfo->ExceptionRecord->ExceptionInformation[1];
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: PDB PARSE AND RUNTIME EXCP.
|
||||
long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo)
|
||||
{
|
||||
// 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_EXECUTE_HANDLER;
|
||||
|
||||
// Goodluck on you if you somehow hit a guard page.
|
||||
if (g_ExceptionToString.find(exceptionInfo->ExceptionRecord->ExceptionCode) == g_ExceptionToString.end())
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
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++)
|
||||
{
|
||||
svMessage += fmt::format("{0}: 0x{1:X}\r\n", stdRegs[i], *(DWORD64*)((std::uintptr_t)pExceptionContext + offsetof(CONTEXT, Rax) + (sizeof(DWORD64) * i)));
|
||||
}
|
||||
|
||||
// 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";
|
||||
|
||||
// Now get the callstack..
|
||||
constexpr int NUM_FRAMES_TO_CAPTURE = 60;
|
||||
void* pStackTrace[NUM_FRAMES_TO_CAPTURE] = { 0 };
|
||||
uint16_t nCapturedFrames = RtlCaptureStackBackTrace(0, NUM_FRAMES_TO_CAPTURE, pStackTrace, NULL);
|
||||
|
||||
for (uint16_t i = 0; i < nCapturedFrames; i++)
|
||||
{
|
||||
svMessage += GetModuleCrashOffsetAndName(reinterpret_cast<uintptr_t>(pStackTrace[i])) + "\n";
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
MessageBoxA(0, svMessage.c_str(), "R5R Crashed :/", MB_ICONERROR | MB_OK);
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
CCrashHandler::CCrashHandler()
|
||||
{
|
||||
m_hExceptionHandler = AddVectoredExceptionHandler(TRUE, ExceptionFilter);
|
||||
}
|
||||
|
||||
CCrashHandler::~CCrashHandler()
|
||||
{
|
||||
RemoveVectoredExceptionHandler(m_hExceptionHandler);
|
||||
}
|
||||
|
||||
|
||||
// Init on DLL init!
|
||||
CCrashHandler* g_CrashHandler = new CCrashHandler();
|
||||
|
||||
#endif // _DEBUG
|
@ -103,6 +103,7 @@
|
||||
<ClCompile Include="..\public\dt_recv.cpp" />
|
||||
<ClCompile Include="..\public\networkvar.cpp" />
|
||||
<ClCompile Include="..\public\utility\binstream.cpp" />
|
||||
<ClCompile Include="..\public\utility\crashhandler.cpp" />
|
||||
<ClCompile Include="..\public\utility\memaddr.cpp" />
|
||||
<ClCompile Include="..\public\utility\module.cpp" />
|
||||
<ClCompile Include="..\public\utility\sigcache.cpp" />
|
||||
|
@ -648,6 +648,9 @@
|
||||
<ClCompile Include="..\public\utility\vtable.cpp">
|
||||
<Filter>sdk\public\utility</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\public\utility\crashhandler.cpp">
|
||||
<Filter>sdk\public\utility</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\client\cdll_engine_int.h">
|
||||
|
@ -563,6 +563,7 @@
|
||||
<ClCompile Include="..\public\datamap.cpp" />
|
||||
<ClCompile Include="..\public\networkvar.cpp" />
|
||||
<ClCompile Include="..\public\utility\binstream.cpp" />
|
||||
<ClCompile Include="..\public\utility\crashhandler.cpp" />
|
||||
<ClCompile Include="..\public\utility\memaddr.cpp" />
|
||||
<ClCompile Include="..\public\utility\module.cpp" />
|
||||
<ClCompile Include="..\public\utility\sigcache.cpp" />
|
||||
|
@ -1622,6 +1622,9 @@
|
||||
<ClCompile Include="..\public\utility\vtable.cpp">
|
||||
<Filter>sdk\public\utility</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\public\utility\crashhandler.cpp">
|
||||
<Filter>sdk\public\utility</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\Dedicated.def" />
|
||||
|
@ -112,6 +112,7 @@
|
||||
<ClCompile Include="..\public\dt_recv.cpp" />
|
||||
<ClCompile Include="..\public\networkvar.cpp" />
|
||||
<ClCompile Include="..\public\utility\binstream.cpp" />
|
||||
<ClCompile Include="..\public\utility\crashhandler.cpp" />
|
||||
<ClCompile Include="..\public\utility\memaddr.cpp" />
|
||||
<ClCompile Include="..\public\utility\module.cpp" />
|
||||
<ClCompile Include="..\public\utility\sigcache.cpp" />
|
||||
|
@ -690,6 +690,9 @@
|
||||
<ClCompile Include="..\public\utility\vtable.cpp">
|
||||
<Filter>sdk\public\utility</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\public\utility\crashhandler.cpp">
|
||||
<Filter>sdk\public\utility</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\client\cdll_engine_int.h">
|
||||
|
Loading…
x
Reference in New Issue
Block a user