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