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