/* PCSX2 - PS2 Emulator for PCs * Copyright (C) 2002-2010 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with PCSX2. * If not, see . */ #include "Threading.h" #include "General.h" #include "Assertions.h" #include "CrashHandler.h" #include #include "fmt/core.h" #ifdef _WIN32 #include "RedtapeWindows.h" #include #include #endif #ifdef __UNIX__ #include #endif static std::mutex s_assertion_failed_mutex; static inline void FreezeThreads(void** handle) { #if defined(_WIN32) HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (snapshot != INVALID_HANDLE_VALUE) { THREADENTRY32 threadEntry; if (Thread32First(snapshot, &threadEntry)) { do { if (threadEntry.th32ThreadID == GetCurrentThreadId()) continue; HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID); if (hThread != nullptr) { SuspendThread(hThread); CloseHandle(hThread); } } while (Thread32Next(snapshot, &threadEntry)); } } *handle = static_cast(snapshot); #else * handle = nullptr; #endif } static inline void ResumeThreads(void* handle) { #if defined(_WIN32) if (handle != INVALID_HANDLE_VALUE) { THREADENTRY32 threadEntry; if (Thread32First(reinterpret_cast(handle), &threadEntry)) { do { if (threadEntry.th32ThreadID == GetCurrentThreadId()) continue; HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID); if (hThread != nullptr) { ResumeThread(hThread); CloseHandle(hThread); } } while (Thread32Next(reinterpret_cast(handle), &threadEntry)); } CloseHandle(reinterpret_cast(handle)); } #else #endif } void pxOnAssertFail(const char* file, int line, const char* func, const char* msg) { std::unique_lock guard(s_assertion_failed_mutex); void* handle; FreezeThreads(&handle); char full_msg[512]; std::snprintf(full_msg, sizeof(full_msg), "%s:%d: assertion failed in function %s: %s\n", file, line, func, msg); #if defined(_WIN32) HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE); if (error_handle != INVALID_HANDLE_VALUE) WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), full_msg, static_cast(std::strlen(full_msg)), NULL, NULL); OutputDebugStringA(full_msg); std::snprintf( full_msg, sizeof(full_msg), "Assertion failed in function %s (%s:%d):\n\n%s\n\nPress Abort to exit, Retry to break to debugger, or Ignore to attempt to continue.", func, file, line, msg); int result = MessageBoxA(NULL, full_msg, NULL, MB_ABORTRETRYIGNORE | MB_ICONERROR); if (result == IDRETRY) { __debugbreak(); } else if (result != IDIGNORE) { // try to save a crash dump before exiting CrashHandler::WriteDumpForCaller(); TerminateProcess(GetCurrentProcess(), 0xBAADC0DE); } #else fputs(full_msg, stderr); fputs("\nAborting application.\n", stderr); fflush(stderr); AbortWithMessage(full_msg); #endif ResumeThreads(handle); }