Browse Source

Added Linux/signal exception handling.

Branimir Karadžić 3 months ago
parent
commit
d2b8b1aab5
8 changed files with 146 additions and 28 deletions
  1. 4 2
      include/bx/bx.h
  2. 14 14
      include/bx/macros.h
  3. 1 1
      include/bx/os.h
  4. 5 4
      src/bx.cpp
  5. 99 3
      src/debug.cpp
  6. 11 2
      src/os.cpp
  7. 5 0
      tests/macros_test.cpp
  8. 7 2
      tests/run_test.cpp

+ 4 - 2
include/bx/bx.h

@@ -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>()>

+ 14 - 14
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::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, ...)                                                                   \
-	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
 	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
 	BX_MACRO_BLOCK_END
 
 
 #define _BX_WARN(_condition, _format, ...)            \
 #define _BX_WARN(_condition, _format, ...)            \

+ 1 - 1
include/bx/os.h

@@ -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);

+ 5 - 4
src/bx.cpp

@@ -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;

+ 99 - 3
src/debug.cpp

@@ -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, width);
+			const StringView fn = strTail(cbData.fileName, kWidth);
 
 
 			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

+ 11 - 2
src/os.cpp

@@ -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)

+ 5 - 0
tests/macros_test.cpp

@@ -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", "")

+ 7 - 2
tests/run_test.cpp

@@ -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);