// (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: 57992 $ | |
// | |
// 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 <boost/test/detail/config.hpp> | |
#include <boost/test/detail/workaround.hpp> | |
#include <boost/test/execution_monitor.hpp> | |
#include <boost/test/debug.hpp> | |
// Boost | |
#include <boost/cstdlib.hpp> // for exit codes | |
#include <boost/config.hpp> // for workarounds | |
#include <boost/exception/get_error_info.hpp> // for get_error_info | |
#include <boost/exception/current_exception_cast.hpp> // for current_exception_cast | |
// STL | |
#include <string> // for std::string | |
#include <new> // for std::bad_alloc | |
#include <typeinfo> // for std::bad_cast, std::bad_typeid | |
#include <exception> // for std::exception, std::bad_exception | |
#include <stdexcept> // for std exception hierarchy | |
#include <cstring> // for C string API | |
#include <cassert> // for assert | |
#include <cstddef> // for NULL | |
#include <cstdio> // for vsnprintf | |
#include <cstdarg> // 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 <stdio.h> | |
# include <stdarg.h> | |
using std::va_list; | |
#endif | |
// to use vsnprintf | |
#if defined(__QNXNTO__) | |
# include <stdio.h> | |
#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 <windows.h> | |
# if defined(__MWERKS__) || (defined(_MSC_VER) && !defined(UNDER_CE)) | |
# include <eh.h> | |
# endif | |
# if defined(__BORLANDC__) && __BORLANDC__ >= 0x560 || defined(__MWERKS__) | |
# include <stdint.h> | |
# 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 <float.h> | |
#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 <crtdbg.h> | |
# 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 <unistd.h> | |
# include <signal.h> | |
# include <setjmp.h> | |
# 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 <errno.h> | |
#endif | |
#include <boost/test/detail/suppress_warnings.hpp> | |
//____________________________________________________________________________// | |
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> | |
typename ErrorInfo::value_type | |
extract( boost::exception const* ex ) | |
{ | |
if( !ex ) | |
return 0; | |
typename ErrorInfo::value_type const * val = boost::get_error_info<ErrorInfo>( *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<throw_file>( be ), | |
extract<throw_line>( be ), | |
extract<throw_function>( 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<typename Tr,typename Functor> | |
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<int> 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<int> 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<int> const& F ) | |
{ | |
return detail::do_invoke( m_custom_translators , F ); | |
} | |
//____________________________________________________________________________// | |
#endif // choose signal handler | |
// ************************************************************************** // | |
// ************** execution_monitor::execute ************** // | |
// ************************************************************************** // | |
int | |
execution_monitor::execute( unit_test::callback0<int> 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<boost::exception const>(), | |
"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<boost::exception const>(), | |
"std::bad_cast" ); } | |
catch( std::bad_typeid const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::bad_typeid" ); } | |
#else | |
catch( std::bad_cast const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::bad_cast: %s", ex.what() ); } | |
catch( std::bad_typeid const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::bad_typeid: %s", ex.what() ); } | |
#endif | |
catch( std::bad_exception const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::bad_exception: %s", ex.what() ); } | |
catch( std::domain_error const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::domain_error: %s", ex.what() ); } | |
catch( std::invalid_argument const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::invalid_argument: %s", ex.what() ); } | |
catch( std::length_error const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::length_error: %s", ex.what() ); } | |
catch( std::out_of_range const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::out_of_range: %s", ex.what() ); } | |
catch( std::range_error const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::range_error: %s", ex.what() ); } | |
catch( std::overflow_error const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::overflow_error: %s", ex.what() ); } | |
catch( std::underflow_error const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::underflow_error: %s", ex.what() ); } | |
catch( std::logic_error const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::logic_error: %s", ex.what() ); } | |
catch( std::runtime_error const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"std::runtime_error: %s", ex.what() ); } | |
catch( std::exception const& ex ) | |
{ detail::report_error( execution_exception::cpp_exception_error, | |
current_exception_cast<boost::exception const>(), | |
"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 <boost/test/detail/enable_warnings.hpp> | |
#endif // BOOST_TEST_EXECUTION_MONITOR_IPP_012205GER | |