#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
+#include <pthread.h>
static void
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;
}
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,
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 ();
}
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"
}
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
}
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]
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)"
}