aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmma Miler <emma.pi@protonmail.com>2022-11-14 01:05:57 +0100
committerGitHub <noreply@github.com>2022-11-14 00:05:57 +0000
commit2139e0ec6d95ed7c5f673b53dec71f39a013dd93 (patch)
tree75bcf71b30f245f46f703f23c3ba46c5097073ea
parent02a473ed10475b5560b3d8100f179a31a487893e (diff)
downloadNorthstarLauncher-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
-rw-r--r--NorthstarDLL/crashhandler.cpp466
-rw-r--r--NorthstarDLL/crashhandler.h22
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(&currentTime, (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(&currentTime, (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;
+};