diff --git a/gdb/testsuite/gdb.base/step-over-clone.c b/gdb/testsuite/gdb.base/step-over-clone.c index 581bf5fdde5..ef6fd922eb1 100644 --- a/gdb/testsuite/gdb.base/step-over-clone.c +++ b/gdb/testsuite/gdb.base/step-over-clone.c @@ -19,6 +19,7 @@ #include #include #include +#include static void marker () @@ -26,9 +27,22 @@ marker () #define STACK_SIZE 0x1000 +/* These are used to signal that the threads have started correctly. The + GLOBAL_THREAD_COUNT is set to the number of threads in main, then + decremented (under a lock) in each new thread. */ +pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER; +int global_thread_count = 0; + static int clone_fn (void *unused) { + /* Signal that this thread has started correctly. */ + if (pthread_mutex_lock (&global_lock) != 0) + abort (); + global_thread_count--; + if (pthread_mutex_unlock (&global_lock) != 0) + abort (); + return 0; } @@ -38,9 +52,21 @@ main (void) int i, pid; unsigned char *stack[6]; + /* Due to bug gdb/19675 the cloned thread _might_ try to reenter main + (this depends on where the displaced instruction is placed for + execution). However, if we do reenter main then lets ensure we fail + hard rather then just silently executing the code below. */ + static int started = 0; + if (!started) + started = 1; + else + abort (); + for (i = 0; i < (sizeof (stack) / sizeof (stack[0])); i++) stack[i] = malloc (STACK_SIZE); + global_thread_count = (sizeof (stack) / sizeof (stack[0])); + for (i = 0; i < (sizeof (stack) / sizeof (stack[0])); i++) { pid = clone (clone_fn, stack[i] + STACK_SIZE, CLONE_FILES | CLONE_VM, @@ -50,5 +76,18 @@ main (void) for (i = 0; i < (sizeof (stack) / sizeof (stack[0])); i++) free (stack[i]); + /* Set an alarm so we don't end up stuck waiting for threads that might + never start correctly. */ + alarm (120); + + /* Now wait for all the threads to start up. */ + while (global_thread_count != 0) + { + /* Force memory barrier so GLOBAL_THREAD_COUNT will be refetched. */ + asm volatile ("" ::: "memory"); + sleep (1); + } + + /* Call marker, this is what GDB is waiting for. */ marker (); } diff --git a/gdb/testsuite/gdb.base/step-over-syscall.exp b/gdb/testsuite/gdb.base/step-over-syscall.exp index ecfb7be481d..5d4a75f810b 100644 --- a/gdb/testsuite/gdb.base/step-over-syscall.exp +++ b/gdb/testsuite/gdb.base/step-over-syscall.exp @@ -41,11 +41,50 @@ if { [istarget "i\[34567\]86-*-linux*"] || [istarget "x86_64-*-linux*"] } { return -1 } -proc_with_prefix check_pc_after_cross_syscall { syscall syscall_insn_next_addr } { +proc_with_prefix check_pc_after_cross_syscall { displaced syscall syscall_insn_next_addr } { + global gdb_prompt + set syscall_insn_next_addr_found [get_hexadecimal_valueof "\$pc" "0"] + # After the 'stepi' we expect thread 1 to still be selected. + # However, when displaced stepping over a clone bug gdb/19675 + # means this might not be the case. + # + # Which thread we end up in depends on a race between the original + # thread-1, and the new thread (created by the clone), so we can't + # guarantee which thread we will be in at this point. + # + # For the fork/vfork syscalls, which are correctly handled by + # displaced stepping we will always be in thread-1 or the original + # process at this point. + set curr_thread "unknown" + gdb_test_multiple "info threads" "" { + -re "Id\\s+Target Id\\s+Frame\\s*\r\n" { + exp_continue + } + -re "^\\* (\\d+)\\s+\[^\r\n\]+\r\n" { + set curr_thread $expect_out(1,string) + exp_continue + } + -re "^\\s+\\d+\\s+\[^\r\n\]+\r\n" { + exp_continue + } + -re "$gdb_prompt " { + } + } + + # If we are displaced stepping over a clone, and we ended up in + # the wrong thread then the following check of the $pc value will + # fail. + if { $displaced == "on" && $syscall == "clone" && $curr_thread != 1 } { + # GDB doesn't support stepping over clone syscall with + # displaced stepping. + setup_kfail "*-*-*" "gdb/19675" + } + gdb_assert {$syscall_insn_next_addr != 0 \ - && $syscall_insn_next_addr == $syscall_insn_next_addr_found} \ + && $syscall_insn_next_addr == $syscall_insn_next_addr_found \ + && $curr_thread == 1} \ "single step over $syscall final pc" } @@ -203,7 +242,12 @@ proc step_over_syscall { syscall } { set testfile "step-over-$syscall" - if [build_executable ${testfile}.exp ${testfile} ${testfile}.c {debug}] { + set options [list debug] + if { $syscall == "clone" } { + lappend options "pthreads" + } + + if [build_executable ${testfile}.exp ${testfile} ${testfile}.c $options] { untested "failed to compile" return -1 } @@ -213,13 +257,6 @@ proc step_over_syscall { syscall } { continue } - if { $displaced == "on" && $syscall == "clone" } { - # GDB doesn't support stepping over clone syscall with - # displaced stepping. - kfail "gdb/19675" "single step over clone" - continue - } - set ret [setup $syscall] set syscall_insn_addr [lindex $ret 0] @@ -256,12 +293,22 @@ proc step_over_syscall { syscall } { if {[gdb_test "stepi" "x/i .*=>.*" "single step over $syscall"] != 0} { return -1 } - check_pc_after_cross_syscall $syscall $syscall_insn_next_addr + check_pc_after_cross_syscall $displaced $syscall $syscall_insn_next_addr # Delete breakpoint syscall insns to avoid interference to other syscalls. delete_breakpoints gdb_test "break marker" "Breakpoint.*at.* file .*${testfile}.c, line.*" + + # If we are displaced stepping over a clone syscall then + # we expect the following check to fail. See also the + # code in check_pc_after_cross_syscall. + if { $displaced == "on" && $syscall == "clone" } { + # GDB doesn't support stepping over clone syscall with + # displaced stepping. + setup_kfail "*-*-*" "gdb/19675" + } + gdb_test "continue" "Continuing\\..*Breakpoint \[0-9\]+, marker \\(\\) at.*" \ "continue to marker ($syscall)" }