From patchwork Tue Apr 7 12:49:28 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 6043 Received: (qmail 129522 invoked by alias); 7 Apr 2015 12:49:57 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 129454 invoked by uid 89); 7 Apr 2015 12:49:56 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.5 required=5.0 tests=AWL, BAYES_00, KAM_LAZY_DOMAIN_SECURITY, SPF_HELO_PASS, T_RP_MATCHES_RCVD autolearn=no version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Tue, 07 Apr 2015 12:49:54 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id t37Cnrqh023850 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Tue, 7 Apr 2015 08:49:53 -0400 Received: from brno.lan (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t37Cnowl022139 for ; Tue, 7 Apr 2015 08:49:52 -0400 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH v2 01/23] Fix gdb.base/sigstep.exp with displaced stepping on software single-step targets Date: Tue, 7 Apr 2015 13:49:28 +0100 Message-Id: <1428410990-28560-2-git-send-email-palves@redhat.com> In-Reply-To: <1428410990-28560-1-git-send-email-palves@redhat.com> References: <1428410990-28560-1-git-send-email-palves@redhat.com> 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 : movl $0x1,0x200943(%rip) # 0x601064 (gdb) bt #0 handler (sig=14) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.base/sigstep.c:35 #1 #2 0x0000000000400622 in _start () (gdb) FAIL: gdb.base/sigstep.exp: displaced=on: step on breakpoint, to handler: backtrace gdb/ChangeLog: 2015-04-07 Pedro Alves * 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-07 Pedro Alves * 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. --- gdb/infrun.c | 40 +++++++++++++++--- gdb/testsuite/gdb.base/sigstep.exp | 86 ++++++++++++++++++++++++++++---------- 2 files changed, 98 insertions(+), 28 deletions(-) diff --git a/gdb/infrun.c b/gdb/infrun.c index 607a6e4..a270ca9 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/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 + } } } }