Added Linux/signal exception handling.

This commit is contained in:
Branimir Karadžić
2025-08-23 09:20:41 -07:00
parent d858859d17
commit d2b8b1aab5
8 changed files with 146 additions and 28 deletions

View File

@@ -115,12 +115,13 @@ namespace bx
/// Assert handler function. /// Assert handler function.
/// ///
/// @param[in] _location Source code location where function is called. /// @param[in] _location Source code location where function is called.
/// @param[in] _skip Skip top N stack frames.
/// @param[in] _format Printf style format. /// @param[in] _format Printf style format.
/// @param[in] _argList Arguments for `_format` specification. /// @param[in] _argList Arguments for `_format` specification.
/// ///
/// @returns True if assert should stop code execution, otherwise returns false. /// @returns True if assert should stop code execution, otherwise returns false.
/// ///
typedef bool (*AssertHandlerFn)(const Location& _location, const char* _format, va_list _argList); typedef bool (*AssertHandlerFn)(const Location& _location, uint32_t _skip, const char* _format, va_list _argList);
/// Set assert handler function. /// Set assert handler function.
/// ///
@@ -133,12 +134,13 @@ namespace bx
/// Assert function calls AssertHandlerFn. /// Assert function calls AssertHandlerFn.
/// ///
/// @param[in] _location Source code location where function is called. /// @param[in] _location Source code location where function is called.
/// @param[in] _skip Skip top N stack frames.
/// @param[in] _format Printf style format. /// @param[in] _format Printf style format.
/// @param[in] ... Arguments for `_format` specification. /// @param[in] ... Arguments for `_format` specification.
/// ///
/// @returns True if assert should stop code execution, otherwise returns false. /// @returns True if assert should stop code execution, otherwise returns false.
/// ///
bool assertFunction(const Location& _location, const char* _format, ...); bool assertFunction(const Location& _location, uint32_t _skip, const char* _format, ...);
/// Arithmetic type `Ty` limits. /// Arithmetic type `Ty` limits.
template<typename Ty, bool SignT = isSigned<Ty>()> template<typename Ty, bool SignT = isSigned<Ty>()>

View File

@@ -260,22 +260,22 @@ extern "C" void* __cdecl _alloca(size_t _size);
bx::debugPrintf("%s(%d): BX " _format "\n", _location.filePath, _location.line, ##__VA_ARGS__); \ bx::debugPrintf("%s(%d): BX " _format "\n", _location.filePath, _location.line, ##__VA_ARGS__); \
BX_MACRO_BLOCK_END BX_MACRO_BLOCK_END
#define _BX_ASSERT(_condition, _format, ...) \ #define _BX_ASSERT(_condition, _format, ...) \
BX_MACRO_BLOCK_BEGIN \ BX_MACRO_BLOCK_BEGIN \
if (!BX_IGNORE_C4127(_condition) \ if (!BX_IGNORE_C4127(_condition) \
&& bx::assertFunction(bx::Location::current(), "ASSERT %s -> " _format, #_condition, ##__VA_ARGS__) ) \ && bx::assertFunction(bx::Location::current(), 0, "ASSERT %s -> " _format, #_condition, ##__VA_ARGS__) ) \
{ \ { \
bx::debugBreak(); \ bx::debugBreak(); \
} \ } \
BX_MACRO_BLOCK_END BX_MACRO_BLOCK_END
#define _BX_ASSERT_LOC(_location, _condition, _format, ...) \ #define _BX_ASSERT_LOC(_location, _condition, _format, ...) \
BX_MACRO_BLOCK_BEGIN \ BX_MACRO_BLOCK_BEGIN \
if (!BX_IGNORE_C4127(_condition) \ if (!BX_IGNORE_C4127(_condition) \
&& bx::assertFunction(_location, "ASSERT %s -> " _format, #_condition, ##__VA_ARGS__) ) \ && bx::assertFunction(_location, 0, "ASSERT %s -> " _format, #_condition, ##__VA_ARGS__) ) \
{ \ { \
bx::debugBreak(); \ bx::debugBreak(); \
} \ } \
BX_MACRO_BLOCK_END BX_MACRO_BLOCK_END
#define _BX_WARN(_condition, _format, ...) \ #define _BX_WARN(_condition, _format, ...) \

View File

@@ -59,7 +59,7 @@ namespace bx
void* exec(const char* const* _argv); void* exec(const char* const* _argv);
/// ///
[[noreturn]] void exit(int32_t _exitCode); [[noreturn]] void exit(int32_t _exitCode, bool _cleanup = true);
/// ///
void* memoryMap(void* _address, size_t _size, Error* _err); void* memoryMap(void* _address, size_t _size, Error* _err);

View File

@@ -5,6 +5,7 @@
#include <bx/debug.h> #include <bx/debug.h>
#include <bx/readerwriter.h> #include <bx/readerwriter.h>
#include <bx/os.h>
#if !BX_CRT_NONE #if !BX_CRT_NONE
# include <string.h> // memcpy, memmove, memset # include <string.h> // memcpy, memmove, memset
@@ -22,7 +23,7 @@ namespace bx
return LocationFull(_function, _filePath, _line); return LocationFull(_function, _filePath, _line);
} }
static bool defaultAssertHandler(const Location& _location, const char* _format, va_list _argList) static bool defaultAssertHandler(const Location& _location, uint32_t _skip, const char* _format, va_list _argList)
{ {
char temp[8192]; char temp[8192];
int32_t total = 0; int32_t total = 0;
@@ -41,7 +42,7 @@ namespace bx
total += write(&smb, "\n\n", &err); total += write(&smb, "\n\n", &err);
uintptr_t stack[32]; uintptr_t stack[32];
const uint32_t num = getCallStack(2 /* skip self */, BX_COUNTOF(stack), stack); const uint32_t num = getCallStack(2 /* skip self */ + _skip, BX_COUNTOF(stack), stack);
total += writeCallstack(&smb, stack, num, &err); total += writeCallstack(&smb, stack, num, &err);
total += write(&smb, &err, total += write(&smb, &err,
@@ -80,11 +81,11 @@ namespace bx
} }
} }
bool assertFunction(const Location& _location, const char* _format, ...) bool assertFunction(const Location& _location, uint32_t _skip, const char* _format, ...)
{ {
va_list argList; va_list argList;
va_start(argList, _format); va_start(argList, _format);
const bool result = s_assertHandler(_location, _format, argList); const bool result = s_assertHandler(_location, _skip, _format, argList);
va_end(argList); va_end(argList);
return result; return result;

View File

@@ -22,6 +22,15 @@
# include <cxxabi.h> // abi::__cxa_demangle # include <cxxabi.h> // abi::__cxa_demangle
#endif // BX_CONFIG_CALLSTACK_* #endif // BX_CONFIG_CALLSTACK_*
#ifndef BX_CONFIG_EXCEPTION_HANDLING_USE_SIGNALS
# define BX_CONFIG_EXCEPTION_HANDLING_USE_SIGNALS 0 \
| (BX_PLATFORM_LINUX && !BX_CRT_NONE)
#endif // BX_CONFIG_EXCEPTION_HANDLING_USE_SIGNALS
#if BX_CONFIG_EXCEPTION_HANDLING_USE_SIGNALS
# include <signal.h>
#endif // BX_CONFIG_EXCEPTION_HANDLING_USE_SIGNALS
#if BX_CRT_NONE #if BX_CRT_NONE
# include <bx/crt0.h> # include <bx/crt0.h>
#elif BX_PLATFORM_ANDROID #elif BX_PLATFORM_ANDROID
@@ -283,6 +292,9 @@ namespace bx
int32_t total = write(_writer, _err, "Callstack (%d):\n", _num); int32_t total = write(_writer, _err, "Callstack (%d):\n", _num);
constexpr uint32_t kWidth = 40;
total += write(_writer, _err, "\t #: %-*s Line: PC --- Function ---\n", kWidth, "File ---");
CallbackData cbData; CallbackData cbData;
for (uint32_t ii = 0; ii < _num && _err->isOk(); ++ii) for (uint32_t ii = 0; ii < _num && _err->isOk(); ++ii)
@@ -311,13 +323,12 @@ namespace bx
demangledName = "???"; demangledName = "???";
} }
constexpr uint32_t width = 40; const StringView fn = strTail(cbData.fileName, kWidth);
const StringView fn = strTail(cbData.fileName, width);
total += write(_writer, _err total += write(_writer, _err
, "\t%2d: %-*S % 5d: %p %S\n" , "\t%2d: %-*S % 5d: %p %S\n"
, ii , ii
, width , kWidth
, &fn , &fn
, cbData.line , cbData.line
, _stack[ii] , _stack[ii]
@@ -363,4 +374,89 @@ namespace bx
writeCallstack(getDebugOut(), stack, num, ErrorIgnore{}); writeCallstack(getDebugOut(), stack, num, ErrorIgnore{});
} }
#if BX_CONFIG_EXCEPTION_HANDLING_USE_SIGNALS
struct Signal
{
int32_t signalId;
const char* name;
};
static const Signal s_signal[] =
{ // Linux
{ /* 4 */ SIGILL, "SIGILL - Illegal instruction signal." },
{ /* 6 */ SIGABRT, "SIGABRT - Abort signal." },
{ /* 8 */ SIGFPE, "SIGFPE - Floating point error signal." },
{ /* 11 */ SIGSEGV, "SIGSEGV - Segmentation violation signal." },
};
class ExceptionHandler
{
public:
ExceptionHandler()
{
stack_t stack;
stack.ss_sp = s_stack;
stack.ss_size = sizeof(s_stack);
stack.ss_flags = 0;
sigaltstack(&stack, &m_oldStack);
struct sigaction sa;
sa.sa_handler = NULL;
sa.sa_sigaction = signalActionHandler;
sa.sa_mask = { 0 };
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
sa.sa_restorer = NULL;
for (uint32_t ii = 0; ii < BX_COUNTOF(s_signal); ++ii)
{
sigaction(s_signal[ii].signalId, &sa, &m_oldSignalAction[ii]);
}
}
~ExceptionHandler()
{
}
static void signalActionHandler(int32_t _signalId, siginfo_t* _info, void* _context)
{
BX_UNUSED(_context);
const char* name = "Unknown signal?";
for (uint32_t ii = 0; ii < BX_COUNTOF(s_signal); ++ii)
{
const Signal& signal = s_signal[ii];
if (signal.signalId == _signalId)
{
name = signal.name;
break;
}
}
if (assertFunction(Location("Exception Handler", -1), 2
, "%s SIGNAL %d, ERRNO %d, CODE %d"
, name
, _info->si_signo
, _info->si_errno
, _info->si_code
) )
{
exit(kExitFailure, false);
}
}
static constexpr uint32_t kExceptionStackSize = 64<<10;
static char s_stack[kExceptionStackSize];
stack_t m_oldStack;
struct sigaction m_oldSignalAction[BX_COUNTOF(s_signal)];
};
char ExceptionHandler::s_stack[kExceptionStackSize];
static ExceptionHandler s_exceptionHandler;
#endif // BX_PLATFORM_LINUX
} // namespace bx } // namespace bx

View File

@@ -361,9 +361,18 @@ namespace bx
#endif // BX_PLATFORM_LINUX #endif // BX_PLATFORM_LINUX
} }
void exit(int32_t _exitCode) void exit(int32_t _exitCode, bool _cleanup)
{ {
::exit(_exitCode); if (_cleanup)
{
::exit(_exitCode);
}
#if BX_PLATFORM_WINDOWS
TerminateProcess(GetCurrentProcess(), _exitCode);
#else
_Exit(_exitCode);
#endif // BX_PLATFORM_*
} }
void* memoryMap(void* _address, size_t _size, Error* _err) void* memoryMap(void* _address, size_t _size, Error* _err)

View File

@@ -31,7 +31,12 @@ BX_NO_INLINE void unusedFunction()
void testAssert() void testAssert()
{ {
BX_PRAGMA_DIAGNOSTIC_PUSH();
BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4804); // warning C4804: '%': unsafe use of type 'bool' in operation)
BX_ASSERT(false % 1, "Assert works!"); BX_ASSERT(false % 1, "Assert works!");
BX_PRAGMA_DIAGNOSTIC_POP();
} }
TEST_CASE("Macros", "") TEST_CASE("Macros", "")

View File

@@ -9,14 +9,17 @@
#include <bx/file.h> #include <bx/file.h>
#include <bx/simd_t.h> #include <bx/simd_t.h>
bool testAssertHandler(const bx::Location& _location, const char* _format, va_list _argList) BX_PRAGMA_DIAGNOSTIC_PUSH();
BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4702); // warning C4702: unreachable code
bool testAssertHandler(const bx::Location& _location, uint32_t _skip, const char* _format, va_list _argList)
{ {
bx::printf("%s(%d): ", _location.filePath, _location.line); bx::printf("%s(%d): ", _location.filePath, _location.line);
bx::vprintf(_format, _argList); bx::vprintf(_format, _argList);
bx::printf("\n"); bx::printf("\n");
uintptr_t stack[32]; uintptr_t stack[32];
const uint32_t num = bx::getCallStack(2 /* skip self */, BX_COUNTOF(stack), stack); const uint32_t num = bx::getCallStack(2 /* skip self */ + _skip, BX_COUNTOF(stack), stack);
bx::writeCallstack(bx::getStdOut(), stack, num, bx::ErrorIgnore{}); bx::writeCallstack(bx::getStdOut(), stack, num, bx::ErrorIgnore{});
// Throwing exceptions is required for testing asserts being trigged. // Throwing exceptions is required for testing asserts being trigged.
@@ -26,6 +29,8 @@ bool testAssertHandler(const bx::Location& _location, const char* _format, va_li
return true; return true;
} }
BX_PRAGMA_DIAGNOSTIC_POP();
int runAllTests(int32_t _argc, const char* _argv[]) int runAllTests(int32_t _argc, const char* _argv[])
{ {
bx::setAssertHandler(testAssertHandler); bx::setAssertHandler(testAssertHandler);