assert.cpp (4403B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "assert.h" 5 #include "crash_handler.h" 6 #include <cstdio> 7 #include <cstdlib> 8 #include <mutex> 9 10 #if defined(_WIN32) 11 #include "windows_headers.h" 12 #include <intrin.h> 13 #include <tlhelp32.h> 14 #endif 15 16 #ifdef __clang__ 17 #pragma clang diagnostic ignored "-Winvalid-noreturn" 18 #endif 19 20 static std::mutex s_AssertFailedMutex; 21 22 static inline void FreezeThreads(void** ppHandle) 23 { 24 #if defined(_WIN32) 25 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 26 if (hSnapshot != INVALID_HANDLE_VALUE) 27 { 28 THREADENTRY32 threadEntry; 29 if (Thread32First(hSnapshot, &threadEntry)) 30 { 31 do 32 { 33 if (threadEntry.th32ThreadID == GetCurrentThreadId()) 34 continue; 35 36 HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID); 37 if (hThread != NULL) 38 { 39 SuspendThread(hThread); 40 CloseHandle(hThread); 41 } 42 } while (Thread32Next(hSnapshot, &threadEntry)); 43 } 44 } 45 46 *ppHandle = (void*)hSnapshot; 47 #else 48 *ppHandle = nullptr; 49 #endif 50 } 51 52 static inline void ResumeThreads(void* pHandle) 53 { 54 #if defined(_WIN32) 55 HANDLE hSnapshot = (HANDLE)pHandle; 56 if (pHandle != INVALID_HANDLE_VALUE) 57 { 58 THREADENTRY32 threadEntry; 59 if (Thread32First(hSnapshot, &threadEntry)) 60 { 61 do 62 { 63 if (threadEntry.th32ThreadID == GetCurrentThreadId()) 64 continue; 65 66 HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID); 67 if (hThread != NULL) 68 { 69 ResumeThread(hThread); 70 CloseHandle(hThread); 71 } 72 } while (Thread32Next(hSnapshot, &threadEntry)); 73 } 74 CloseHandle(hSnapshot); 75 } 76 #else 77 #endif 78 } 79 80 void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine) 81 { 82 std::lock_guard<std::mutex> guard(s_AssertFailedMutex); 83 84 void* pHandle; 85 FreezeThreads(&pHandle); 86 87 char szMsg[512]; 88 std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine); 89 90 #if defined(_WIN32) 91 SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); 92 WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast<DWORD>(std::strlen(szMsg)), NULL, NULL); 93 OutputDebugStringA(szMsg); 94 95 std::snprintf( 96 szMsg, sizeof(szMsg), 97 "%s in function %s (%s:%u)\nPress Abort to exit, Retry to break to debugger, or Ignore to attempt to continue.", 98 szMessage, szFunction, szFile, uLine); 99 100 int result = MessageBoxA(NULL, szMsg, NULL, MB_ABORTRETRYIGNORE | MB_ICONERROR); 101 if (result == IDRETRY) 102 { 103 __debugbreak(); 104 } 105 else if (result != IDIGNORE) 106 { 107 CrashHandler::WriteDumpForCaller(); 108 TerminateProcess(GetCurrentProcess(), 0xBAADC0DE); 109 } 110 #else 111 std::fputs(szMsg, stderr); 112 CrashHandler::WriteDumpForCaller(); 113 std::fputs("Aborting application.\n", stderr); 114 std::fflush(stderr); 115 std::abort(); 116 #endif 117 118 ResumeThreads(pHandle); 119 } 120 121 [[noreturn]] void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine) 122 { 123 std::lock_guard<std::mutex> guard(s_AssertFailedMutex); 124 125 void* pHandle; 126 FreezeThreads(&pHandle); 127 128 char szMsg[512]; 129 std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine); 130 131 #if defined(_WIN32) 132 SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); 133 WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast<DWORD>(std::strlen(szMsg)), NULL, NULL); 134 OutputDebugStringA(szMsg); 135 136 std::snprintf(szMsg, sizeof(szMsg), 137 "%s in function %s (%s:%u)\nDo you want to attempt to break into a debugger? Pressing Cancel will " 138 "abort the application.", 139 szMessage, szFunction, szFile, uLine); 140 141 int result = MessageBoxA(NULL, szMsg, NULL, MB_OKCANCEL | MB_ICONERROR); 142 if (result == IDOK) 143 __debugbreak(); 144 else 145 CrashHandler::WriteDumpForCaller(); 146 147 TerminateProcess(GetCurrentProcess(), 0xBAADC0DE); 148 #else 149 std::fputs(szMsg, stderr); 150 CrashHandler::WriteDumpForCaller(); 151 std::fputs("Aborting application.\n", stderr); 152 std::fflush(stderr); 153 std::abort(); 154 #endif 155 156 ResumeThreads(pHandle); 157 }