Common: Report assertion failure/panic message for Android
This commit is contained in:
parent
3a661a1c3d
commit
7f3687de81
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue