Common: Report assertion failure/panic message for Android

This commit is contained in:
Stenzek 2024-12-08 02:56:40 +10:00
parent 3a661a1c3d
commit 7f3687de81
No known key found for this signature in database
3 changed files with 61 additions and 50 deletions

View File

@ -3,15 +3,17 @@
#include "assert.h" #include "assert.h"
#include "crash_handler.h" #include "crash_handler.h"
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <mutex>
#if defined(_WIN32) #ifdef _WIN32
#include "windows_headers.h" #include "windows_headers.h"
#include <intrin.h> #include <intrin.h>
#include <tlhelp32.h> #include <tlhelp32.h>
#endif
#include <mutex>
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic ignored "-Winvalid-noreturn" #pragma clang diagnostic ignored "-Winvalid-noreturn"
@ -19,9 +21,8 @@
static std::mutex s_AssertFailedMutex; static std::mutex s_AssertFailedMutex;
static inline void FreezeThreads(void** ppHandle) static HANDLE FreezeThreads()
{ {
#if defined(_WIN32)
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnapshot != INVALID_HANDLE_VALUE) if (hSnapshot != INVALID_HANDLE_VALUE)
{ {
@ -43,17 +44,12 @@ static inline void FreezeThreads(void** ppHandle)
} }
} }
*ppHandle = (void*)hSnapshot; return hSnapshot;
#else
*ppHandle = nullptr;
#endif
} }
static inline void ResumeThreads(void* pHandle) static void ResumeThreads(HANDLE hSnapshot)
{ {
#if defined(_WIN32) if (hSnapshot != INVALID_HANDLE_VALUE)
HANDLE hSnapshot = (HANDLE)pHandle;
if (pHandle != INVALID_HANDLE_VALUE)
{ {
THREADENTRY32 threadEntry; THREADENTRY32 threadEntry;
if (Thread32First(hSnapshot, &threadEntry)) if (Thread32First(hSnapshot, &threadEntry))
@ -73,21 +69,42 @@ static inline void ResumeThreads(void* pHandle)
} }
CloseHandle(hSnapshot); CloseHandle(hSnapshot);
} }
}
#else #else
#ifdef __ANDROID__
// Define as a weak symbol for ancient devices that don't have it.
extern "C" __attribute__((weak)) void android_set_abort_message(const char*);
#endif
[[noreturn]] ALWAYS_INLINE static void AbortWithMessage(const char* szMsg)
{
#ifndef __ANDROID__
std::fputs(szMsg, stderr);
CrashHandler::WriteDumpForCaller();
std::fputs("Aborting application.\n", stderr);
std::fflush(stderr);
std::abort();
#else
if (&android_set_abort_message)
android_set_abort_message(szMsg);
std::abort();
#endif #endif
} }
#endif // _WIN32
void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine) void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine)
{ {
std::lock_guard<std::mutex> guard(s_AssertFailedMutex);
void* pHandle;
FreezeThreads(&pHandle);
char szMsg[512]; char szMsg[512];
std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine); std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine);
#if defined(_WIN32) #if defined(_WIN32)
std::unique_lock lock(s_AssertFailedMutex);
HANDLE pHandle = FreezeThreads();
SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast<DWORD>(std::strlen(szMsg)), NULL, NULL); WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast<DWORD>(std::strlen(szMsg)), NULL, NULL);
OutputDebugStringA(szMsg); OutputDebugStringA(szMsg);
@ -107,28 +124,22 @@ void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char*
CrashHandler::WriteDumpForCaller(); CrashHandler::WriteDumpForCaller();
TerminateProcess(GetCurrentProcess(), 0xBAADC0DE); TerminateProcess(GetCurrentProcess(), 0xBAADC0DE);
} }
#else
std::fputs(szMsg, stderr);
CrashHandler::WriteDumpForCaller();
std::fputs("Aborting application.\n", stderr);
std::fflush(stderr);
std::abort();
#endif
ResumeThreads(pHandle); ResumeThreads(pHandle);
#else
AbortWithMessage(szMsg);
#endif
} }
[[noreturn]] void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine) [[noreturn]] void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine)
{ {
std::lock_guard<std::mutex> guard(s_AssertFailedMutex);
void* pHandle;
FreezeThreads(&pHandle);
char szMsg[512]; char szMsg[512];
std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine); std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine);
#if defined(_WIN32) #if defined(_WIN32)
std::unique_lock guard(s_AssertFailedMutex);
HANDLE pHandle = FreezeThreads();
SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast<DWORD>(std::strlen(szMsg)), NULL, NULL); WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast<DWORD>(std::strlen(szMsg)), NULL, NULL);
OutputDebugStringA(szMsg); OutputDebugStringA(szMsg);
@ -145,13 +156,9 @@ void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char*
CrashHandler::WriteDumpForCaller(); CrashHandler::WriteDumpForCaller();
TerminateProcess(GetCurrentProcess(), 0xBAADC0DE); TerminateProcess(GetCurrentProcess(), 0xBAADC0DE);
#else
std::fputs(szMsg, stderr);
CrashHandler::WriteDumpForCaller();
std::fputs("Aborting application.\n", stderr);
std::fflush(stderr);
std::abort();
#endif
ResumeThreads(pHandle); ResumeThreads(pHandle);
#else
AbortWithMessage(szMsg);
#endif
} }

View File

@ -9,27 +9,31 @@ void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char*
[[noreturn]] void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine); [[noreturn]] void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine);
#define Assert(expr) \ #define Assert(expr) \
if (!(expr)) \ do \
{ \ { \
if (!(expr)) \
Y_OnAssertFailed("Assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \ Y_OnAssertFailed("Assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \
} } while (0)
#define AssertMsg(expr, msg) \ #define AssertMsg(expr, msg) \
if (!(expr)) \ do \
{ \ { \
if (!(expr)) \
Y_OnAssertFailed("Assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \ Y_OnAssertFailed("Assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \
} } while (0)
#if defined(_DEBUG) || defined(_DEVEL) #if defined(_DEBUG) || defined(_DEVEL)
#define DebugAssert(expr) \ #define DebugAssert(expr) \
if (!(expr)) \ do \
{ \ { \
if (!(expr)) \
Y_OnAssertFailed("Debug assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \ Y_OnAssertFailed("Debug assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \
} } while (0)
#define DebugAssertMsg(expr, msg) \ #define DebugAssertMsg(expr, msg) \
if (!(expr)) \ do \
{ \ { \
if (!(expr)) \
Y_OnAssertFailed("Debug assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \ Y_OnAssertFailed("Debug assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \
} } while (0)
#else #else
#define DebugAssert(expr) #define DebugAssert(expr)
#define DebugAssertMsg(expr, msg) #define DebugAssertMsg(expr, msg)

View File

@ -367,7 +367,7 @@ void CrashHandler::WriteDumpForCaller()
LogCallstack(0, nullptr); LogCallstack(0, nullptr);
} }
#else #elif !defined(__ANDROID__)
bool CrashHandler::Install(CleanupHandler cleanup_handler) bool CrashHandler::Install(CleanupHandler cleanup_handler)
{ {