// (C) Copyright Gennadiy Rozental 2001. // (C) Copyright Beman Dawes and Ullrich Koethe 1995-2001. // Use, modification, and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // http://www.boost.org/LICENSE_1_0.txt) // See http://www.boost.org/libs/test for the library home page. // /// @file /// Provides execution monitor implementation for all supported /// configurations, including Microsoft structured exception based, unix signals /// based and special workarounds for borland /// /// Note that when testing requirements or user wishes preclude use of this /// file as a separate compilation unit, it may be included as a header file. /// /// Header dependencies are deliberately restricted to reduce coupling to other /// boost libraries. // *************************************************************************** #ifndef BOOST_TEST_EXECUTION_MONITOR_IPP_012205GER #define BOOST_TEST_EXECUTION_MONITOR_IPP_012205GER // Boost.Test #include #include #include #include #include // Boost #include // for exit codes #include // for workarounds #include // for ignore_unused #ifndef BOOST_NO_EXCEPTIONS #include // for get_error_info #include // for current_exception_cast #endif // STL #include // for std::string #include // for std::bad_alloc #include // for std::bad_cast, std::bad_typeid #include // for std::exception, std::bad_exception #include // for std exception hierarchy #include // for C string API #include // for assert #include // for NULL #include // for vsnprintf #include // for varargs #include // for varargs #ifdef BOOST_NO_STDC_NAMESPACE namespace std { using ::strerror; using ::strlen; using ::strncat; } #endif // to use vsnprintf #if defined(__SUNPRO_CC) || defined(__SunOS) # include # include using std::va_list; #endif // to use vsnprintf #if defined(__QNXNTO__) # include using std::va_list; #endif #ifdef BOOST_SEH_BASED_SIGNAL_HANDLING # include # if defined(__MWERKS__) || (defined(_MSC_VER) && !defined(UNDER_CE)) # include # endif # if defined(__BORLANDC__) && __BORLANDC__ >= 0x560 || defined(__MWERKS__) # include # endif # if defined(__BORLANDC__) && __BORLANDC__ < 0x560 typedef unsigned uintptr_t; # endif # if defined(UNDER_CE) && BOOST_WORKAROUND(_MSC_VER, < 1500 ) typedef void* uintptr_t; # elif defined(UNDER_CE) # include # endif # if !defined(NDEBUG) && defined(_MSC_VER) && !defined(UNDER_CE) # include # define BOOST_TEST_CRT_HOOK_TYPE _CRT_REPORT_HOOK # define BOOST_TEST_CRT_ASSERT _CRT_ASSERT # define BOOST_TEST_CRT_ERROR _CRT_ERROR # define BOOST_TEST_CRT_SET_HOOK(H) _CrtSetReportHook(H) # else # define BOOST_TEST_CRT_HOOK_TYPE void* # define BOOST_TEST_CRT_ASSERT 2 # define BOOST_TEST_CRT_ERROR 1 # define BOOST_TEST_CRT_SET_HOOK(H) (void*)(H) # endif # if (!BOOST_WORKAROUND(_MSC_VER, >= 1400 ) && \ !defined(BOOST_COMO)) || defined(UNDER_CE) typedef void* _invalid_parameter_handler; inline _invalid_parameter_handler _set_invalid_parameter_handler( _invalid_parameter_handler arg ) { return arg; } # endif # if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x0564)) || defined(UNDER_CE) namespace { void _set_se_translator( void* ) {} } # endif #elif defined(BOOST_HAS_SIGACTION) # define BOOST_SIGACTION_BASED_SIGNAL_HANDLING # include # include # include # if defined(__FreeBSD__) # include # ifndef SIGPOLL # define SIGPOLL SIGIO # endif # if (__FreeBSD_version < 70100) # define ILL_ILLADR 0 // ILL_RESAD_FAULT # define ILL_PRVOPC ILL_PRIVIN_FAULT # define ILL_ILLOPN 2 // ILL_RESOP_FAULT # define ILL_COPROC ILL_FPOP_FAULT # define BOOST_TEST_LIMITED_SIGNAL_DETAILS # endif # endif # if defined(__ANDROID__) # include # endif // documentation of BOOST_TEST_DISABLE_ALT_STACK in execution_monitor.hpp # if !defined(__CYGWIN__) && !defined(__QNXNTO__) && !defined(__bgq__) && \ (!defined(__ANDROID__) || __ANDROID_API__ >= 8) && \ !defined(BOOST_TEST_DISABLE_ALT_STACK) # define BOOST_TEST_USE_ALT_STACK # endif # if defined(SIGPOLL) && !defined(__CYGWIN__) && \ !(defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)) && \ !defined(__NetBSD__) && \ !defined(__QNXNTO__) # define BOOST_TEST_CATCH_SIGPOLL # endif # ifdef BOOST_TEST_USE_ALT_STACK # define BOOST_TEST_ALT_STACK_SIZE SIGSTKSZ # endif #else # define BOOST_NO_SIGNAL_HANDLING #endif #ifndef UNDER_CE #include #endif #if !defined(BOOST_NO_TYPEID) && !defined(BOOST_NO_RTTI) # include #endif #include //____________________________________________________________________________// namespace boost { // ************************************************************************** // // ************** throw_exception ************** // // ************************************************************************** // #ifdef BOOST_NO_EXCEPTIONS void throw_exception( std::exception const & e ) { abort(); } #endif // ************************************************************************** // // ************** report_error ************** // // ************************************************************************** // namespace detail { #ifdef __BORLANDC__ # define BOOST_TEST_VSNPRINTF( a1, a2, a3, a4 ) std::vsnprintf( (a1), (a2), (a3), (a4) ) #elif BOOST_WORKAROUND(_MSC_VER, <= 1310) || \ BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3000)) || \ defined(UNDER_CE) # define BOOST_TEST_VSNPRINTF( a1, a2, a3, a4 ) _vsnprintf( (a1), (a2), (a3), (a4) ) #else # define BOOST_TEST_VSNPRINTF( a1, a2, a3, a4 ) vsnprintf( (a1), (a2), (a3), (a4) ) #endif #ifndef BOOST_NO_EXCEPTIONS template typename ErrorInfo::value_type extract( boost::exception const* ex ) { if( !ex ) return 0; typename ErrorInfo::value_type const * val = boost::get_error_info( *ex ); return val ? *val : 0; } //____________________________________________________________________________// static void report_error( execution_exception::error_code ec, boost::exception const* be, char const* format, va_list* args ) { static const int REPORT_ERROR_BUFFER_SIZE = 4096; static char buf[REPORT_ERROR_BUFFER_SIZE]; BOOST_TEST_VSNPRINTF( buf, sizeof(buf)-1, format, *args ); buf[sizeof(buf)-1] = 0; va_end( *args ); BOOST_TEST_I_THROW(execution_exception( ec, buf, execution_exception::location( extract( be ), (size_t)extract( be ), extract( be ) ) )); } //____________________________________________________________________________// static void report_error( execution_exception::error_code ec, boost::exception const* be, char const* format, ... ) { va_list args; va_start( args, format ); report_error( ec, be, format, &args ); } #endif //____________________________________________________________________________// static void report_error( execution_exception::error_code ec, char const* format, ... ) { va_list args; va_start( args, format ); report_error( ec, 0, format, &args ); } //____________________________________________________________________________// template inline int do_invoke( Tr const& tr, Functor const& F ) { return tr ? (*tr)( F ) : F(); } //____________________________________________________________________________// struct fpe_except_guard { explicit fpe_except_guard( unsigned detect_fpe ) : m_detect_fpe( detect_fpe ) { // prepare fp exceptions control m_previously_enabled = fpe::disable( fpe::BOOST_FPE_ALL ); if( m_previously_enabled != fpe::BOOST_FPE_INV && detect_fpe != fpe::BOOST_FPE_OFF ) fpe::enable( detect_fpe ); } ~fpe_except_guard() { if( m_detect_fpe != fpe::BOOST_FPE_OFF ) fpe::disable( m_detect_fpe ); if( m_previously_enabled != fpe::BOOST_FPE_INV ) fpe::enable( m_previously_enabled ); } unsigned m_detect_fpe; unsigned m_previously_enabled; }; // ************************************************************************** // // ************** typeid_name ************** // // ************************************************************************** // #if !defined(BOOST_NO_TYPEID) && !defined(BOOST_NO_RTTI) template std::string typeid_name( T const& t ) { return boost::core::demangle(typeid(t).name()); } #endif } // namespace detail #if defined(BOOST_SIGACTION_BASED_SIGNAL_HANDLING) // ************************************************************************** // // ************** Sigaction based signal handling ************** // // ************************************************************************** // namespace detail { // ************************************************************************** // // ************** boost::detail::system_signal_exception ************** // // ************************************************************************** // class system_signal_exception { public: // Constructor system_signal_exception() : m_sig_info( 0 ) , m_context( 0 ) {} // Access methods void operator()( siginfo_t* i, void* c ) { m_sig_info = i; m_context = c; } void report() const; private: // Data members siginfo_t* m_sig_info; // system signal detailed info void* m_context; // signal context }; //____________________________________________________________________________// void system_signal_exception::report() const { if( !m_sig_info ) return; // no error actually occur? switch( m_sig_info->si_code ) { case SI_USER: report_error( execution_exception::system_error, "signal: generated by kill() (or family); uid=%d; pid=%d", (int)m_sig_info->si_uid, (int)m_sig_info->si_pid ); break; case SI_QUEUE: report_error( execution_exception::system_error, "signal: sent by sigqueue()" ); break; case SI_TIMER: report_error( execution_exception::system_error, "signal: the expiration of a timer set by timer_settimer()" ); break; case SI_ASYNCIO: report_error( execution_exception::system_error, "signal: generated by the completion of an asynchronous I/O request" ); break; case SI_MESGQ: report_error( execution_exception::system_error, "signal: generated by the the arrival of a message on an empty message queue" ); break; default: break; } switch( m_sig_info->si_signo ) { case SIGILL: switch( m_sig_info->si_code ) { #ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS case ILL_ILLOPC: report_error( execution_exception::system_fatal_error, "signal: illegal opcode; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case ILL_ILLTRP: report_error( execution_exception::system_fatal_error, "signal: illegal trap; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case ILL_PRVREG: report_error( execution_exception::system_fatal_error, "signal: privileged register; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case ILL_BADSTK: report_error( execution_exception::system_fatal_error, "signal: internal stack error; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; #endif case ILL_ILLOPN: report_error( execution_exception::system_fatal_error, "signal: illegal operand; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case ILL_ILLADR: report_error( execution_exception::system_fatal_error, "signal: illegal addressing mode; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case ILL_PRVOPC: report_error( execution_exception::system_fatal_error, "signal: privileged opcode; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case ILL_COPROC: report_error( execution_exception::system_fatal_error, "signal: co-processor error; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; default: report_error( execution_exception::system_fatal_error, "signal: SIGILL, si_code: %d (illegal instruction; address of failing instruction: 0x%08lx)", m_sig_info->si_addr, m_sig_info->si_code ); break; } break; case SIGFPE: switch( m_sig_info->si_code ) { case FPE_INTDIV: report_error( execution_exception::system_error, "signal: integer divide by zero; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case FPE_INTOVF: report_error( execution_exception::system_error, "signal: integer overflow; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case FPE_FLTDIV: report_error( execution_exception::system_error, "signal: floating point divide by zero; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case FPE_FLTOVF: report_error( execution_exception::system_error, "signal: floating point overflow; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case FPE_FLTUND: report_error( execution_exception::system_error, "signal: floating point underflow; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case FPE_FLTRES: report_error( execution_exception::system_error, "signal: floating point inexact result; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case FPE_FLTINV: report_error( execution_exception::system_error, "signal: invalid floating point operation; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; case FPE_FLTSUB: report_error( execution_exception::system_error, "signal: subscript out of range; address of failing instruction: 0x%08lx", m_sig_info->si_addr ); break; default: report_error( execution_exception::system_error, "signal: SIGFPE, si_code: %d (errnoneous arithmetic operations; address of failing instruction: 0x%08lx)", m_sig_info->si_addr, m_sig_info->si_code ); break; } break; case SIGSEGV: switch( m_sig_info->si_code ) { #ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS case SEGV_MAPERR: report_error( execution_exception::system_fatal_error, "memory access violation at address: 0x%08lx: no mapping at fault address", m_sig_info->si_addr ); break; case SEGV_ACCERR: report_error( execution_exception::system_fatal_error, "memory access violation at address: 0x%08lx: invalid permissions", m_sig_info->si_addr ); break; #endif default: report_error( execution_exception::system_fatal_error, "signal: SIGSEGV, si_code: %d (memory access violation at address: 0x%08lx)", m_sig_info->si_addr, m_sig_info->si_code ); break; } break; case SIGBUS: switch( m_sig_info->si_code ) { #ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS case BUS_ADRALN: report_error( execution_exception::system_fatal_error, "memory access violation at address: 0x%08lx: invalid address alignment", m_sig_info->si_addr ); break; case BUS_ADRERR: report_error( execution_exception::system_fatal_error, "memory access violation at address: 0x%08lx: non-existent physical address", m_sig_info->si_addr ); break; case BUS_OBJERR: report_error( execution_exception::system_fatal_error, "memory access violation at address: 0x%08lx: object specific hardware error", m_sig_info->si_addr ); break; #endif default: report_error( execution_exception::system_fatal_error, "signal: SIGSEGV, si_code: %d (memory access violation at address: 0x%08lx)", m_sig_info->si_addr, m_sig_info->si_code ); break; } break; #if defined(BOOST_TEST_CATCH_SIGPOLL) case SIGPOLL: switch( m_sig_info->si_code ) { #ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS case POLL_IN: report_error( execution_exception::system_error, "data input available; band event %d", (int)m_sig_info->si_band ); break; case POLL_OUT: report_error( execution_exception::system_error, "output buffers available; band event %d", (int)m_sig_info->si_band ); break; case POLL_MSG: report_error( execution_exception::system_error, "input message available; band event %d", (int)m_sig_info->si_band ); break; case POLL_ERR: report_error( execution_exception::system_error, "i/o error; band event %d", (int)m_sig_info->si_band ); break; case POLL_PRI: report_error( execution_exception::system_error, "high priority input available; band event %d", (int)m_sig_info->si_band ); break; #if defined(POLL_ERR) && defined(POLL_HUP) && (POLL_ERR - POLL_HUP) case POLL_HUP: report_error( execution_exception::system_error, "device disconnected; band event %d", (int)m_sig_info->si_band ); break; #endif #endif default: report_error( execution_exception::system_error, "signal: SIGPOLL, si_code: %d (asynchronous I/O event occurred; band event %d)", (int)m_sig_info->si_band, m_sig_info->si_code ); break; } break; #endif case SIGABRT: report_error( execution_exception::system_error, "signal: SIGABRT (application abort requested)" ); break; case SIGALRM: report_error( execution_exception::timeout_error, "signal: SIGALRM (timeout while executing function)" ); break; default: report_error( execution_exception::system_error, "unrecognized signal %d", m_sig_info->si_signo ); } } //____________________________________________________________________________// // ************************************************************************** // // ************** boost::detail::signal_action ************** // // ************************************************************************** // // Forward declaration extern "C" { static void boost_execution_monitor_jumping_signal_handler( int sig, siginfo_t* info, void* context ); static void boost_execution_monitor_attaching_signal_handler( int sig, siginfo_t* info, void* context ); } class signal_action { typedef struct sigaction* sigaction_ptr; public: //Constructor signal_action(); signal_action( int sig, bool install, bool attach_dbg, char* alt_stack ); ~signal_action(); private: // Data members int m_sig; bool m_installed; struct sigaction m_new_action; struct sigaction m_old_action; }; //____________________________________________________________________________// signal_action::signal_action() : m_installed( false ) {} //____________________________________________________________________________// signal_action::signal_action( int sig, bool install, bool attach_dbg, char* alt_stack ) : m_sig( sig ) , m_installed( install ) { if( !install ) return; std::memset( &m_new_action, 0, sizeof(struct sigaction) ); BOOST_TEST_SYS_ASSERT( ::sigaction( m_sig , sigaction_ptr(), &m_new_action ) != -1 ); if( m_new_action.sa_sigaction || m_new_action.sa_handler ) { m_installed = false; return; } m_new_action.sa_flags |= SA_SIGINFO; m_new_action.sa_sigaction = attach_dbg ? &boost_execution_monitor_attaching_signal_handler : &boost_execution_monitor_jumping_signal_handler; BOOST_TEST_SYS_ASSERT( sigemptyset( &m_new_action.sa_mask ) != -1 ); #ifdef BOOST_TEST_USE_ALT_STACK if( alt_stack ) m_new_action.sa_flags |= SA_ONSTACK; #endif BOOST_TEST_SYS_ASSERT( ::sigaction( m_sig, &m_new_action, &m_old_action ) != -1 ); } //____________________________________________________________________________// signal_action::~signal_action() { if( m_installed ) ::sigaction( m_sig, &m_old_action , sigaction_ptr() ); } //____________________________________________________________________________// // ************************************************************************** // // ************** boost::detail::signal_handler ************** // // ************************************************************************** // class signal_handler { public: // Constructor explicit signal_handler( bool catch_system_errors, bool detect_fpe, unsigned timeout, bool attach_dbg, char* alt_stack ); // Destructor ~signal_handler(); // access methods static sigjmp_buf& jump_buffer() { assert( !!s_active_handler ); return s_active_handler->m_sigjmp_buf; } static system_signal_exception& sys_sig() { assert( !!s_active_handler ); return s_active_handler->m_sys_sig; } private: // Data members signal_handler* m_prev_handler; unsigned m_timeout; // Note: We intentionality do not catch SIGCHLD. Users have to deal with it themselves signal_action m_ILL_action; signal_action m_FPE_action; signal_action m_SEGV_action; signal_action m_BUS_action; signal_action m_CHLD_action; signal_action m_POLL_action; signal_action m_ABRT_action; signal_action m_ALRM_action; sigjmp_buf m_sigjmp_buf; system_signal_exception m_sys_sig; static signal_handler* s_active_handler; }; // !! need to be placed in thread specific storage typedef signal_handler* signal_handler_ptr; signal_handler* signal_handler::s_active_handler = signal_handler_ptr(); //____________________________________________________________________________// signal_handler::signal_handler( bool catch_system_errors, bool detect_fpe, unsigned timeout, bool attach_dbg, char* alt_stack ) : m_prev_handler( s_active_handler ) , m_timeout( timeout ) , m_ILL_action ( SIGILL , catch_system_errors, attach_dbg, alt_stack ) , m_FPE_action ( SIGFPE , detect_fpe , attach_dbg, alt_stack ) , m_SEGV_action( SIGSEGV, catch_system_errors, attach_dbg, alt_stack ) , m_BUS_action ( SIGBUS , catch_system_errors, attach_dbg, alt_stack ) #ifdef BOOST_TEST_CATCH_SIGPOLL , m_POLL_action( SIGPOLL, catch_system_errors, attach_dbg, alt_stack ) #endif , m_ABRT_action( SIGABRT, catch_system_errors, attach_dbg, alt_stack ) , m_ALRM_action( SIGALRM, timeout > 0 , attach_dbg, alt_stack ) { s_active_handler = this; if( m_timeout > 0 ) { ::alarm( 0 ); ::alarm( timeout ); } #ifdef BOOST_TEST_USE_ALT_STACK if( alt_stack ) { stack_t sigstk; std::memset( &sigstk, 0, sizeof(stack_t) ); BOOST_TEST_SYS_ASSERT( ::sigaltstack( 0, &sigstk ) != -1 ); if( sigstk.ss_flags & SS_DISABLE ) { sigstk.ss_sp = alt_stack; sigstk.ss_size = BOOST_TEST_ALT_STACK_SIZE; sigstk.ss_flags = 0; BOOST_TEST_SYS_ASSERT( ::sigaltstack( &sigstk, 0 ) != -1 ); } } #endif } //____________________________________________________________________________// signal_handler::~signal_handler() { assert( s_active_handler == this ); if( m_timeout > 0 ) ::alarm( 0 ); #ifdef BOOST_TEST_USE_ALT_STACK #ifdef __GNUC__ // We shouldn't need to explicitly initialize all the members here, // but gcc warns if we don't, so add initializers for each of the // members specified in the POSIX std: stack_t sigstk = { 0, 0, 0 }; #else stack_t sigstk = { }; #endif sigstk.ss_size = MINSIGSTKSZ; sigstk.ss_flags = SS_DISABLE; if( ::sigaltstack( &sigstk, 0 ) == -1 ) { int error_n = errno; std::cerr << "******** errors disabling the alternate stack:" << std::endl << "\t#error:" << error_n << std::endl << "\t" << std::strerror( error_n ) << std::endl; } #endif s_active_handler = m_prev_handler; } //____________________________________________________________________________// // ************************************************************************** // // ************** execution_monitor_signal_handler ************** // // ************************************************************************** // extern "C" { static void boost_execution_monitor_jumping_signal_handler( int sig, siginfo_t* info, void* context ) { signal_handler::sys_sig()( info, context ); siglongjmp( signal_handler::jump_buffer(), sig ); } //____________________________________________________________________________// static void boost_execution_monitor_attaching_signal_handler( int sig, siginfo_t* info, void* context ) { if( !debug::attach_debugger( false ) ) boost_execution_monitor_jumping_signal_handler( sig, info, context ); // debugger attached; it will handle the signal BOOST_TEST_SYS_ASSERT( ::signal( sig, SIG_DFL ) != SIG_ERR ); } //____________________________________________________________________________// } } // namespace detail // ************************************************************************** // // ************** execution_monitor::catch_signals ************** // // ************************************************************************** // int execution_monitor::catch_signals( boost::function const& F ) { using namespace detail; #if defined(__CYGWIN__) p_catch_system_errors.value = false; #endif #ifdef BOOST_TEST_USE_ALT_STACK if( !!p_use_alt_stack && !m_alt_stack ) m_alt_stack.reset( new char[BOOST_TEST_ALT_STACK_SIZE] ); #else p_use_alt_stack.value = false; #endif signal_handler local_signal_handler( p_catch_system_errors, p_catch_system_errors || (p_detect_fp_exceptions != fpe::BOOST_FPE_OFF), p_timeout, p_auto_start_dbg, !p_use_alt_stack ? 0 : m_alt_stack.get() ); if( !sigsetjmp( signal_handler::jump_buffer(), 1 ) ) return detail::do_invoke( m_custom_translators , F ); else BOOST_TEST_I_THROW( local_signal_handler.sys_sig() ); } //____________________________________________________________________________// #elif defined(BOOST_SEH_BASED_SIGNAL_HANDLING) // ************************************************************************** // // ************** Microsoft structured exception handling ************** // // ************************************************************************** // #if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x0564)) namespace { void _set_se_translator( void* ) {} } #endif namespace detail { // ************************************************************************** // // ************** boost::detail::system_signal_exception ************** // // ************************************************************************** // class system_signal_exception { public: // Constructor explicit system_signal_exception( execution_monitor* em ) : m_em( em ) , m_se_id( 0 ) , m_fault_address( 0 ) , m_dir( false ) {} void report() const; int operator()( unsigned id, _EXCEPTION_POINTERS* exps ); private: // Data members execution_monitor* m_em; unsigned m_se_id; void* m_fault_address; bool m_dir; }; //____________________________________________________________________________// #if BOOST_WORKAROUND( BOOST_MSVC, <= 1310) static void seh_catch_preventer( unsigned /* id */, _EXCEPTION_POINTERS* /* exps */ ) { throw; } #endif //____________________________________________________________________________// int system_signal_exception::operator()( unsigned id, _EXCEPTION_POINTERS* exps ) { const unsigned MSFT_CPP_EXCEPT = 0xE06d7363; // EMSC // C++ exception - allow to go through if( id == MSFT_CPP_EXCEPT ) return EXCEPTION_CONTINUE_SEARCH; // FPE detection is enabled, while system exception detection is not - check if this is actually FPE if( !m_em->p_catch_system_errors ) { if( !m_em->p_detect_fp_exceptions ) return EXCEPTION_CONTINUE_SEARCH; switch( id ) { case EXCEPTION_FLT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_INEXACT_RESULT: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_UNDERFLOW: case EXCEPTION_FLT_INVALID_OPERATION: case STATUS_FLOAT_MULTIPLE_FAULTS: case STATUS_FLOAT_MULTIPLE_TRAPS: break; default: return EXCEPTION_CONTINUE_SEARCH; } } if( !!m_em->p_auto_start_dbg && debug::attach_debugger( false ) ) { m_em->p_catch_system_errors.value = false; #if BOOST_WORKAROUND( BOOST_MSVC, <= 1310) _set_se_translator( &seh_catch_preventer ); #endif return EXCEPTION_CONTINUE_EXECUTION; } m_se_id = id; if( m_se_id == EXCEPTION_ACCESS_VIOLATION && exps->ExceptionRecord->NumberParameters == 2 ) { m_fault_address = (void*)exps->ExceptionRecord->ExceptionInformation[1]; m_dir = exps->ExceptionRecord->ExceptionInformation[0] == 0; } return EXCEPTION_EXECUTE_HANDLER; } //____________________________________________________________________________// void system_signal_exception::report() const { switch( m_se_id ) { // cases classified as system_fatal_error case EXCEPTION_ACCESS_VIOLATION: { if( !m_fault_address ) detail::report_error( execution_exception::system_fatal_error, "memory access violation" ); else detail::report_error( execution_exception::system_fatal_error, "memory access violation occurred at address 0x%08lx, while attempting to %s", m_fault_address, m_dir ? " read inaccessible data" : " write to an inaccessible (or protected) address" ); break; } case EXCEPTION_ILLEGAL_INSTRUCTION: detail::report_error( execution_exception::system_fatal_error, "illegal instruction" ); break; case EXCEPTION_PRIV_INSTRUCTION: detail::report_error( execution_exception::system_fatal_error, "tried to execute an instruction whose operation is not allowed in the current machine mode" ); break; case EXCEPTION_IN_PAGE_ERROR: detail::report_error( execution_exception::system_fatal_error, "access to a memory page that is not present" ); break; case EXCEPTION_STACK_OVERFLOW: detail::report_error( execution_exception::system_fatal_error, "stack overflow" ); break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: detail::report_error( execution_exception::system_fatal_error, "tried to continue execution after a non continuable exception occurred" ); break; // cases classified as (non-fatal) system_trap case EXCEPTION_DATATYPE_MISALIGNMENT: detail::report_error( execution_exception::system_error, "data misalignment" ); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: detail::report_error( execution_exception::system_error, "integer divide by zero" ); break; case EXCEPTION_INT_OVERFLOW: detail::report_error( execution_exception::system_error, "integer overflow" ); break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: detail::report_error( execution_exception::system_error, "array bounds exceeded" ); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: detail::report_error( execution_exception::system_error, "floating point divide by zero" ); break; case EXCEPTION_FLT_STACK_CHECK: detail::report_error( execution_exception::system_error, "stack overflowed or underflowed as the result of a floating-point operation" ); break; case EXCEPTION_FLT_DENORMAL_OPERAND: detail::report_error( execution_exception::system_error, "operand of floating point operation is denormal" ); break; case EXCEPTION_FLT_INEXACT_RESULT: detail::report_error( execution_exception::system_error, "result of a floating-point operation cannot be represented exactly" ); break; case EXCEPTION_FLT_OVERFLOW: detail::report_error( execution_exception::system_error, "exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type" ); break; case EXCEPTION_FLT_UNDERFLOW: detail::report_error( execution_exception::system_error, "exponent of a floating-point operation is less than the magnitude allowed by the corresponding type" ); break; case EXCEPTION_FLT_INVALID_OPERATION: detail::report_error( execution_exception::system_error, "floating point error" ); break; case STATUS_FLOAT_MULTIPLE_FAULTS: detail::report_error( execution_exception::system_error, "multiple floating point errors" ); break; case STATUS_FLOAT_MULTIPLE_TRAPS: detail::report_error( execution_exception::system_error, "multiple floating point errors" ); break; case EXCEPTION_BREAKPOINT: detail::report_error( execution_exception::system_error, "breakpoint encountered" ); break; default: detail::report_error( execution_exception::system_error, "unrecognized exception. Id: 0x%08lx", m_se_id ); break; } } //____________________________________________________________________________// // ************************************************************************** // // ************** assert_reporting_function ************** // // ************************************************************************** // int BOOST_TEST_CALL_DECL assert_reporting_function( int reportType, char* userMessage, int* ) { // write this way instead of switch to avoid unreachable statements if( reportType == BOOST_TEST_CRT_ASSERT || reportType == BOOST_TEST_CRT_ERROR ) detail::report_error( reportType == BOOST_TEST_CRT_ASSERT ? execution_exception::user_error : execution_exception::system_error, userMessage ); return 0; } // assert_reporting_function //____________________________________________________________________________// void BOOST_TEST_CALL_DECL invalid_param_handler( wchar_t const* /* expr */, wchar_t const* /* func */, wchar_t const* /* file */, unsigned /* line */, uintptr_t /* reserved */) { detail::report_error( execution_exception::user_error, "Invalid parameter detected by C runtime library" ); } //____________________________________________________________________________// } // namespace detail // ************************************************************************** // // ************** execution_monitor::catch_signals ************** // // ************************************************************************** // int execution_monitor::catch_signals( boost::function const& F ) { _invalid_parameter_handler old_iph = _invalid_parameter_handler(); BOOST_TEST_CRT_HOOK_TYPE old_crt_hook = 0; if( p_catch_system_errors ) { old_crt_hook = BOOST_TEST_CRT_SET_HOOK( &detail::assert_reporting_function ); old_iph = _set_invalid_parameter_handler( reinterpret_cast<_invalid_parameter_handler>( &detail::invalid_param_handler ) ); } else if( !p_detect_fp_exceptions ) { #if BOOST_WORKAROUND( BOOST_MSVC, <= 1310) _set_se_translator( &detail::seh_catch_preventer ); #endif } detail::system_signal_exception SSE( this ); int ret_val = 0; // clang windows workaround: this not available in __finally scope bool l_catch_system_errors = p_catch_system_errors; __try { __try { ret_val = detail::do_invoke( m_custom_translators, F ); } __except( SSE( GetExceptionCode(), GetExceptionInformation() ) ) { throw SSE; } } __finally { if( l_catch_system_errors ) { BOOST_TEST_CRT_SET_HOOK( old_crt_hook ); _set_invalid_parameter_handler( old_iph ); } } return ret_val; } //____________________________________________________________________________// #else // default signal handler namespace detail { class system_signal_exception { public: void report() const {} }; } // namespace detail int execution_monitor::catch_signals( boost::function const& F ) { return detail::do_invoke( m_custom_translators , F ); } //____________________________________________________________________________// #endif // choose signal handler // ************************************************************************** // // ************** execution_monitor ************** // // ************************************************************************** // execution_monitor::execution_monitor() : p_catch_system_errors( true ) , p_auto_start_dbg( false ) , p_timeout( 0 ) , p_use_alt_stack( true ) , p_detect_fp_exceptions( fpe::BOOST_FPE_OFF ) {} //____________________________________________________________________________// int execution_monitor::execute( boost::function const& F ) { if( debug::under_debugger() ) p_catch_system_errors.value = false; BOOST_TEST_I_TRY { detail::fpe_except_guard G( p_detect_fp_exceptions ); unit_test::ut_detail::ignore_unused_variable_warning( G ); return catch_signals( F ); } #ifndef BOOST_NO_EXCEPTIONS // Catch-clause reference arguments are a bit different from function // arguments (ISO 15.3 paragraphs 18 & 19). Apparently const isn't // required. Programmers ask for const anyhow, so we supply it. That's // easier than answering questions about non-const usage. catch( char const* ex ) { detail::report_error( execution_exception::cpp_exception_error, "C string: %s", ex ); } catch( std::string const& ex ) { detail::report_error( execution_exception::cpp_exception_error, "std::string: %s", ex.c_str() ); } // std:: exceptions #if defined(BOOST_NO_TYPEID) || defined(BOOST_NO_RTTI) #define CATCH_AND_REPORT_STD_EXCEPTION( ex_name ) \ catch( ex_name const& ex ) \ { detail::report_error( execution_exception::cpp_exception_error, \ current_exception_cast(), \ #ex_name ": %s", ex.what() ); } \ /**/ #else #define CATCH_AND_REPORT_STD_EXCEPTION( ex_name ) \ catch( ex_name const& ex ) \ { detail::report_error( execution_exception::cpp_exception_error, \ current_exception_cast(), \ "%s: %s", detail::typeid_name(ex).c_str(), ex.what() ); } \ /**/ #endif CATCH_AND_REPORT_STD_EXCEPTION( std::bad_alloc ) #if BOOST_WORKAROUND(__BORLANDC__, <= 0x0551) CATCH_AND_REPORT_STD_EXCEPTION( std::bad_cast ) CATCH_AND_REPORT_STD_EXCEPTION( std::bad_typeid ) #else CATCH_AND_REPORT_STD_EXCEPTION( std::bad_cast ) CATCH_AND_REPORT_STD_EXCEPTION( std::bad_typeid ) #endif CATCH_AND_REPORT_STD_EXCEPTION( std::bad_exception ) CATCH_AND_REPORT_STD_EXCEPTION( std::domain_error ) CATCH_AND_REPORT_STD_EXCEPTION( std::invalid_argument ) CATCH_AND_REPORT_STD_EXCEPTION( std::length_error ) CATCH_AND_REPORT_STD_EXCEPTION( std::out_of_range ) CATCH_AND_REPORT_STD_EXCEPTION( std::range_error ) CATCH_AND_REPORT_STD_EXCEPTION( std::overflow_error ) CATCH_AND_REPORT_STD_EXCEPTION( std::underflow_error ) CATCH_AND_REPORT_STD_EXCEPTION( std::logic_error ) CATCH_AND_REPORT_STD_EXCEPTION( std::runtime_error ) CATCH_AND_REPORT_STD_EXCEPTION( std::exception ) #undef CATCH_AND_REPORT_STD_EXCEPTION catch( boost::exception const& ex ) { detail::report_error( execution_exception::cpp_exception_error, &ex, #if defined(BOOST_NO_TYPEID) || defined(BOOST_NO_RTTI) "unknown boost::exception" ); } #else typeid(ex).name() ); } #endif // system errors catch( system_error const& ex ) { detail::report_error( execution_exception::cpp_exception_error, "system_error produced by: %s: %s", ex.p_failed_exp, std::strerror( ex.p_errno ) ); } catch( detail::system_signal_exception const& ex ) { ex.report(); } // not an error catch( execution_aborted const& ) { return 0; } // just forward catch( execution_exception const& ) { throw; } // unknown error catch( ... ) { detail::report_error( execution_exception::cpp_exception_error, "unknown type" ); } #endif // !BOOST_NO_EXCEPTIONS return 0; // never reached; supplied to quiet compiler warnings } // execute //____________________________________________________________________________// namespace detail { struct forward { explicit forward( boost::function const& F ) : m_F( F ) {} int operator()() { m_F(); return 0; } boost::function const& m_F; }; } // namespace detail void execution_monitor::vexecute( boost::function const& F ) { execute( detail::forward( F ) ); } // ************************************************************************** // // ************** system_error ************** // // ************************************************************************** // system_error::system_error( char const* exp ) #ifdef UNDER_CE : p_errno( GetLastError() ) #else : p_errno( errno ) #endif , p_failed_exp( exp ) {} //____________________________________________________________________________// // ************************************************************************** // // ************** execution_exception ************** // // ************************************************************************** // execution_exception::execution_exception( error_code ec_, const_string what_msg_, location const& location_ ) : m_error_code( ec_ ) , m_what( what_msg_.empty() ? BOOST_TEST_L( "uncaught exception, system error or abort requested" ) : what_msg_ ) , m_location( location_ ) {} //____________________________________________________________________________// execution_exception::location::location( char const* file_name, size_t line_num, char const* func ) : m_file_name( file_name ? file_name : "unknown location" ) , m_line_num( line_num ) , m_function( func ) {} //____________________________________________________________________________// // ************************************************************************** // // **************Floating point exception management interface ************** // // ************************************************************************** // namespace fpe { unsigned enable( unsigned mask ) { boost::ignore_unused(mask); #if defined(BOOST_TEST_FPE_SUPPORT_WITH_SEH__) _clearfp(); #if BOOST_WORKAROUND( BOOST_MSVC, <= 1310) unsigned old_cw = ::_controlfp( 0, 0 ); ::_controlfp( old_cw & ~mask, BOOST_FPE_ALL ); #else unsigned old_cw; if( ::_controlfp_s( &old_cw, 0, 0 ) != 0 ) return BOOST_FPE_INV; // Set the control word if( ::_controlfp_s( 0, old_cw & ~mask, BOOST_FPE_ALL ) != 0 ) return BOOST_FPE_INV; #endif return ~old_cw & BOOST_FPE_ALL; #elif defined(BOOST_TEST_FPE_SUPPORT_WITH_GLIBC_EXTENSIONS__) // same macro definition as in execution_monitor.hpp if (BOOST_FPE_ALL == BOOST_FPE_OFF) /* Not Implemented */ return BOOST_FPE_OFF; feclearexcept(BOOST_FPE_ALL); int res = feenableexcept( mask ); return res == -1 ? (unsigned)BOOST_FPE_INV : (unsigned)res; #else /* Not Implemented */ return BOOST_FPE_OFF; #endif } //____________________________________________________________________________// unsigned disable( unsigned mask ) { boost::ignore_unused(mask); #if defined(BOOST_TEST_FPE_SUPPORT_WITH_SEH__) _clearfp(); #if BOOST_WORKAROUND( BOOST_MSVC, <= 1310) unsigned old_cw = ::_controlfp( 0, 0 ); ::_controlfp( old_cw | mask, BOOST_FPE_ALL ); #else unsigned old_cw; if( ::_controlfp_s( &old_cw, 0, 0 ) != 0 ) return BOOST_FPE_INV; // Set the control word if( ::_controlfp_s( 0, old_cw | mask, BOOST_FPE_ALL ) != 0 ) return BOOST_FPE_INV; #endif return ~old_cw & BOOST_FPE_ALL; #elif defined(BOOST_TEST_FPE_SUPPORT_WITH_GLIBC_EXTENSIONS__) if (BOOST_FPE_ALL == BOOST_FPE_OFF) /* Not Implemented */ return BOOST_FPE_INV; feclearexcept(BOOST_FPE_ALL); int res = fedisableexcept( mask ); return res == -1 ? (unsigned)BOOST_FPE_INV : (unsigned)res; #else /* Not Implemented */ return BOOST_FPE_INV; #endif } //____________________________________________________________________________// } // namespace fpe } // namespace boost #include #endif // BOOST_TEST_EXECUTION_MONITOR_IPP_012205GER