From patchwork Tue Oct 14 17:48:30 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 3223 Received: (qmail 31440 invoked by alias); 14 Oct 2014 17:48:35 -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 31430 invoked by uid 89); 14 Oct 2014 17:48:34 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.0 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham 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, 14 Oct 2014 17:48:33 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s9EHmVnC007778 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Tue, 14 Oct 2014 13:48:32 -0400 Received: from brno.lan (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s9EHmUfv016761 for ; Tue, 14 Oct 2014 13:48:31 -0400 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH] stepi/nexti: skip signal handler if "handle nostop" signal arrives Date: Tue, 14 Oct 2014 18:48:30 +0100 Message-Id: <1413308910-30423-1-git-send-email-palves@redhat.com> I noticed that "si" behaves differently when a "handle nostop" signal arrives while the step is in progress, depending on whether the program was stopped at a breakpoint when "si" was entered. Specifically, in case GDB needs to step off a breakpoint, the handler is skipped and the program stops in the next "mainline" instruction. Otherwise, the "si" stops in the first instruction of the signal handler. I was surprised the testsuite doesn't catch this difference. Turns out gdb.base/sigstep.exp covers a bunch of cases related to stepping and signal handlers, but does not test stepi nor nexti, only step/next/continue. My first reaction was that stopping in the signal handler was the correct thing to do, as it's where the next user-visible instruction that is executed is. I considered then "nexti" -- a signal handler could be reasonably considered a subroutine call to step over, it'd seem intuitive to me that "nexti" would skip it. But then, I realized that signals that arrive while a plain/line "step" is in progress _also_ have their handler skipped. A user might well be excused for being confused by this, given: (gdb) help step Step program until it reaches a different source line. And the signal handler's sources will be in different source lines, after all. I think that having to explain that "stepi" steps into handlers, (and that "nexti" wouldn't according to my reasoning above), while "step" does, is a sign of an awkward interface. E.g., if a user truly is interested in stepping into signal handlers, then it's odd that she has to either force the signal to "handle stop", or recall to do "stepi" whenever such a signal might be delivered. For that use case, it'd seem nicer to me if "step" also stepped into handlers. This suggests to me that we either need a global "step-into-handlers" setting, or perhaps better, make "handle pass/nopass stop/nostop print/noprint" have have an additional axis - "handle stepinto/nostepinto", so that the user could configure whether handlers for specific signals should be stepped into. In any case, I think it's simpler (and thus better) for all step commands to behave the same. This commit thus makes "si/ni" skip handlers for "handle nostop" signals that arrive while the command was already in progress, like step/next do. To be clear, nothing changes if the program was stopped for a signal, and the user enters a stepping command _then_ -- GDB still steps into the handler. The change concerns signals that don't cause a stop and that arrive while the step is in progress. Tested on x86_64 Fedora 20, native and gdbserver. gdb/ 2014-10-14 Pedro Alves * infrun.c (handle_signal_stop): Also skip handlers when a random signal arrives while handling a "stepi" or a "nexti". Set the thread's 'step_after_step_resume_breakpoint' flag. gdb/doc/ 2014-10-14 Pedro Alves * gdb.texinfo (Signals): Clarify stepping and signal handlers. gdb/testsuite/ 2014-10-14 Pedro Alves * gdb.base/sigstep.c (dummy): New global. (main): Issue a couple writes to the new global. * gdb.base/sigstep.exp (get_next_pc, test_skip_handler): New procedures. (skip_over_handler): Use test_skip_handler. (top level): Call skip_over_handler for stepi and nexti too. (breakpoint_over_handler): Use test_skip_handler. (top level): Call breakpoint_over_handler for stepi and nexti too. --- gdb/doc/gdb.texinfo | 9 +++++++ gdb/infrun.c | 4 ++- gdb/testsuite/gdb.base/sigstep.c | 7 +++-- gdb/testsuite/gdb.base/sigstep.exp | 52 ++++++++++++++++++++++++++++---------- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 429c650..46c327e 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -5526,6 +5526,11 @@ Their full names are: @value{GDBN} should not stop your program when this signal happens. It may still print a message telling you that the signal has come in. +If this signal arrives while a stepping command (e.g., @code{step}) is +in progress, the signal's handler is skipped (though still executed if +@code{pass} is in effect; see below). @value{GDBN} will still stop +your program if the handler hits a breakpoint. + @item stop @value{GDBN} should stop your program when this signal happens. This implies the @code{print} keyword as well. @@ -5558,6 +5563,10 @@ after @value{GDBN} reports a signal, you can use the @code{handle} command with @code{pass} or @code{nopass} to control whether your program sees that signal when you continue. +If a stepping command is issued after the program stops for a signal, +and @code{pass} is in effect for that signal, @value{GDBN} steps into +the signal's handler (if the target supports it). + The default is set to @code{nostop}, @code{noprint}, @code{pass} for non-erroneous signals such as @code{SIGALRM}, @code{SIGWINCH} and @code{SIGCHLD}, and to @code{stop}, @code{print}, @code{pass} for the diff --git a/gdb/infrun.c b/gdb/infrun.c index d61cc12..3682765 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -4455,7 +4455,8 @@ handle_signal_stop (struct execution_control_state *ecs) if (ecs->event_thread->control.step_range_end != 0 && ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_0 - && pc_in_thread_step_range (stop_pc, ecs->event_thread) + && (pc_in_thread_step_range (stop_pc, ecs->event_thread) + || ecs->event_thread->control.step_range_end == 1) && frame_id_eq (get_stack_frame_id (frame), ecs->event_thread->control.step_stack_frame_id) && ecs->event_thread->control.step_resume_breakpoint == NULL) @@ -4475,6 +4476,7 @@ handle_signal_stop (struct execution_control_state *ecs) "single-step range\n"); insert_hp_step_resume_breakpoint_at_frame (frame); + 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); diff --git a/gdb/testsuite/gdb.base/sigstep.c b/gdb/testsuite/gdb.base/sigstep.c index aa2384a..25a4647 100644 --- a/gdb/testsuite/gdb.base/sigstep.c +++ b/gdb/testsuite/gdb.base/sigstep.c @@ -24,6 +24,7 @@ #include static volatile int done; +static volatile int dummy; static void handler (int sig) @@ -74,8 +75,10 @@ main () return 1; } } - /* Wait. */ - while (!done); + /* Wait. Issue a couple writes to a dummy volatile var to be + reasonably sure our simple "get-next-pc" logic doesn't + stumble on branches. */ + dummy = 0; dummy = 1; while (!done); done = 0; } return 0; diff --git a/gdb/testsuite/gdb.base/sigstep.exp b/gdb/testsuite/gdb.base/sigstep.exp index 184d46e..b589e12 100644 --- a/gdb/testsuite/gdb.base/sigstep.exp +++ b/gdb/testsuite/gdb.base/sigstep.exp @@ -269,9 +269,35 @@ proc skip_to_handler_entry { i } { gdb_test "clear *handler" ".*" "$prefix; clear handler" } -skip_to_handler_entry step -skip_to_handler_entry next -skip_to_handler_entry continue +foreach cmd {stepi nexti step next continue} { + skip_to_handler_entry $cmd +} + +# Get the address of where a single-step should land. +proc get_next_pc {test} { + global gdb_prompt + global hex + + set next "" + gdb_test_multiple "x/2i \$pc" $test { + -re "$hex .*:\[^\r\n\]+\r\n\[ \]+($hex).*\.\r\n$gdb_prompt $" { + set next $expect_out(1,string) + pass $test + } + } + + return $next +} + +proc test_skip_handler {prefix i} { + if {$i == "stepi" || $i == "nexti"} { + set next_pc [get_next_pc "$prefix; get next PC"] + gdb_test "$i" "dummy = 0.*" "$prefix; performing $i" + gdb_test "p /x \$pc" " = $next_pc" "$prefix; advanced" + } else { + gdb_test "$i" "done = 0.*" "$prefix; performing $i" + } +} # Try stepping when there's a signal pending but no breakpoints. # Should skip the handler advancing to the next line. @@ -295,13 +321,13 @@ proc skip_over_handler { i } { # Make the signal pending sleep 1 - - gdb_test "$i" "done = 0.*" "$prefix; performing $i" + + test_skip_handler $prefix $i } -skip_over_handler step -skip_over_handler next -skip_over_handler continue +foreach cmd {stepi nexti step next continue} { + skip_over_handler $cmd +} # Try stepping when there's a signal pending, a pre-existing # breakpoint at the current instruction, and a breakpoint in the @@ -385,7 +411,7 @@ breakpoint_to_handler_entry continue # 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. +# handler. Should advance to the next line/instruction. proc breakpoint_over_handler { i } { global gdb_prompt @@ -409,10 +435,10 @@ proc breakpoint_over_handler { i } { # Make the signal pending sleep 1 - gdb_test "$i" "done = 0.*" "$prefix; performing $i" + test_skip_handler $prefix $i gdb_test "clear $infinite_loop" ".*" "$prefix; clear infinite loop" } -breakpoint_over_handler step -breakpoint_over_handler next -breakpoint_over_handler continue +foreach cmd {stepi nexti step next continue} { + breakpoint_over_handler $cmd +}