aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.threads
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2014-03-20 13:26:32 +0000
committerPedro Alves <palves@redhat.com>2014-03-20 13:43:28 +0000
commit99619beac6252113fed212fdb9e1ab97bface423 (patch)
tree0eb88ffc72535a5a34430c00dac959bc4a4f14f9 /gdb/testsuite/gdb.threads
parent2adfaa28b5ba2fb78ba5113977082c4d04752bd6 (diff)
downloadgdb-99619beac6252113fed212fdb9e1ab97bface423.zip
gdb-99619beac6252113fed212fdb9e1ab97bface423.tar.gz
gdb-99619beac6252113fed212fdb9e1ab97bface423.tar.bz2
Handle multiple step-overs.
This test fails with current mainline. If the program stopped for a breakpoint in thread 1, and then the user switches to thread 2, and resumes the program, GDB first switches back to thread 1 to step it over the breakpoint, in order to make progress. However, that logic only considers the last reported event, assuming only one thread needs that stepping over dance. That's actually not true when we play with scheduler-locking. The patch adds an example to the testsuite of multiple threads needing a step-over before the stepping thread can be resumed. With current mainline, the program re-traps the same breakpoint it had already trapped before. E.g.: Breakpoint 2, main () at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:99 99 wait_threads (); /* set wait-threads breakpoint here */ (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: continue to breakpoint: run to breakpoint info threads Id Target Id Frame 3 Thread 0x7ffff77c9700 (LWP 4310) "multiple-step-o" 0x00000000004007ca in child_function_3 (arg=0x1) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:43 2 Thread 0x7ffff7fca700 (LWP 4309) "multiple-step-o" 0x0000000000400827 in child_function_2 (arg=0x0) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:60 * 1 Thread 0x7ffff7fcb740 (LWP 4305) "multiple-step-o" main () at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:99 (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: info threads shows all threads set scheduler-locking on (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: set scheduler-locking on break 44 Breakpoint 3 at 0x4007d3: file ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c, line 44. (gdb) break 61 Breakpoint 4 at 0x40082d: file ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c, line 61. (gdb) thread 3 [Switching to thread 3 (Thread 0x7ffff77c9700 (LWP 4310))] #0 0x00000000004007ca in child_function_3 (arg=0x1) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:43 43 (*myp) ++; (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: thread 3 continue Continuing. Breakpoint 3, child_function_3 (arg=0x1) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:44 44 callme (); /* set breakpoint thread 3 here */ (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: continue to breakpoint: run to breakpoint in thread 3 p *myp = 0 $1 = 0 (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: unbreak loop in thread 3 thread 2 [Switching to thread 2 (Thread 0x7ffff7fca700 (LWP 4309))] #0 0x0000000000400827 in child_function_2 (arg=0x0) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:60 60 (*myp) ++; (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: thread 2 continue Continuing. Breakpoint 4, child_function_2 (arg=0x0) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:61 61 callme (); /* set breakpoint thread 2 here */ (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: continue to breakpoint: run to breakpoint in thread 2 p *myp = 0 $2 = 0 (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: unbreak loop in thread 2 thread 1 [Switching to thread 1 (Thread 0x7ffff7fcb740 (LWP 4305))] #0 main () at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:99 99 wait_threads (); /* set wait-threads breakpoint here */ (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: thread 1 set scheduler-locking off (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: set scheduler-locking off At this point all thread are stopped for a breakpoint that needs stepping over. (gdb) step Breakpoint 2, main () at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:99 99 wait_threads (); /* set wait-threads breakpoint here */ (gdb) FAIL: gdb.threads/multiple-step-overs.exp: step But that "step" retriggers the same breakpoint instead of making progress. The patch teaches GDB to step over all breakpoints of all threads before resuming the stepping thread. Tested on x86_64 Fedora 17, against pristine mainline, and also my branch that implements software single-stepping on x86. gdb/ 2014-03-20 Pedro Alves <palves@redhat.com> * infrun.c (prepare_to_proceed): Delete. (thread_still_needs_step_over): New function. (find_thread_needs_step_over): New function. (proceed): If the current thread needs a step-over, set its steping_over_breakpoint flag. Adjust to use find_thread_needs_step_over instead of prepare_to_proceed. (process_event_stop_test): For BPSTAT_WHAT_STOP_NOISY and BPSTAT_WHAT_STOP_SILENT, assume the thread stopped for a breakpoint. (switch_back_to_stepped_thread): Step over breakpoints of all threads not the stepping thread, before switching back to the stepping thread. gdb/testsuite/ 2014-03-20 Pedro Alves <palves@redhat.com> * gdb.threads/multiple-step-overs.c: New file. * gdb.threads/multiple-step-overs.exp: New file. * gdb.threads/signal-while-stepping-over-bp-other-thread.exp: Adjust expected infrun debug output.
Diffstat (limited to 'gdb/testsuite/gdb.threads')
-rw-r--r--gdb/testsuite/gdb.threads/multiple-step-overs.c105
-rw-r--r--gdb/testsuite/gdb.threads/multiple-step-overs.exp80
-rw-r--r--gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp2
3 files changed, 186 insertions, 1 deletions
diff --git a/gdb/testsuite/gdb.threads/multiple-step-overs.c b/gdb/testsuite/gdb.threads/multiple-step-overs.c
new file mode 100644
index 0000000..523a88a
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/multiple-step-overs.c
@@ -0,0 +1,105 @@
+/* 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];
+
+pthread_barrier_t barrier;
+pthread_t child_thread_2, child_thread_3;
+
+void
+callme (void)
+{
+}
+
+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) ++;
+ callme (); /* set breakpoint thread 3 here */
+ }
+
+ pthread_exit (NULL);
+}
+
+void *
+child_function_2 (void *arg)
+{
+ int my_number = (long) arg;
+ volatile int *myp = (int *) &args[my_number];
+
+ pthread_barrier_wait (&barrier);
+
+ while (*myp > 0)
+ {
+ (*myp) ++;
+ callme (); /* set breakpoint thread 2 here */
+ }
+
+ pthread_exit (NULL);
+}
+
+static int
+wait_threads (void)
+{
+ return 1; /* in wait_threads */
+}
+
+int
+main ()
+{
+ int res;
+ long i;
+
+ /* 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);
+
+ i = 0;
+ args[i] = 1;
+ res = pthread_create (&child_thread_2,
+ NULL, child_function_2, (void *) i);
+ pthread_barrier_wait (&barrier);
+ callme ();
+
+ i = 1;
+ args[i] = 1;
+ res = pthread_create (&child_thread_3,
+ NULL, child_function_3, (void *) i);
+ pthread_barrier_wait (&barrier);
+ wait_threads (); /* set wait-threads breakpoint here */
+
+ pthread_join (child_thread_2, NULL);
+ pthread_join (child_thread_3, NULL);
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/gdb/testsuite/gdb.threads/multiple-step-overs.exp b/gdb/testsuite/gdb.threads/multiple-step-overs.exp
new file mode 100644
index 0000000..c79e5d2
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/multiple-step-overs.exp
@@ -0,0 +1,80 @@
+# 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 steps over all breakpoints of threads not the stepping
+# thread, before actually proceeding with the stepped thread.
+
+standard_testfile
+set executable ${testfile}
+
+if [target_info exists gdb,nosignals] {
+ verbose "Skipping ${testfile}.exp because of nosignals."
+ return -1
+}
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
+ executable [list debug "incdir=${objdir}"]] != "" } {
+ return -1
+}
+
+# Prepare environment for test. PREFIX is used as prefix in test
+# messages.
+
+proc setup { prefix } {
+ global executable
+
+ with_test_prefix $prefix {
+ clean_restart $executable
+
+ if ![runto_main] {
+ return -1
+ }
+
+ gdb_breakpoint [gdb_get_line_number "set wait-threads breakpoint here"]
+ gdb_continue_to_breakpoint "run to breakpoint"
+ gdb_test "info threads" "3 .* 2 .*\\\* 1.*" "info threads shows all threads"
+
+ gdb_test_no_output "set scheduler-locking on"
+
+ gdb_breakpoint [gdb_get_line_number "set breakpoint thread 3 here"]
+ gdb_breakpoint [gdb_get_line_number "set breakpoint thread 2 here"]
+
+ gdb_test "thread 3" "Switching.*"
+ gdb_continue_to_breakpoint "run to breakpoint in thread 3"
+ gdb_test "p *myp = 0" " = 0" "unbreak loop in thread 3"
+
+ gdb_test "thread 2" "Switching.*"
+ gdb_continue_to_breakpoint "run to breakpoint in thread 2"
+ gdb_test "p *myp = 0" " = 0" "unbreak loop in thread 2"
+
+ # Switch back to thread 1 and disable scheduler locking.
+ gdb_test "thread 1" "Switching.*"
+ gdb_test_no_output "set scheduler-locking off"
+
+ # Now all 3 threads are stopped for a breakpoint that needs to
+ # be stepped over before thread 1 is resumed.
+ }
+}
+
+setup "step"
+gdb_test "step" "in wait_threads .*"
+
+setup "next"
+gdb_test "set debug infrun 1" ".*"
+gdb_test "next" "pthread_join.*"
+
+setup "continue"
+gdb_breakpoint [gdb_get_line_number "EXIT_SUCCESS"]
+gdb_test "continue" "EXIT_SUCCESS.*"
diff --git a/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp b/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp
index bf5ea60..c46dad2 100644
--- a/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp
+++ b/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp
@@ -107,7 +107,7 @@ 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.*" \
+ ".*need to step-over.*resume \\(step=1.*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"]