]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/test/include/boost/test/impl/debug.ipp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / test / include / boost / test / impl / debug.ipp
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
44 namespace 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
111 namespace boost {
112 namespace debug {
113
114 using unit_test::const_string;
115
116 // ************************************************************************** //
117 // ************** debug::info_t ************** //
118 // ************************************************************************** //
119
120 namespace {
121
122 #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32
123
124 template<typename T>
125 inline void
126 dyn_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
138 static 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
155 info_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
171 struct 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
181 private:
182 // Data members
183 int m_fd;
184 };
185
186
187 // ************************************************************************** //
188 // ************** process_info ************** //
189 // ************************************************************************** //
190
191 struct 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
200 private:
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
217 process_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
299 static char*
300 prepare_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
327 typedef unit_test::basic_cstring<char> mbuffer;
328
329 inline char*
330 copy_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
346 bool
347 safe_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
394 static void
395 start_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
413 static char const*
414 prepare_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
453 static void
454 start_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
466 static void
467 start_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
482 static void
483 start_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
497 static void
498 start_gdb_in_xemacs( dbg_startup_info const& )
499 {
500 // !! ??
501 }
502
503 //____________________________________________________________________________//
504
505 // ************************************************************************** //
506 // ************** dbx starters ************** //
507 // ************************************************************************** //
508
509 static char const*
510 prepare_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
524 static void
525 start_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
535 static void
536 start_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
552 static void
553 start_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
564 static void
565 start_dbx_in_xemacs( dbg_startup_info const& )
566 {
567 // !! ??
568 }
569
570 //____________________________________________________________________________//
571
572 static void
573 start_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
592 static 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
605 info_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
633 bool
634 under_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
673 void
674 debugger_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
726 std::string
727 set_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
741 std::string
742 set_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
757 struct 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
778 bool
779 attach_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 &reg_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
953 void
954 detect_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
989 void
990 break_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