duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 }