LCOV - code coverage report
Current view: top level - opt/homebrew/include/boost/test/impl - debug.ipp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 17 26 65.4 %
Date: 2026-06-25 07:23:43 Functions: 3 6 50.0 %

          Line data    Source code
       1             : //  (C) Copyright Gennadiy Rozental 2001.
       2             : //  Use, modification, and distribution are subject to the
       3             : //  Boost Software License, Version 1.0. (See accompanying file
       4             : //  http://www.boost.org/LICENSE_1_0.txt)
       5             : 
       6             : //  See http://www.boost.org/libs/test for the library home page.
       7             : //
       8             : //  File        : $RCSfile$
       9             : //
      10             : //  Version     : $Revision$
      11             : //
      12             : //  Description : debug interfaces implementation
      13             : // ***************************************************************************
      14             : 
      15             : #ifndef BOOST_TEST_DEBUG_API_IPP_112006GER
      16             : #define BOOST_TEST_DEBUG_API_IPP_112006GER
      17             : 
      18             : // Boost.Test
      19             : #include <boost/test/detail/config.hpp>
      20             : #include <boost/test/detail/global_typedef.hpp>
      21             : 
      22             : #include <boost/test/debug.hpp>
      23             : #include <boost/test/debug_config.hpp>
      24             : 
      25             : #include <boost/core/ignore_unused.hpp>
      26             : 
      27             : // Implementation on Windows
      28             : #if defined(_WIN32) && !defined(UNDER_CE) && !defined(BOOST_DISABLE_WIN32) // ******* WIN32
      29             : 
      30             : #  define BOOST_WIN32_BASED_DEBUG
      31             : 
      32             : // SYSTEM API
      33             : #  include <windows.h>
      34             : #  include <winreg.h>
      35             : #  include <cstdio>
      36             : #  include <cstring>
      37             : 
      38             : #  if !defined(NDEBUG) && defined(_MSC_VER)
      39             : #    define BOOST_MS_CRT_BASED_DEBUG
      40             : #    include <crtdbg.h>
      41             : #  endif
      42             : 
      43             : 
      44             : #  ifdef BOOST_NO_STDC_NAMESPACE
      45             : namespace std { using ::memset; using ::sprintf; }
      46             : #  endif
      47             : 
      48             : #elif defined(unix) || defined(__unix) // ********************* UNIX
      49             : 
      50             : #  define BOOST_UNIX_BASED_DEBUG
      51             : 
      52             : // Boost.Test
      53             : #include <boost/test/utils/class_properties.hpp>
      54             : #include <boost/test/utils/algorithm.hpp>
      55             : 
      56             : // STL
      57             : #include <cstring>  // std::memcpy
      58             : #include <map>
      59             : #include <cstdio>
      60             : #include <stdarg.h> // !! ?? cstdarg
      61             : 
      62             : // SYSTEM API
      63             : #  include <unistd.h>
      64             : #  include <signal.h>
      65             : #  include <fcntl.h>
      66             : 
      67             : #  include <sys/types.h>
      68             : #  include <sys/stat.h>
      69             : #  include <sys/wait.h>
      70             : #  include <sys/time.h>
      71             : #  include <stdio.h>
      72             : #  include <stdlib.h>
      73             : 
      74             : #  if defined(sun) || defined(__sun)
      75             : 
      76             : #    define BOOST_SUN_BASED_DEBUG
      77             : 
      78             : #    ifndef BOOST_TEST_DBG_LIST
      79             : #      define BOOST_TEST_DBG_LIST dbx;gdb
      80             : #    endif
      81             : 
      82             : #    define BOOST_TEST_CNL_DBG  dbx
      83             : #    define BOOST_TEST_GUI_DBG  dbx-ddd
      84             : 
      85             : #    include <procfs.h>
      86             : 
      87             : #  elif defined(linux) || defined(__linux__)
      88             : 
      89             : #    define BOOST_LINUX_BASED_DEBUG
      90             : 
      91             : #    include <sys/ptrace.h>
      92             : 
      93             : #    ifndef BOOST_TEST_STAT_LINE_MAX
      94             : #      define BOOST_TEST_STAT_LINE_MAX 500
      95             : #    endif
      96             : 
      97             : #    ifndef BOOST_TEST_DBG_LIST
      98             : #      define BOOST_TEST_DBG_LIST gdb;lldb
      99             : #    endif
     100             : 
     101             : #    define BOOST_TEST_CNL_DBG  gdb
     102             : #    define BOOST_TEST_GUI_DBG  gdb-xterm
     103             : 
     104             : #  endif
     105             : 
     106             : #elif defined(__APPLE__) // ********************* APPLE
     107             : 
     108             : #  define BOOST_APPLE_BASED_DEBUG
     109             : 
     110             : #  include <assert.h>
     111             : #  include <sys/types.h>
     112             : #  include <unistd.h>
     113             : #  include <sys/sysctl.h>
     114             : 
     115             : #endif
     116             : 
     117             : #include <boost/test/detail/suppress_warnings.hpp>
     118             : 
     119             : //____________________________________________________________________________//
     120             : 
     121             : namespace boost {
     122             : namespace debug {
     123             : 
     124             : using unit_test::const_string;
     125             : 
     126             : // ************************************************************************** //
     127             : // **************                debug::info_t                 ************** //
     128             : // ************************************************************************** //
     129             : 
     130             : namespace {
     131             : 
     132             : #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32
     133             : 
     134             : template<typename T>
     135             : inline void
     136             : dyn_symbol( T& res, char const* module_name, char const* symbol_name )
     137             : {
     138             :     HMODULE m = ::GetModuleHandleA( module_name );
     139             : 
     140             :     if( !m )
     141             :         m = ::LoadLibraryA( module_name );
     142             : 
     143             :     res = reinterpret_cast<T>( ::GetProcAddress( m, symbol_name ) );
     144             : }
     145             : 
     146             : //____________________________________________________________________________//
     147             : 
     148             : static struct info_t {
     149             :     typedef BOOL (WINAPI* IsDebuggerPresentT)();
     150             :     typedef LONG (WINAPI* RegQueryValueExT)( HKEY, char const* /*LPTSTR*/, LPDWORD, LPDWORD, LPBYTE, LPDWORD );
     151             :     typedef LONG (WINAPI* RegOpenKeyT)( HKEY, char const* /*LPCTSTR*/, PHKEY );
     152             :     typedef LONG (WINAPI* RegCloseKeyT)( HKEY );
     153             : 
     154             :     info_t();
     155             : 
     156             :     IsDebuggerPresentT  m_is_debugger_present;
     157             :     RegOpenKeyT         m_reg_open_key;
     158             :     RegQueryValueExT    m_reg_query_value;
     159             :     RegCloseKeyT        m_reg_close_key;
     160             : 
     161             : } s_info;
     162             : 
     163             : //____________________________________________________________________________//
     164             : 
     165             : info_t::info_t()
     166             : {
     167             :     dyn_symbol( m_is_debugger_present, "kernel32", "IsDebuggerPresent" );
     168             :     dyn_symbol( m_reg_open_key, "advapi32", "RegOpenKeyA" );
     169             :     dyn_symbol( m_reg_query_value, "advapi32", "RegQueryValueExA" );
     170             :     dyn_symbol( m_reg_close_key, "advapi32", "RegCloseKey" );
     171             : }
     172             : 
     173             : //____________________________________________________________________________//
     174             : 
     175             : #elif defined(BOOST_UNIX_BASED_DEBUG)
     176             : 
     177             : // ************************************************************************** //
     178             : // **************                   fd_holder                  ************** //
     179             : // ************************************************************************** //
     180             : 
     181             : struct fd_holder {
     182             :     explicit fd_holder( int fd ) : m_fd( fd ) {}
     183             :     ~fd_holder()
     184             :     {
     185             :         if( m_fd != -1 )
     186             :             ::close( m_fd );
     187             :     }
     188             : 
     189             :     operator int() { return m_fd; }
     190             : 
     191             : private:
     192             :     // Data members
     193             :     int m_fd;
     194             : };
     195             : 
     196             : 
     197             : // ************************************************************************** //
     198             : // **************                 process_info                 ************** //
     199             : // ************************************************************************** //
     200             : 
     201             : struct process_info {
     202             :     // Constructor
     203             :     explicit        process_info( int pid );
     204             : 
     205             :     // access methods
     206             :     int             parent_pid() const  { return m_parent_pid; }
     207             :     const_string    binary_name() const { return m_binary_name; }
     208             :     const_string    binary_path() const { return m_binary_path; }
     209             : 
     210             : private:
     211             :     // Data members
     212             :     int             m_parent_pid;
     213             :     const_string    m_binary_name;
     214             :     const_string    m_binary_path;
     215             : 
     216             : #if defined(BOOST_SUN_BASED_DEBUG)
     217             :     struct psinfo   m_psi;
     218             :     char            m_binary_path_buff[500+1]; // !! ??
     219             : #elif defined(BOOST_LINUX_BASED_DEBUG)
     220             :     char            m_stat_line[BOOST_TEST_STAT_LINE_MAX+1];
     221             :     char            m_binary_path_buff[500+1]; // !! ??
     222             : #endif
     223             : };
     224             : 
     225             : //____________________________________________________________________________//
     226             : 
     227             : process_info::process_info( int pid )
     228             : : m_parent_pid( 0 )
     229             : {
     230             : #if defined(BOOST_SUN_BASED_DEBUG)
     231             :     char fname_buff[30];
     232             : 
     233             :     ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/psinfo", pid );
     234             : 
     235             :     fd_holder psinfo_fd( ::open( fname_buff, O_RDONLY ) );
     236             : 
     237             :     if( psinfo_fd == -1 )
     238             :         return;
     239             : 
     240             :     if( ::read( psinfo_fd, &m_psi, sizeof(m_psi) ) == -1 )
     241             :         return;
     242             : 
     243             :     m_parent_pid = m_psi.pr_ppid;
     244             : 
     245             :     m_binary_name.assign( m_psi.pr_fname );
     246             : 
     247             :     //-------------------------- //
     248             : 
     249             :     ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/as", pid );
     250             : 
     251             :     fd_holder as_fd( ::open( fname_buff, O_RDONLY ) );
     252             :     uintptr_t   binary_name_pos;
     253             : 
     254             :     // !! ?? could we avoid reading whole m_binary_path_buff?
     255             :     if( as_fd == -1 ||
     256             :         ::lseek( as_fd, m_psi.pr_argv, SEEK_SET ) == -1 ||
     257             :         ::read ( as_fd, &binary_name_pos, sizeof(binary_name_pos) ) == -1 ||
     258             :         ::lseek( as_fd, binary_name_pos, SEEK_SET ) == -1 ||
     259             :         ::read ( as_fd, m_binary_path_buff, sizeof(m_binary_path_buff) ) == -1 )
     260             :         return;
     261             : 
     262             :     m_binary_path.assign( m_binary_path_buff );
     263             : 
     264             : #elif defined(BOOST_LINUX_BASED_DEBUG)
     265             :     char fname_buff[30];
     266             : 
     267             :     ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/stat", pid );
     268             : 
     269             :     fd_holder psinfo_fd( ::open( fname_buff, O_RDONLY ) );
     270             : 
     271             :     if( psinfo_fd == -1 )
     272             :         return;
     273             : 
     274             :     ssize_t num_read = ::read( psinfo_fd, m_stat_line, sizeof(m_stat_line)-1 );
     275             :     if( num_read == -1 )
     276             :         return;
     277             : 
     278             :     m_stat_line[num_read] = 0;
     279             : 
     280             :     char const* name_beg = m_stat_line;
     281             :     while( *name_beg && *name_beg != '(' )
     282             :         ++name_beg;
     283             : 
     284             :     char const* name_end = name_beg+1;
     285             :     while( *name_end && *name_end != ')' )
     286             :         ++name_end;
     287             : 
     288             :     std::sscanf( name_end+1, "%*s%d", &m_parent_pid );
     289             : 
     290             :     m_binary_name.assign( name_beg+1, name_end );
     291             : 
     292             :     ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/exe", pid );
     293             :     num_read = ::readlink( fname_buff, m_binary_path_buff, sizeof(m_binary_path_buff)-1 );
     294             : 
     295             :     if( num_read == -1 )
     296             :         return;
     297             : 
     298             :     m_binary_path_buff[num_read] = 0;
     299             :     m_binary_path.assign( m_binary_path_buff, num_read );
     300             : #else
     301             :     (void) pid;  // silence 'unused variable' warning
     302             : #endif
     303             : }
     304             : 
     305             : //____________________________________________________________________________//
     306             : 
     307             : // ************************************************************************** //
     308             : // **************             prepare_window_title             ************** //
     309             : // ************************************************************************** //
     310             : 
     311             : static char*
     312             : prepare_window_title( dbg_startup_info const& dsi )
     313             : {
     314             :     typedef unit_test::const_string str_t;
     315             : 
     316             :     static char title_str[50];
     317             : 
     318             :     str_t path_sep( "\\/" );
     319             : 
     320             :     str_t::iterator  it = unit_test::utils::find_last_of( dsi.binary_path.begin(), dsi.binary_path.end(),
     321             :                                                           path_sep.begin(), path_sep.end() );
     322             : 
     323             :     if( it == dsi.binary_path.end() )
     324             :         it = dsi.binary_path.begin();
     325             :     else
     326             :         ++it;
     327             : 
     328             :     ::snprintf( title_str, sizeof(title_str), "%*s %ld", (int)(dsi.binary_path.end()-it), it, dsi.pid );
     329             : 
     330             :     return title_str;
     331             : }
     332             : 
     333             : //____________________________________________________________________________//
     334             : 
     335             : // ************************************************************************** //
     336             : // **************                  save_execlp                 ************** //
     337             : // ************************************************************************** //
     338             : 
     339             : typedef unit_test::basic_cstring<char> mbuffer;
     340             : 
     341             : inline char*
     342             : copy_arg( mbuffer& dest, const_string arg )
     343             : {
     344             :     if( dest.size() < arg.size()+1 )
     345             :         return 0;
     346             : 
     347             :     char* res = dest.begin();
     348             : 
     349             :     std::memcpy( res, arg.begin(), arg.size()+1 );
     350             : 
     351             :     dest.trim_left( arg.size()+1 );
     352             : 
     353             :     return res;
     354             : }
     355             : 
     356             : //____________________________________________________________________________//
     357             : 
     358             : bool
     359             : safe_execlp( char const* file, ... )
     360             : {
     361             :     static char* argv_buff[200];
     362             : 
     363             :     va_list     args;
     364             :     char const* arg;
     365             : 
     366             :     // first calculate actual number of arguments
     367             :     int         num_args = 2; // file name and 0 at least
     368             : 
     369             :     va_start( args, file );
     370             :     while( !!(arg = va_arg( args, char const* )) )
     371             :         num_args++;
     372             :     va_end( args );
     373             : 
     374             :     // reserve space for the argument pointers array
     375             :     char**      argv_it  = argv_buff;
     376             :     mbuffer     work_buff( reinterpret_cast<char*>(argv_buff), sizeof(argv_buff) );
     377             :     work_buff.trim_left( num_args * sizeof(char*) );
     378             : 
     379             :     // copy all the argument values into local storage
     380             :     if( !(*argv_it++ = copy_arg( work_buff, file )) )
     381             :         return false;
     382             : 
     383             :     printf( "!! %s\n", file );
     384             : 
     385             :     va_start( args, file );
     386             :     while( !!(arg = va_arg( args, char const* )) ) {
     387             :         printf( "!! %s\n", arg );
     388             :         if( !(*argv_it++ = copy_arg( work_buff, arg )) ) {
     389             :             va_end( args );
     390             :             return false;
     391             :         }
     392             :     }
     393             :     va_end( args );
     394             : 
     395             :     *argv_it = 0;
     396             : 
     397             :     return ::execvp( file, argv_buff ) != -1;
     398             : }
     399             : 
     400             : //____________________________________________________________________________//
     401             : 
     402             : // ************************************************************************** //
     403             : // **************            start_debugger_in_emacs           ************** //
     404             : // ************************************************************************** //
     405             : 
     406             : static void
     407             : start_debugger_in_emacs( dbg_startup_info const& dsi, char const* emacs_name, char const* dbg_command )
     408             : {
     409             :     char const* title = prepare_window_title( dsi );
     410             : 
     411             :     if( !title )
     412             :         return;
     413             : 
     414             :     dsi.display.is_empty()
     415             :         ? safe_execlp( emacs_name, "-title", title, "--eval", dbg_command, 0 )
     416             :         : safe_execlp( emacs_name, "-title", title, "-display", dsi.display.begin(), "--eval", dbg_command, 0 );
     417             : }
     418             : 
     419             : //____________________________________________________________________________//
     420             : 
     421             : // ************************************************************************** //
     422             : // **************                 gdb starters                 ************** //
     423             : // ************************************************************************** //
     424             : 
     425             : static char const*
     426             : prepare_gdb_cmnd_file( dbg_startup_info const& dsi )
     427             : {
     428             :     // prepare pid value
     429             :     char pid_buff[16];
     430             :     ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid );
     431             :     unit_test::const_string pid_str( pid_buff );
     432             : 
     433             :     static char cmd_file_name[] = "/tmp/btl_gdb_cmd_XXXXXX"; // !! ??
     434             : 
     435             :     // prepare commands
     436             :     const mode_t cur_umask = ::umask( S_IRWXO | S_IRWXG );
     437             :     fd_holder cmd_fd( ::mkstemp( cmd_file_name ) );
     438             :     ::umask( cur_umask );
     439             : 
     440             :     if( cmd_fd == -1 )
     441             :         return 0;
     442             : 
     443             : #define WRITE_STR( str )  if( ::write( cmd_fd, str.begin(), str.size() ) == -1 ) return 0;
     444             : #define WRITE_CSTR( str ) if( ::write( cmd_fd, str, sizeof( str )-1 ) == -1 ) return 0;
     445             : 
     446             :     WRITE_CSTR( "file " );
     447             :     WRITE_STR( dsi.binary_path );
     448             :     WRITE_CSTR( "\nattach " );
     449             :     WRITE_STR( pid_str );
     450             :     WRITE_CSTR( "\nshell unlink " );
     451             :     WRITE_STR( dsi.init_done_lock );
     452             :     WRITE_CSTR( "\ncont" );
     453             :     if( dsi.break_or_continue )
     454             :         WRITE_CSTR( "\nup 4" );
     455             : 
     456             :     WRITE_CSTR( "\necho \\n" ); // !! ??
     457             :     WRITE_CSTR( "\nlist -" );
     458             :     WRITE_CSTR( "\nlist" );
     459             :     WRITE_CSTR( "\nshell unlink " );
     460             :     WRITE_CSTR( cmd_file_name );
     461             : 
     462             :     return cmd_file_name;
     463             : }
     464             : 
     465             : //____________________________________________________________________________//
     466             : 
     467             : static void
     468             : start_gdb_in_console( dbg_startup_info const& dsi )
     469             : {
     470             :     char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi );
     471             : 
     472             :     if( !cmnd_file_name )
     473             :         return;
     474             : 
     475             :     safe_execlp( "gdb", "-q", "-x", cmnd_file_name, 0 );
     476             : }
     477             : 
     478             : //____________________________________________________________________________//
     479             : 
     480             : static void
     481             : start_gdb_in_xterm( dbg_startup_info const& dsi )
     482             : {
     483             :     char const* title           = prepare_window_title( dsi );
     484             :     char const* cmnd_file_name  = prepare_gdb_cmnd_file( dsi );
     485             : 
     486             :     if( !title || !cmnd_file_name )
     487             :         return;
     488             : 
     489             :     safe_execlp( "xterm", "-T", title, "-display", dsi.display.begin(),
     490             :                     "-bg", "black", "-fg", "white", "-geometry", "88x30+10+10", "-fn", "9x15", "-e",
     491             :                  "gdb", "-q", "-x", cmnd_file_name, 0 );
     492             : }
     493             : 
     494             : //____________________________________________________________________________//
     495             : 
     496             : static void
     497             : start_gdb_in_emacs( dbg_startup_info const& dsi )
     498             : {
     499             :     char const* cmnd_file_name  = prepare_gdb_cmnd_file( dsi );
     500             :     if( !cmnd_file_name )
     501             :         return;
     502             : 
     503             :     char dbg_cmd_buff[500]; // !! ??
     504             :     ::snprintf( dbg_cmd_buff, sizeof(dbg_cmd_buff), "(progn (gdb \"gdb -q -x %s\"))", cmnd_file_name );
     505             : 
     506             :     start_debugger_in_emacs( dsi, "emacs", dbg_cmd_buff );
     507             : }
     508             : 
     509             : //____________________________________________________________________________//
     510             : 
     511             : static void
     512             : start_gdb_in_xemacs( dbg_startup_info const& )
     513             : {
     514             :     // !! ??
     515             : }
     516             : 
     517             : //____________________________________________________________________________//
     518             : 
     519             : // ************************************************************************** //
     520             : // **************                 dbx starters                 ************** //
     521             : // ************************************************************************** //
     522             : 
     523             : static char const*
     524             : prepare_dbx_cmd_line( dbg_startup_info const& dsi, bool list_source = true )
     525             : {
     526             :     static char cmd_line_buff[500]; // !! ??
     527             : 
     528             :     ::snprintf( cmd_line_buff, sizeof(cmd_line_buff), "unlink %s;cont;%s%s",
     529             :                    dsi.init_done_lock.begin(),
     530             :                    dsi.break_or_continue ? "up 2;": "",
     531             :                    list_source ? "echo \" \";list -w3;" : "" );
     532             : 
     533             :     return cmd_line_buff;
     534             : }
     535             : 
     536             : //____________________________________________________________________________//
     537             : 
     538             : static void
     539             : start_dbx_in_console( dbg_startup_info const& dsi )
     540             : {
     541             :     char pid_buff[16];
     542             :     ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid );
     543             : 
     544             :     safe_execlp( "dbx", "-q", "-c", prepare_dbx_cmd_line( dsi ), dsi.binary_path.begin(), pid_buff, 0 );
     545             : }
     546             : 
     547             : //____________________________________________________________________________//
     548             : 
     549             : static void
     550             : start_dbx_in_xterm( dbg_startup_info const& dsi )
     551             : {
     552             :     char const* title = prepare_window_title( dsi );
     553             :     if( !title )
     554             :         return;
     555             : 
     556             :     char pid_buff[16]; // !! ??
     557             :     ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid );
     558             : 
     559             :     safe_execlp( "xterm", "-T", title, "-display", dsi.display.begin(),
     560             :                     "-bg", "black", "-fg", "white", "-geometry", "88x30+10+10", "-fn", "9x15", "-e",
     561             :                  "dbx", "-q", "-c", prepare_dbx_cmd_line( dsi ), dsi.binary_path.begin(), pid_buff, 0 );
     562             : }
     563             : 
     564             : //____________________________________________________________________________//
     565             : 
     566             : static void
     567             : start_dbx_in_emacs( dbg_startup_info const& /*dsi*/ )
     568             : {
     569             : //    char dbg_cmd_buff[500]; // !! ??
     570             : //
     571             : //    ::snprintf( dbg_cmd_buff, sizeof(dbg_cmd_buff), "(progn (dbx \"dbx -q -c cont %s %ld\"))", dsi.binary_path.begin(), dsi.pid );
     572             : 
     573             : //    start_debugger_in_emacs( dsi, "emacs", dbg_cmd_buff );
     574             : }
     575             : 
     576             : //____________________________________________________________________________//
     577             : 
     578             : static void
     579             : start_dbx_in_xemacs( dbg_startup_info const& )
     580             : {
     581             :     // !! ??
     582             : }
     583             : 
     584             : //____________________________________________________________________________//
     585             : 
     586             : static void
     587             : start_dbx_in_ddd( dbg_startup_info const& dsi )
     588             : {
     589             :     char const* title = prepare_window_title( dsi );
     590             :     if( !title )
     591             :         return;
     592             : 
     593             :     char pid_buff[16]; // !! ??
     594             :     ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid );
     595             : 
     596             :     safe_execlp( "ddd", "-display", dsi.display.begin(),
     597             :                  "--dbx", "-q", "-c", prepare_dbx_cmd_line( dsi, false ), dsi.binary_path.begin(), pid_buff, 0 );
     598             : }
     599             : 
     600             : //____________________________________________________________________________//
     601             : 
     602             : // ************************************************************************** //
     603             : // **************                debug::info_t                 ************** //
     604             : // ************************************************************************** //
     605             : 
     606             : static struct info_t {
     607             :     // Constructor
     608             :     info_t();
     609             : 
     610             :     // Public properties
     611             :     unit_test::readwrite_property<std::string>  p_dbg;
     612             : 
     613             :     // Data members
     614             :     std::map<std::string,dbg_starter>           m_dbg_starter_reg;
     615             : } s_info;
     616             : 
     617             : //____________________________________________________________________________//
     618             : 
     619             : info_t::info_t()
     620             : {
     621             :     p_dbg.value = ::getenv( "DISPLAY" )
     622             :         ? std::string( BOOST_STRINGIZE( BOOST_TEST_GUI_DBG ) )
     623             :         : std::string( BOOST_STRINGIZE( BOOST_TEST_CNL_DBG ) );
     624             : 
     625             :     m_dbg_starter_reg[std::string("gdb")]           = &start_gdb_in_console;
     626             :     m_dbg_starter_reg[std::string("gdb-emacs")]     = &start_gdb_in_emacs;
     627             :     m_dbg_starter_reg[std::string("gdb-xterm")]     = &start_gdb_in_xterm;
     628             :     m_dbg_starter_reg[std::string("gdb-xemacs")]    = &start_gdb_in_xemacs;
     629             : 
     630             :     m_dbg_starter_reg[std::string("dbx")]           = &start_dbx_in_console;
     631             :     m_dbg_starter_reg[std::string("dbx-emacs")]     = &start_dbx_in_emacs;
     632             :     m_dbg_starter_reg[std::string("dbx-xterm")]     = &start_dbx_in_xterm;
     633             :     m_dbg_starter_reg[std::string("dbx-xemacs")]    = &start_dbx_in_xemacs;
     634             :     m_dbg_starter_reg[std::string("dbx-ddd")]       = &start_dbx_in_ddd;
     635             : }
     636             : 
     637             : //____________________________________________________________________________//
     638             : 
     639             : #endif
     640             : 
     641             : } // local namespace
     642             : 
     643             : // ************************************************************************** //
     644             : // **************  check if program is running under debugger  ************** //
     645             : // ************************************************************************** //
     646             : 
     647             : bool
     648        1315 : under_debugger()
     649             : {
     650             : #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32
     651             : 
     652             :     return !!s_info.m_is_debugger_present && s_info.m_is_debugger_present();
     653             : 
     654             : #elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX
     655             : 
     656             :     // !! ?? could/should we cache the result somehow?
     657             :     const_string    dbg_list = BOOST_TEST_STRINGIZE( BOOST_TEST_DBG_LIST );
     658             : 
     659             :     pid_t pid = ::getpid();
     660             : 
     661             :     while( pid != 0 ) {
     662             :         process_info pi( pid );
     663             : 
     664             :         // !! ?? should we use tokenizer here instead?
     665             :         if( dbg_list.find( pi.binary_name() ) != const_string::npos )
     666             :             return true;
     667             : 
     668             :         pid = (pi.parent_pid() == pid ? 0 : pi.parent_pid());
     669             :     }
     670             : 
     671             :     return false;
     672             : 
     673             : #elif defined(BOOST_APPLE_BASED_DEBUG) // ********************** APPLE
     674             : 
     675             :     // See https://developer.apple.com/library/mac/qa/qa1361/_index.html
     676             :     int                 junk;
     677             :     int                 mib[4];
     678             :     struct kinfo_proc   info;
     679             :     size_t              size;
     680             : 
     681             :     // Initialize the flags so that, if sysctl fails for some bizarre
     682             :     // reason, we get a predictable result.
     683        1315 :     info.kp_proc.p_flag = 0;
     684             : 
     685             :     // Initialize mib, which tells sysctl the info we want, in this case
     686             :     // we're looking for information about a specific process ID.
     687        1315 :     mib[0] = CTL_KERN;
     688        1315 :     mib[1] = KERN_PROC;
     689        1315 :     mib[2] = KERN_PROC_PID;
     690        1315 :     mib[3] = getpid();
     691             : 
     692             :     // Call sysctl.
     693        1315 :     size = sizeof(info);
     694        1315 :     junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
     695        1315 :     assert(junk == 0);
     696             :     (void)junk; // Silence unused variable warning
     697             : 
     698             :     // We're being debugged if the P_TRACED flag is set.
     699        1315 :     return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
     700             : 
     701             : #else // ****************************************************** default
     702             : 
     703             :     return false;
     704             : 
     705             : #endif
     706             : }
     707             : 
     708             : //____________________________________________________________________________//
     709             : 
     710             : // ************************************************************************** //
     711             : // **************       cause program to break execution       ************** //
     712             : // **************           in debugger at call point          ************** //
     713             : // ************************************************************************** //
     714             : 
     715             : void
     716           0 : debugger_break()
     717             : {
     718             :     // !! ?? auto-start debugger?
     719             : 
     720             : #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32
     721             : 
     722             : #if defined(__GNUC__) && !defined(__MINGW32__)   ||  \
     723             :     defined(__INTEL_COMPILER) || defined(BOOST_EMBTC)
     724             : #   define BOOST_DEBUG_BREAK    __debugbreak
     725             : #else
     726             : #   define BOOST_DEBUG_BREAK    DebugBreak
     727             : #endif
     728             : 
     729             : #ifndef __MINGW32__
     730             :     if( !under_debugger() ) {
     731             :         __try {
     732             :             __try {
     733             :                 BOOST_DEBUG_BREAK();
     734             :             }
     735             :             __except( UnhandledExceptionFilter(GetExceptionInformation()) )
     736             :             {
     737             :                 // User opted to ignore the breakpoint
     738             :                 return;
     739             :             }
     740             :         }
     741             :         __except (EXCEPTION_EXECUTE_HANDLER)
     742             :         {
     743             :             // If we got here, the user has pushed Debug. Debugger is already attached to our process and we
     744             :             // continue to let the another BOOST_DEBUG_BREAK to be called.
     745             :         }
     746             :     }
     747             : #endif
     748             : 
     749             :     BOOST_DEBUG_BREAK();
     750             : 
     751             : #elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX
     752             : 
     753             :     ::kill( ::getpid(), SIGTRAP );
     754             : 
     755             : #else // ****************************************************** default
     756             : 
     757             : #endif
     758           0 : }
     759             : 
     760             : //____________________________________________________________________________//
     761             : 
     762             : // ************************************************************************** //
     763             : // **************            console debugger setup            ************** //
     764             : // ************************************************************************** //
     765             : 
     766             : #if defined(BOOST_UNIX_BASED_DEBUG) // ************************ UNIX
     767             : 
     768             : std::string
     769             : set_debugger( unit_test::const_string dbg_id, dbg_starter s )
     770             : {
     771             :     std::string old = s_info.p_dbg;
     772             : 
     773             :     assign_op( s_info.p_dbg.value, dbg_id, 0 );
     774             : 
     775             :     if( !!s )
     776             :         s_info.m_dbg_starter_reg[s_info.p_dbg.get()] = s;
     777             : 
     778             :     return old;
     779             : }
     780             : 
     781             : #else  // ***************************************************** default
     782             : 
     783             : std::string
     784           0 : set_debugger( unit_test::const_string, dbg_starter )
     785             : {
     786           0 :     return std::string();
     787             : }
     788             : 
     789             : #endif
     790             : 
     791             : //____________________________________________________________________________//
     792             : 
     793             : // ************************************************************************** //
     794             : // **************    attach debugger to the current process    ************** //
     795             : // ************************************************************************** //
     796             : 
     797             : #if defined(BOOST_WIN32_BASED_DEBUG)
     798             : 
     799             : struct safe_handle_helper
     800             : {
     801             :     HANDLE& handle;
     802             :     safe_handle_helper(HANDLE &handle_) : handle(handle_) {}
     803             : 
     804             :     void close_handle()
     805             :     {
     806             :         if( handle != INVALID_HANDLE_VALUE )
     807             :         {
     808             :             ::CloseHandle( handle );
     809             :             handle = INVALID_HANDLE_VALUE;
     810             :         }
     811             :     }
     812             : 
     813             :     ~safe_handle_helper()
     814             :     {
     815             :         close_handle();
     816             :     }
     817             : };
     818             : #endif
     819             : 
     820             : bool
     821           0 : attach_debugger( bool break_or_continue )
     822             : {
     823           0 :     if( under_debugger() )
     824           0 :         return false;
     825             : 
     826             : #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32
     827             : 
     828             :     const int MAX_CMD_LINE = 200;
     829             : 
     830             :     // *************************************************** //
     831             :     // Debugger "ready" event
     832             : 
     833             :     SECURITY_ATTRIBUTES attr;
     834             :     attr.nLength                = sizeof(attr);
     835             :     attr.lpSecurityDescriptor   = NULL;
     836             :     attr.bInheritHandle         = true;
     837             : 
     838             :     // manual resettable, initially non signaled, unnamed event,
     839             :     // that will signal me that debugger initialization is done
     840             :     HANDLE dbg_init_done_ev = ::CreateEvent(
     841             :         &attr,          // pointer to security attributes
     842             :         true,           // flag for manual-reset event
     843             :         false,          // flag for initial state
     844             :         NULL            // pointer to event-object name
     845             :     );
     846             : 
     847             :     if( !dbg_init_done_ev )
     848             :         return false;
     849             : 
     850             :     safe_handle_helper safe_handle_obj( dbg_init_done_ev );
     851             : 
     852             :     // *************************************************** //
     853             :     // Debugger command line format
     854             : 
     855             :     HKEY reg_key;
     856             : 
     857             :     if( !s_info.m_reg_open_key || (*s_info.m_reg_open_key)(
     858             :             HKEY_LOCAL_MACHINE,                                         // handle of open key
     859             :             "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", // name of subkey to open
     860             :             &reg_key ) != ERROR_SUCCESS )                               // address of handle of open key
     861             :         return false;
     862             : 
     863             :     char  format[MAX_CMD_LINE];
     864             :     DWORD format_size = MAX_CMD_LINE;
     865             :     DWORD type = REG_SZ;
     866             : 
     867             :     bool b_read_key = s_info.m_reg_query_value &&
     868             :           ((*s_info.m_reg_query_value)(
     869             :             reg_key,                            // handle of open key
     870             :             "Debugger",                         // name of subkey to query
     871             :             0,                                  // reserved
     872             :             &type,                              // value type
     873             :             (LPBYTE)format,                     // buffer for returned string
     874             :             &format_size ) == ERROR_SUCCESS );  // in: buffer size; out: actual size of returned string
     875             : 
     876             :     if( !s_info.m_reg_close_key || (*s_info.m_reg_close_key)( reg_key ) != ERROR_SUCCESS )
     877             :         return false;
     878             : 
     879             :     if( !b_read_key )
     880             :         return false;
     881             : 
     882             :     // *************************************************** //
     883             :     // Debugger command line
     884             : 
     885             :     char cmd_line[MAX_CMD_LINE];
     886             :     std::sprintf( cmd_line, format, ::GetCurrentProcessId(), dbg_init_done_ev );
     887             : 
     888             :     // *************************************************** //
     889             :     // Debugger window parameters
     890             : 
     891             :     STARTUPINFOA    startup_info;
     892             :     std::memset( &startup_info, 0, sizeof(startup_info) );
     893             : 
     894             :     startup_info.cb             = sizeof(startup_info);
     895             :     startup_info.dwFlags        = STARTF_USESHOWWINDOW;
     896             :     startup_info.wShowWindow    = SW_SHOWNORMAL;
     897             : 
     898             :     // debugger process s_info
     899             :     PROCESS_INFORMATION debugger_info;
     900             : 
     901             :     bool created = !!::CreateProcessA(
     902             :         NULL,           // pointer to name of executable module; NULL - use the one in command line
     903             :         cmd_line,       // pointer to command line string
     904             :         NULL,           // pointer to process security attributes; NULL - debugger's handle can't be inherited
     905             :         NULL,           // pointer to thread security attributes; NULL - debugger's handle can't be inherited
     906             :         true,           // debugger inherit opened handles
     907             :         0,              // priority flags; 0 - normal priority
     908             :         NULL,           // pointer to new environment block; NULL - use this process environment
     909             :         NULL,           // pointer to current directory name; NULL - use this process correct directory
     910             :         &startup_info,  // pointer to STARTUPINFO that specifies main window appearance
     911             :         &debugger_info  // pointer to PROCESS_INFORMATION that will contain the new process identification
     912             :     );
     913             : 
     914             :     bool debugger_run_ok = false;
     915             :     if( created )
     916             :     {
     917             :         DWORD ret_code = ::WaitForSingleObject( dbg_init_done_ev, INFINITE );
     918             :         debugger_run_ok = ( ret_code == WAIT_OBJECT_0 );
     919             :     }
     920             : 
     921             :     safe_handle_obj.close_handle();
     922             : 
     923             :     if( !created || !debugger_run_ok )
     924             :         return false;
     925             : 
     926             :     if( break_or_continue )
     927             :         debugger_break();
     928             : 
     929             :     return true;
     930             : 
     931             : #elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX
     932             : 
     933             :     char init_done_lock_fn[] = "/tmp/btl_dbg_init_done_XXXXXX";
     934             :     const mode_t cur_umask = ::umask( S_IRWXO | S_IRWXG );
     935             :     fd_holder init_done_lock_fd( ::mkstemp( init_done_lock_fn ) );
     936             :     ::umask( cur_umask );
     937             : 
     938             :     if( init_done_lock_fd == -1 )
     939             :         return false;
     940             : 
     941             :     pid_t child_pid = fork();
     942             : 
     943             :     if( child_pid == -1 )
     944             :         return false;
     945             : 
     946             :     if( child_pid != 0 ) { // parent process - here we will start the debugger
     947             :         dbg_startup_info dsi;
     948             : 
     949             :         process_info pi( child_pid );
     950             :         if( pi.binary_path().is_empty() )
     951             :             ::exit( -1 );
     952             : 
     953             :         dsi.pid                 = child_pid;
     954             :         dsi.break_or_continue   = break_or_continue;
     955             :         dsi.binary_path         = pi.binary_path();
     956             :         dsi.display             = ::getenv( "DISPLAY" );
     957             :         dsi.init_done_lock      = init_done_lock_fn;
     958             : 
     959             :         dbg_starter starter = s_info.m_dbg_starter_reg[s_info.p_dbg];
     960             :         if( !!starter )
     961             :             starter( dsi );
     962             : 
     963             :         ::perror( "Boost.Test execution monitor failed to start a debugger:" );
     964             : 
     965             :         ::exit( -1 );
     966             :     }
     967             : 
     968             :     // child process - here we will continue our test module execution ; // !! ?? should it be vice versa
     969             : 
     970             :     while( ::access( init_done_lock_fn, F_OK ) == 0 ) {
     971             :         struct timeval to = { 0, 100 };
     972             : 
     973             :         ::select( 0, 0, 0, 0, &to );
     974             :     }
     975             : 
     976             : //    char dummy;
     977             : //    while( ::read( init_done_lock_fd, &dummy, sizeof(char) ) == 0 );
     978             : 
     979             :     if( break_or_continue )
     980             :         debugger_break();
     981             : 
     982             :     return true;
     983             : 
     984             : #else // ****************************************************** default
     985             :     (void) break_or_continue; // silence 'unused variable' warning
     986           0 :     return false;
     987             : 
     988             : #endif
     989           0 : }
     990             : 
     991             : //____________________________________________________________________________//
     992             : 
     993             : // ************************************************************************** //
     994             : // **************   switch on/off detect memory leaks feature  ************** //
     995             : // ************************************************************************** //
     996             : 
     997             : void
     998         146 : detect_memory_leaks( bool on_off, unit_test::const_string report_file )
     999             : {
    1000         146 :     boost::ignore_unused( on_off );
    1001             : 
    1002             : #ifdef BOOST_MS_CRT_BASED_DEBUG
    1003             :     int flags = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
    1004             : 
    1005             :     if( !on_off )
    1006             :         flags &= ~_CRTDBG_LEAK_CHECK_DF;
    1007             :     else  {
    1008             :         flags |= _CRTDBG_LEAK_CHECK_DF;
    1009             :         _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    1010             : 
    1011             :         if( report_file.is_empty() )
    1012             :             _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
    1013             :         else {
    1014             :             HANDLE hreport_f = ::CreateFileA( report_file.begin(),
    1015             :                                               GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    1016             :             _CrtSetReportFile(_CRT_WARN, hreport_f );
    1017             :         }
    1018             :     }
    1019             : 
    1020             :     _CrtSetDbgFlag ( flags );
    1021             : #else
    1022         146 :     boost::ignore_unused( report_file );
    1023             : #endif // BOOST_MS_CRT_BASED_DEBUG
    1024         146 : }
    1025             : 
    1026             : //____________________________________________________________________________//
    1027             : 
    1028             : // ************************************************************************** //
    1029             : // **************      cause program to break execution in     ************** //
    1030             : // **************     debugger at specific allocation point    ************** //
    1031             : // ************************************************************************** //
    1032             : 
    1033             : void
    1034         146 : break_memory_alloc( long mem_alloc_order_num )
    1035             : {
    1036         146 :     boost::ignore_unused( mem_alloc_order_num );
    1037             : 
    1038             : #ifdef BOOST_MS_CRT_BASED_DEBUG
    1039             :     // only set the value if one was supplied (do not use default used by UTF just as a indicator to enable leak detection)
    1040             :     if( mem_alloc_order_num > 1 )
    1041             :         _CrtSetBreakAlloc( mem_alloc_order_num );
    1042             : #endif // BOOST_MS_CRT_BASED_DEBUG
    1043         146 : }
    1044             : 
    1045             : //____________________________________________________________________________//
    1046             : 
    1047             : } // namespace debug
    1048             : } // namespace boost
    1049             : 
    1050             : #include <boost/test/detail/enable_warnings.hpp>
    1051             : 
    1052             : #endif // BOOST_TEST_DEBUG_API_IPP_112006GER

Generated by: LCOV version 1.16