Chris@16: // (C) Copyright Gennadiy Rozental 2006-2008. Chris@16: // Use, modification, and distribution are subject to the Chris@16: // Boost Software License, Version 1.0. (See accompanying file Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: // See http://www.boost.org/libs/test for the library home page. Chris@16: // Chris@16: // File : $RCSfile$ Chris@16: // Chris@101: // Version : $Revision$ Chris@16: // Chris@16: // Description : debug interfaces implementation Chris@16: // *************************************************************************** Chris@16: Chris@16: #ifndef BOOST_TEST_DEBUG_API_IPP_112006GER Chris@16: #define BOOST_TEST_DEBUG_API_IPP_112006GER Chris@16: Chris@16: // Boost.Test Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: // Implementation on Windows Chris@16: #if defined(_WIN32) && !defined(UNDER_CE) && !defined(BOOST_DISABLE_WIN32) // ******* WIN32 Chris@16: Chris@16: # define BOOST_WIN32_BASED_DEBUG Chris@16: Chris@16: // SYSTEM API Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: Chris@16: # if !defined(NDEBUG) && defined(_MSC_VER) Chris@16: # define BOOST_MS_CRT_BASED_DEBUG Chris@16: # include Chris@16: # endif Chris@16: Chris@16: Chris@16: # if BOOST_WORKAROUND( BOOST_MSVC, <1300) Chris@16: # define snprintf _snprintf Chris@16: # endif Chris@16: Chris@16: # ifdef BOOST_NO_STDC_NAMESPACE Chris@16: namespace std { using ::memset; using ::sprintf; } Chris@16: # endif Chris@16: Chris@16: #elif defined(unix) || defined(__unix) // ********************* UNIX Chris@16: Chris@16: # define BOOST_UNIX_BASED_DEBUG Chris@16: Chris@16: // Boost.Test Chris@16: #include Chris@16: #include Chris@16: Chris@16: // STL Chris@16: #include // std::memcpy Chris@16: #include Chris@16: #include Chris@16: #include // !! ?? cstdarg Chris@16: Chris@16: // SYSTEM API Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: Chris@16: # if defined(sun) || defined(__sun) Chris@16: Chris@16: # define BOOST_SUN_BASED_DEBUG Chris@16: Chris@16: # ifndef BOOST_TEST_DBG_LIST Chris@16: # define BOOST_TEST_DBG_LIST dbx;gdb Chris@16: # endif Chris@16: Chris@16: # define BOOST_TEST_CNL_DBG dbx Chris@16: # define BOOST_TEST_GUI_DBG dbx-ddd Chris@16: Chris@16: # include Chris@16: Chris@16: # elif defined(linux) || defined(__linux) Chris@16: Chris@16: # define BOOST_LINUX_BASED_DEBUG Chris@16: Chris@16: # include Chris@16: Chris@16: # ifndef BOOST_TEST_STAT_LINE_MAX Chris@16: # define BOOST_TEST_STAT_LINE_MAX 500 Chris@16: # endif Chris@16: Chris@16: # ifndef BOOST_TEST_DBG_LIST Chris@16: # define BOOST_TEST_DBG_LIST gdb Chris@16: # endif Chris@16: Chris@16: # define BOOST_TEST_CNL_DBG gdb Chris@16: # define BOOST_TEST_GUI_DBG gdb-xterm Chris@16: Chris@16: # endif Chris@16: Chris@16: #endif Chris@16: Chris@16: #include Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: namespace boost { Chris@16: Chris@16: namespace debug { Chris@16: Chris@16: using unit_test::const_string; Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** debug::info_t ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: namespace { Chris@16: Chris@16: #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32 Chris@16: Chris@16: template Chris@16: inline void Chris@16: dyn_symbol( T& res, char const* module_name, char const* symbol_name ) Chris@16: { Chris@16: HMODULE m = ::GetModuleHandleA( module_name ); Chris@16: Chris@16: if( !m ) Chris@16: m = ::LoadLibraryA( module_name ); Chris@16: Chris@16: res = reinterpret_cast( ::GetProcAddress( m, symbol_name ) ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static struct info_t { Chris@16: typedef BOOL (WINAPI* IsDebuggerPresentT)(); Chris@16: typedef LONG (WINAPI* RegQueryValueExT)( HKEY, char const* /*LPTSTR*/, LPDWORD, LPDWORD, LPBYTE, LPDWORD ); Chris@16: typedef LONG (WINAPI* RegOpenKeyT)( HKEY, char const* /*LPCTSTR*/, PHKEY ); Chris@16: typedef LONG (WINAPI* RegCloseKeyT)( HKEY ); Chris@16: Chris@16: info_t(); Chris@16: Chris@16: IsDebuggerPresentT m_is_debugger_present; Chris@16: RegOpenKeyT m_reg_open_key; Chris@16: RegQueryValueExT m_reg_query_value; Chris@16: RegCloseKeyT m_reg_close_key; Chris@16: Chris@16: } s_info; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: info_t::info_t() Chris@16: { Chris@16: dyn_symbol( m_is_debugger_present, "kernel32", "IsDebuggerPresent" ); Chris@16: dyn_symbol( m_reg_open_key, "advapi32", "RegOpenKeyA" ); Chris@16: dyn_symbol( m_reg_query_value, "advapi32", "RegQueryValueExA" ); Chris@16: dyn_symbol( m_reg_close_key, "advapi32", "RegCloseKey" ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: #elif defined(BOOST_UNIX_BASED_DEBUG) Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** fd_holder ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: struct fd_holder { Chris@16: explicit fd_holder( int fd ) : m_fd( fd ) {} Chris@16: ~fd_holder() Chris@16: { Chris@16: if( m_fd != -1 ) Chris@16: ::close( m_fd ); Chris@16: } Chris@16: Chris@16: operator int() { return m_fd; } Chris@16: Chris@16: private: Chris@16: // Data members Chris@16: int m_fd; Chris@16: }; Chris@16: Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** process_info ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: struct process_info { Chris@16: // Constructor Chris@16: explicit process_info( int pid ); Chris@16: Chris@16: // access methods Chris@16: int parent_pid() const { return m_parent_pid; } Chris@16: const_string binary_name() const { return m_binary_name; } Chris@16: const_string binary_path() const { return m_binary_path; } Chris@16: Chris@16: private: Chris@16: // Data members Chris@16: int m_parent_pid; Chris@16: const_string m_binary_name; Chris@16: const_string m_binary_path; Chris@16: Chris@16: #if defined(BOOST_SUN_BASED_DEBUG) Chris@16: struct psinfo m_psi; Chris@16: #elif defined(BOOST_LINUX_BASED_DEBUG) Chris@16: char m_stat_line[BOOST_TEST_STAT_LINE_MAX+1]; Chris@16: #endif Chris@16: char m_binary_path_buff[500+1]; // !! ?? Chris@16: }; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: process_info::process_info( int pid ) Chris@16: : m_parent_pid( 0 ) Chris@16: { Chris@16: #if defined(BOOST_SUN_BASED_DEBUG) Chris@16: char fname_buff[30]; Chris@16: Chris@16: ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/psinfo", pid ); Chris@16: Chris@16: fd_holder psinfo_fd( ::open( fname_buff, O_RDONLY ) ); Chris@16: Chris@16: if( psinfo_fd == -1 ) Chris@16: return; Chris@16: Chris@16: if( ::read( psinfo_fd, &m_psi, sizeof(m_psi) ) == -1 ) Chris@16: return; Chris@16: Chris@16: m_parent_pid = m_psi.pr_ppid; Chris@16: Chris@16: m_binary_name.assign( m_psi.pr_fname ); Chris@16: Chris@16: //-------------------------- // Chris@16: Chris@16: ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/as", pid ); Chris@16: Chris@16: fd_holder as_fd( ::open( fname_buff, O_RDONLY ) ); Chris@16: uintptr_t binary_name_pos; Chris@16: Chris@16: // !! ?? could we avoid reading whole m_binary_path_buff? Chris@16: if( as_fd == -1 || Chris@16: ::lseek( as_fd, m_psi.pr_argv, SEEK_SET ) == -1 || Chris@16: ::read ( as_fd, &binary_name_pos, sizeof(binary_name_pos) ) == -1 || Chris@16: ::lseek( as_fd, binary_name_pos, SEEK_SET ) == -1 || Chris@16: ::read ( as_fd, m_binary_path_buff, sizeof(m_binary_path_buff) ) == -1 ) Chris@16: return; Chris@16: Chris@16: m_binary_path.assign( m_binary_path_buff ); Chris@16: Chris@16: #elif defined(BOOST_LINUX_BASED_DEBUG) Chris@16: char fname_buff[30]; Chris@16: Chris@16: ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/stat", pid ); Chris@16: Chris@16: fd_holder psinfo_fd( ::open( fname_buff, O_RDONLY ) ); Chris@16: Chris@16: if( psinfo_fd == -1 ) Chris@16: return; Chris@16: Chris@16: ssize_t num_read = ::read( psinfo_fd, m_stat_line, sizeof(m_stat_line)-1 ); Chris@16: if( num_read == -1 ) Chris@16: return; Chris@16: Chris@16: m_stat_line[num_read] = 0; Chris@16: Chris@16: char const* name_beg = m_stat_line; Chris@16: while( *name_beg && *name_beg != '(' ) Chris@16: ++name_beg; Chris@16: Chris@16: char const* name_end = name_beg+1; Chris@16: while( *name_end && *name_end != ')' ) Chris@16: ++name_end; Chris@16: Chris@16: std::sscanf( name_end+1, "%*s%d", &m_parent_pid ); Chris@16: Chris@16: m_binary_name.assign( name_beg+1, name_end ); Chris@16: Chris@16: ::snprintf( fname_buff, sizeof(fname_buff), "/proc/%d/exe", pid ); Chris@16: num_read = ::readlink( fname_buff, m_binary_path_buff, sizeof(m_binary_path_buff)-1 ); Chris@16: Chris@16: if( num_read == -1 ) Chris@16: return; Chris@16: Chris@16: m_binary_path_buff[num_read] = 0; Chris@16: m_binary_path.assign( m_binary_path_buff, num_read ); Chris@16: #endif Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** prepare_window_title ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: static char* Chris@16: prepare_window_title( dbg_startup_info const& dsi ) Chris@16: { Chris@16: typedef unit_test::const_string str_t; Chris@16: Chris@16: static char title_str[50]; Chris@16: Chris@16: str_t path_sep( "\\/" ); Chris@16: Chris@16: str_t::iterator it = unit_test::find_last_of( dsi.binary_path.begin(), dsi.binary_path.end(), Chris@16: path_sep.begin(), path_sep.end() ); Chris@16: Chris@16: if( it == dsi.binary_path.end() ) Chris@16: it = dsi.binary_path.begin(); Chris@16: else Chris@16: ++it; Chris@16: Chris@16: ::snprintf( title_str, sizeof(title_str), "%*s %ld", (int)(dsi.binary_path.end()-it), it, dsi.pid ); Chris@16: Chris@16: return title_str; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** save_execlp ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: typedef unit_test::basic_cstring mbuffer; Chris@16: Chris@16: inline char* Chris@16: copy_arg( mbuffer& dest, const_string arg ) Chris@16: { Chris@16: if( dest.size() < arg.size()+1 ) Chris@16: return 0; Chris@16: Chris@16: char* res = dest.begin(); Chris@16: Chris@16: std::memcpy( res, arg.begin(), arg.size()+1 ); Chris@16: Chris@16: dest.trim_left( arg.size()+1 ); Chris@16: Chris@16: return res; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: bool Chris@16: safe_execlp( char const* file, ... ) Chris@16: { Chris@16: static char* argv_buff[200]; Chris@16: Chris@16: va_list args; Chris@16: char const* arg; Chris@16: Chris@16: // first calculate actual number of arguments Chris@16: int num_args = 2; // file name and 0 at least Chris@16: Chris@16: va_start( args, file ); Chris@16: while( !!(arg = va_arg( args, char const* )) ) Chris@16: num_args++; Chris@16: va_end( args ); Chris@16: Chris@16: // reserve space for the argument pointers array Chris@16: char** argv_it = argv_buff; Chris@16: mbuffer work_buff( reinterpret_cast(argv_buff), sizeof(argv_buff) ); Chris@16: work_buff.trim_left( num_args * sizeof(char*) ); Chris@16: Chris@16: // copy all the argument values into local storage Chris@16: if( !(*argv_it++ = copy_arg( work_buff, file )) ) Chris@16: return false; Chris@16: Chris@16: printf( "!! %s\n", file ); Chris@16: Chris@16: va_start( args, file ); Chris@16: while( !!(arg = va_arg( args, char const* )) ) { Chris@16: printf( "!! %s\n", arg ); Chris@16: if( !(*argv_it++ = copy_arg( work_buff, arg )) ) Chris@16: return false; Chris@16: } Chris@16: va_end( args ); Chris@16: Chris@16: *argv_it = 0; Chris@16: Chris@16: return ::execvp( file, argv_buff ) != -1; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** start_debugger_in_emacs ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: static void Chris@16: start_debugger_in_emacs( dbg_startup_info const& dsi, char const* emacs_name, char const* dbg_command ) Chris@16: { Chris@16: char const* title = prepare_window_title( dsi ); Chris@16: Chris@16: if( !title ) Chris@16: return; Chris@16: Chris@16: dsi.display.is_empty() Chris@16: ? safe_execlp( emacs_name, "-title", title, "--eval", dbg_command, 0 ) Chris@16: : safe_execlp( emacs_name, "-title", title, "-display", dsi.display.begin(), "--eval", dbg_command, 0 ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** gdb starters ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: static char const* Chris@16: prepare_gdb_cmnd_file( dbg_startup_info const& dsi ) Chris@16: { Chris@16: // prepare pid value Chris@16: char pid_buff[16]; Chris@16: ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid ); Chris@16: unit_test::const_string pid_str( pid_buff ); Chris@16: Chris@16: static char cmd_file_name[] = "/tmp/btl_gdb_cmd_XXXXXX"; // !! ?? Chris@16: Chris@16: // prepare commands Chris@16: fd_holder cmd_fd( ::mkstemp( cmd_file_name ) ); Chris@16: Chris@16: if( cmd_fd == -1 ) Chris@16: return 0; Chris@16: Chris@16: #define WRITE_STR( str ) if( ::write( cmd_fd, str.begin(), str.size() ) == -1 ) return 0; Chris@16: #define WRITE_CSTR( str ) if( ::write( cmd_fd, str, sizeof( str )-1 ) == -1 ) return 0; Chris@16: Chris@16: WRITE_CSTR( "file " ); Chris@16: WRITE_STR( dsi.binary_path ); Chris@16: WRITE_CSTR( "\nattach " ); Chris@16: WRITE_STR( pid_str ); Chris@16: WRITE_CSTR( "\nshell unlink " ); Chris@16: WRITE_STR( dsi.init_done_lock ); Chris@16: WRITE_CSTR( "\ncont" ); Chris@16: if( dsi.break_or_continue ) Chris@16: WRITE_CSTR( "\nup 4" ); Chris@16: Chris@16: WRITE_CSTR( "\necho \\n" ); // !! ?? Chris@16: WRITE_CSTR( "\nlist -" ); Chris@16: WRITE_CSTR( "\nlist" ); Chris@16: WRITE_CSTR( "\nshell unlink " ); Chris@16: WRITE_CSTR( cmd_file_name ); Chris@16: Chris@16: return cmd_file_name; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static void Chris@16: start_gdb_in_console( dbg_startup_info const& dsi ) Chris@16: { Chris@16: char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi ); Chris@16: Chris@16: if( !cmnd_file_name ) Chris@16: return; Chris@16: Chris@16: safe_execlp( "gdb", "-q", "-x", cmnd_file_name, 0 ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static void Chris@16: start_gdb_in_xterm( dbg_startup_info const& dsi ) Chris@16: { Chris@16: char const* title = prepare_window_title( dsi ); Chris@16: char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi ); Chris@16: Chris@16: if( !title || !cmnd_file_name ) Chris@16: return; Chris@16: Chris@16: safe_execlp( "xterm", "-T", title, "-display", dsi.display.begin(), Chris@16: "-bg", "black", "-fg", "white", "-geometry", "88x30+10+10", "-fn", "9x15", "-e", Chris@16: "gdb", "-q", "-x", cmnd_file_name, 0 ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static void Chris@16: start_gdb_in_emacs( dbg_startup_info const& dsi ) Chris@16: { Chris@16: char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi ); Chris@16: if( !cmnd_file_name ) Chris@16: return; Chris@16: Chris@16: char dbg_cmd_buff[500]; // !! ?? Chris@16: ::snprintf( dbg_cmd_buff, sizeof(dbg_cmd_buff), "(progn (gdb \"gdb -q -x %s\"))", cmnd_file_name ); Chris@16: Chris@16: start_debugger_in_emacs( dsi, "emacs", dbg_cmd_buff ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static void Chris@16: start_gdb_in_xemacs( dbg_startup_info const& ) Chris@16: { Chris@16: // !! ?? Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** dbx starters ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: static char const* Chris@16: prepare_dbx_cmd_line( dbg_startup_info const& dsi, bool list_source = true ) Chris@16: { Chris@16: static char cmd_line_buff[500]; // !! ?? Chris@16: Chris@16: ::snprintf( cmd_line_buff, sizeof(cmd_line_buff), "unlink %s;cont;%s%s", Chris@16: dsi.init_done_lock.begin(), Chris@16: dsi.break_or_continue ? "up 2;": "", Chris@16: list_source ? "echo \" \";list -w3;" : "" ); Chris@16: Chris@16: return cmd_line_buff; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static void Chris@16: start_dbx_in_console( dbg_startup_info const& dsi ) Chris@16: { Chris@16: char pid_buff[16]; Chris@16: ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid ); Chris@16: Chris@16: safe_execlp( "dbx", "-q", "-c", prepare_dbx_cmd_line( dsi ), dsi.binary_path.begin(), pid_buff, 0 ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static void Chris@16: start_dbx_in_xterm( dbg_startup_info const& dsi ) Chris@16: { Chris@16: char const* title = prepare_window_title( dsi ); Chris@16: if( !title ) Chris@16: return; Chris@16: Chris@16: char pid_buff[16]; // !! ?? Chris@16: ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid ); Chris@16: Chris@16: safe_execlp( "xterm", "-T", title, "-display", dsi.display.begin(), Chris@16: "-bg", "black", "-fg", "white", "-geometry", "88x30+10+10", "-fn", "9x15", "-e", Chris@16: "dbx", "-q", "-c", prepare_dbx_cmd_line( dsi ), dsi.binary_path.begin(), pid_buff, 0 ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static void Chris@16: start_dbx_in_emacs( dbg_startup_info const& /*dsi*/ ) Chris@16: { Chris@16: // char dbg_cmd_buff[500]; // !! ?? Chris@16: // Chris@16: // ::snprintf( dbg_cmd_buff, sizeof(dbg_cmd_buff), "(progn (dbx \"dbx -q -c cont %s %ld\"))", dsi.binary_path.begin(), dsi.pid ); Chris@16: Chris@16: // start_debugger_in_emacs( dsi, "emacs", dbg_cmd_buff ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static void Chris@16: start_dbx_in_xemacs( dbg_startup_info const& ) Chris@16: { Chris@16: // !! ?? Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: static void Chris@16: start_dbx_in_ddd( dbg_startup_info const& dsi ) Chris@16: { Chris@16: char const* title = prepare_window_title( dsi ); Chris@16: if( !title ) Chris@16: return; Chris@16: Chris@16: char pid_buff[16]; // !! ?? Chris@16: ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid ); Chris@16: Chris@16: safe_execlp( "ddd", "-display", dsi.display.begin(), Chris@16: "--dbx", "-q", "-c", prepare_dbx_cmd_line( dsi, false ), dsi.binary_path.begin(), pid_buff, 0 ); Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** debug::info_t ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: static struct info_t { Chris@16: // Constructor Chris@16: info_t(); Chris@16: Chris@16: // Public properties Chris@16: unit_test::readwrite_property p_dbg; Chris@16: Chris@16: // Data members Chris@16: std::map m_dbg_starter_reg; Chris@16: } s_info; Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: info_t::info_t() Chris@16: { Chris@16: p_dbg.value = ::getenv( "DISPLAY" ) Chris@16: ? std::string( BOOST_STRINGIZE( BOOST_TEST_GUI_DBG ) ) Chris@16: : std::string( BOOST_STRINGIZE( BOOST_TEST_CNL_DBG ) ); Chris@16: Chris@16: m_dbg_starter_reg[std::string("gdb")] = &start_gdb_in_console; Chris@16: m_dbg_starter_reg[std::string("gdb-emacs")] = &start_gdb_in_emacs; Chris@16: m_dbg_starter_reg[std::string("gdb-xterm")] = &start_gdb_in_xterm; Chris@16: m_dbg_starter_reg[std::string("gdb-xemacs")] = &start_gdb_in_xemacs; Chris@16: Chris@16: m_dbg_starter_reg[std::string("dbx")] = &start_dbx_in_console; Chris@16: m_dbg_starter_reg[std::string("dbx-emacs")] = &start_dbx_in_emacs; Chris@16: m_dbg_starter_reg[std::string("dbx-xterm")] = &start_dbx_in_xterm; Chris@16: m_dbg_starter_reg[std::string("dbx-xemacs")] = &start_dbx_in_xemacs; Chris@16: m_dbg_starter_reg[std::string("dbx-ddd")] = &start_dbx_in_ddd; Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: #endif Chris@16: Chris@16: } // local namespace Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** check if program is running under debugger ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: bool Chris@16: under_debugger() Chris@16: { Chris@16: #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32 Chris@16: Chris@16: return !!s_info.m_is_debugger_present && s_info.m_is_debugger_present(); Chris@16: Chris@16: #elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX Chris@16: Chris@16: // !! ?? could/should we cache the result somehow? Chris@16: const_string dbg_list = BOOST_TEST_STRINGIZE( BOOST_TEST_DBG_LIST ); Chris@16: Chris@16: pid_t pid = ::getpid(); Chris@16: Chris@16: while( pid != 0 ) { Chris@16: process_info pi( pid ); Chris@16: Chris@16: // !! ?? should we use tokenizer here instead? Chris@16: if( dbg_list.find( pi.binary_name() ) != const_string::npos ) Chris@16: return true; Chris@16: Chris@16: pid = (pi.parent_pid() == pid ? 0 : pi.parent_pid()); Chris@16: } Chris@16: Chris@16: return false; Chris@16: Chris@16: #else // ****************************************************** default Chris@16: Chris@16: return false; Chris@16: Chris@16: #endif Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** cause program to break execution ************** // Chris@16: // ************** in debugger at call point ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: void Chris@16: debugger_break() Chris@16: { Chris@16: // !! ?? auto-start debugger? Chris@16: Chris@16: #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32 Chris@16: Chris@16: #if BOOST_WORKAROUND(BOOST_MSVC, >= 1300) || \ Chris@16: BOOST_WORKAROUND(__GNUC__, >= 3) && !defined(__MINGW32__) || \ Chris@16: defined(__INTEL_COMPILER) Chris@16: # define BOOST_DEBUG_BREAK __debugbreak Chris@16: #else Chris@16: # define BOOST_DEBUG_BREAK DebugBreak Chris@16: #endif Chris@16: Chris@16: #ifndef __MINGW32__ Chris@16: if( !under_debugger() ) { Chris@16: __try { Chris@16: __try { Chris@16: BOOST_DEBUG_BREAK(); Chris@16: } Chris@16: __except( UnhandledExceptionFilter(GetExceptionInformation()) ) Chris@16: { Chris@16: // User opted to ignore the breakpoint Chris@16: return; Chris@16: } Chris@16: } Chris@16: __except (EXCEPTION_EXECUTE_HANDLER) Chris@16: { Chris@16: // If we got here, the user has pushed Debug. Debugger is already attached to our process and we Chris@16: // continue to let the another BOOST_DEBUG_BREAK to be called. Chris@16: } Chris@16: } Chris@16: #endif Chris@16: Chris@16: BOOST_DEBUG_BREAK(); Chris@16: Chris@16: #elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX Chris@16: Chris@16: ::kill( ::getpid(), SIGTRAP ); Chris@16: Chris@16: #else // ****************************************************** default Chris@16: Chris@16: #endif Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** console debugger setup ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: #if defined(BOOST_UNIX_BASED_DEBUG) // ************************ UNIX Chris@16: Chris@16: std::string Chris@16: set_debugger( unit_test::const_string dbg_id, dbg_starter s ) Chris@16: { Chris@16: std::string old = s_info.p_dbg; Chris@16: Chris@16: assign_op( s_info.p_dbg.value, dbg_id, 0 ); Chris@16: Chris@16: if( !!s ) Chris@16: s_info.m_dbg_starter_reg[s_info.p_dbg] = s; Chris@16: Chris@16: return old; Chris@16: } Chris@16: Chris@16: #else // ***************************************************** default Chris@16: Chris@16: std::string Chris@16: set_debugger( unit_test::const_string, dbg_starter ) Chris@16: { Chris@16: return std::string(); Chris@16: } Chris@16: Chris@16: #endif Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** attach debugger to the current process ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: bool Chris@16: attach_debugger( bool break_or_continue ) Chris@16: { Chris@16: if( under_debugger() ) Chris@16: return false; Chris@16: Chris@16: #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32 Chris@16: Chris@16: const int MAX_CMD_LINE = 200; Chris@16: Chris@16: // *************************************************** // Chris@16: // Debugger "ready" event Chris@16: Chris@16: SECURITY_ATTRIBUTES attr; Chris@16: attr.nLength = sizeof(attr); Chris@16: attr.lpSecurityDescriptor = NULL; Chris@16: attr.bInheritHandle = true; Chris@16: Chris@16: // manual resettable, initially non signaled, unnamed event, Chris@16: // that will signal me that debugger initialization is done Chris@16: HANDLE dbg_init_done_ev = ::CreateEvent( Chris@16: &attr, // pointer to security attributes Chris@16: true, // flag for manual-reset event Chris@16: false, // flag for initial state Chris@16: NULL // pointer to event-object name Chris@16: ); Chris@16: Chris@16: if( !dbg_init_done_ev ) Chris@16: return false; Chris@16: Chris@16: // *************************************************** // Chris@16: // Debugger command line format Chris@16: Chris@16: HKEY reg_key; Chris@16: Chris@16: if( !s_info.m_reg_open_key || (*s_info.m_reg_open_key)( Chris@16: HKEY_LOCAL_MACHINE, // handle of open key Chris@16: "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", // name of subkey to open Chris@16: ®_key ) != ERROR_SUCCESS ) // address of handle of open key Chris@16: return false; Chris@16: Chris@16: char format[MAX_CMD_LINE]; Chris@16: DWORD format_size = MAX_CMD_LINE; Chris@16: DWORD type = REG_SZ; Chris@16: Chris@16: if( !s_info.m_reg_query_value || (*s_info.m_reg_query_value)( Chris@16: reg_key, // handle of open key Chris@16: "Debugger", // name of subkey to query Chris@16: 0, // reserved Chris@16: &type, // value type Chris@16: (LPBYTE)format, // buffer for returned string Chris@16: &format_size ) != ERROR_SUCCESS ) // in: buffer size; out: actual size of returned string Chris@16: return false; Chris@16: Chris@16: if( !s_info.m_reg_close_key || (*s_info.m_reg_close_key)( reg_key ) != ERROR_SUCCESS ) Chris@16: return false; Chris@16: Chris@16: // *************************************************** // Chris@16: // Debugger command line Chris@16: Chris@16: char cmd_line[MAX_CMD_LINE]; Chris@16: std::sprintf( cmd_line, format, ::GetCurrentProcessId(), dbg_init_done_ev ); Chris@16: Chris@16: // *************************************************** // Chris@16: // Debugger window parameters Chris@16: Chris@16: STARTUPINFOA startup_info; Chris@16: std::memset( &startup_info, 0, sizeof(startup_info) ); Chris@16: Chris@16: startup_info.cb = sizeof(startup_info); Chris@16: startup_info.dwFlags = STARTF_USESHOWWINDOW; Chris@16: startup_info.wShowWindow = SW_SHOWNORMAL; Chris@16: Chris@16: // debugger process s_info Chris@16: PROCESS_INFORMATION debugger_info; Chris@16: Chris@16: bool created = !!::CreateProcessA( Chris@16: NULL, // pointer to name of executable module; NULL - use the one in command line Chris@16: cmd_line, // pointer to command line string Chris@16: NULL, // pointer to process security attributes; NULL - debugger's handle can't be inherited Chris@16: NULL, // pointer to thread security attributes; NULL - debugger's handle can't be inherited Chris@16: true, // debugger inherit opened handles Chris@16: 0, // priority flags; 0 - normal priority Chris@16: NULL, // pointer to new environment block; NULL - use this process environment Chris@16: NULL, // pointer to current directory name; NULL - use this process correct directory Chris@16: &startup_info, // pointer to STARTUPINFO that specifies main window appearance Chris@16: &debugger_info // pointer to PROCESS_INFORMATION that will contain the new process identification Chris@16: ); Chris@16: Chris@16: if( created ) Chris@16: ::WaitForSingleObject( dbg_init_done_ev, INFINITE ); Chris@16: Chris@16: ::CloseHandle( dbg_init_done_ev ); Chris@16: Chris@16: if( !created ) Chris@16: return false; Chris@16: Chris@16: if( break_or_continue ) Chris@16: debugger_break(); Chris@16: Chris@16: return true; Chris@16: Chris@16: #elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX Chris@16: Chris@16: char init_done_lock_fn[] = "/tmp/btl_dbg_init_done_XXXXXX"; Chris@16: fd_holder init_done_lock_fd( ::mkstemp( init_done_lock_fn ) ); Chris@16: Chris@16: if( init_done_lock_fd == -1 ) Chris@16: return false; Chris@16: Chris@16: pid_t child_pid = fork(); Chris@16: Chris@16: if( child_pid == -1 ) Chris@16: return false; Chris@16: Chris@16: if( child_pid != 0 ) { // parent process - here we will start the debugger Chris@16: dbg_startup_info dsi; Chris@16: Chris@16: process_info pi( child_pid ); Chris@16: if( pi.binary_path().is_empty() ) Chris@16: ::exit( -1 ); Chris@16: Chris@16: dsi.pid = child_pid; Chris@16: dsi.break_or_continue = break_or_continue; Chris@16: dsi.binary_path = pi.binary_path(); Chris@16: dsi.display = ::getenv( "DISPLAY" ); Chris@16: dsi.init_done_lock = init_done_lock_fn; Chris@16: Chris@16: dbg_starter starter = s_info.m_dbg_starter_reg[s_info.p_dbg]; Chris@16: if( !!starter ) Chris@16: starter( dsi ); Chris@16: Chris@16: ::perror( "Boost.Test execution monitor failed to start a debugger:" ); Chris@16: Chris@16: ::exit( -1 ); Chris@16: } Chris@16: Chris@16: // child process - here we will continue our test module execution ; // !! ?? should it be vice versa Chris@16: Chris@16: while( ::access( init_done_lock_fn, F_OK ) == 0 ) { Chris@16: struct timeval to = { 0, 100 }; Chris@16: Chris@16: ::select( 0, 0, 0, 0, &to ); Chris@16: } Chris@16: Chris@16: // char dummy; Chris@16: // while( ::read( init_done_lock_fd, &dummy, sizeof(char) ) == 0 ); Chris@16: Chris@16: if( break_or_continue ) Chris@16: debugger_break(); Chris@16: Chris@16: return true; Chris@16: Chris@16: #else // ****************************************************** default Chris@16: Chris@16: return false; Chris@16: Chris@16: #endif Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** switch on/off detect memory leaks feature ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: void Chris@16: detect_memory_leaks( bool on_off ) Chris@16: { Chris@16: unit_test::ut_detail::ignore_unused_variable_warning( on_off ); Chris@16: Chris@16: #ifdef BOOST_MS_CRT_BASED_DEBUG Chris@16: int flags = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); Chris@16: Chris@16: if( !on_off ) Chris@16: flags &= ~_CRTDBG_LEAK_CHECK_DF; Chris@16: else { Chris@16: flags |= _CRTDBG_LEAK_CHECK_DF; Chris@16: _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); Chris@16: _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT); Chris@16: } Chris@16: Chris@16: _CrtSetDbgFlag ( flags ); Chris@16: #endif // BOOST_MS_CRT_BASED_DEBUG Chris@16: } Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: // ************************************************************************** // Chris@16: // ************** cause program to break execution in ************** // Chris@16: // ************** debugger at specific allocation point ************** // Chris@16: // ************************************************************************** // Chris@16: Chris@16: void Chris@16: break_memory_alloc( long mem_alloc_order_num ) Chris@16: { Chris@16: unit_test::ut_detail::ignore_unused_variable_warning( mem_alloc_order_num ); Chris@16: Chris@16: #ifdef BOOST_MS_CRT_BASED_DEBUG Chris@16: _CrtSetBreakAlloc( mem_alloc_order_num ); Chris@16: #endif // BOOST_MS_CRT_BASED_DEBUG Chris@16: } Chris@16: Chris@16: } // namespace debug Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: //____________________________________________________________________________// Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // BOOST_TEST_DEBUG_API_IPP_112006GER Chris@16: