old-cross-binutils/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp
Pedro Alves 99619beac6 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.
2014-03-20 13:43:28 +00:00

123 lines
3.7 KiB
Text

# 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"]
if { $testpid == -1 } {
return -1
}
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" \
".*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"]
# 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
}