diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/infrun.c | 6 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/testsuite/gdb.threads/step-after-sr-lock.c | 145 | ||||
-rw-r--r-- | gdb/testsuite/gdb.threads/step-after-sr-lock.exp | 120 |
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 +} |