diff --git a/include/bx/bx.h b/include/bx/bx.h index 660655a..83b721f 100644 --- a/include/bx/bx.h +++ b/include/bx/bx.h @@ -115,12 +115,13 @@ namespace bx /// Assert handler function. /// /// @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] _argList Arguments for `_format` specification. /// /// @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. /// @@ -133,12 +134,13 @@ namespace bx /// Assert function calls AssertHandlerFn. /// /// @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] ... Arguments for `_format` specification. /// /// @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. template()> diff --git a/include/bx/macros.h b/include/bx/macros.h index 89ed230..f2124be 100644 --- a/include/bx/macros.h +++ b/include/bx/macros.h @@ -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_MACRO_BLOCK_END -#define _BX_ASSERT(_condition, _format, ...) \ - BX_MACRO_BLOCK_BEGIN \ - if (!BX_IGNORE_C4127(_condition) \ - && bx::assertFunction(bx::Location::current(), "ASSERT %s -> " _format, #_condition, ##__VA_ARGS__) ) \ - { \ - bx::debugBreak(); \ - } \ +#define _BX_ASSERT(_condition, _format, ...) \ + BX_MACRO_BLOCK_BEGIN \ + if (!BX_IGNORE_C4127(_condition) \ + && bx::assertFunction(bx::Location::current(), 0, "ASSERT %s -> " _format, #_condition, ##__VA_ARGS__) ) \ + { \ + bx::debugBreak(); \ + } \ BX_MACRO_BLOCK_END -#define _BX_ASSERT_LOC(_location, _condition, _format, ...) \ - BX_MACRO_BLOCK_BEGIN \ - if (!BX_IGNORE_C4127(_condition) \ - && bx::assertFunction(_location, "ASSERT %s -> " _format, #_condition, ##__VA_ARGS__) ) \ - { \ - bx::debugBreak(); \ - } \ +#define _BX_ASSERT_LOC(_location, _condition, _format, ...) \ + BX_MACRO_BLOCK_BEGIN \ + if (!BX_IGNORE_C4127(_condition) \ + && bx::assertFunction(_location, 0, "ASSERT %s -> " _format, #_condition, ##__VA_ARGS__) ) \ + { \ + bx::debugBreak(); \ + } \ BX_MACRO_BLOCK_END #define _BX_WARN(_condition, _format, ...) \ diff --git a/include/bx/os.h b/include/bx/os.h index a676ed8..f5075fb 100644 --- a/include/bx/os.h +++ b/include/bx/os.h @@ -59,7 +59,7 @@ namespace bx 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); diff --git a/src/bx.cpp b/src/bx.cpp index 1ea805a..4122fab 100644 --- a/src/bx.cpp +++ b/src/bx.cpp @@ -5,6 +5,7 @@ #include #include +#include #if !BX_CRT_NONE # include // memcpy, memmove, memset @@ -22,7 +23,7 @@ namespace bx 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]; int32_t total = 0; @@ -41,7 +42,7 @@ namespace bx total += write(&smb, "\n\n", &err); 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 += 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_start(argList, _format); - const bool result = s_assertHandler(_location, _format, argList); + const bool result = s_assertHandler(_location, _skip, _format, argList); va_end(argList); return result; diff --git a/src/debug.cpp b/src/debug.cpp index e4ea549..b1c95c3 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -22,6 +22,15 @@ # include // abi::__cxa_demangle #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 +#endif // BX_CONFIG_EXCEPTION_HANDLING_USE_SIGNALS + #if BX_CRT_NONE # include #elif BX_PLATFORM_ANDROID @@ -283,6 +292,9 @@ namespace bx 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; for (uint32_t ii = 0; ii < _num && _err->isOk(); ++ii) @@ -311,13 +323,12 @@ namespace bx demangledName = "???"; } - constexpr uint32_t width = 40; - const StringView fn = strTail(cbData.fileName, width); + const StringView fn = strTail(cbData.fileName, kWidth); total += write(_writer, _err , "\t%2d: %-*S % 5d: %p %S\n" , ii - , width + , kWidth , &fn , cbData.line , _stack[ii] @@ -363,4 +374,89 @@ namespace bx 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 diff --git a/src/os.cpp b/src/os.cpp index 41b97a0..47465a2 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -361,9 +361,18 @@ namespace bx #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) diff --git a/tests/macros_test.cpp b/tests/macros_test.cpp index 01c66c5..2a9bc4c 100644 --- a/tests/macros_test.cpp +++ b/tests/macros_test.cpp @@ -31,7 +31,12 @@ BX_NO_INLINE void unusedFunction() 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_PRAGMA_DIAGNOSTIC_POP(); } TEST_CASE("Macros", "") diff --git a/tests/run_test.cpp b/tests/run_test.cpp index 8e1a775..b9af298 100644 --- a/tests/run_test.cpp +++ b/tests/run_test.cpp @@ -9,14 +9,17 @@ #include #include -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::vprintf(_format, _argList); bx::printf("\n"); 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{}); // 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; } +BX_PRAGMA_DIAGNOSTIC_POP(); + int runAllTests(int32_t _argc, const char* _argv[]) { bx::setAssertHandler(testAssertHandler);