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