diff options
author | Emma Miler <emma.pi@protonmail.com> | 2022-11-14 01:05:57 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-14 00:05:57 +0000 |
commit | 2139e0ec6d95ed7c5f673b53dec71f39a013dd93 (patch) | |
tree | 75bcf71b30f245f46f703f23c3ba46c5097073ea /NorthstarDLL | |
parent | 02a473ed10475b5560b3d8100f179a31a487893e (diff) | |
download | NorthstarLauncher-2139e0ec6d95ed7c5f673b53dec71f39a013dd93.tar.gz NorthstarLauncher-2139e0ec6d95ed7c5f673b53dec71f39a013dd93.zip |
New crashhandler (#325)
* New crashhandler
* Formatting
* Add version number to crash log
* Fix Xmm dumping
* Update crashhandler.cpp
* Fix formatting
* Moved to using reinterpret_cast and foreground messagebox
* Update crashhandler.cpp
Diffstat (limited to 'NorthstarDLL')
-rw-r--r-- | NorthstarDLL/crashhandler.cpp | 466 | ||||
-rw-r--r-- | NorthstarDLL/crashhandler.h | 22 |
2 files changed, 320 insertions, 168 deletions
diff --git a/NorthstarDLL/crashhandler.cpp b/NorthstarDLL/crashhandler.cpp index a631eb3a..2532afd0 100644 --- a/NorthstarDLL/crashhandler.cpp +++ b/NorthstarDLL/crashhandler.cpp @@ -2,207 +2,336 @@ #include "crashhandler.h" #include "dedicated.h" #include "nsprefix.h" +#include "version.h" +#include "modmanager.h" #include <minidumpapiset.h> HANDLE hExceptionFilter; -long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) -{ - static bool logged = false; - if (logged) - return EXCEPTION_CONTINUE_SEARCH; +std::shared_ptr<ExceptionLog> storedException {}; - if (!IsDebuggerPresent()) - { - const DWORD exceptionCode = exceptionInfo->ExceptionRecord->ExceptionCode; - if (exceptionCode != EXCEPTION_ACCESS_VIOLATION && exceptionCode != EXCEPTION_ARRAY_BOUNDS_EXCEEDED && - exceptionCode != EXCEPTION_DATATYPE_MISALIGNMENT && exceptionCode != EXCEPTION_FLT_DENORMAL_OPERAND && - exceptionCode != EXCEPTION_FLT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_FLT_INEXACT_RESULT && - exceptionCode != EXCEPTION_FLT_INVALID_OPERATION && exceptionCode != EXCEPTION_FLT_OVERFLOW && - exceptionCode != EXCEPTION_FLT_STACK_CHECK && exceptionCode != EXCEPTION_FLT_UNDERFLOW && - exceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION && exceptionCode != EXCEPTION_IN_PAGE_ERROR && - exceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_INT_OVERFLOW && - exceptionCode != EXCEPTION_INVALID_DISPOSITION && exceptionCode != EXCEPTION_NONCONTINUABLE_EXCEPTION && - exceptionCode != EXCEPTION_PRIV_INSTRUCTION && exceptionCode != EXCEPTION_STACK_OVERFLOW) - return EXCEPTION_CONTINUE_SEARCH; +#define RUNTIME_EXCEPTION 3765269347 +// clang format did this :/ +std::map<int, std::string> ExceptionNames = { + {EXCEPTION_ACCESS_VIOLATION, "Access Violation"}, {EXCEPTION_IN_PAGE_ERROR, "Access Violation"}, + {EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array bounds exceeded"}, {EXCEPTION_DATATYPE_MISALIGNMENT, "Datatype misalignment"}, + {EXCEPTION_FLT_DENORMAL_OPERAND, "Denormal operand"}, {EXCEPTION_FLT_DIVIDE_BY_ZERO, "Divide by zero (float)"}, + {EXCEPTION_FLT_INEXACT_RESULT, "Inexact float result"}, {EXCEPTION_FLT_INVALID_OPERATION, "Invalid operation"}, + {EXCEPTION_FLT_OVERFLOW, "Numeric overflow (float)"}, {EXCEPTION_FLT_STACK_CHECK, "Stack check"}, + {EXCEPTION_FLT_UNDERFLOW, "Numeric underflow (float)"}, {EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal instruction"}, + {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero (int)"}, {EXCEPTION_INT_OVERFLOW, "Numeric overfloat (int)"}, + {EXCEPTION_INVALID_DISPOSITION, "Invalid disposition"}, {EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-continuable exception"}, + {EXCEPTION_PRIV_INSTRUCTION, "Priviledged instruction"}, {EXCEPTION_STACK_OVERFLOW, "Stack overflow"}, + {RUNTIME_EXCEPTION, "Uncaught runtime exception:"}, +}; - std::stringstream exceptionCause; - exceptionCause << "Cause: "; - switch (exceptionCode) - { - case EXCEPTION_ACCESS_VIOLATION: - case EXCEPTION_IN_PAGE_ERROR: +void PrintExceptionLog(ExceptionLog& exc) +{ + // General crash message + spdlog::error("Northstar version: {}", version); + spdlog::error("Northstar has crashed! a minidump has been written and exception info is available below:"); + spdlog::error("Loaded mods: "); + for (const auto& mod : g_pModManager->m_LoadedMods) + { + if (mod.m_bEnabled) { - exceptionCause << "Access Violation" << std::endl; + spdlog::error("{} {}", mod.Name, mod.Version); + } + } + spdlog::error(exc.cause); + // If this was a runtime error, print the message + if (exc.runtimeInfo.length() != 0) + spdlog::error("\"{}\"", exc.runtimeInfo); + spdlog::error("At: {} + {}", exc.trace[0].name, exc.trace[0].relativeAddress); + spdlog::error(""); + spdlog::error("Stack trace:"); - auto exceptionInfo0 = exceptionInfo->ExceptionRecord->ExceptionInformation[0]; - auto exceptionInfo1 = exceptionInfo->ExceptionRecord->ExceptionInformation[1]; + // Generate format string for stack trace + std::stringstream formatString; + formatString << " {:<" << exc.longestModuleNameLength + 2 << "} {:<" << exc.longestRelativeAddressLength << "} {}"; + std::string guide = fmt::format(formatString.str(), "Module Name", "Offset", "Full Address"); + std::string line(guide.length() + 2, '-'); + spdlog::error(guide); + spdlog::error(line); - if (!exceptionInfo0) - exceptionCause << "Attempted to read from: 0x" << (void*)exceptionInfo1; - else if (exceptionInfo0 == 1) - exceptionCause << "Attempted to write to: 0x" << (void*)exceptionInfo1; - else if (exceptionInfo0 == 8) - exceptionCause << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex << exceptionInfo1; - else - exceptionCause << "Unknown access violation at: 0x" << (void*)exceptionInfo1; + for (const auto& module : exc.trace) + spdlog::error(formatString.str(), module.name, module.relativeAddress, module.address); - break; - } - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - exceptionCause << "Array bounds exceeded"; - break; - case EXCEPTION_DATATYPE_MISALIGNMENT: - exceptionCause << "Datatype misalignment"; - break; - case EXCEPTION_FLT_DENORMAL_OPERAND: - exceptionCause << "Denormal operand"; - break; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - exceptionCause << "Divide by zero (float)"; - break; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - exceptionCause << "Divide by zero (int)"; - break; - case EXCEPTION_FLT_INEXACT_RESULT: - exceptionCause << "Inexact result"; - break; - case EXCEPTION_FLT_INVALID_OPERATION: - exceptionCause << "Invalid operation"; - break; - case EXCEPTION_FLT_OVERFLOW: - case EXCEPTION_INT_OVERFLOW: - exceptionCause << "Numeric overflow"; - break; - case EXCEPTION_FLT_UNDERFLOW: - exceptionCause << "Numeric underflow"; - break; - case EXCEPTION_FLT_STACK_CHECK: - exceptionCause << "Stack check"; - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - exceptionCause << "Illegal instruction"; - break; - case EXCEPTION_INVALID_DISPOSITION: - exceptionCause << "Invalid disposition"; - break; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - exceptionCause << "Noncontinuable exception"; - break; - case EXCEPTION_PRIV_INSTRUCTION: - exceptionCause << "Priviledged instruction"; - break; - case EXCEPTION_STACK_OVERFLOW: - exceptionCause << "Stack overflow"; - break; - default: - exceptionCause << "Unknown"; - break; - } + // Print dump of most CPU registers + spdlog::error(""); + for (const auto& reg : exc.registerDump) + spdlog::error(reg); - void* exceptionAddress = exceptionInfo->ExceptionRecord->ExceptionAddress; + if (!IsDedicatedServer()) + MessageBoxA( + 0, + "Northstar has crashed! Crash info can be found in R2Northstar/logs", + "Northstar has crashed!", + MB_ICONERROR | MB_OK | MB_SYSTEMMODAL); +} - HMODULE crashedModuleHandle; - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(exceptionAddress), &crashedModuleHandle); +std::string GetExceptionName(ExceptionLog& exc) +{ + const DWORD exceptionCode = exc.exceptionRecord->ExceptionCode; + auto name = ExceptionNames[exceptionCode]; + if (exceptionCode == EXCEPTION_ACCESS_VIOLATION || exceptionCode == EXCEPTION_IN_PAGE_ERROR) + { + std::stringstream returnString; + returnString << name << ": "; - MODULEINFO crashedModuleInfo; - GetModuleInformation(GetCurrentProcess(), crashedModuleHandle, &crashedModuleInfo, sizeof(crashedModuleInfo)); + auto exceptionInfo0 = exc.exceptionRecord->ExceptionInformation[0]; + auto exceptionInfo1 = exc.exceptionRecord->ExceptionInformation[1]; - char crashedModuleFullName[MAX_PATH]; - GetModuleFileNameExA(GetCurrentProcess(), crashedModuleHandle, crashedModuleFullName, MAX_PATH); - char* crashedModuleName = strrchr(crashedModuleFullName, '\\') + 1; + if (!exceptionInfo0) + returnString << "Attempted to read from: 0x" << (void*)exceptionInfo1; + else if (exceptionInfo0 == 1) + returnString << "Attempted to write to: 0x" << (void*)exceptionInfo1; + else if (exceptionInfo0 == 8) + returnString << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex << exceptionInfo1; + else + returnString << "Unknown access violation at: 0x" << (void*)exceptionInfo1; + return returnString.str(); + } + return name; +} - DWORD64 crashedModuleOffset = ((DWORD64)exceptionAddress) - ((DWORD64)crashedModuleInfo.lpBaseOfDll); - CONTEXT* exceptionContext = exceptionInfo->ContextRecord; +// Custom formatter for the Xmm registers +template <> struct fmt::formatter<M128A> : fmt::formatter<string_view> +{ + template <typename FormatContext> auto format(const M128A& obj, FormatContext& ctx) + { + // Masking the top and bottom half of the long long + 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); + } +}; - spdlog::error("Northstar has crashed! a minidump has been written and exception info is available below:"); - spdlog::error(exceptionCause.str()); - spdlog::error("At: {} + {}", crashedModuleName, (void*)crashedModuleOffset); +void GenerateTrace(ExceptionLog& exc, bool skipErrorHandlingFrames = true, int numSkipFrames = 0) +{ - PVOID framesToCapture[62]; - int frames = RtlCaptureStackBackTrace(0, 62, framesToCapture, NULL); - bool haveSkippedErrorHandlingFrames = false; - for (int i = 0; i < frames; i++) - { - HMODULE backtraceModuleHandle; - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(framesToCapture[i]), &backtraceModuleHandle); + MODULEINFO crashedModuleInfo; + GetModuleInformation(GetCurrentProcess(), exc.crashedModule, &crashedModuleInfo, sizeof(crashedModuleInfo)); - char backtraceModuleFullName[MAX_PATH]; - GetModuleFileNameExA(GetCurrentProcess(), backtraceModuleHandle, backtraceModuleFullName, MAX_PATH); - char* backtraceModuleName = strrchr(backtraceModuleFullName, '\\') + 1; + char crashedModuleFullName[MAX_PATH]; + GetModuleFileNameExA(GetCurrentProcess(), exc.crashedModule, crashedModuleFullName, MAX_PATH); + char* crashedModuleName = strrchr(crashedModuleFullName, '\\') + 1; - if (!haveSkippedErrorHandlingFrames) + DWORD64 crashedModuleOffset = ((DWORD64)exc.exceptionRecord->ExceptionAddress) - ((DWORD64)crashedModuleInfo.lpBaseOfDll); + + PVOID framesToCapture[62]; + int frames = RtlCaptureStackBackTrace(0, 62, framesToCapture, NULL); + bool haveSkippedErrorHandlingFrames = false; + + for (int i = 0; i < frames; i++) + { + + HMODULE backtraceModuleHandle; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(framesToCapture[i]), &backtraceModuleHandle); + + char backtraceModuleFullName[MAX_PATH]; + GetModuleFileNameExA(GetCurrentProcess(), backtraceModuleHandle, backtraceModuleFullName, MAX_PATH); + char* backtraceModuleName = strrchr(backtraceModuleFullName, '\\') + 1; + + if (!haveSkippedErrorHandlingFrames) + { + if (!strncmp(backtraceModuleFullName, crashedModuleFullName, MAX_PATH) && + !strncmp(backtraceModuleName, crashedModuleName, MAX_PATH)) { - if (!strncmp(backtraceModuleFullName, crashedModuleFullName, MAX_PATH) && - !strncmp(backtraceModuleName, crashedModuleName, MAX_PATH)) - { - haveSkippedErrorHandlingFrames = true; - } - else - { - continue; - } + haveSkippedErrorHandlingFrames = true; } + else + { + continue; + } + } - void* actualAddress = (void*)framesToCapture[i]; - void* relativeAddress = (void*)(uintptr_t(actualAddress) - uintptr_t(backtraceModuleHandle)); - - spdlog::error(" {} + {} ({})", backtraceModuleName, relativeAddress, actualAddress); + if (numSkipFrames > 0) + { + numSkipFrames--; + continue; } - spdlog::error("RAX: 0x{0:x}", exceptionContext->Rax); - spdlog::error("RBX: 0x{0:x}", exceptionContext->Rbx); - spdlog::error("RCX: 0x{0:x}", exceptionContext->Rcx); - spdlog::error("RDX: 0x{0:x}", exceptionContext->Rdx); - spdlog::error("RSI: 0x{0:x}", exceptionContext->Rsi); - spdlog::error("RDI: 0x{0:x}", exceptionContext->Rdi); - spdlog::error("RBP: 0x{0:x}", exceptionContext->Rbp); - spdlog::error("RSP: 0x{0:x}", exceptionContext->Rsp); - spdlog::error("R8: 0x{0:x}", exceptionContext->R8); - spdlog::error("R9: 0x{0:x}", exceptionContext->R9); - spdlog::error("R10: 0x{0:x}", exceptionContext->R10); - spdlog::error("R11: 0x{0:x}", exceptionContext->R11); - spdlog::error("R12: 0x{0:x}", exceptionContext->R12); - spdlog::error("R13: 0x{0:x}", exceptionContext->R13); - spdlog::error("R14: 0x{0:x}", exceptionContext->R14); - spdlog::error("R15: 0x{0:x}", exceptionContext->R15); - - time_t time = std::time(nullptr); - tm currentTime = *std::localtime(&time); - std::stringstream stream; - stream << std::put_time(¤tTime, (GetNorthstarPrefix() + "/logs/nsdump%Y-%m-%d %H-%M-%S.dmp").c_str()); - - auto hMinidumpFile = CreateFileA(stream.str().c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); - if (hMinidumpFile) + void* actualAddress = (void*)framesToCapture[i]; + void* relativeAddress = (void*)(uintptr_t(actualAddress) - uintptr_t(backtraceModuleHandle)); + std::string s_moduleName {backtraceModuleName}; + std::string s_relativeAddress {fmt::format("{}", relativeAddress)}; + // These are used for formatting later on + if (s_moduleName.length() > exc.longestModuleNameLength) { - MINIDUMP_EXCEPTION_INFORMATION dumpExceptionInfo; - dumpExceptionInfo.ThreadId = GetCurrentThreadId(); - dumpExceptionInfo.ExceptionPointers = exceptionInfo; - dumpExceptionInfo.ClientPointers = false; - - MiniDumpWriteDump( - GetCurrentProcess(), - GetCurrentProcessId(), - hMinidumpFile, - MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory), - &dumpExceptionInfo, - nullptr, - nullptr); - CloseHandle(hMinidumpFile); + exc.longestModuleNameLength = s_moduleName.length(); } - else - spdlog::error("Failed to write minidump file {}!", stream.str()); + if (s_relativeAddress.length() > exc.longestRelativeAddressLength) + { + exc.longestRelativeAddressLength = s_relativeAddress.length(); + } + + exc.trace.push_back(BacktraceModule {s_moduleName, s_relativeAddress, fmt::format("{}", actualAddress)}); + } - if (!IsDedicatedServer()) - MessageBoxA( - 0, "Northstar has crashed! Crash info can be found in R2Northstar/logs", "Northstar has crashed!", MB_ICONERROR | MB_OK); + CONTEXT* exceptionContext = exc.contextRecord; + + exc.registerDump.push_back(fmt::format("Flags: 0b{0:b}", exceptionContext->ContextFlags)); + exc.registerDump.push_back(fmt::format("RIP: 0x{0:x}", exceptionContext->Rip)); + exc.registerDump.push_back(fmt::format("CS : 0x{0:x}", exceptionContext->SegCs)); + exc.registerDump.push_back(fmt::format("DS : 0x{0:x}", exceptionContext->SegDs)); + exc.registerDump.push_back(fmt::format("ES : 0x{0:x}", exceptionContext->SegEs)); + exc.registerDump.push_back(fmt::format("SS : 0x{0:x}", exceptionContext->SegSs)); + exc.registerDump.push_back(fmt::format("FS : 0x{0:x}", exceptionContext->SegFs)); + exc.registerDump.push_back(fmt::format("GS : 0x{0:x}", exceptionContext->SegGs)); + + exc.registerDump.push_back(fmt::format("RAX: 0x{0:x}", exceptionContext->Rax)); + exc.registerDump.push_back(fmt::format("RBX: 0x{0:x}", exceptionContext->Rbx)); + exc.registerDump.push_back(fmt::format("RCX: 0x{0:x}", exceptionContext->Rcx)); + exc.registerDump.push_back(fmt::format("RDX: 0x{0:x}", exceptionContext->Rdx)); + exc.registerDump.push_back(fmt::format("RSI: 0x{0:x}", exceptionContext->Rsi)); + exc.registerDump.push_back(fmt::format("RDI: 0x{0:x}", exceptionContext->Rdi)); + exc.registerDump.push_back(fmt::format("RBP: 0x{0:x}", exceptionContext->Rbp)); + exc.registerDump.push_back(fmt::format("RSP: 0x{0:x}", exceptionContext->Rsp)); + exc.registerDump.push_back(fmt::format("R8 : 0x{0:x}", exceptionContext->R8)); + exc.registerDump.push_back(fmt::format("R9 : 0x{0:x}", exceptionContext->R9)); + exc.registerDump.push_back(fmt::format("R10: 0x{0:x}", exceptionContext->R10)); + exc.registerDump.push_back(fmt::format("R11: 0x{0:x}", exceptionContext->R11)); + exc.registerDump.push_back(fmt::format("R12: 0x{0:x}", exceptionContext->R12)); + exc.registerDump.push_back(fmt::format("R13: 0x{0:x}", exceptionContext->R13)); + exc.registerDump.push_back(fmt::format("R14: 0x{0:x}", exceptionContext->R14)); + exc.registerDump.push_back(fmt::format("R15: 0x{0:x}", exceptionContext->R15)); + + exc.registerDump.push_back(fmt::format("Xmm0 : {}", exceptionContext->Xmm0)); + exc.registerDump.push_back(fmt::format("Xmm1 : {}", exceptionContext->Xmm1)); + exc.registerDump.push_back(fmt::format("Xmm2 : {}", exceptionContext->Xmm2)); + exc.registerDump.push_back(fmt::format("Xmm3 : {}", exceptionContext->Xmm3)); + exc.registerDump.push_back(fmt::format("Xmm4 : {}", exceptionContext->Xmm4)); + exc.registerDump.push_back(fmt::format("Xmm5 : {}", exceptionContext->Xmm5)); + exc.registerDump.push_back(fmt::format("Xmm6 : {}", exceptionContext->Xmm6)); + exc.registerDump.push_back(fmt::format("Xmm7 : {}", exceptionContext->Xmm7)); + exc.registerDump.push_back(fmt::format("Xmm8 : {}", exceptionContext->Xmm8)); + exc.registerDump.push_back(fmt::format("Xmm9 : {}", exceptionContext->Xmm9)); + exc.registerDump.push_back(fmt::format("Xmm10: {}", exceptionContext->Xmm10)); + exc.registerDump.push_back(fmt::format("Xmm11: {}", exceptionContext->Xmm11)); + exc.registerDump.push_back(fmt::format("Xmm12: {}", exceptionContext->Xmm12)); + exc.registerDump.push_back(fmt::format("Xmm13: {}", exceptionContext->Xmm13)); + exc.registerDump.push_back(fmt::format("Xmm14: {}", exceptionContext->Xmm14)); + exc.registerDump.push_back(fmt::format("Xmm15: {}", exceptionContext->Xmm15)); +} + +void CreateMiniDump(EXCEPTION_POINTERS* exceptionInfo) +{ + time_t time = std::time(nullptr); + tm currentTime = *std::localtime(&time); + std::stringstream stream; + stream << std::put_time(¤tTime, (GetNorthstarPrefix() + "/logs/nsdump%Y-%m-%d %H-%M-%S.dmp").c_str()); + + auto hMinidumpFile = CreateFileA(stream.str().c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (hMinidumpFile) + { + MINIDUMP_EXCEPTION_INFORMATION dumpExceptionInfo; + dumpExceptionInfo.ThreadId = GetCurrentThreadId(); + dumpExceptionInfo.ExceptionPointers = exceptionInfo; + dumpExceptionInfo.ClientPointers = false; + + MiniDumpWriteDump( + GetCurrentProcess(), + GetCurrentProcessId(), + hMinidumpFile, + MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory), + &dumpExceptionInfo, + nullptr, + nullptr); + CloseHandle(hMinidumpFile); + } + else + spdlog::error("Failed to write minidump file {}!", stream.str()); +} + +long GenerateExceptionLog(EXCEPTION_POINTERS* exceptionInfo) +{ + const DWORD exceptionCode = exceptionInfo->ExceptionRecord->ExceptionCode; + + void* exceptionAddress = exceptionInfo->ExceptionRecord->ExceptionAddress; + + auto exc = std::make_shared<ExceptionLog>(); + exc->exceptionRecord = exceptionInfo->ExceptionRecord; + exc->contextRecord = exceptionInfo->ContextRecord; + exc->cause = GetExceptionName(*exc); + + HMODULE crashedModuleHandle; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(exceptionAddress), &crashedModuleHandle); + + exc->crashedModule = crashedModuleHandle; + + // When encountering a runtime exception, we store the exception to be displayed later + // We then have to return EXCEPTION_CONTINUE_SEARCH so that our runtime handler may be called + // This might possibly cause some issues if client and server are crashing at the same time, but honestly i don't care + if (exceptionCode == RUNTIME_EXCEPTION) + { + GenerateTrace(*exc, false, 2); + storedException = exc; + return EXCEPTION_CONTINUE_SEARCH; } - logged = true; + GenerateTrace(*exc, true, 0); + CreateMiniDump(exceptionInfo); + PrintExceptionLog(*exc); return EXCEPTION_EXECUTE_HANDLER; } +long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) +{ + if (!IsDebuggerPresent()) + { + // Check if we are capable of handling this type of exception + if (ExceptionNames.find(exceptionInfo->ExceptionRecord->ExceptionCode) == ExceptionNames.end()) + return EXCEPTION_CONTINUE_SEARCH; + CreateMiniDump(exceptionInfo); + return GenerateExceptionLog(exceptionInfo); + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +void RuntimeExceptionHandler() +{ + auto ptr = std::current_exception(); + if (ptr) + { + try + { + // This is to generate an actual std::exception object that we can then inspect + std::rethrow_exception(ptr); + } + catch (std::exception& e) + { + storedException->runtimeInfo = e.what(); + } + catch (...) + { + storedException->runtimeInfo = "Unknown runtime exception type"; + } + PrintExceptionLog(*storedException); + exit(-1); + } + else + { + spdlog::error( + "std::current_exception() returned nullptr while being handled by RuntimeExceptionHandler. This should never happen!"); + std::abort(); + } +} + BOOL WINAPI ConsoleHandlerRoutine(DWORD eventCode) { switch (eventCode) @@ -222,6 +351,7 @@ void InitialiseCrashHandler() { hExceptionFilter = AddVectoredExceptionHandler(TRUE, ExceptionFilter); SetConsoleCtrlHandler(ConsoleHandlerRoutine, true); + std::set_terminate(RuntimeExceptionHandler); } void RemoveCrashHandler() diff --git a/NorthstarDLL/crashhandler.h b/NorthstarDLL/crashhandler.h index e0dc6906..990457cc 100644 --- a/NorthstarDLL/crashhandler.h +++ b/NorthstarDLL/crashhandler.h @@ -2,3 +2,25 @@ void InitialiseCrashHandler(); void RemoveCrashHandler(); + +struct BacktraceModule +{ + std::string name; + std::string relativeAddress; + std::string address; +}; + +struct ExceptionLog +{ + std::string cause; + HMODULE crashedModule; + PEXCEPTION_RECORD exceptionRecord; + PCONTEXT contextRecord; + std::vector<BacktraceModule> trace; + std::vector<std::string> registerDump; + + std::string runtimeInfo; + + int longestModuleNameLength; + int longestRelativeAddressLength; +}; |