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