aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2015-04-10 10:55:09 +0100
committerPedro Alves <palves@redhat.com>2015-04-10 10:55:09 +0100
commit8f572e5c0f71fd6641768985855b2e0955a78f51 (patch)
tree27be9a1fc92573ff956307a59b8aa8f5bc6c28d4
parentd07a1b059d790af3da88b9c750925d5a8db51250 (diff)
downloadgdb-8f572e5c0f71fd6641768985855b2e0955a78f51.zip
gdb-8f572e5c0f71fd6641768985855b2e0955a78f51.tar.gz
gdb-8f572e5c0f71fd6641768985855b2e0955a78f51.tar.bz2
Fix gdb.base/sigstep.exp with displaced stepping on software single-step targets
TL;DR: When stepping over a breakpoint with displaced stepping, the core must be notified of all signals, otherwise the displaced step fixup code confuses a breakpoint trap in the signal handler for the expected trap indicating the displaced instruction was single-stepped normally/successfully. Detailed version: Running sigstep.exp with displaced stepping on, against my x86 software single-step branch, I got: FAIL: gdb.base/sigstep.exp: step on breakpoint, to handler: performing step FAIL: gdb.base/sigstep.exp: next on breakpoint, to handler: performing next FAIL: gdb.base/sigstep.exp: continue on breakpoint, to handler: performing continue Turning on debug logs, we see: (gdb) step infrun: clear_proceed_status_thread (process 32147) infrun: proceed (addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT) infrun: resume (step=1, signal=GDB_SIGNAL_0), trap_expected=1, current thread [process 32147] at 0x400842 displaced: stepping process 32147 now displaced: saved 0x400622: 49 89 d1 5e 48 89 e2 48 83 e4 f0 50 54 49 c7 c0 displaced: %rip-relative addressing used. displaced: using temp reg 2, old value 0x3615eafd37, new value 0x40084c displaced: copy 0x400842->0x400622: c7 81 1c 08 20 00 00 00 00 00 displaced: displaced pc to 0x400622 displaced: run 0x400622: c7 81 1c 08 LLR: Preparing to resume process 32147, 0, inferior_ptid process 32147 LLR: PTRACE_CONT process 32147, 0 (resume event thread) linux_nat_wait: [process -1], [TARGET_WNOHANG] LLW: enter LNW: waitpid(-1, ...) returned 32147, No child processes LLW: waitpid 32147 received Alarm clock (stopped) LLW: PTRACE_CONT process 32147, Alarm clock (preempt 'handle') LNW: waitpid(-1, ...) returned 0, No child processes LLW: exit (ignore) sigchld infrun: target_wait (-1.0.0, status) = infrun: -1.0.0 [process -1], infrun: status->kind = ignore infrun: TARGET_WAITKIND_IGNORE infrun: prepare_to_wait linux_nat_wait: [process -1], [TARGET_WNOHANG] LLW: enter LNW: waitpid(-1, ...) returned 32147, No child processes LLW: waitpid 32147 received Trace/breakpoint trap (stopped) CSBB: process 32147 stopped by software breakpoint LNW: waitpid(-1, ...) returned 0, No child processes LLW: trap ptid is process 32147. LLW: exit infrun: target_wait (-1.0.0, status) = infrun: 32147.32147.0 [process 32147], infrun: status->kind = stopped, signal = GDB_SIGNAL_TRAP infrun: TARGET_WAITKIND_STOPPED displaced: restored process 32147 0x400622 displaced: fixup (0x400842, 0x400622), insn = 0xc7 0x81 ... displaced: restoring reg 2 to 0x3615eafd37 displaced: relocated %rip from 0x400717 to 0x400937 infrun: stop_pc = 0x400937 infrun: delayed software breakpoint trap, ignoring infrun: no line number info infrun: stop_waiting 0x0000000000400937 in __dso_handle () 1: x/i $pc => 0x400937: and %ah,0xa0d64(%rip) # 0x4a16a1 (gdb) FAIL: gdb.base/sigstep.exp: displaced=on: step on breakpoint, to handler: performing step What should have happened is that the breakpoint hit in the signal handler should have been presented to the user. But note that "preempt 'handle'" -- what happened instead is that displaced_step_fixup confused the breakpoint in the signal handler for the expected SIGTRAP indicating the displaced instruction was single-stepped normally/successfully. This should be affecting all software single-step targets in the same way. The fix is to make sure the core sees all signals when displaced stepping, just like we already must see all signals when doing an stepping over a breakpoint in-line. We now get: infrun: target_wait (-1.0.0, status) = infrun: 570.570.0 [process 570], infrun: status->kind = stopped, signal = GDB_SIGNAL_ALRM infrun: TARGET_WAITKIND_STOPPED displaced: restored process 570 0x400622 infrun: stop_pc = 0x400842 infrun: random signal (GDB_SIGNAL_ALRM) infrun: signal arrived while stepping over breakpoint infrun: inserting step-resume breakpoint at 0x400842 infrun: resume (step=0, signal=GDB_SIGNAL_ALRM), trap_expected=0, current thread [process 570] at 0x400842 LLR: Preparing to resume process 570, Alarm clock, inferior_ptid process 570 LLR: PTRACE_CONT process 570, Alarm clock (resume event thread) infrun: prepare_to_wait linux_nat_wait: [process -1], [TARGET_WNOHANG] LLW: enter LNW: waitpid(-1, ...) returned 0, No child processes LLW: exit (ignore) infrun: target_wait (-1.0.0, status) = infrun: -1.0.0 [process -1], infrun: status->kind = ignore sigchld infrun: TARGET_WAITKIND_IGNORE infrun: prepare_to_wait linux_nat_wait: [process -1], [TARGET_WNOHANG] LLW: enter LNW: waitpid(-1, ...) returned 570, No child processes LLW: waitpid 570 received Trace/breakpoint trap (stopped) CSBB: process 570 stopped by software breakpoint LNW: waitpid(-1, ...) returned 0, No child processes LLW: trap ptid is process 570. LLW: exit infrun: target_wait (-1.0.0, status) = infrun: 570.570.0 [process 570], infrun: status->kind = stopped, signal = GDB_SIGNAL_TRAP infrun: TARGET_WAITKIND_STOPPED infrun: stop_pc = 0x400717 infrun: BPSTAT_WHAT_STOP_NOISY infrun: stop_waiting Breakpoint 3, handler (sig=14) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/sigstep.c:35 35 done = 1; Hardware single-step targets already behave this way, because the Linux backends (both native and gdbserver) always report signals to the core if the thread was single-stepping. As mentioned in the new comment in do_target_resume, we can't fix this by instead making the displaced_step_fixup phase skip fixing up the PC if the single step stopped somewhere we didn't expect. Here's what the backtrace would look like if we did that: Breakpoint 3, handler (sig=14) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/sigstep.c:35 35 done = 1; 1: x/i $pc => 0x400717 <handler+7>: movl $0x1,0x200943(%rip) # 0x601064 <done> (gdb) bt #0 handler (sig=14) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/sigstep.c:35 #1 <signal handler called> #2 0x0000000000400622 in _start () (gdb) FAIL: gdb.base/sigstep.exp: displaced=on: step on breakpoint, to handler: backtrace gdb/ChangeLog: 2015-04-10 Pedro Alves <palves@redhat.com> * infrun.c (displaced_step_in_progress): New function. (do_target_resume): Advise target to report all signals if displaced stepping. gdb/testsuite/ChangeLog: 2015-04-10 Pedro Alves <palves@redhat.com> * gdb.base/sigstep.exp (breakpoint_to_handler) (breakpoint_to_handler_entry): New parameter 'displaced'. Use it. Test "backtrace" in handler. (breakpoint_over_handler): New parameter 'displaced'. Use it. (top level): Add new "displaced" test axis to breakpoint_to_handler, breakpoint_to_handler_entry and breakpoint_over_handler.
-rw-r--r--gdb/ChangeLog6
-rw-r--r--gdb/infrun.c40
-rw-r--r--gdb/testsuite/ChangeLog10
-rw-r--r--gdb/testsuite/gdb.base/sigstep.exp86
4 files changed, 114 insertions, 28 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 83f9df6..3de085a 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,11 @@
2015-04-10 Pedro Alves <palves@redhat.com>
+ * infrun.c (displaced_step_in_progress): New function.
+ (do_target_resume): Advise target to report all signals if
+ displaced stepping.
+
+2015-04-10 Pedro Alves <palves@redhat.com>
+
PR gdb/18216
* infrun.c (process_event_stop_test): Don't assume a step-resume
is set if tp->stepped_breakpoint is true.
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 3fcc55f..c7567c6 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1465,6 +1465,20 @@ get_displaced_stepping_state (int pid)
return NULL;
}
+/* Return true if process PID has a thread doing a displaced step. */
+
+static int
+displaced_step_in_progress (int pid)
+{
+ struct displaced_step_inferior_state *displaced;
+
+ displaced = get_displaced_stepping_state (pid);
+ if (displaced != NULL && !ptid_equal (displaced->step_ptid, null_ptid))
+ return 1;
+
+ return 0;
+}
+
/* Add a new displaced stepping state for process PID to the displaced
stepping state list, or return a pointer to an already existing
entry, if it already exists. Never returns NULL. */
@@ -2047,11 +2061,27 @@ do_target_resume (ptid_t resume_ptid, int step, enum gdb_signal sig)
happens to apply to another thread. */
tp->suspend.stop_signal = GDB_SIGNAL_0;
- /* Advise target which signals may be handled silently. If we have
- removed breakpoints because we are stepping over one (in any
- thread), we need to receive all signals to avoid accidentally
- skipping a breakpoint during execution of a signal handler. */
- if (step_over_info_valid_p ())
+ /* Advise target which signals may be handled silently.
+
+ If we have removed breakpoints because we are stepping over one
+ in-line (in any thread), we need to receive all signals to avoid
+ accidentally skipping a breakpoint during execution of a signal
+ handler.
+
+ Likewise if we're displaced stepping, otherwise a trap for a
+ breakpoint in a signal handler might be confused with the
+ displaced step finishing. We don't make the displaced_step_fixup
+ step distinguish the cases instead, because:
+
+ - a backtrace while stopped in the signal handler would show the
+ scratch pad as frame older than the signal handler, instead of
+ the real mainline code.
+
+ - when the thread is later resumed, the signal handler would
+ return to the scratch pad area, which would no longer be
+ valid. */
+ if (step_over_info_valid_p ()
+ || displaced_step_in_progress (ptid_get_pid (tp->ptid)))
target_pass_signals (0, NULL);
else
target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass);
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index c47c6ac..af6104a 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,15 @@
2015-04-10 Pedro Alves <palves@redhat.com>
+ * gdb.base/sigstep.exp (breakpoint_to_handler)
+ (breakpoint_to_handler_entry): New parameter 'displaced'. Use it.
+ Test "backtrace" in handler.
+ (breakpoint_over_handler): New parameter 'displaced'. Use it.
+ (top level): Add new "displaced" test axis to
+ breakpoint_to_handler, breakpoint_to_handler_entry and
+ breakpoint_over_handler.
+
+2015-04-10 Pedro Alves <palves@redhat.com>
+
PR gdb/18216
* gdb.threads/multiple-step-overs.exp: Remove expected eof.
diff --git a/gdb/testsuite/gdb.base/sigstep.exp b/gdb/testsuite/gdb.base/sigstep.exp
index c4f7e91..3c9454c 100644
--- a/gdb/testsuite/gdb.base/sigstep.exp
+++ b/gdb/testsuite/gdb.base/sigstep.exp
@@ -409,13 +409,19 @@ foreach cmd {"stepi" "nexti" "step" "next" "continue"} {
# Try stepping when there's a signal pending, a pre-existing
# breakpoint at the current instruction, and a breakpoint in the
-# handler. Should advance to the signal handler.
+# handler. Should advance to the signal handler. DISPLACED indicates
+# whether to try with or without displaced stepping (to exercise the
+# different techniques of stepping over the breakpoint at the current
+# instruction).
-proc breakpoint_to_handler { cmd } {
+proc breakpoint_to_handler { displaced cmd } {
global infinite_loop
- with_test_prefix "$cmd on breakpoint, to handler" {
+ with_test_prefix "displaced=$displaced: $cmd on breakpoint, to handler" {
restart
+
+ gdb_test_no_output "set displaced-stepping $displaced"
+
# Use the real-time itimer, as otherwize the process never gets
# enough time to expire the timer.
gdb_test_no_output "set itimer = itimer_real"
@@ -430,11 +436,21 @@ proc breakpoint_to_handler { cmd } {
sleep 1
gdb_test "$cmd" " handler .*" "performing $cmd"
+
+ # Make sure we the displaced stepping scratch pad isn't in the
+ # backtrace.
+ gdb_test_sequence "bt" "backtrace" {
+ "\[\r\n\]+.0 \[^\r\n\]* handler "
+ "\[\r\n\]+.1 .signal handler called."
+ "\[\r\n\]+.2 \[^\r\n\]* main "
+ }
}
}
-foreach cmd {"step" "next" "continue"} {
- breakpoint_to_handler $cmd
+foreach displaced {"off" "on"} {
+ foreach cmd {"step" "next" "continue"} {
+ breakpoint_to_handler $displaced $cmd
+ }
}
# Try stepping when there's a signal pending, and a breakpoint at the
@@ -449,11 +465,17 @@ foreach cmd {"step" "next" "continue"} {
# have been called by the trampoline code. This test checks that it
# is possible to stop the inferior, even at that first instruction.
-proc breakpoint_to_handler_entry { cmd } {
+# DISPLACED indicates whether to try with or without displaced
+# stepping (to exercise the different techniques of stepping over the
+# breakpoint at the current instruction).
+proc breakpoint_to_handler_entry { displaced cmd } {
global infinite_loop
- with_test_prefix "$cmd on breakpoint, to handler entry" {
+ with_test_prefix "displaced=$displaced: $cmd on breakpoint, to handler entry" {
restart
+
+ gdb_test_no_output "set displaced-stepping $displaced"
+
# Use the real-time itimer, as otherwize the process never gets
# enough time to expire the timer.
gdb_test_no_output "set itimer = itimer_real"
@@ -468,24 +490,37 @@ proc breakpoint_to_handler_entry { cmd } {
sleep 1
gdb_test "$cmd" " handler .*" "performing $cmd"
+
+ # Make sure we the displaced stepping scratch pad isn't in the
+ # backtrace.
+ gdb_test_sequence "bt" "backtrace" {
+ "\[\r\n\]+.0 \[^\r\n\]* handler "
+ "\[\r\n\]+.1 .signal handler called."
+ "\[\r\n\]+.2 \[^\r\n\]* main "
+ }
}
}
-foreach cmd {"step" "next" "continue"} {
- breakpoint_to_handler_entry $cmd
+foreach displaced {"off" "on"} {
+ foreach cmd {"step" "next" "continue"} {
+ breakpoint_to_handler_entry $displaced $cmd
+ }
}
# Try stepping when there's a signal pending, and a pre-existing
# breakpoint at the current instruction, and no breakpoint in the
-# handler. Should advance to the next line/instruction. If SW_WATCH
-# is true, set a software watchpoint, which exercises stepping the
-# breakpoint instruction while delivering a signal at the same time.
-# If NO_HANDLER, arrange for the signal's handler be SIG_IGN, thus
-# when the software watchpoint is also set, testing stepping a
-# breakpoint instruction and immediately triggering the breakpoint
-# (exercises adjust_pc_after_break logic).
-
-proc breakpoint_over_handler { cmd with_sw_watch no_handler } {
+# handler. Should advance to the next line/instruction. DISPLACED
+# indicates whether to try with or without displaced stepping (to
+# exercise the different techniques of stepping over the breakpoint at
+# the current instruction). If SW_WATCH is true, set a software
+# watchpoint, which exercises stepping the breakpoint instruction
+# while delivering a signal at the same time. If NO_HANDLER, arrange
+# for the signal's handler be SIG_IGN, thus when the software
+# watchpoint is also set, testing stepping a breakpoint instruction
+# and immediately triggering the breakpoint (exercises
+# adjust_pc_after_break logic).
+
+proc breakpoint_over_handler { displaced cmd with_sw_watch no_handler } {
global infinite_loop
global clear_done
@@ -497,8 +532,11 @@ proc breakpoint_over_handler { cmd with_sw_watch no_handler } {
append prefix ", no handler"
}
- with_test_prefix "$prefix" {
+ with_test_prefix "displaced=$displaced: $prefix" {
restart
+
+ gdb_test_no_output "set displaced-stepping $displaced"
+
# Use the real-time itimer, as otherwize the process never gets
# enough time to expire the timer.
gdb_test_no_output "set itimer = itimer_real"
@@ -534,10 +572,12 @@ proc breakpoint_over_handler { cmd with_sw_watch no_handler } {
}
}
-foreach cmd {"stepi" "nexti" "step" "next" "continue"} {
- foreach with_sw_watch {0 1} {
- foreach no_handler {0 1} {
- breakpoint_over_handler $cmd $with_sw_watch $no_handler
+foreach displaced {"off" "on"} {
+ foreach cmd {"stepi" "nexti" "step" "next" "continue"} {
+ foreach with_sw_watch {0 1} {
+ foreach no_handler {0 1} {
+ breakpoint_over_handler $displaced $cmd $with_sw_watch $no_handler
+ }
}
}
}