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