diff options
Diffstat (limited to 'NorthstarDLL/logging/crashhandler.cpp')
-rw-r--r-- | NorthstarDLL/logging/crashhandler.cpp | 590 |
1 files changed, 0 insertions, 590 deletions
diff --git a/NorthstarDLL/logging/crashhandler.cpp b/NorthstarDLL/logging/crashhandler.cpp deleted file mode 100644 index a01de5a1..00000000 --- a/NorthstarDLL/logging/crashhandler.cpp +++ /dev/null @@ -1,590 +0,0 @@ -#include "crashhandler.h" -#include "config/profile.h" -#include "dedicated/dedicated.h" -#include "util/version.h" -#include "mods/modmanager.h" -#include "plugins/plugins.h" - -#include <minidumpapiset.h> - -#define CRASHHANDLER_MAX_FRAMES 32 -#define CRASHHANDLER_GETMODULEHANDLE_FAIL "GetModuleHandleExA failed!" - -//----------------------------------------------------------------------------- -// Purpose: Vectored exception callback -//----------------------------------------------------------------------------- -LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* pExceptionInfo) -{ - g_pCrashHandler->Lock(); - - g_pCrashHandler->SetExceptionInfos(pExceptionInfo); - - // Check if we should handle this - // NOTE [Fifty]: This gets called before even a try{} catch() {} can handle an exception - // we don't handle these unless "-crash_handle_all" is passed as a launch arg - if (!g_pCrashHandler->IsExceptionFatal() && !g_pCrashHandler->GetAllFatal()) - { - g_pCrashHandler->Unlock(); - return EXCEPTION_CONTINUE_SEARCH; - } - - // Don't run if a debbuger is attached - if (IsDebuggerPresent()) - { - g_pCrashHandler->Unlock(); - return EXCEPTION_CONTINUE_SEARCH; - } - - // Prevent recursive calls - if (g_pCrashHandler->GetState()) - { - g_pCrashHandler->Unlock(); - ExitProcess(1); - } - - g_pCrashHandler->SetState(true); - - // Needs to be called first as we use the members this sets later on - g_pCrashHandler->SetCrashedModule(); - - // Format - g_pCrashHandler->FormatException(); - g_pCrashHandler->FormatCallstack(); - g_pCrashHandler->FormatRegisters(); - g_pCrashHandler->FormatLoadedMods(); - g_pCrashHandler->FormatLoadedPlugins(); - g_pCrashHandler->FormatModules(); - - // Flush - NS::log::FlushLoggers(); - - // Write minidump - g_pCrashHandler->WriteMinidump(); - - // Show message box - g_pCrashHandler->ShowPopUpMessage(); - - g_pCrashHandler->Unlock(); - - // We showed the "Northstar has crashed" message box - // make sure we terminate - if (!g_pCrashHandler->IsExceptionFatal()) - ExitProcess(1); - - return EXCEPTION_EXECUTE_HANDLER; -} - -//----------------------------------------------------------------------------- -// Purpose: console control signal handler -//----------------------------------------------------------------------------- -BOOL WINAPI ConsoleCtrlRoutine(DWORD dwCtrlType) -{ - // NOTE [Fifty]: When closing the process by closing the console we don't want - // to trigger the crash handler so we remove it - switch (dwCtrlType) - { - case CTRL_CLOSE_EVENT: - spdlog::info("Exiting due to console close..."); - delete g_pCrashHandler; - g_pCrashHandler = nullptr; - std::exit(EXIT_SUCCESS); - return TRUE; - } - - return FALSE; -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -CCrashHandler::CCrashHandler() - : m_hExceptionFilter(nullptr) - , m_pExceptionInfos(nullptr) - , m_bHasSetConsolehandler(false) - , m_bAllExceptionsFatal(false) - , m_bHasShownCrashMsg(false) - , m_bState(false) -{ - Init(); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -CCrashHandler::~CCrashHandler() -{ - Shutdown(); -} - -//----------------------------------------------------------------------------- -// Purpose: Initilazes crash handler -//----------------------------------------------------------------------------- -void CCrashHandler::Init() -{ - m_hExceptionFilter = AddVectoredExceptionHandler(TRUE, ExceptionFilter); - m_bHasSetConsolehandler = SetConsoleCtrlHandler(ConsoleCtrlRoutine, TRUE); -} - -//----------------------------------------------------------------------------- -// Purpose: Shutdowns crash handler -//----------------------------------------------------------------------------- -void CCrashHandler::Shutdown() -{ - if (m_hExceptionFilter) - { - RemoveVectoredExceptionHandler(m_hExceptionFilter); - m_hExceptionFilter = nullptr; - } - - if (m_bHasSetConsolehandler) - { - SetConsoleCtrlHandler(ConsoleCtrlRoutine, FALSE); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Sets the exception info -//----------------------------------------------------------------------------- -void CCrashHandler::SetExceptionInfos(EXCEPTION_POINTERS* pExceptionPointers) -{ - m_pExceptionInfos = pExceptionPointers; -} -//----------------------------------------------------------------------------- -// Purpose: Sets the exception stirngs for message box -//----------------------------------------------------------------------------- -void CCrashHandler::SetCrashedModule() -{ - LPCSTR pCrashAddress = static_cast<LPCSTR>(m_pExceptionInfos->ExceptionRecord->ExceptionAddress); - HMODULE hCrashedModule; - if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, pCrashAddress, &hCrashedModule)) - { - m_svCrashedModule = CRASHHANDLER_GETMODULEHANDLE_FAIL; - m_svCrashedOffset = ""; - - DWORD dwErrorID = GetLastError(); - if (dwErrorID != 0) - { - LPSTR pszBuffer; - DWORD dwSize = FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - dwErrorID, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&pszBuffer, - 0, - NULL); - - if (dwSize > 0) - { - m_svError = pszBuffer; - LocalFree(pszBuffer); - } - } - - return; - } - - // Get module filename - CHAR szCrashedModulePath[MAX_PATH]; - GetModuleFileNameExA(GetCurrentProcess(), hCrashedModule, szCrashedModulePath, sizeof(szCrashedModulePath)); - - const CHAR* pszCrashedModuleFileName = strrchr(szCrashedModulePath, '\\') + 1; - - // Get relative address - LPCSTR pModuleBase = reinterpret_cast<LPCSTR>(pCrashAddress - reinterpret_cast<LPCSTR>(hCrashedModule)); - - m_svCrashedModule = pszCrashedModuleFileName; - m_svCrashedOffset = fmt::format("{:#x}", reinterpret_cast<DWORD64>(pModuleBase)); -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the exception null terminated stirng -//----------------------------------------------------------------------------- - -const CHAR* CCrashHandler::GetExceptionString() const -{ - return GetExceptionString(m_pExceptionInfos->ExceptionRecord->ExceptionCode); -} - -//----------------------------------------------------------------------------- -// Purpose: Gets the exception null terminated stirng -//----------------------------------------------------------------------------- -const CHAR* CCrashHandler::GetExceptionString(DWORD dwExceptionCode) const -{ - // clang-format off - switch (dwExceptionCode) - { - case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION"; - case EXCEPTION_DATATYPE_MISALIGNMENT: return "EXCEPTION_DATATYPE_MISALIGNMENT"; - case EXCEPTION_BREAKPOINT: return "EXCEPTION_BREAKPOINT"; - case EXCEPTION_SINGLE_STEP: return "EXCEPTION_SINGLE_STEP"; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; - case EXCEPTION_FLT_DENORMAL_OPERAND: return "EXCEPTION_FLT_DENORMAL_OPERAND"; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; - case EXCEPTION_FLT_INEXACT_RESULT: return "EXCEPTION_FLT_INEXACT_RESULT"; - case EXCEPTION_FLT_INVALID_OPERATION: return "EXCEPTION_FLT_INVALID_OPERATION"; - case EXCEPTION_FLT_OVERFLOW: return "EXCEPTION_FLT_OVERFLOW"; - case EXCEPTION_FLT_STACK_CHECK: return "EXCEPTION_FLT_STACK_CHECK"; - case EXCEPTION_FLT_UNDERFLOW: return "EXCEPTION_FLT_UNDERFLOW"; - case EXCEPTION_INT_DIVIDE_BY_ZERO: return "EXCEPTION_INT_DIVIDE_BY_ZERO"; - case EXCEPTION_INT_OVERFLOW: return "EXCEPTION_INT_OVERFLOW"; - case EXCEPTION_PRIV_INSTRUCTION: return "EXCEPTION_PRIV_INSTRUCTION"; - case EXCEPTION_IN_PAGE_ERROR: return "EXCEPTION_IN_PAGE_ERROR"; - case EXCEPTION_ILLEGAL_INSTRUCTION: return "EXCEPTION_ILLEGAL_INSTRUCTION"; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; - case EXCEPTION_STACK_OVERFLOW: return "EXCEPTION_STACK_OVERFLOW"; - case EXCEPTION_INVALID_DISPOSITION: return "EXCEPTION_INVALID_DISPOSITION"; - case EXCEPTION_GUARD_PAGE: return "EXCEPTION_GUARD_PAGE"; - case EXCEPTION_INVALID_HANDLE: return "EXCEPTION_INVALID_HANDLE"; - case 3765269347: return "RUNTIME_EXCEPTION"; - } - // clang-format on - return "UNKNOWN_EXCEPTION"; -} - -//----------------------------------------------------------------------------- -// Purpose: Returns true if exception is known -//----------------------------------------------------------------------------- -bool CCrashHandler::IsExceptionFatal() const -{ - return IsExceptionFatal(m_pExceptionInfos->ExceptionRecord->ExceptionCode); -} - -//----------------------------------------------------------------------------- -// Purpose: Returns true if exception is known -//----------------------------------------------------------------------------- -bool CCrashHandler::IsExceptionFatal(DWORD dwExceptionCode) const -{ - // clang-format off - switch (dwExceptionCode) - { - case EXCEPTION_ACCESS_VIOLATION: - case EXCEPTION_DATATYPE_MISALIGNMENT: - case EXCEPTION_BREAKPOINT: - case EXCEPTION_SINGLE_STEP: - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - case EXCEPTION_FLT_DENORMAL_OPERAND: - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - case EXCEPTION_FLT_INEXACT_RESULT: - case EXCEPTION_FLT_INVALID_OPERATION: - case EXCEPTION_FLT_OVERFLOW: - case EXCEPTION_FLT_STACK_CHECK: - case EXCEPTION_FLT_UNDERFLOW: - case EXCEPTION_INT_DIVIDE_BY_ZERO: - case EXCEPTION_INT_OVERFLOW: - case EXCEPTION_PRIV_INSTRUCTION: - case EXCEPTION_IN_PAGE_ERROR: - case EXCEPTION_ILLEGAL_INSTRUCTION: - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - case EXCEPTION_STACK_OVERFLOW: - case EXCEPTION_INVALID_DISPOSITION: - case EXCEPTION_GUARD_PAGE: - case EXCEPTION_INVALID_HANDLE: - return true; - } - // clang-format on - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Shows a message box -//----------------------------------------------------------------------------- -void CCrashHandler::ShowPopUpMessage() -{ - if (m_bHasShownCrashMsg) - return; - - m_bHasShownCrashMsg = true; - - if (!IsDedicatedServer()) - { - std::string svMessage = fmt::format( - "Northstar has crashed! Crash info can be found at {}/logs!\n\n{}\n{} + {}", - GetNorthstarPrefix(), - GetExceptionString(), - m_svCrashedModule, - m_svCrashedOffset); - - MessageBoxA(GetForegroundWindow(), svMessage.c_str(), "Northstar has crashed!", MB_ICONERROR | MB_OK); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrashHandler::FormatException() -{ - spdlog::error("-------------------------------------------"); - spdlog::error("Northstar has crashed!"); - spdlog::error("\tVersion: {}", version); - if (!m_svError.empty()) - { - spdlog::info("\tEncountered an error when gathering crash information!"); - spdlog::info("\tWinApi Error: {}", m_svError.c_str()); - } - spdlog::error("\t{}", GetExceptionString()); - - DWORD dwExceptionCode = m_pExceptionInfos->ExceptionRecord->ExceptionCode; - if (dwExceptionCode == EXCEPTION_ACCESS_VIOLATION || dwExceptionCode == EXCEPTION_IN_PAGE_ERROR) - { - ULONG_PTR uExceptionInfo0 = m_pExceptionInfos->ExceptionRecord->ExceptionInformation[0]; - ULONG_PTR uExceptionInfo1 = m_pExceptionInfos->ExceptionRecord->ExceptionInformation[1]; - - if (!uExceptionInfo0) - spdlog::error("\tAttempted to read from: {:#x}", uExceptionInfo1); - else if (uExceptionInfo0 == 1) - spdlog::error("\tAttempted to write to: {:#x}", uExceptionInfo1); - else if (uExceptionInfo0 == 8) - spdlog::error("\tData Execution Prevention (DEP) at: {:#x}", uExceptionInfo1); - else - spdlog::error("\tUnknown access violation at: {:#x}", uExceptionInfo1); - } - - spdlog::error("\tAt: {} + {}", m_svCrashedModule, m_svCrashedOffset); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrashHandler::FormatCallstack() -{ - spdlog::error("Callstack:"); - - PVOID pFrames[CRASHHANDLER_MAX_FRAMES]; - - int iFrames = RtlCaptureStackBackTrace(0, CRASHHANDLER_MAX_FRAMES, pFrames, NULL); - - // Above call gives us frames after the crash occured, we only want to print the ones starting from where - // the exception was called - bool bSkipExceptionHandlingFrames = true; - - // We ran into an error when getting the offset, just print all frames - if (m_svCrashedOffset.empty()) - bSkipExceptionHandlingFrames = false; - - for (int i = 0; i < iFrames; i++) - { - const CHAR* pszModuleFileName; - - LPCSTR pAddress = static_cast<LPCSTR>(pFrames[i]); - HMODULE hModule; - if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, pAddress, &hModule)) - { - pszModuleFileName = CRASHHANDLER_GETMODULEHANDLE_FAIL; - // If we fail here it's too late to do any damage control - } - else - { - CHAR szModulePath[MAX_PATH]; - GetModuleFileNameExA(GetCurrentProcess(), hModule, szModulePath, sizeof(szModulePath)); - pszModuleFileName = strrchr(szModulePath, '\\') + 1; - } - - // Get relative address - LPCSTR pCrashOffset = reinterpret_cast<LPCSTR>(pAddress - reinterpret_cast<LPCSTR>(hModule)); - std::string svCrashOffset = fmt::format("{:#x}", reinterpret_cast<DWORD64>(pCrashOffset)); - - // Should we log this frame - if (bSkipExceptionHandlingFrames) - { - if (m_svCrashedModule == pszModuleFileName && m_svCrashedOffset == svCrashOffset) - { - bSkipExceptionHandlingFrames = false; - } - else - { - continue; - } - } - - // Log module + offset - spdlog::error("\t{} + {:#x}", pszModuleFileName, reinterpret_cast<DWORD64>(pCrashOffset)); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrashHandler::FormatFlags(const CHAR* pszRegister, DWORD nValue) -{ - spdlog::error("\t{}: {:#b}", pszRegister, nValue); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrashHandler::FormatIntReg(const CHAR* pszRegister, DWORD64 nValue) -{ - spdlog::error("\t{}: {:#x}", pszRegister, nValue); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrashHandler::FormatFloatReg(const CHAR* pszRegister, M128A nValue) -{ - DWORD nVec[4] = { - static_cast<DWORD>(nValue.Low & UINT_MAX), - static_cast<DWORD>(nValue.Low >> 32), - static_cast<DWORD>(nValue.High & UINT_MAX), - static_cast<DWORD>(nValue.High >> 32)}; - - spdlog::error( - "\t{}: [ {:G}, {:G}, {:G}, {:G} ]; [ {:#x}, {:#x}, {:#x}, {:#x} ]", - pszRegister, - static_cast<float>(nVec[0]), - static_cast<float>(nVec[1]), - static_cast<float>(nVec[2]), - static_cast<float>(nVec[3]), - nVec[0], - nVec[1], - nVec[2], - nVec[3]); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrashHandler::FormatRegisters() -{ - spdlog::error("Registers:"); - - PCONTEXT pContext = m_pExceptionInfos->ContextRecord; - - FormatFlags("Flags:", pContext->ContextFlags); - - FormatIntReg("Rax", pContext->Rax); - FormatIntReg("Rcx", pContext->Rcx); - FormatIntReg("Rdx", pContext->Rdx); - FormatIntReg("Rbx", pContext->Rbx); - FormatIntReg("Rsp", pContext->Rsp); - FormatIntReg("Rbp", pContext->Rbp); - FormatIntReg("Rsi", pContext->Rsi); - FormatIntReg("Rdi", pContext->Rdi); - FormatIntReg("R8 ", pContext->R8); - FormatIntReg("R9 ", pContext->R9); - FormatIntReg("R10", pContext->R10); - FormatIntReg("R11", pContext->R11); - FormatIntReg("R12", pContext->R12); - FormatIntReg("R13", pContext->R13); - FormatIntReg("R14", pContext->R14); - FormatIntReg("R15", pContext->R15); - FormatIntReg("Rip", pContext->Rip); - - FormatFloatReg("Xmm0 ", pContext->Xmm0); - FormatFloatReg("Xmm1 ", pContext->Xmm1); - FormatFloatReg("Xmm2 ", pContext->Xmm2); - FormatFloatReg("Xmm3 ", pContext->Xmm3); - FormatFloatReg("Xmm4 ", pContext->Xmm4); - FormatFloatReg("Xmm5 ", pContext->Xmm5); - FormatFloatReg("Xmm6 ", pContext->Xmm6); - FormatFloatReg("Xmm7 ", pContext->Xmm7); - FormatFloatReg("Xmm8 ", pContext->Xmm8); - FormatFloatReg("Xmm9 ", pContext->Xmm9); - FormatFloatReg("Xmm10", pContext->Xmm10); - FormatFloatReg("Xmm11", pContext->Xmm11); - FormatFloatReg("Xmm12", pContext->Xmm12); - FormatFloatReg("Xmm13", pContext->Xmm13); - FormatFloatReg("Xmm14", pContext->Xmm14); - FormatFloatReg("Xmm15", pContext->Xmm15); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrashHandler::FormatLoadedMods() -{ - if (g_pModManager) - { - spdlog::error("Enabled mods:"); - for (const Mod& mod : g_pModManager->m_LoadedMods) - { - if (!mod.m_bEnabled) - continue; - - spdlog::error("\t{}", mod.Name); - } - - spdlog::error("Disabled mods:"); - for (const Mod& mod : g_pModManager->m_LoadedMods) - { - if (mod.m_bEnabled) - continue; - - spdlog::error("\t{}", mod.Name); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrashHandler::FormatLoadedPlugins() -{ - if (g_pPluginManager) - { - spdlog::error("Loaded Plugins:"); - for (const Plugin& plugin : g_pPluginManager->m_vLoadedPlugins) - { - spdlog::error("\t{}", plugin.name); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrashHandler::FormatModules() -{ - spdlog::error("Loaded modules:"); - HMODULE hModules[1024]; - DWORD cbNeeded; - - if (EnumProcessModules(GetCurrentProcess(), hModules, sizeof(hModules), &cbNeeded)) - { - for (DWORD i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) - { - CHAR szModulePath[MAX_PATH]; - if (GetModuleFileNameExA(GetCurrentProcess(), hModules[i], szModulePath, sizeof(szModulePath))) - { - const CHAR* pszModuleFileName = strrchr(szModulePath, '\\') + 1; - spdlog::error("\t{}", pszModuleFileName); - } - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: Writes minidump to disk -//----------------------------------------------------------------------------- -void CCrashHandler::WriteMinidump() -{ - 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()); - - HANDLE 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 = m_pExceptionInfos; - 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()); -} - -//----------------------------------------------------------------------------- -CCrashHandler* g_pCrashHandler = nullptr; |