diff --git a/src/system.cpp b/src/system.cpp index a6532ac5..21d8279c 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -18,7 +18,8 @@ #include #include -#if defined(__x86_64__) +#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) + #include // or #include #endif @@ -311,41 +312,6 @@ static std::string getSourceLocationFromFrame( void const * const address ) return ""; } -/** - * @brief Return a string representing the current floating point exception. - * @return A string representing the current floating point exception. - */ -static std::string getFpeDetails() -{ - std::ostringstream oss; - int const fpe = fetestexcept( FE_ALL_EXCEPT ); - - oss << "Floating point exception:"; - - if( fpe & FE_DIVBYZERO ) - { - oss << " Division by zero;"; - } - if( fpe & FE_INEXACT ) - { - oss << " Inexact result;"; - } - if( fpe & FE_INVALID ) - { - oss << " Invalid argument;"; - } - if( fpe & FE_OVERFLOW ) - { - oss << " Overflow;"; - } - if( fpe & FE_UNDERFLOW ) - { - oss << " Underflow;"; - } - - return oss.str(); -} - namespace LvArray { namespace system @@ -465,74 +431,121 @@ void callErrorHandler() } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void stackTraceHandler( int const sig, bool const exit ) + +void signalHandler( int sig, siginfo_t * info, void * /*ucontext*/ ) { std::ostringstream oss; if( sig >= 0 && sig < NSIG ) { - // sys_signame not available on linux, so just print the code; strsignal is POSIX oss << "Received signal " << sig << ": " << strsignal( sig ) << "\n"; if( sig == SIGFPE ) { - oss << getFpeDetails() << "\n"; + if( info ) + { + oss << " SIGFPE si_code = " << info->si_code << " "; + + switch( info->si_code ) + { + case FPE_FLTDIV: oss << "(floating divide by zero)\n"; break; + case FPE_FLTOVF: oss << "(floating overflow)\n"; break; + case FPE_FLTUND: oss << "(floating underflow)\n"; break; + case FPE_FLTINV: oss << "(floating invalid operation)\n"; break; + case FPE_FLTRES: oss << "(floating inexact)\n"; break; + case FPE_INTDIV: oss << "(integer divide by zero)\n"; break; + case FPE_INTOVF: oss << "(integer overflow)\n"; break; + default: oss << "(other)\n"; break; + } + } } } oss << stackTrace( true ) << std::endl; - std::cout << oss.str(); + std::cerr << oss.str(); - if( exit ) - { - // An infinite loop was encountered when an FPE was received. Resetting the handlers didn't - // fix it because they would just recurse. This does. - setSignalHandling( nullptr ); - callErrorHandler(); - } + std::_Exit( 1 ); } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void setSignalHandling( void (* handler)( int ) ) -{ - initialHandler[SIGHUP] = signal( SIGHUP, handler ); - initialHandler[SIGINT] = signal( SIGINT, handler ); - initialHandler[SIGQUIT] = signal( SIGQUIT, handler ); - initialHandler[SIGILL] = signal( SIGILL, handler ); - initialHandler[SIGTRAP] = signal( SIGTRAP, handler ); - initialHandler[SIGABRT] = signal( SIGABRT, handler ); -#if (defined(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE)) - initialHandler[SIGPOLL] = signal( SIGPOLL, handler ); -#else - initialHandler[SIGIOT] = signal( SIGIOT, handler ); - initialHandler[SIGEMT] = signal( SIGEMT, handler ); -#endif - initialHandler[SIGFPE] = signal( SIGFPE, handler ); - initialHandler[SIGKILL] = signal( SIGKILL, handler ); - initialHandler[SIGBUS] = signal( SIGBUS, handler ); - initialHandler[SIGSEGV] = signal( SIGSEGV, handler ); - initialHandler[SIGSYS] = signal( SIGSYS, handler ); - initialHandler[SIGPIPE] = signal( SIGPIPE, handler ); - initialHandler[SIGTERM] = signal( SIGTERM, handler ); - - return; -} -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void resetSignalHandling() +static struct sigaction g_oldAction[NSIG]; + +void setSignalHandling( void (* handler)( int, siginfo_t * info, void * ) ) { - for( auto a : initialHandler ) + struct sigaction sa; + sigemptyset( &sa.sa_mask ); + sa.sa_sigaction = handler; + sa.sa_flags = SA_SIGINFO; + + auto install = [&]( int sig ) { - signal( a.first, a.second ); - } + sigaction( sig, &sa, &g_oldAction[sig] ); + }; + + install( SIGHUP ); + install( SIGINT ); + install( SIGQUIT ); + install( SIGILL ); + install( SIGTRAP ); + install( SIGABRT ); + install( SIGFPE ); + install( SIGBUS ); + install( SIGSEGV ); + install( SIGSYS ); + install( SIGPIPE ); + install( SIGTERM ); + // Do NOT try SIGKILL/SIGSTOP: they can’t be caught. } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int getDefaultFloatingPointExceptions() { return ( FE_DIVBYZERO | FE_OVERFLOW | FE_INVALID ); } +unsigned long long int translateFloatingPointException( unsigned long long int const exception ) +{ + unsigned long long int result = 0; +#if defined(__APPLE__) && defined(__MACH__) // if apple + if( exception & FE_INEXACT ) + { + result |= __fpcr_trap_inexact; + } + if( exception & FE_UNDERFLOW ) + { + result |= __fpcr_trap_underflow; + } + if( exception & FE_OVERFLOW ) + { + result |= __fpcr_trap_overflow; + } + if( exception & FE_DIVBYZERO ) + { + result |= __fpcr_trap_divbyzero; + } + if( exception & FE_INVALID ) + { + result |= __fpcr_trap_invalid; + } + +#if defined(__arm__) || defined(__arm64__) // if apple arm +#elif defined(__x86_64__) // if apple x86_64 +#else // if apple but not arm or x86_64 + std::cerr<< "LvArray::system::translateFloatingPointException() not implemented for this architecture" << std::endl; +#endif + + +#else // if not apple +#if defined(__x86_64__) + result = exception; +#endif +#endif + +return result; +} + #if defined(__APPLE__) && defined(__MACH__)&& !defined(__x86_64__) static void fpe_signal_handler( int sig, siginfo_t *sip, void *scp ) @@ -542,10 +555,8 @@ fpe_signal_handler( int sig, siginfo_t *sip, void *scp ) int fe_code = sip->si_code; - printf( "In signal handler : " ); - if( fe_code == ILL_ILLTRP ) - printf( "Illegal trap detected\n" ); + printf( "Illegal trap detected. If you see this you have a FPE, but Apple Silicon doesn't provide data on which FPE has occured.\n" ); else printf( "Code detected : %d\n", fe_code ); @@ -559,19 +570,22 @@ int enableFloatingPointExceptions( int const exceptions ) #if defined(__APPLE__) && defined(__MACH__) #if !defined(__x86_64__) - LVARRAY_UNUSED_VARIABLE( exceptions ); + unsigned long long int const exceptionMasks = translateFloatingPointException( exceptions ); fenv_t env; fegetenv( &env ); - env.__fpcr = env.__fpcr | __fpcr_trap_invalid; +// std::cout< #include #include +#include + + namespace LvArray { @@ -77,13 +80,14 @@ void callErrorHandler(); * @param sig The signal received. * @param exit If true abort execution. */ -void stackTraceHandler( int const sig, bool const exit ); +void signalHandler( int sig, siginfo_t * info, void * /*ucontext*/ ); + /** * @brief Set the signal handler for common signals. * @param handler The signal handler. */ -void setSignalHandling( void (* handler)( int ) ); +void setSignalHandling( void (* handler)( int, siginfo_t * info, void * ) = signalHandler ); /** * @brief Rest the signal handling back to the original state. diff --git a/unitTests/testFloatingPointExceptions.cpp b/unitTests/testFloatingPointExceptions.cpp index be6c6684..1ae5ee80 100644 --- a/unitTests/testFloatingPointExceptions.cpp +++ b/unitTests/testFloatingPointExceptions.cpp @@ -18,9 +18,6 @@ #include #include - -#if defined(__x86_64__) - using namespace testFloatingPointExceptionsHelpers; const char IGNORE_OUTPUT[] = ".*"; @@ -32,53 +29,39 @@ namespace testing TEST( TestFloatingPointEnvironment, Underflow ) { - system::enableFloatingPointExceptions( FE_UNDERFLOW ); - EXPECT_DEATH_IF_SUPPORTED( divide( DBL_MIN, 2 ), IGNORE_OUTPUT ); - system::disableFloatingPointExceptions( FE_UNDERFLOW ); - system::setFPE(); double fpnum = divide( DBL_MIN, 2 ); - int fpclassification = std::fpclassify( fpnum ); - EXPECT_NE( fpclassification, FP_SUBNORMAL ); + EXPECT_DOUBLE_EQ( fpnum, 0.0 ); } TEST( TestFloatingPointEnvironment, DivideByZero ) { system::setFPE(); - EXPECT_DEATH_IF_SUPPORTED( divide( 1, 0 ), IGNORE_OUTPUT ); + EXPECT_DEATH_IF_SUPPORTED( divide( 1, 0 ), R"((floating divide by zero)(.|\n)*StackTrace)" ); } TEST( TestFloatingPointEnvironment, Overlow ) { system::setFPE(); - EXPECT_DEATH_IF_SUPPORTED( multiply( DBL_MAX, 2 ), IGNORE_OUTPUT ); + EXPECT_DEATH_IF_SUPPORTED( multiply( DBL_MAX, 2 ), R"((floating overflow)(.|\n)*StackTrace)" ); } TEST( TestFloatingPointEnvironment, Invalid ) { system::setFPE(); - EXPECT_DEATH_IF_SUPPORTED( invalid(), IGNORE_OUTPUT ); -} - -TEST( TestFloatingPointEnvironment, FloatingPointExceptionGuard ) -{ - system::setFPE(); - - { - system::FloatingPointExceptionGuard guard( FE_UNDERFLOW ); - divide( DBL_MIN, 2 ); - EXPECT_DEATH_IF_SUPPORTED( multiply( DBL_MAX, 2 ), IGNORE_OUTPUT ); - } + EXPECT_DEATH_IF_SUPPORTED( invalid(), R"((floating invalid operation)(.|\n)*StackTrace)" ); } } // namespace testing } // namespace LvArray -#endif // This is the default gtest main method. It is included for ease of debugging. int main( int argc, char * * argv ) { + + LvArray::system::setSignalHandling(); + ::testing::InitGoogleTest( &argc, argv ); int const result = RUN_ALL_TESTS(); return result;