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