//  (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 <boost/test/detail/config.hpp>
#include <boost/test/detail/throw_exception.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/core/ignore_unused.hpp> // for ignore_unused
#ifndef BOOST_NO_EXCEPTIONS
#include <boost/exception/get_error_info.hpp> // for get_error_info
#include <boost/exception/current_exception_cast.hpp> // for current_exception_cast
#include <boost/exception/diagnostic_information.hpp>
#endif

// 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 <stdio.h>
#include <cstdarg>              // for varargs
#include <stdarg.h>
#include <cmath>                // for ceil

#include <iostream>              // for varargs

#ifdef BOOST_NO_STDC_NAMESPACE
namespace std { using ::strerror; using ::strlen; using ::strncat; using ::ceil; }
#endif

// to use vsnprintf
#if defined(__SUNPRO_CC) || defined(__SunOS) || defined(__QNXNTO__) || defined(__VXWORKS__)
using std::va_list;
#endif

#if defined(__VXWORKS__)
# define BOOST_TEST_LIMITED_SIGNAL_DETAILS
#endif

#ifdef BOOST_SEH_BASED_SIGNAL_HANDLING

# if !defined(_WIN32_WINNT) // WinXP
#   define _WIN32_WINNT  0x0501
# endif

#  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 defined(UNDER_CE) && BOOST_WORKAROUND(_MSC_VER,  < 1500 )
   typedef void* uintptr_t;
#  elif defined(UNDER_CE)
#  include <crtdefs.h>
#  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 defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) /* WinXP */
#    define BOOST_TEST_WIN32_WAITABLE_TIMERS
#  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 <unistd.h>
#  include <signal.h>
#  include <setjmp.h>

#  if defined(__FreeBSD__)

#    include <osreldate.h>

#    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 <android/api-level.h>
#  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 <errno.h>
#endif

#if !defined(BOOST_NO_TYPEID) && !defined(BOOST_NO_RTTI)
#  include <boost/core/demangle.hpp>
#endif

#include <boost/test/detail/suppress_warnings.hpp>

//____________________________________________________________________________//

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) || \
      (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
#  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>
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 = 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<throw_file>( be ),
                                                                                    (size_t)extract<throw_line>( be ),
                                                                                    extract<throw_function>( 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<typename Tr,typename Functor>
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<typename T>
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 ) {
#ifdef __VXWORKS__
// a bit of a hack to adapt code to small m_sig_info VxWorks uses
#define si_addr si_value.sival_int
#define si_band si_value.sival_int
#else
    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;
#endif
    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_microseconds, 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_microseconds;

    // 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_microseconds, bool attach_dbg, char* alt_stack )
: m_prev_handler( s_active_handler )
, m_timeout_microseconds( timeout_microseconds )
, 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_microseconds > 0, attach_dbg, alt_stack )
{
    s_active_handler = this;

    if( m_timeout_microseconds > 0 ) {
        ::alarm( 0 );
        ::alarm( static_cast<unsigned int>(std::ceil(timeout_microseconds / 1E6) )); // alarm has a precision to the seconds
    }

#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_microseconds > 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<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_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 )
    , m_timeout( false )
    {}

    void                set_timed_out();
    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;
    bool                m_timeout;
};

//____________________________________________________________________________//

#if BOOST_WORKAROUND( BOOST_MSVC, <= 1310)
static void
seh_catch_preventer( unsigned /* id */, _EXCEPTION_POINTERS* /* exps */ )
{
    throw;
}
#endif

//____________________________________________________________________________//

void
system_signal_exception::set_timed_out()
{
    m_timeout = true;
}

//____________________________________________________________________________//

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:
        if( m_timeout ) {
            detail::report_error(execution_exception::timeout_error, "timeout while executing function");
        }
        else {
            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<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 ) {
        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
    }

#if defined(BOOST_TEST_WIN32_WAITABLE_TIMERS)
    HANDLE htimer = INVALID_HANDLE_VALUE;
    BOOL bTimerSuccess = FALSE;

    if( p_timeout ) {
        htimer = ::CreateWaitableTimer(
            NULL,
            TRUE,
            NULL); // naming the timer might create collisions

        if( htimer != INVALID_HANDLE_VALUE ) {
            LARGE_INTEGER liDueTime;
            liDueTime.QuadPart = - static_cast<signed long int>(p_timeout) * 10; // resolution of 100 ns

            bTimerSuccess = ::SetWaitableTimer(
                htimer,
                &liDueTime,
                0,
                0,
                0,
                FALSE);           // Do not restore a suspended system
        }
    }
#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;
        }

        // we check for time outs: we do not have any signaling facility on Win32
        // however, we signal a timeout as a hard error as for the other operating systems
        // and throw the signal error handler
        if( bTimerSuccess && htimer != INVALID_HANDLE_VALUE) {
            if (::WaitForSingleObject(htimer, 0) == WAIT_OBJECT_0) {
                SSE.set_timed_out();
                throw SSE;
            }
        }

    }
    __finally {

#if defined(BOOST_TEST_WIN32_WAITABLE_TIMERS)
        if( htimer != INVALID_HANDLE_VALUE ) {
            ::CloseHandle(htimer);
        }
#endif

        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<int ()> 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<int ()> 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 );
        boost::ignore_unused( 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() ); }

    // boost::exception (before std::exception, with extended diagnostic)
    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
                              boost::diagnostic_information(ex).c_str() ); }
#endif

    //  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<boost::exception const>(), \
                          #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<boost::exception const>(), \
                          "%s: %s", detail::typeid_name(ex).c_str(), ex.what() ); } \
/**/
#endif

    CATCH_AND_REPORT_STD_EXCEPTION( std::bad_alloc )
    CATCH_AND_REPORT_STD_EXCEPTION( std::bad_cast )
    CATCH_AND_REPORT_STD_EXCEPTION( std::bad_typeid )
    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

    // 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<void ()> const& F ) : m_F( F ) {}

    int         operator()() { m_F(); return 0; }

    boost::function<void ()> const& m_F;
};

} // namespace detail
void
execution_monitor::vexecute( boost::function<void ()> 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 )
{}

execution_exception::location::location(const_string file_name, size_t line_num, char const* func )
: m_file_name( file_name )
, 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 <boost/test/detail/enable_warnings.hpp>

#endif // BOOST_TEST_EXECUTION_MONITOR_IPP_012205GER
