Line data Source code
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
45 : namespace 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 :
121 : namespace boost {
122 : namespace debug {
123 :
124 : using unit_test::const_string;
125 :
126 : // ************************************************************************** //
127 : // ************** debug::info_t ************** //
128 : // ************************************************************************** //
129 :
130 : namespace {
131 :
132 : #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32
133 :
134 : template<typename T>
135 : inline void
136 : dyn_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 :
148 : static 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 :
165 : info_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 :
181 : struct 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 :
191 : private:
192 : // Data members
193 : int m_fd;
194 : };
195 :
196 :
197 : // ************************************************************************** //
198 : // ************** process_info ************** //
199 : // ************************************************************************** //
200 :
201 : struct 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 :
210 : private:
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 :
227 : process_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 : #else
301 : (void) pid; // silence 'unused variable' warning
302 : #endif
303 : }
304 :
305 : //____________________________________________________________________________//
306 :
307 : // ************************************************************************** //
308 : // ************** prepare_window_title ************** //
309 : // ************************************************************************** //
310 :
311 : static char*
312 : prepare_window_title( dbg_startup_info const& dsi )
313 : {
314 : typedef unit_test::const_string str_t;
315 :
316 : static char title_str[50];
317 :
318 : str_t path_sep( "\\/" );
319 :
320 : str_t::iterator it = unit_test::utils::find_last_of( dsi.binary_path.begin(), dsi.binary_path.end(),
321 : path_sep.begin(), path_sep.end() );
322 :
323 : if( it == dsi.binary_path.end() )
324 : it = dsi.binary_path.begin();
325 : else
326 : ++it;
327 :
328 : ::snprintf( title_str, sizeof(title_str), "%*s %ld", (int)(dsi.binary_path.end()-it), it, dsi.pid );
329 :
330 : return title_str;
331 : }
332 :
333 : //____________________________________________________________________________//
334 :
335 : // ************************************************************************** //
336 : // ************** save_execlp ************** //
337 : // ************************************************************************** //
338 :
339 : typedef unit_test::basic_cstring<char> mbuffer;
340 :
341 : inline char*
342 : copy_arg( mbuffer& dest, const_string arg )
343 : {
344 : if( dest.size() < arg.size()+1 )
345 : return 0;
346 :
347 : char* res = dest.begin();
348 :
349 : std::memcpy( res, arg.begin(), arg.size()+1 );
350 :
351 : dest.trim_left( arg.size()+1 );
352 :
353 : return res;
354 : }
355 :
356 : //____________________________________________________________________________//
357 :
358 : bool
359 : safe_execlp( char const* file, ... )
360 : {
361 : static char* argv_buff[200];
362 :
363 : va_list args;
364 : char const* arg;
365 :
366 : // first calculate actual number of arguments
367 : int num_args = 2; // file name and 0 at least
368 :
369 : va_start( args, file );
370 : while( !!(arg = va_arg( args, char const* )) )
371 : num_args++;
372 : va_end( args );
373 :
374 : // reserve space for the argument pointers array
375 : char** argv_it = argv_buff;
376 : mbuffer work_buff( reinterpret_cast<char*>(argv_buff), sizeof(argv_buff) );
377 : work_buff.trim_left( num_args * sizeof(char*) );
378 :
379 : // copy all the argument values into local storage
380 : if( !(*argv_it++ = copy_arg( work_buff, file )) )
381 : return false;
382 :
383 : printf( "!! %s\n", file );
384 :
385 : va_start( args, file );
386 : while( !!(arg = va_arg( args, char const* )) ) {
387 : printf( "!! %s\n", arg );
388 : if( !(*argv_it++ = copy_arg( work_buff, arg )) ) {
389 : va_end( args );
390 : return false;
391 : }
392 : }
393 : va_end( args );
394 :
395 : *argv_it = 0;
396 :
397 : return ::execvp( file, argv_buff ) != -1;
398 : }
399 :
400 : //____________________________________________________________________________//
401 :
402 : // ************************************************************************** //
403 : // ************** start_debugger_in_emacs ************** //
404 : // ************************************************************************** //
405 :
406 : static void
407 : start_debugger_in_emacs( dbg_startup_info const& dsi, char const* emacs_name, char const* dbg_command )
408 : {
409 : char const* title = prepare_window_title( dsi );
410 :
411 : if( !title )
412 : return;
413 :
414 : dsi.display.is_empty()
415 : ? safe_execlp( emacs_name, "-title", title, "--eval", dbg_command, 0 )
416 : : safe_execlp( emacs_name, "-title", title, "-display", dsi.display.begin(), "--eval", dbg_command, 0 );
417 : }
418 :
419 : //____________________________________________________________________________//
420 :
421 : // ************************************************************************** //
422 : // ************** gdb starters ************** //
423 : // ************************************************************************** //
424 :
425 : static char const*
426 : prepare_gdb_cmnd_file( dbg_startup_info const& dsi )
427 : {
428 : // prepare pid value
429 : char pid_buff[16];
430 : ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid );
431 : unit_test::const_string pid_str( pid_buff );
432 :
433 : static char cmd_file_name[] = "/tmp/btl_gdb_cmd_XXXXXX"; // !! ??
434 :
435 : // prepare commands
436 : const mode_t cur_umask = ::umask( S_IRWXO | S_IRWXG );
437 : fd_holder cmd_fd( ::mkstemp( cmd_file_name ) );
438 : ::umask( cur_umask );
439 :
440 : if( cmd_fd == -1 )
441 : return 0;
442 :
443 : #define WRITE_STR( str ) if( ::write( cmd_fd, str.begin(), str.size() ) == -1 ) return 0;
444 : #define WRITE_CSTR( str ) if( ::write( cmd_fd, str, sizeof( str )-1 ) == -1 ) return 0;
445 :
446 : WRITE_CSTR( "file " );
447 : WRITE_STR( dsi.binary_path );
448 : WRITE_CSTR( "\nattach " );
449 : WRITE_STR( pid_str );
450 : WRITE_CSTR( "\nshell unlink " );
451 : WRITE_STR( dsi.init_done_lock );
452 : WRITE_CSTR( "\ncont" );
453 : if( dsi.break_or_continue )
454 : WRITE_CSTR( "\nup 4" );
455 :
456 : WRITE_CSTR( "\necho \\n" ); // !! ??
457 : WRITE_CSTR( "\nlist -" );
458 : WRITE_CSTR( "\nlist" );
459 : WRITE_CSTR( "\nshell unlink " );
460 : WRITE_CSTR( cmd_file_name );
461 :
462 : return cmd_file_name;
463 : }
464 :
465 : //____________________________________________________________________________//
466 :
467 : static void
468 : start_gdb_in_console( dbg_startup_info const& dsi )
469 : {
470 : char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi );
471 :
472 : if( !cmnd_file_name )
473 : return;
474 :
475 : safe_execlp( "gdb", "-q", "-x", cmnd_file_name, 0 );
476 : }
477 :
478 : //____________________________________________________________________________//
479 :
480 : static void
481 : start_gdb_in_xterm( dbg_startup_info const& dsi )
482 : {
483 : char const* title = prepare_window_title( dsi );
484 : char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi );
485 :
486 : if( !title || !cmnd_file_name )
487 : return;
488 :
489 : safe_execlp( "xterm", "-T", title, "-display", dsi.display.begin(),
490 : "-bg", "black", "-fg", "white", "-geometry", "88x30+10+10", "-fn", "9x15", "-e",
491 : "gdb", "-q", "-x", cmnd_file_name, 0 );
492 : }
493 :
494 : //____________________________________________________________________________//
495 :
496 : static void
497 : start_gdb_in_emacs( dbg_startup_info const& dsi )
498 : {
499 : char const* cmnd_file_name = prepare_gdb_cmnd_file( dsi );
500 : if( !cmnd_file_name )
501 : return;
502 :
503 : char dbg_cmd_buff[500]; // !! ??
504 : ::snprintf( dbg_cmd_buff, sizeof(dbg_cmd_buff), "(progn (gdb \"gdb -q -x %s\"))", cmnd_file_name );
505 :
506 : start_debugger_in_emacs( dsi, "emacs", dbg_cmd_buff );
507 : }
508 :
509 : //____________________________________________________________________________//
510 :
511 : static void
512 : start_gdb_in_xemacs( dbg_startup_info const& )
513 : {
514 : // !! ??
515 : }
516 :
517 : //____________________________________________________________________________//
518 :
519 : // ************************************************************************** //
520 : // ************** dbx starters ************** //
521 : // ************************************************************************** //
522 :
523 : static char const*
524 : prepare_dbx_cmd_line( dbg_startup_info const& dsi, bool list_source = true )
525 : {
526 : static char cmd_line_buff[500]; // !! ??
527 :
528 : ::snprintf( cmd_line_buff, sizeof(cmd_line_buff), "unlink %s;cont;%s%s",
529 : dsi.init_done_lock.begin(),
530 : dsi.break_or_continue ? "up 2;": "",
531 : list_source ? "echo \" \";list -w3;" : "" );
532 :
533 : return cmd_line_buff;
534 : }
535 :
536 : //____________________________________________________________________________//
537 :
538 : static void
539 : start_dbx_in_console( dbg_startup_info const& dsi )
540 : {
541 : char pid_buff[16];
542 : ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid );
543 :
544 : safe_execlp( "dbx", "-q", "-c", prepare_dbx_cmd_line( dsi ), dsi.binary_path.begin(), pid_buff, 0 );
545 : }
546 :
547 : //____________________________________________________________________________//
548 :
549 : static void
550 : start_dbx_in_xterm( dbg_startup_info const& dsi )
551 : {
552 : char const* title = prepare_window_title( dsi );
553 : if( !title )
554 : return;
555 :
556 : char pid_buff[16]; // !! ??
557 : ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid );
558 :
559 : safe_execlp( "xterm", "-T", title, "-display", dsi.display.begin(),
560 : "-bg", "black", "-fg", "white", "-geometry", "88x30+10+10", "-fn", "9x15", "-e",
561 : "dbx", "-q", "-c", prepare_dbx_cmd_line( dsi ), dsi.binary_path.begin(), pid_buff, 0 );
562 : }
563 :
564 : //____________________________________________________________________________//
565 :
566 : static void
567 : start_dbx_in_emacs( dbg_startup_info const& /*dsi*/ )
568 : {
569 : // char dbg_cmd_buff[500]; // !! ??
570 : //
571 : // ::snprintf( dbg_cmd_buff, sizeof(dbg_cmd_buff), "(progn (dbx \"dbx -q -c cont %s %ld\"))", dsi.binary_path.begin(), dsi.pid );
572 :
573 : // start_debugger_in_emacs( dsi, "emacs", dbg_cmd_buff );
574 : }
575 :
576 : //____________________________________________________________________________//
577 :
578 : static void
579 : start_dbx_in_xemacs( dbg_startup_info const& )
580 : {
581 : // !! ??
582 : }
583 :
584 : //____________________________________________________________________________//
585 :
586 : static void
587 : start_dbx_in_ddd( dbg_startup_info const& dsi )
588 : {
589 : char const* title = prepare_window_title( dsi );
590 : if( !title )
591 : return;
592 :
593 : char pid_buff[16]; // !! ??
594 : ::snprintf( pid_buff, sizeof(pid_buff), "%ld", dsi.pid );
595 :
596 : safe_execlp( "ddd", "-display", dsi.display.begin(),
597 : "--dbx", "-q", "-c", prepare_dbx_cmd_line( dsi, false ), dsi.binary_path.begin(), pid_buff, 0 );
598 : }
599 :
600 : //____________________________________________________________________________//
601 :
602 : // ************************************************************************** //
603 : // ************** debug::info_t ************** //
604 : // ************************************************************************** //
605 :
606 : static struct info_t {
607 : // Constructor
608 : info_t();
609 :
610 : // Public properties
611 : unit_test::readwrite_property<std::string> p_dbg;
612 :
613 : // Data members
614 : std::map<std::string,dbg_starter> m_dbg_starter_reg;
615 : } s_info;
616 :
617 : //____________________________________________________________________________//
618 :
619 : info_t::info_t()
620 : {
621 : p_dbg.value = ::getenv( "DISPLAY" )
622 : ? std::string( BOOST_STRINGIZE( BOOST_TEST_GUI_DBG ) )
623 : : std::string( BOOST_STRINGIZE( BOOST_TEST_CNL_DBG ) );
624 :
625 : m_dbg_starter_reg[std::string("gdb")] = &start_gdb_in_console;
626 : m_dbg_starter_reg[std::string("gdb-emacs")] = &start_gdb_in_emacs;
627 : m_dbg_starter_reg[std::string("gdb-xterm")] = &start_gdb_in_xterm;
628 : m_dbg_starter_reg[std::string("gdb-xemacs")] = &start_gdb_in_xemacs;
629 :
630 : m_dbg_starter_reg[std::string("dbx")] = &start_dbx_in_console;
631 : m_dbg_starter_reg[std::string("dbx-emacs")] = &start_dbx_in_emacs;
632 : m_dbg_starter_reg[std::string("dbx-xterm")] = &start_dbx_in_xterm;
633 : m_dbg_starter_reg[std::string("dbx-xemacs")] = &start_dbx_in_xemacs;
634 : m_dbg_starter_reg[std::string("dbx-ddd")] = &start_dbx_in_ddd;
635 : }
636 :
637 : //____________________________________________________________________________//
638 :
639 : #endif
640 :
641 : } // local namespace
642 :
643 : // ************************************************************************** //
644 : // ************** check if program is running under debugger ************** //
645 : // ************************************************************************** //
646 :
647 : bool
648 1315 : under_debugger()
649 : {
650 : #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32
651 :
652 : return !!s_info.m_is_debugger_present && s_info.m_is_debugger_present();
653 :
654 : #elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX
655 :
656 : // !! ?? could/should we cache the result somehow?
657 : const_string dbg_list = BOOST_TEST_STRINGIZE( BOOST_TEST_DBG_LIST );
658 :
659 : pid_t pid = ::getpid();
660 :
661 : while( pid != 0 ) {
662 : process_info pi( pid );
663 :
664 : // !! ?? should we use tokenizer here instead?
665 : if( dbg_list.find( pi.binary_name() ) != const_string::npos )
666 : return true;
667 :
668 : pid = (pi.parent_pid() == pid ? 0 : pi.parent_pid());
669 : }
670 :
671 : return false;
672 :
673 : #elif defined(BOOST_APPLE_BASED_DEBUG) // ********************** APPLE
674 :
675 : // See https://developer.apple.com/library/mac/qa/qa1361/_index.html
676 : int junk;
677 : int mib[4];
678 : struct kinfo_proc info;
679 : size_t size;
680 :
681 : // Initialize the flags so that, if sysctl fails for some bizarre
682 : // reason, we get a predictable result.
683 1315 : info.kp_proc.p_flag = 0;
684 :
685 : // Initialize mib, which tells sysctl the info we want, in this case
686 : // we're looking for information about a specific process ID.
687 1315 : mib[0] = CTL_KERN;
688 1315 : mib[1] = KERN_PROC;
689 1315 : mib[2] = KERN_PROC_PID;
690 1315 : mib[3] = getpid();
691 :
692 : // Call sysctl.
693 1315 : size = sizeof(info);
694 1315 : junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
695 1315 : assert(junk == 0);
696 : (void)junk; // Silence unused variable warning
697 :
698 : // We're being debugged if the P_TRACED flag is set.
699 1315 : return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
700 :
701 : #else // ****************************************************** default
702 :
703 : return false;
704 :
705 : #endif
706 : }
707 :
708 : //____________________________________________________________________________//
709 :
710 : // ************************************************************************** //
711 : // ************** cause program to break execution ************** //
712 : // ************** in debugger at call point ************** //
713 : // ************************************************************************** //
714 :
715 : void
716 0 : debugger_break()
717 : {
718 : // !! ?? auto-start debugger?
719 :
720 : #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32
721 :
722 : #if defined(__GNUC__) && !defined(__MINGW32__) || \
723 : defined(__INTEL_COMPILER) || defined(BOOST_EMBTC)
724 : # define BOOST_DEBUG_BREAK __debugbreak
725 : #else
726 : # define BOOST_DEBUG_BREAK DebugBreak
727 : #endif
728 :
729 : #ifndef __MINGW32__
730 : if( !under_debugger() ) {
731 : __try {
732 : __try {
733 : BOOST_DEBUG_BREAK();
734 : }
735 : __except( UnhandledExceptionFilter(GetExceptionInformation()) )
736 : {
737 : // User opted to ignore the breakpoint
738 : return;
739 : }
740 : }
741 : __except (EXCEPTION_EXECUTE_HANDLER)
742 : {
743 : // If we got here, the user has pushed Debug. Debugger is already attached to our process and we
744 : // continue to let the another BOOST_DEBUG_BREAK to be called.
745 : }
746 : }
747 : #endif
748 :
749 : BOOST_DEBUG_BREAK();
750 :
751 : #elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX
752 :
753 : ::kill( ::getpid(), SIGTRAP );
754 :
755 : #else // ****************************************************** default
756 :
757 : #endif
758 0 : }
759 :
760 : //____________________________________________________________________________//
761 :
762 : // ************************************************************************** //
763 : // ************** console debugger setup ************** //
764 : // ************************************************************************** //
765 :
766 : #if defined(BOOST_UNIX_BASED_DEBUG) // ************************ UNIX
767 :
768 : std::string
769 : set_debugger( unit_test::const_string dbg_id, dbg_starter s )
770 : {
771 : std::string old = s_info.p_dbg;
772 :
773 : assign_op( s_info.p_dbg.value, dbg_id, 0 );
774 :
775 : if( !!s )
776 : s_info.m_dbg_starter_reg[s_info.p_dbg.get()] = s;
777 :
778 : return old;
779 : }
780 :
781 : #else // ***************************************************** default
782 :
783 : std::string
784 0 : set_debugger( unit_test::const_string, dbg_starter )
785 : {
786 0 : return std::string();
787 : }
788 :
789 : #endif
790 :
791 : //____________________________________________________________________________//
792 :
793 : // ************************************************************************** //
794 : // ************** attach debugger to the current process ************** //
795 : // ************************************************************************** //
796 :
797 : #if defined(BOOST_WIN32_BASED_DEBUG)
798 :
799 : struct safe_handle_helper
800 : {
801 : HANDLE& handle;
802 : safe_handle_helper(HANDLE &handle_) : handle(handle_) {}
803 :
804 : void close_handle()
805 : {
806 : if( handle != INVALID_HANDLE_VALUE )
807 : {
808 : ::CloseHandle( handle );
809 : handle = INVALID_HANDLE_VALUE;
810 : }
811 : }
812 :
813 : ~safe_handle_helper()
814 : {
815 : close_handle();
816 : }
817 : };
818 : #endif
819 :
820 : bool
821 0 : attach_debugger( bool break_or_continue )
822 : {
823 0 : if( under_debugger() )
824 0 : return false;
825 :
826 : #if defined(BOOST_WIN32_BASED_DEBUG) // *********************** WIN32
827 :
828 : const int MAX_CMD_LINE = 200;
829 :
830 : // *************************************************** //
831 : // Debugger "ready" event
832 :
833 : SECURITY_ATTRIBUTES attr;
834 : attr.nLength = sizeof(attr);
835 : attr.lpSecurityDescriptor = NULL;
836 : attr.bInheritHandle = true;
837 :
838 : // manual resettable, initially non signaled, unnamed event,
839 : // that will signal me that debugger initialization is done
840 : HANDLE dbg_init_done_ev = ::CreateEvent(
841 : &attr, // pointer to security attributes
842 : true, // flag for manual-reset event
843 : false, // flag for initial state
844 : NULL // pointer to event-object name
845 : );
846 :
847 : if( !dbg_init_done_ev )
848 : return false;
849 :
850 : safe_handle_helper safe_handle_obj( dbg_init_done_ev );
851 :
852 : // *************************************************** //
853 : // Debugger command line format
854 :
855 : HKEY reg_key;
856 :
857 : if( !s_info.m_reg_open_key || (*s_info.m_reg_open_key)(
858 : HKEY_LOCAL_MACHINE, // handle of open key
859 : "Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", // name of subkey to open
860 : ®_key ) != ERROR_SUCCESS ) // address of handle of open key
861 : return false;
862 :
863 : char format[MAX_CMD_LINE];
864 : DWORD format_size = MAX_CMD_LINE;
865 : DWORD type = REG_SZ;
866 :
867 : bool b_read_key = s_info.m_reg_query_value &&
868 : ((*s_info.m_reg_query_value)(
869 : reg_key, // handle of open key
870 : "Debugger", // name of subkey to query
871 : 0, // reserved
872 : &type, // value type
873 : (LPBYTE)format, // buffer for returned string
874 : &format_size ) == ERROR_SUCCESS ); // in: buffer size; out: actual size of returned string
875 :
876 : if( !s_info.m_reg_close_key || (*s_info.m_reg_close_key)( reg_key ) != ERROR_SUCCESS )
877 : return false;
878 :
879 : if( !b_read_key )
880 : return false;
881 :
882 : // *************************************************** //
883 : // Debugger command line
884 :
885 : char cmd_line[MAX_CMD_LINE];
886 : std::sprintf( cmd_line, format, ::GetCurrentProcessId(), dbg_init_done_ev );
887 :
888 : // *************************************************** //
889 : // Debugger window parameters
890 :
891 : STARTUPINFOA startup_info;
892 : std::memset( &startup_info, 0, sizeof(startup_info) );
893 :
894 : startup_info.cb = sizeof(startup_info);
895 : startup_info.dwFlags = STARTF_USESHOWWINDOW;
896 : startup_info.wShowWindow = SW_SHOWNORMAL;
897 :
898 : // debugger process s_info
899 : PROCESS_INFORMATION debugger_info;
900 :
901 : bool created = !!::CreateProcessA(
902 : NULL, // pointer to name of executable module; NULL - use the one in command line
903 : cmd_line, // pointer to command line string
904 : NULL, // pointer to process security attributes; NULL - debugger's handle can't be inherited
905 : NULL, // pointer to thread security attributes; NULL - debugger's handle can't be inherited
906 : true, // debugger inherit opened handles
907 : 0, // priority flags; 0 - normal priority
908 : NULL, // pointer to new environment block; NULL - use this process environment
909 : NULL, // pointer to current directory name; NULL - use this process correct directory
910 : &startup_info, // pointer to STARTUPINFO that specifies main window appearance
911 : &debugger_info // pointer to PROCESS_INFORMATION that will contain the new process identification
912 : );
913 :
914 : bool debugger_run_ok = false;
915 : if( created )
916 : {
917 : DWORD ret_code = ::WaitForSingleObject( dbg_init_done_ev, INFINITE );
918 : debugger_run_ok = ( ret_code == WAIT_OBJECT_0 );
919 : }
920 :
921 : safe_handle_obj.close_handle();
922 :
923 : if( !created || !debugger_run_ok )
924 : return false;
925 :
926 : if( break_or_continue )
927 : debugger_break();
928 :
929 : return true;
930 :
931 : #elif defined(BOOST_UNIX_BASED_DEBUG) // ********************** UNIX
932 :
933 : char init_done_lock_fn[] = "/tmp/btl_dbg_init_done_XXXXXX";
934 : const mode_t cur_umask = ::umask( S_IRWXO | S_IRWXG );
935 : fd_holder init_done_lock_fd( ::mkstemp( init_done_lock_fn ) );
936 : ::umask( cur_umask );
937 :
938 : if( init_done_lock_fd == -1 )
939 : return false;
940 :
941 : pid_t child_pid = fork();
942 :
943 : if( child_pid == -1 )
944 : return false;
945 :
946 : if( child_pid != 0 ) { // parent process - here we will start the debugger
947 : dbg_startup_info dsi;
948 :
949 : process_info pi( child_pid );
950 : if( pi.binary_path().is_empty() )
951 : ::exit( -1 );
952 :
953 : dsi.pid = child_pid;
954 : dsi.break_or_continue = break_or_continue;
955 : dsi.binary_path = pi.binary_path();
956 : dsi.display = ::getenv( "DISPLAY" );
957 : dsi.init_done_lock = init_done_lock_fn;
958 :
959 : dbg_starter starter = s_info.m_dbg_starter_reg[s_info.p_dbg];
960 : if( !!starter )
961 : starter( dsi );
962 :
963 : ::perror( "Boost.Test execution monitor failed to start a debugger:" );
964 :
965 : ::exit( -1 );
966 : }
967 :
968 : // child process - here we will continue our test module execution ; // !! ?? should it be vice versa
969 :
970 : while( ::access( init_done_lock_fn, F_OK ) == 0 ) {
971 : struct timeval to = { 0, 100 };
972 :
973 : ::select( 0, 0, 0, 0, &to );
974 : }
975 :
976 : // char dummy;
977 : // while( ::read( init_done_lock_fd, &dummy, sizeof(char) ) == 0 );
978 :
979 : if( break_or_continue )
980 : debugger_break();
981 :
982 : return true;
983 :
984 : #else // ****************************************************** default
985 : (void) break_or_continue; // silence 'unused variable' warning
986 0 : return false;
987 :
988 : #endif
989 0 : }
990 :
991 : //____________________________________________________________________________//
992 :
993 : // ************************************************************************** //
994 : // ************** switch on/off detect memory leaks feature ************** //
995 : // ************************************************************************** //
996 :
997 : void
998 146 : detect_memory_leaks( bool on_off, unit_test::const_string report_file )
999 : {
1000 146 : boost::ignore_unused( on_off );
1001 :
1002 : #ifdef BOOST_MS_CRT_BASED_DEBUG
1003 : int flags = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
1004 :
1005 : if( !on_off )
1006 : flags &= ~_CRTDBG_LEAK_CHECK_DF;
1007 : else {
1008 : flags |= _CRTDBG_LEAK_CHECK_DF;
1009 : _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
1010 :
1011 : if( report_file.is_empty() )
1012 : _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
1013 : else {
1014 : HANDLE hreport_f = ::CreateFileA( report_file.begin(),
1015 : GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1016 : _CrtSetReportFile(_CRT_WARN, hreport_f );
1017 : }
1018 : }
1019 :
1020 : _CrtSetDbgFlag ( flags );
1021 : #else
1022 146 : boost::ignore_unused( report_file );
1023 : #endif // BOOST_MS_CRT_BASED_DEBUG
1024 146 : }
1025 :
1026 : //____________________________________________________________________________//
1027 :
1028 : // ************************************************************************** //
1029 : // ************** cause program to break execution in ************** //
1030 : // ************** debugger at specific allocation point ************** //
1031 : // ************************************************************************** //
1032 :
1033 : void
1034 146 : break_memory_alloc( long mem_alloc_order_num )
1035 : {
1036 146 : boost::ignore_unused( mem_alloc_order_num );
1037 :
1038 : #ifdef BOOST_MS_CRT_BASED_DEBUG
1039 : // only set the value if one was supplied (do not use default used by UTF just as a indicator to enable leak detection)
1040 : if( mem_alloc_order_num > 1 )
1041 : _CrtSetBreakAlloc( mem_alloc_order_num );
1042 : #endif // BOOST_MS_CRT_BASED_DEBUG
1043 146 : }
1044 :
1045 : //____________________________________________________________________________//
1046 :
1047 : } // namespace debug
1048 : } // namespace boost
1049 :
1050 : #include <boost/test/detail/enable_warnings.hpp>
1051 :
1052 : #endif // BOOST_TEST_DEBUG_API_IPP_112006GER
|