// (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$ // // Description : debug interfaces implementation // *************************************************************************** #ifndef BOOST_TEST_DEBUG_API_IPP_112006GER #define BOOST_TEST_DEBUG_API_IPP_112006GER // Boost.Test #include #include #include #include #include // Implementation on Windows #if defined(_WIN32) && !defined(UNDER_CE) && !defined(BOOST_DISABLE_WIN32) // ******* WIN32 # define BOOST_WIN32_BASED_DEBUG // SYSTEM API # include # include # include # include # if !defined(NDEBUG) && defined(_MSC_VER) # define BOOST_MS_CRT_BASED_DEBUG # include # 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 #include // STL #include // std::memcpy #include #include #include // !! ?? cstdarg // SYSTEM API # include # include # include # include # include # include # include # include # include # 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 # elif defined(linux) || defined(__linux) # define BOOST_LINUX_BASED_DEBUG # include # 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 //____________________________________________________________________________// namespace boost { namespace debug { using unit_test::const_string; // ************************************************************************** // // ************** debug::info_t ************** // // ************************************************************************** // namespace { #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32 template 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( ::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 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(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 p_dbg; // Data members std::map 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 #endif // BOOST_TEST_DEBUG_API_IPP_112006GER