// (C) Copyright Gennadiy Rozental 2001-2008. // (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 : $RCSfile$ // // Version : $Revision$ // // Description : 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 // Boost #include // for exit codes #include // for workarounds #include // for get_error_info #include // for current_exception_cast // 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 #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 #endif #if defined(_WIN32) && !defined(BOOST_DISABLE_WIN32) && \ (!defined(__COMO__) && !defined(__MWERKS__) && !defined(__GNUC__) || \ BOOST_WORKAROUND(__MWERKS__, >= 0x3000)) # define 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 BOOST_WORKAROUND(_MSC_VER, < 1300 ) || defined(UNDER_CE) typedef void* uintptr_t; # endif // for the FP control routines #include #ifndef EM_INVALID #define EM_INVALID _EM_INVALID #endif #ifndef EM_DENORMAL #define EM_DENORMAL _EM_DENORMAL #endif #ifndef EM_ZERODIVIDE #define EM_ZERODIVIDE _EM_ZERODIVIDE #endif #ifndef EM_OVERFLOW #define EM_OVERFLOW _EM_OVERFLOW #endif #ifndef EM_UNDERFLOW #define EM_UNDERFLOW _EM_UNDERFLOW #endif #ifndef MCW_EM #define MCW_EM _MCW_EM #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(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__) # 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 # define BOOST_TEST_IGNORE_SIGCHLD # endif # endif # if !defined(__CYGWIN__) && !defined(__QNXNTO__) # 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 #include //____________________________________________________________________________// namespace boost { // ************************************************************************** // // ************** 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 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 = 512; static char buf[REPORT_ERROR_BUFFER_SIZE]; BOOST_TEST_VSNPRINTF( buf, sizeof(buf)-1, format, *args ); buf[sizeof(buf)-1] = 0; va_end( *args ); throw execution_exception( ec, buf, execution_exception::location( extract( be ), extract( be ), extract( be ) ) ); } //____________________________________________________________________________// 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 ); } //____________________________________________________________________________// 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 ); } //____________________________________________________________________________// template inline int do_invoke( Tr const& tr, Functor const& F ) { return tr ? (*tr)( F ) : F(); } //____________________________________________________________________________// } // 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; case SIGCHLD: switch( m_sig_info->si_code ) { #ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS case CLD_EXITED: report_error( execution_exception::system_error, "child has exited; pid: %d; uid: %d; exit value: %d", (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); break; case CLD_KILLED: report_error( execution_exception::system_error, "child was killed; pid: %d; uid: %d; exit value: %d", (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); break; case CLD_DUMPED: report_error( execution_exception::system_error, "child terminated abnormally; pid: %d; uid: %d; exit value: %d", (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); break; case CLD_TRAPPED: report_error( execution_exception::system_error, "traced child has trapped; pid: %d; uid: %d; exit value: %d", (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); break; case CLD_STOPPED: report_error( execution_exception::system_error, "child has stopped; pid: %d; uid: %d; exit value: %d", (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); break; case CLD_CONTINUED: report_error( execution_exception::system_error, "stopped child had continued; pid: %d; uid: %d; exit value: %d", (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status ); break; #endif default: report_error( execution_exception::system_error, "signal: SIGCHLD, si_code: %d (child process has terminated; pid: %d; uid: %d; exit value: %d)", (int)m_sig_info->si_pid, (int)m_sig_info->si_uid, (int)m_sig_info->si_status, 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 occured; 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" ); } } //____________________________________________________________________________// // ************************************************************************** // // ************** boost::detail::signal_action ************** // // ************************************************************************** // // Forward declaration extern "C" { static void execution_monitor_jumping_signal_handler( int sig, siginfo_t* info, void* context ); static void 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 ? &execution_monitor_attaching_signal_handler : &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, int 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; int m_timeout; 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, int 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 , catch_system_errors, 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 ) #ifndef BOOST_TEST_IGNORE_SIGCHLD , m_CHLD_action( SIGCHLD, catch_system_errors, attach_dbg, alt_stack ) #endif #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; BOOST_TEST_SYS_ASSERT( ::sigaltstack( &sigstk, 0 ) != -1 ); #endif s_active_handler = m_prev_handler; } //____________________________________________________________________________// // ************************************************************************** // // ************** execution_monitor_signal_handler ************** // // ************************************************************************** // extern "C" { static bool ignore_sigchild( siginfo_t* info ) { return info->si_signo == SIGCHLD #ifndef BOOST_TEST_LIMITED_SIGNAL_DETAILS && info->si_code == CLD_EXITED #endif #ifdef BOOST_TEST_IGNORE_NON_ZERO_CHILD_CODE ; #else && (int)info->si_status == 0; #endif } //____________________________________________________________________________// static void execution_monitor_jumping_signal_handler( int sig, siginfo_t* info, void* context ) { if( ignore_sigchild( info ) ) return; signal_handler::sys_sig()( info, context ); siglongjmp( signal_handler::jump_buffer(), sig ); } //____________________________________________________________________________// static void execution_monitor_attaching_signal_handler( int sig, siginfo_t* info, void* context ) { if( ignore_sigchild( info ) ) return; if( !debug::attach_debugger( false ) ) 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( unit_test::callback0 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_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 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 int id, _EXCEPTION_POINTERS* exps ); private: // Data members execution_monitor* m_em; unsigned int m_se_id; void* m_fault_address; bool m_dir; }; static void seh_catch_preventer( unsigned int /* id */, _EXCEPTION_POINTERS* /* exps */ ) { throw; } //____________________________________________________________________________// int system_signal_exception::operator()( unsigned int id, _EXCEPTION_POINTERS* exps ) { const unsigned int MSFT_CPP_EXCEPT = 0xE06d7363; // EMSC if( !m_em->p_catch_system_errors || (id == MSFT_CPP_EXCEPT) ) return EXCEPTION_CONTINUE_SEARCH; if( !!m_em->p_auto_start_dbg && debug::attach_debugger( false ) ) { m_em->p_catch_system_errors.value = false; _set_se_translator( &seh_catch_preventer ); 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; # if 0 // !! ?? case EXCEPTION_FLT_INEXACT_RESULT: detail::report_error( execution_exception::system_error, "result of a floating-point operation cannot be represented exactly" ); break; #endif 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 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* ) { switch( reportType ) { case BOOST_TEST_CRT_ASSERT: detail::report_error( execution_exception::user_error, userMessage ); return 1; // return value and retVal are not important since we never reach this line case BOOST_TEST_CRT_ERROR: detail::report_error( execution_exception::system_error, userMessage ); return 1; // return value and retVal are not important since we never reach this line default: return 0; // use usual reporting method } } // assert_reporting_function //____________________________________________________________________________// void BOOST_TEST_CALL_DECL invalid_param_handler( wchar_t const* /* expr */, wchar_t const* /* func */, wchar_t const* /* file */, unsigned int /* line */, uintptr_t /* reserved */) { detail::report_error( execution_exception::user_error, "Invalid parameter detected by C runtime library" ); } //____________________________________________________________________________// void BOOST_TEST_CALL_DECL switch_fp_exceptions( bool on_off ) { if( !on_off ) _clearfp(); int cw = ::_controlfp( 0, 0 ); int exceptions_mask = EM_INVALID|EM_DENORMAL|EM_ZERODIVIDE|EM_OVERFLOW|EM_UNDERFLOW; if( on_off ) cw &= ~exceptions_mask; // Set the exception masks on, turn exceptions off else cw |= exceptions_mask; // Set the exception masks off, turn exceptions on if( on_off ) _clearfp(); // Set the control word ::_controlfp( cw, MCW_EM ); } //____________________________________________________________________________// } // namespace detail // ************************************************************************** // // ************** execution_monitor::catch_signals ************** // // ************************************************************************** // int execution_monitor::catch_signals( unit_test::callback0 const& F ) { _invalid_parameter_handler old_iph = _invalid_parameter_handler(); BOOST_TEST_CRT_HOOK_TYPE old_crt_hook = 0; if( !p_catch_system_errors ) _set_se_translator( &detail::seh_catch_preventer ); else { if( !!p_detect_fp_exceptions ) detail::switch_fp_exceptions( true ); 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 ) ); } detail::system_signal_exception SSE( this ); int ret_val = 0; __try { __try { ret_val = detail::do_invoke( m_custom_translators, F ); } __except( SSE( GetExceptionCode(), GetExceptionInformation() ) ) { throw SSE; } } __finally { if( !!p_catch_system_errors ) { if( !!p_detect_fp_exceptions ) detail::switch_fp_exceptions( false ); 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( unit_test::callback0 const& F ) { return detail::do_invoke( m_custom_translators , F ); } //____________________________________________________________________________// #endif // choose signal handler // ************************************************************************** // // ************** execution_monitor::execute ************** // // ************************************************************************** // int execution_monitor::execute( unit_test::callback0 const& F ) { if( debug::under_debugger() ) p_catch_system_errors.value = false; try { return catch_signals( F ); } // 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 catch( std::bad_alloc const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::bad_alloc: %s", ex.what() ); } #if BOOST_WORKAROUND(__BORLANDC__, <= 0x0551) catch( std::bad_cast const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::bad_cast" ); } catch( std::bad_typeid const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::bad_typeid" ); } #else catch( std::bad_cast const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::bad_cast: %s", ex.what() ); } catch( std::bad_typeid const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::bad_typeid: %s", ex.what() ); } #endif catch( std::bad_exception const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::bad_exception: %s", ex.what() ); } catch( std::domain_error const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::domain_error: %s", ex.what() ); } catch( std::invalid_argument const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::invalid_argument: %s", ex.what() ); } catch( std::length_error const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::length_error: %s", ex.what() ); } catch( std::out_of_range const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::out_of_range: %s", ex.what() ); } catch( std::range_error const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::range_error: %s", ex.what() ); } catch( std::overflow_error const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::overflow_error: %s", ex.what() ); } catch( std::underflow_error const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::underflow_error: %s", ex.what() ); } catch( std::logic_error const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::logic_error: %s", ex.what() ); } catch( std::runtime_error const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::runtime_error: %s", ex.what() ); } catch( std::exception const& ex ) { detail::report_error( execution_exception::cpp_exception_error, current_exception_cast(), "std::exception: %s", ex.what() ); } catch( boost::exception const& ex ) { detail::report_error( execution_exception::cpp_exception_error, &ex, "unknown boost::exception" ); } // 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.get(), 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" ); } return 0; // never reached; supplied to quiet compiler warnings } // execute //____________________________________________________________________________// // ************************************************************************** // // ************** 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 ) {} //____________________________________________________________________________// } // namespace boost #include #endif // BOOST_TEST_EXECUTION_MONITOR_IPP_012205GER