aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog5
-rw-r--r--gdb/infrun.c6
-rw-r--r--gdb/testsuite/ChangeLog6
-rw-r--r--gdb/testsuite/gdb.threads/step-after-sr-lock.c145
-rw-r--r--gdb/testsuite/gdb.threads/step-after-sr-lock.exp120
5 files changed, 281 insertions, 1 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 6bcc205..99ed610 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,8 @@
+2014-02-07 Pedro Alves <palves@redhat.com>
+
+ * infrun.c (handle_signal_stop) <signal arrives while stepping
+ over a breakpoint>: Switch back to the stepping thread.
+
2014-02-07 Yao Qi <yao@codesourcery.com>
* target.c (target_xfer_partial): Return zero if LEN is zero.
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c0df124..5d60a90 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -4384,7 +4384,11 @@ handle_signal_stop (struct execution_control_state *ecs)
ecs->event_thread->step_after_step_resume_breakpoint = 1;
/* Reset trap_expected to ensure breakpoints are re-inserted. */
ecs->event_thread->control.trap_expected = 0;
- keep_going (ecs);
+
+ /* If we were nexting/stepping some other thread, switch to
+ it, so that we don't continue it, losing control. */
+ if (!switch_back_to_stepped_thread (ecs))
+ keep_going (ecs);
return;
}
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 89f879b..7c1fd10 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2014-02-07 Pedro Alves <pedro@codesourcery.com>
+ Pedro Alves <palves@redhat.com>
+
+ * gdb.threads/step-after-sr-lock.c: New file.
+ * gdb.threads/step-after-sr-lock.exp: New file.
+
2014-02-07 Pedro Alves <palves@redhat.com>
* gdb.threads/stepi-random-signal.exp: Set SIGCHLD to print.
diff --git a/gdb/testsuite/gdb.threads/step-after-sr-lock.c b/gdb/testsuite/gdb.threads/step-after-sr-lock.c
new file mode 100644
index 0000000..a4634f2
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/step-after-sr-lock.c
@@ -0,0 +1,145 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009-2014 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+
+unsigned int args[2];
+
+pid_t pid;
+pthread_barrier_t barrier;
+pthread_t child_thread_2, child_thread_3;
+
+void
+handler (int signo)
+{
+ /* so that thread 3 is sure to run, in case the bug is present. */
+ usleep (10);
+}
+
+void
+callme (void)
+{
+}
+
+void
+block_signals (void)
+{
+ sigset_t mask;
+
+ sigfillset (&mask);
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+}
+
+void
+unblock_signals (void)
+{
+ sigset_t mask;
+
+ sigfillset (&mask);
+ sigprocmask (SIG_UNBLOCK, &mask, NULL);
+}
+
+void *
+child_function_3 (void *arg)
+{
+ int my_number = (long) arg;
+ volatile int *myp = (int *) &args[my_number];
+
+ pthread_barrier_wait (&barrier);
+
+ while (*myp > 0)
+ {
+ (*myp) ++; /* set breakpoint child_two here */
+ callme ();
+ }
+
+ pthread_exit (NULL);
+}
+
+void *
+child_function_2 (void *arg)
+{
+ int my_number = (long) arg;
+ volatile int *myp = (int *) &args[my_number];
+
+ unblock_signals ();
+
+ pthread_barrier_wait (&barrier);
+
+ while (*myp > 0)
+ {
+ (*myp) ++;
+ callme (); /* set breakpoint child_one here */
+ }
+
+ *myp = 1;
+ while (*myp > 0)
+ {
+ (*myp) ++;
+ callme ();
+ }
+
+ pthread_exit (NULL);
+}
+
+
+int
+main ()
+{
+ int res;
+ long i;
+
+ /* Block signals in all threads but one, so that we're sure which
+ thread gets the signal we send from the command line. */
+ block_signals ();
+
+ signal (SIGUSR1, handler);
+
+ /* Call these early so that PLTs for these are resolved soon,
+ instead of in the threads. RTLD_NOW should work as well. */
+ usleep (0);
+ pthread_barrier_init (&barrier, NULL, 1);
+ pthread_barrier_wait (&barrier);
+
+ pthread_barrier_init (&barrier, NULL, 2);
+
+ /* The test uses this global to know where to send the signal
+ to. */
+ pid = getpid ();
+
+ i = 0;
+ args[i] = 1;
+ res = pthread_create (&child_thread_2,
+ NULL, child_function_2, (void *) i);
+ pthread_barrier_wait (&barrier);
+ callme (); /* set wait-thread-2 breakpoint here */
+
+ i = 1;
+ args[i] = 1;
+ res = pthread_create (&child_thread_3,
+ NULL, child_function_3, (void *) i);
+ pthread_barrier_wait (&barrier);
+ callme (); /* set wait-thread-3 breakpoint here */
+
+ pthread_join (child_thread_2, NULL);
+ pthread_join (child_thread_3, NULL);
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/gdb/testsuite/gdb.threads/step-after-sr-lock.exp b/gdb/testsuite/gdb.threads/step-after-sr-lock.exp
new file mode 100644
index 0000000..6b93d9c
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/step-after-sr-lock.exp
@@ -0,0 +1,120 @@
+# Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test that GDB doesn't inadvertently resume the stepped thread when a
+# signal arrives while stepping over the breakpoint that last caused a
+# stop, when the thread that hit that breakpoint is not the stepped
+# thread.
+
+standard_testfile
+set executable ${testfile}
+
+if [target_info exists gdb,nosignals] {
+ verbose "Skipping ${testfile}.exp because of nosignals."
+ return -1
+}
+
+# Test uses host "kill".
+if { [is_remote target] } {
+ return -1
+}
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
+ executable [list debug "incdir=${objdir}"]] != "" } {
+ return -1
+}
+
+proc get_value {var test} {
+ global expect_out
+ global gdb_prompt
+ global decimal
+
+ set value -1
+ gdb_test_multiple "print $var" "$test" {
+ -re ".*= ($decimal).*\r\n$gdb_prompt $" {
+ set value $expect_out(1,string)
+ pass "$test"
+ }
+ }
+ return ${value}
+}
+
+# Start with a fresh gdb.
+
+clean_restart $executable
+
+if ![runto_main] {
+ return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "set wait-thread-2 breakpoint here"]
+gdb_continue_to_breakpoint "run to wait-thread-2 breakpoint"
+gdb_test "info threads" "" "info threads with thread 2"
+
+gdb_breakpoint [gdb_get_line_number "set wait-thread-3 breakpoint here"]
+gdb_continue_to_breakpoint "run to breakpoint"
+gdb_test "info threads" "" "info threads with thread 3"
+
+set testpid [get_value "pid" "get pid of inferior"]
+
+gdb_test "set scheduler-locking on"
+
+gdb_breakpoint [gdb_get_line_number "set breakpoint child_two here"]
+gdb_breakpoint [gdb_get_line_number "set breakpoint child_one here"]
+
+gdb_test "thread 3" "" "switch to thread 3 to run to its breakpoint"
+gdb_continue_to_breakpoint "run to breakpoint in thread 3"
+
+gdb_test "thread 2" "" "switch to thread 2 to run to its breakpoint"
+gdb_continue_to_breakpoint "run to breakpoint in thread 2"
+
+delete_breakpoints
+
+gdb_test "b *\$pc" "" "set breakpoint to be stepped over"
+# Make sure the first loop breaks without hitting the breakpoint
+# again.
+gdb_test "p *myp = 0" " = 0" "force loop break in thread 2"
+
+# We want "print" to make sure the target reports the signal to the
+# core.
+gdb_test "handle SIGUSR1 print nostop pass" "" ""
+
+# Queue a signal in thread 2.
+remote_exec host "kill -SIGUSR1 ${testpid}"
+
+gdb_test "thread 3" "" "switch to thread 3 for stepping"
+set my_number [get_value "my_number" "get my_number"]
+set cnt_before [get_value "args\[$my_number\]" "get count before step"]
+gdb_test "set scheduler-locking off"
+
+# Make sure we're exercising the paths we want to.
+gdb_test "set debug infrun 1"
+
+gdb_test \
+ "step" \
+ ".*prepare_to_proceed \\(step=1\\), switched to.*signal arrived while stepping over breakpoint.*switching back to stepped thread.*stepped to a different line.*callme.*" \
+ "step"
+
+set cnt_after [get_value "args\[$my_number\]" "get count after step"]
+
+# Test that GDB doesn't inadvertently resume the stepped thread when a
+# signal arrives while stepping over a breakpoint in another thread.
+
+set test "stepped thread under control"
+if { $cnt_before + 1 == $cnt_after } {
+ pass $test
+} else {
+ fail $test
+}