Need to convince myself of the approach.
Change-Id: If649d30233ac1c2e57ea09cc8050e35f7a4bcf12
call. */
int in_infcall = 0;
+ /* True if this thread is single stepping due to a software
+ watchpoint. This is set on the selected thread when an execution
+ command is issued. It is not set on other threads that happen to
+ run because schedlock disabled. This is so all-stop targets and
+ all-stop-on-top-of-non-stop targets behave the same as GDB
+ historically has behaved with all-stop backends -- with a
+ software watchpoint, only the selected/leader thread is single
+ stepped. */
+ bool stepped_for_sw_watch = false;
+
enum step_over_calls_kind step_over_calls = STEP_OVER_NONE;
/* Nonzero if stopped due to a step command. */
/* If we have a breakpoint to step over, make sure to do a single
step only. Same if we have software watchpoints. */
- if (tp->control.trap_expected || bpstat_should_step ())
+ if (tp->control.trap_expected || tp->control.stepped_for_sw_watch)
tp->control.may_range_step = 0;
/* If displaced stepping is enabled, step over breakpoints by executing a
tp->control.step_stack_frame_id = null_frame_id;
tp->control.step_over_calls = STEP_OVER_UNDEBUGGABLE;
tp->control.step_start_function = nullptr;
+ tp->control.stepped_for_sw_watch = false;
tp->stop_requested = false;
tp->control.stop_step = 0;
Ctrl-C from within target_pass_ctrlc). */
target_terminal::inferior ();
+ /* Set the selected thread single stepping if there's a software
+ watchpoint. Do this before GDB decides to switch to another
+ thread to step it past a breakpoint. */
+ cur_thr->control.stepped_for_sw_watch = bpstat_should_step ();
+
/* In a multi-threaded task we may select another thread and
then continue or step.
&& tp->control.step_resume_breakpoint == nullptr)
|| tp->control.trap_expected
|| tp->stepped_breakpoint
- || bpstat_should_step ());
+ || tp->control.stepped_for_sw_watch);
}
/* Inferior has stepped into a subroutine call with source code that
foo4.val[3] = 33;
}
-int main ()
+int test_main ()
{
struct1.val = 1;
struct2.val = 2;
return 0;
}
+
+#if USE_THREADS
+#include <pthread.h>
+
+#define NUM 5
+
+/* Barrier used to wait until all threads are started, before calling
+ test_main. */
+static pthread_barrier_t threads_started_barrier;
+
+/* Barrier used to prevent threads from exiting until test_main
+ returns. */
+static pthread_barrier_t threads_exit_barrier;
+
+static void *
+thread_function (void *arg)
+{
+ pthread_barrier_wait (&threads_started_barrier);
+ pthread_barrier_wait (&threads_exit_barrier);
+}
+
+#endif /* USE_THREADS */
+
+int
+main ()
+{
+ int res;
+#if USE_THREADS
+ pthread_t threads[NUM];
+ long i;
+
+ pthread_barrier_init (&threads_started_barrier, NULL, NUM + 1);
+ pthread_barrier_init (&threads_exit_barrier, NULL, NUM + 1);
+
+ for (i = 0; i < NUM; i++)
+ pthread_create (&threads[i], NULL, thread_function, NULL);
+
+ pthread_barrier_wait (&threads_started_barrier);
+#endif /* USE_THREADS */
+
+ res = test_main ();
+
+#if USE_THREADS
+ pthread_barrier_wait (&threads_exit_barrier);
+
+ for (i = 0; i < NUM; i++)
+ pthread_join (threads[i], NULL);
+#endif /* USE_THREADS */
+
+ return res;
+}
standard_testfile
-if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
- untested "failed to compile"
- return -1
+if {[build_executable "failed to prepare" $testfile $srcfile {debug}]} {
+ return -1
+}
+
+if {[build_executable "failed to prepare" $testfile-threads $srcfile \
+ {debug pthreads additional_flags=-DUSE_THREADS=1}]} {
+ set use_threads 0
+} else {
+ set use_threads 1
}
# True if we're forcing no hardware watchpoints.
if [target_info exists gdb,noresults] { return }
- gdb_continue_to_end "continue to exit in test_simple_watchpoint"
+ gdb_continue_to_end "continue to exit in test_simple_watchpoint" continue 1
}
# Test disabling watchpoints.
if [target_info exists gdb,noresults] { return }
- gdb_continue_to_end "continue to exit in test_disabling_watchpoints"
+ gdb_continue_to_end "continue to exit in test_disabling_watchpoints" continue 1
}
# Test stepping and other mundane operations with watchpoints enabled
if [target_info exists gdb,noresults] { return }
- gdb_continue_to_end "continue to exit in test_watchpoint_triggered_in_syscall"
+ gdb_continue_to_end "continue to exit in test_watchpoint_triggered_in_syscall" continue 1
}
}
if [target_info exists gdb,noresults] { return }
- gdb_continue_to_end "continue to exit in test_complex_watchpoint"
+ gdb_continue_to_end "continue to exit in test_complex_watchpoint" continue 1
}
}
# (This proves rather little on kernels that don't support
# fast watchpoints, but still...)
#
- if {![runto_main]} {
+ if {![runto test_main]} {
return
}
}
}
+# Test software watchpoints when the program is multithreaded. On
+# some systems, the runtime always spawns some helper threads.
+# Testing on one such system -- Cygwin -- exposed paths in the
+# software single-step implementation that weren't exercised anywhere
+# else. We thus here test the basic software watchpoint support with
+# explicitly started extra threads, to exercise it on all systems
+# (that can do threads).
+
+proc do_threaded_sw_tests {} {
+ global testfile
+ global no_hw
+ global allow_hw_watchpoint_tests_p
+
+ clean_restart $testfile-threads
+
+ gdb_test_no_output "set can-use-hw-watchpoints 0"\
+ "disable fast watches"
+
+ if {[initialize]} {
+ test_simple_watchpoint
+ }
+}
+
+if {$use_threads} {
+ with_test_prefix "threaded-no-hw" {
+ do_threaded_sw_tests
+ }
+}
+
# Restore old timeout
set timeout $prev_timeout
verbose "Timeout now $timeout sec.\n"