+2014-11-12 Pedro Alves <palves@redhat.com>
+
+ * infrun.c (resume): Clear the thread's 'stepped_breakpoint' flag.
+ Rewrite stepping over a permanent breakpoint.
+ (thread_still_needs_step_over, proceed): Don't set
+ stepping_over_breakpoint for permanent breakpoints.
+ (handle_signal_stop): Don't clear stepped_breakpoint. Also pull
+ single-step breakpoints out of the target on hardware step
+ targets.
+ (process_event_stop_test): If stepping a permanent breakpoint
+ doesn't hit the step-resume breakpoint, delete the step-resume
+ breakpoint.
+ (switch_back_to_stepped_thread): Also check if the stepped thread
+ has advanced already on hardware step targets.
+ (currently_stepping): Return true if the thread stepped a
+ breakpoint.
+
2014-11-12 Pedro Alves <palves@redhat.com>
Mark locations as permanent, not the whole breakpoint.
applies, it's the callers intention that counts. */
const int entry_step = step;
+ tp->stepped_breakpoint = 0;
+
QUIT;
if (current_inferior ()->waiting_for_vfork_done)
breakpoints can't be removed. So we have to test for it here. */
if (breakpoint_here_p (aspace, pc) == permanent_breakpoint_here)
{
- gdbarch_skip_permanent_breakpoint (gdbarch, regcache);
+ if (sig != GDB_SIGNAL_0)
+ {
+ /* We have a signal to pass to the inferior. The resume
+ may, or may not take us to the signal handler. If this
+ is a step, we'll need to stop in the signal handler, if
+ there's one, (if the target supports stepping into
+ handlers), or in the next mainline instruction, if
+ there's no handler. If this is a continue, we need to be
+ sure to run the handler with all breakpoints inserted.
+ In all cases, set a breakpoint at the current address
+ (where the handler returns to), and once that breakpoint
+ is hit, resume skipping the permanent breakpoint. If
+ that breakpoint isn't hit, then we've stepped into the
+ signal handler (or hit some other event). We'll delete
+ the step-resume breakpoint then. */
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: resume: skipping permanent breakpoint, "
+ "deliver signal first\n");
+
+ clear_step_over_info ();
+ tp->control.trap_expected = 0;
+
+ if (tp->control.step_resume_breakpoint == NULL)
+ {
+ /* Set a "high-priority" step-resume, as we don't want
+ user breakpoints at PC to trigger (again) when this
+ hits. */
+ insert_hp_step_resume_breakpoint_at_frame (get_current_frame ());
+ gdb_assert (tp->control.step_resume_breakpoint->loc->permanent);
+
+ tp->step_after_step_resume_breakpoint = step;
+ }
+
+ insert_breakpoints ();
+ }
+ else
+ {
+ /* There's no signal to pass, we can go ahead and skip the
+ permanent breakpoint manually. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: resume: skipping permanent breakpoint\n");
+ gdbarch_skip_permanent_breakpoint (gdbarch, regcache);
+ /* Update pc to reflect the new address from which we will
+ execute instructions. */
+ pc = regcache_read_pc (regcache);
+
+ if (step)
+ {
+ /* We've already advanced the PC, so the stepping part
+ is done. Now we need to arrange for a trap to be
+ reported to handle_inferior_event. Set a breakpoint
+ at the current PC, and run to it. Don't update
+ prev_pc, because if we end in
+ switch_back_to_stepping, we want the "expected thread
+ advanced also" branch to be taken. IOW, we don't
+ want this thread to step further from PC
+ (overstep). */
+ insert_single_step_breakpoint (gdbarch, aspace, pc);
+ insert_breakpoints ();
+
+ tp->suspend.stop_signal = GDB_SIGNAL_0;
+ /* We're continuing with all breakpoints inserted. It's
+ safe to let the target bypass signals. */
+ target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass);
+ /* ... and safe to let other threads run, according to
+ schedlock. */
+ resume_ptid = user_visible_resume_ptid (entry_step);
+ target_resume (resume_ptid, 0, GDB_SIGNAL_0);
+ discard_cleanups (old_cleanups);
+ return;
+ }
+ }
}
/* If we have a breakpoint to step over, make sure to do a single
struct regcache *regcache = get_thread_regcache (tp->ptid);
if (breakpoint_here_p (get_regcache_aspace (regcache),
- regcache_read_pc (regcache)))
+ regcache_read_pc (regcache))
+ == ordinary_breakpoint_here)
return 1;
tp->stepping_over_breakpoint = 0;
if (addr == (CORE_ADDR) -1)
{
- if (pc == stop_pc && breakpoint_here_p (aspace, pc)
+ if (pc == stop_pc
+ && breakpoint_here_p (aspace, pc) == ordinary_breakpoint_here
&& execution_direction != EXEC_REVERSE)
/* There is a breakpoint at the address we will resume at,
step one instruction before inserting breakpoints so that
gdbarch = get_frame_arch (frame);
/* Pull the single step breakpoints out of the target. */
- if (gdbarch_software_single_step_p (gdbarch))
+ if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
{
- if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
- {
- struct regcache *regcache;
- struct address_space *aspace;
- CORE_ADDR pc;
+ struct regcache *regcache;
+ struct address_space *aspace;
+ CORE_ADDR pc;
- regcache = get_thread_regcache (ecs->ptid);
- aspace = get_regcache_aspace (regcache);
- pc = regcache_read_pc (regcache);
+ regcache = get_thread_regcache (ecs->ptid);
+ aspace = get_regcache_aspace (regcache);
+ pc = regcache_read_pc (regcache);
- /* However, before doing so, if this single-step breakpoint was
- actually for another thread, set this thread up for moving
- past it. */
- if (!thread_has_single_step_breakpoint_here (ecs->event_thread,
- aspace, pc))
- {
- if (single_step_breakpoint_inserted_here_p (aspace, pc))
- {
- if (debug_infrun)
- {
- fprintf_unfiltered (gdb_stdlog,
- "infrun: [%s] hit another thread's "
- "single-step breakpoint\n",
- target_pid_to_str (ecs->ptid));
- }
- ecs->hit_singlestep_breakpoint = 1;
- }
- }
- else
+ /* However, before doing so, if this single-step breakpoint was
+ actually for another thread, set this thread up for moving
+ past it. */
+ if (!thread_has_single_step_breakpoint_here (ecs->event_thread,
+ aspace, pc))
+ {
+ if (single_step_breakpoint_inserted_here_p (aspace, pc))
{
if (debug_infrun)
{
fprintf_unfiltered (gdb_stdlog,
- "infrun: [%s] hit its "
+ "infrun: [%s] hit another thread's "
"single-step breakpoint\n",
target_pid_to_str (ecs->ptid));
}
+ ecs->hit_singlestep_breakpoint = 1;
+ }
+ }
+ else
+ {
+ if (debug_infrun)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: [%s] hit its "
+ "single-step breakpoint\n",
+ target_pid_to_str (ecs->ptid));
}
}
-
- delete_just_stopped_threads_single_step_breakpoints ();
}
+ delete_just_stopped_threads_single_step_breakpoints ();
if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
&& ecs->event_thread->control.trap_expected
return;
}
- ecs->event_thread->stepped_breakpoint = 0;
ecs->event_thread->stepping_over_breakpoint = 0;
ecs->event_thread->stepping_over_watchpoint = 0;
bpstat_clear (&ecs->event_thread->control.stop_bpstat);
break;
}
+ /* If we stepped a permanent breakpoint and we had a high priority
+ step-resume breakpoint for the address we stepped, but we didn't
+ hit it, then we must have stepped into the signal handler. The
+ step-resume was only necessary to catch the case of _not_
+ stepping into the handler, so delete it, and fall through to
+ checking whether the step finished. */
+ if (ecs->event_thread->stepped_breakpoint)
+ {
+ struct breakpoint *sr_bp
+ = ecs->event_thread->control.step_resume_breakpoint;
+
+ if (sr_bp->loc->permanent
+ && sr_bp->type == bp_hp_step_resume
+ && sr_bp->loc->address == ecs->event_thread->prev_pc)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepped permanent breakpoint, stopped in "
+ "handler\n");
+ delete_step_resume_breakpoint (ecs->event_thread);
+ ecs->event_thread->step_after_step_resume_breakpoint = 0;
+ }
+ }
+
/* We come here if we hit a breakpoint but should not stop for it.
Possibly we also were stepping and should stop for that. So fall
through and test for stepping. But, if not stepping, do not
breakpoint forward, one instruction at a time,
overstepping. */
- if (gdbarch_software_single_step_p (gdbarch)
- && stop_pc != tp->prev_pc)
+ if (stop_pc != tp->prev_pc)
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
return ((tp->control.step_range_end
&& tp->control.step_resume_breakpoint == NULL)
|| tp->control.trap_expected
+ || tp->stepped_breakpoint
|| bpstat_should_step ());
}
+2014-11-12 Pedro Alves <palves@redhat.com>
+
+ * gdb.arch/i386-bp_permanent.c: New file.
+ * gdb.arch/i386-bp_permanent.exp: Don't skip on x86_64.
+ (srcfile): Set to i386-bp_permanent.c.
+ (top level): Adjust to work in both 32-bit and 64-bit modes. Test
+ that stepi does not execute the 'leave' instruction, instead of
+ testing it does execute.
+ * gdb.base/bp-permanent.c: New file.
+ * gdb.base/bp-permanent.exp: New file.
+
2014-11-10 Doug Evans <xdje42@gmail.com>
PR symtab/17564
--- /dev/null
+/* Copyright (C) 2003-2014 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#ifdef SYMBOL_PREFIX
+#define SYMBOL(str) SYMBOL_PREFIX #str
+#else
+#define SYMBOL(str) #str
+#endif
+
+int
+main (void)
+{
+ standard ();
+ return 0;
+}
+
+/* A normal prologue. */
+
+#ifdef __x86_64__
+asm(".text\n"
+ " .align 8\n"
+ SYMBOL (standard) ":\n"
+ " push %rbp\n"
+ " mov %rsp, %rbp\n"
+ " push %rdi\n"
+ " int3\n"
+ " leaveq\n"
+ " retq\n");
+
+#else
+
+asm(".text\n"
+ " .align 8\n"
+ " .global " SYMBOL(standard) "\n"
+ SYMBOL (standard) ":\n"
+ " pushl %ebp\n"
+ " movl %esp, %ebp\n"
+ " pushl %edi\n"
+ " int3\n"
+ " leave\n"
+ " ret\n");
+
+#endif
# Test stepping over permanent breakpoints on i386.
-if { ![is_x86_like_target] } then {
+if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } then {
verbose "Skipping skip over permanent breakpoint on i386 tests."
return
}
set testfile "i386-bp_permanent"
-set srcfile i386-prologue.c
+set srcfile i386-bp_permanent.c
set binfile ${objdir}/${subdir}/${testfile}
# some targets have leading underscores on assembly symbols.
return -1
}
-set function standard
+set function "standard"
set retcode [gdb_test_multiple "disassemble $function" "Disassemble function '$function'" {
- -re ".*($hex) <\\+0>.*($hex) <\\+4>.*($hex) <\\+5>.*($hex) <\\+6>.*$gdb_prompt $" {
- set function_start $expect_out(1,string)
- set address $expect_out(2,string)
- set address1 $expect_out(3,string)
- set address2 $expect_out(4,string)
+ -re "($hex) <\\+0>.*($hex) <\\+$decimal>.*int3.*($hex) <\\+$decimal>.*leave.*$gdb_prompt $" {
+ set address_bp $expect_out(2,string)
+ set address_after_bp $expect_out(3,string)
}
}]
return -1
}
-gdb_breakpoint "*$function_start"
+gdb_breakpoint "*$address_bp"
-gdb_breakpoint "*$address"
+gdb_test "continue" "Breakpoint .*, $address_bp in $function.*" \
+ "stop at permanent breakpoint"
-gdb_test "continue" "Breakpoint .*, $function_start in $function.*" \
- "Stop at the '$function' start breakpoint (fetching esp)."
-
-# We want to fetch esp at the start of '$function' function to make sure
-# skip_permanent_breakpoint implementation really skips only the perm.
-# breakpoint. If, for whatever reason, 'leave' instruction doesn't get
-# executed, esp will not have this value.
-set start_esp 0
-gdb_test_multiple "print \$esp" "Fetch esp value." {
+# We want to fetch the stack pointer at the start of '$function'
+# function to make sure the skip_permanent_breakpoint implementation
+# really skips only the permanent breakpoint. If, for whatever
+# reason, the 'leave' instruction executes, the stack pointer will not
+# have this value.
+set start_sp 0
+gdb_test_multiple "print \$sp" "fetch stack pointer value" {
-re "\\\$1.*($hex).*$gdb_prompt $" {
- set start_esp $expect_out(1,string)
+ set start_sp $expect_out(1,string)
}
}
-gdb_test "continue" "Breakpoint .*, $address in $function.*" \
- "Stop at permanent breakpoint."
-
-gdb_test "stepi" "$address1|$address2 in $function.*" \
- "Single stepping past permanent breakpoint."
-
-gdb_test "print \$esp" ".*$start_esp.*" \
- "ESP value does not match - step_permanent_breakpoint wrong."
+gdb_test "stepi" "$address_after_bp in $function.*" \
+ "single-step past permanent breakpoint"
+gdb_test "print \$sp" ".*$start_sp.*" \
+ "stack pointer value matches"
--- /dev/null
+/* Copyright (C) 2014 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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 <string.h>
+#ifdef SIGNALS
+#include <signal.h>
+#endif
+
+#define NOP asm("nop")
+
+/* Buffer holding the breakpoint instruction. */
+unsigned char buffer[16];
+
+volatile unsigned char *addr_bp;
+volatile unsigned char *addr_after_bp;
+int counter = 0;
+
+void
+test (void)
+{
+ NOP;
+ NOP;
+ NOP;
+ NOP; /* write permanent bp here */
+ NOP; /* after permanent bp */
+ NOP;
+ NOP;
+ NOP;
+ NOP;
+ NOP;
+
+ counter++;
+}
+
+void
+setup (void)
+{
+ memcpy (buffer, (void *) addr_bp, addr_after_bp - addr_bp);
+}
+
+void
+test_basics (void)
+{
+ test (); /* for SIGTRAP */
+ test (); /* for breakpoint once */
+ test (); /* for breakpoint twice */
+ test (); /* for disabled bp SIGTRAP */
+ test (); /* for breakpoint thrice */
+}
+
+void
+test_next (void)
+{
+ test (); /* for next */
+ counter = 0; /* after next */
+}
+
+#ifdef SIGNALS
+
+static void
+test_signal_handler (int sig)
+{
+}
+
+void
+test_signal_with_handler (void)
+{
+ signal (SIGUSR1, test_signal_handler);
+ test ();
+}
+
+void
+test_signal_no_handler (void)
+{
+ signal (SIGUSR1, SIG_IGN);
+ test ();
+}
+
+static void
+test_signal_nested_handler ()
+{
+ test ();
+}
+
+void
+test_signal_nested_done (void)
+{
+}
+
+void
+test_signal_nested (void)
+{
+ counter = 0;
+ signal (SIGALRM, test_signal_nested_handler);
+ alarm (1);
+ test ();
+ test_signal_nested_done ();
+}
+
+#endif
+
+int
+main (void)
+{
+ setup ();
+ test_basics ();
+ test_next ();
+#ifdef SIGNALS
+ test_signal_nested ();
+ test_signal_with_handler ();
+ test_signal_no_handler ();
+#endif
+ return 0;
+}
--- /dev/null
+# Copyright (C) 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/>.
+
+# This file is part of the gdb testsuite.
+
+# Test stepping over permanent breakpoints.
+
+standard_testfile
+
+set options { debug }
+if { ![target_info exists gdb,nosignals] } {
+ lappend options "additional_flags=-DSIGNALS"
+}
+
+if {[build_executable "failed to prepare" $testfile $srcfile $options]} {
+ return -1
+}
+
+set line_bp [gdb_get_line_number "write permanent bp"]
+
+# The test proper. ALWAYS_INSERTED indicates whether testing in
+# "breakpoint always-inserted" mode. If SW_WATCHPOINT is true, set a
+# software watchpoint, which forces constantly single-stepping, and
+# exercises stepping the permanent breakpoint while delivering a
+# signal at the same time.
+
+proc test {always_inserted sw_watchpoint} {
+ global line_bp
+ global hex decimal
+ global gdb_prompt
+ global srcfile binfile
+
+ clean_restart $binfile
+
+ if ![runto_main] then {
+ return -1
+ }
+
+ gdb_test "set breakpoint always-inserted $always_inserted"
+
+ if {$sw_watchpoint} {
+ # Watching a convenience variable forces a software
+ # watchpoint.
+ gdb_test "watch \$dummy_convenience" "Watchpoint .*"
+ }
+
+ set address_bp ""
+ set address_after_bp ""
+
+ with_test_prefix "setup" {
+
+ # Set a breakpoint where we'll manually plant a permanent
+ # breakpoint.
+ set test "set probe breakpoint"
+ gdb_test_multiple "break $line_bp" $test {
+ -re "Breakpoint .* at ($hex).*$gdb_prompt $" {
+ set address_bp $expect_out(1,string)
+ pass $test
+ }
+ }
+ if {$address_bp == ""} {
+ return
+ }
+
+ # Get the size of the instruction where the breakpoint will
+ # manually inserted.
+ set test "get size of instruction"
+ gdb_test_multiple "x/2i $address_bp" $test {
+ -re ".*$hex <test\\+$decimal>:\[^\r\n\]+\r\n\[ \]+($hex).*\.\r\n$gdb_prompt $" {
+ set address_after_bp $expect_out(1,string)
+ pass $test
+ }
+ }
+ if {$address_after_bp == ""} {
+ return
+ }
+
+ # Write address range where the breakpoint is inserted to the
+ # corresponding variables in the inferior.
+ gdb_test "p /x addr_bp = $address_bp" " = $address_bp" \
+ "write addr_bp"
+ gdb_test "p /x addr_after_bp = $address_after_bp" " = $address_after_bp" \
+ "write addr_after_bp"
+
+ # Run the "setup" function in the inferior. This memcpy's the
+ # breakpoint instruction to a buffer in the inferior.
+ gdb_test "next" "test.*" "next over setup"
+
+ delete_breakpoints
+
+ # We now have the breakpoint instruction stored in 'buffer'. Poke it
+ # to memory manually.
+ set count [expr $address_after_bp - $address_bp]
+ for {set i 0} {$i < $count} {incr i} {
+ gdb_test "p /x addr_bp\[$i\] = buffer\[$i\]" " = .*"
+ }
+ }
+
+ with_test_prefix "basics" {
+ # Run to the permanent breakpoint, just to make sure we've inserted it
+ # correctly.
+ gdb_test "continue" "Program received signal SIGTRAP.*" \
+ "permanent breakpoint causes random signal"
+
+ # Now set a breakpoint on top, thus creating a permanent breakpoint.
+ gdb_breakpoint "$line_bp"
+
+ # Depending on whether this is a decr_pc_after_break arch, the PC will
+ # be either pointing at the permanent breakpoint address, or just
+ # after. Set the GDB breakpoint on top, and continue, twice. At
+ # least once, GDB will need to step-over the permanent breakpoint.
+
+ gdb_test "continue" "Breakpoint .*" "stop at permanent breakpoint"
+
+ gdb_test "p \$prev_counter = counter" " = $decimal"
+
+ gdb_test "continue" "Breakpoint .*" "stop at permanent breakpoint twice"
+
+ # Check that indeed the continue made progress, instead of re-trapping
+ # without advancing.
+ gdb_test "p counter - \$prev_counter" " = 1"
+
+ gdb_test "info breakpoints" \
+ "breakpoint.*keep.*y.*$hex.*in test at .*$srcfile:$line_bp.*already hit 2 times.*" \
+ "info breakpoints show enabled breakpoint"
+
+ gdb_test "disable \$bpnum"
+
+ gdb_test "commands\nset \$commands_ran = 1\nend" "" \
+ "set breakpoint commands"
+
+ gdb_test "info breakpoints" \
+ "breakpoint.*keep.*n.*$hex.*in test at .*$srcfile:$line_bp.*already hit 2 times.*" \
+ "info breakpoints shows disabled breakpoint"
+
+ # Run to the permanent breakpoint again. This time, since it's
+ # disabled, it should act as if we hadn't created it in the first
+ # place. IOW, we should get a random signal, and, the breakpoint's
+ # command should not run.
+ gdb_test "continue" "Program received signal SIGTRAP.*" \
+ "disabled permanent breakpoint doesn't explain stop"
+
+ gdb_test "info breakpoints" \
+ "breakpoint.*keep.*n.*$hex.*in test at .*$srcfile:$line_bp.*already hit 2 times.*" \
+ "info breakpoints still shows same number of hits"
+
+ gdb_test "print \$commands_ran" " = void" \
+ "breakpoint commands didn't run"
+
+ # Reenable the breakpoint, and check that it gets hit and accounted
+ # for this time.
+ gdb_test "enable \$bpnum" "" "reenable breakpoint"
+
+ gdb_test "continue" "Breakpoint .*" \
+ "stop at permanent breakpoint thrice"
+
+ gdb_test "info breakpoints" \
+ "breakpoint.*keep.*y.*$hex.*in test at .*$srcfile:$line_bp.*already hit 3 times.*" \
+ "info breakpoints shows one more hit"
+
+ gdb_test "print \$commands_ran" " = 1" "breakpoint commands ran"
+
+ # Check that stepi advances only past the permanent breakpoint, and
+ # not a single instruction more.
+ gdb_test "stepi" "after permanent bp .*" \
+ "single-step past permanent breakpoint"
+ }
+
+ with_test_prefix "next trips on permanent bp" {
+ delete_breakpoints
+
+ gdb_breakpoint "test_next"
+ gdb_continue_to_breakpoint "test_next"
+
+ gdb_breakpoint "$line_bp"
+ gdb_test "condition \$bpnum 0"
+
+ gdb_test "next" "after next .*"
+ }
+
+ if ![target_info exists gdb,nosignals] {
+
+ with_test_prefix "continue trips on nested permanent bp" {
+ delete_breakpoints
+
+ gdb_breakpoint "test_signal_nested"
+ gdb_continue_to_breakpoint "test_signal_nested"
+
+ gdb_breakpoint "$line_bp"
+ gdb_continue_to_breakpoint "permanent bp"
+ gdb_test "condition \$bpnum 0"
+
+ # Let SIGALRM trigger.
+ sleep 2
+
+ # We're now stopped at a permanent breakpoint, with a
+ # signal pending.
+ gdb_breakpoint "test_signal_nested_done"
+ gdb_continue_to_breakpoint "test_signal_nested_done"
+
+ # Ensure that the handler did run. There's one call to
+ # test in the mainline code, and another in the signal
+ # handler.
+ gdb_test "p counter" " = 2"
+ }
+
+ if [can_single_step_to_signal_handler] {
+
+ with_test_prefix "stepi signal with handler" {
+ delete_breakpoints
+
+ gdb_breakpoint "test_signal_with_handler"
+ gdb_continue_to_breakpoint "test_signal_with_handler"
+
+ gdb_breakpoint "$line_bp"
+
+ gdb_test "continue" "Breakpoint .*" "stop at permanent breakpoint"
+
+ gdb_test "queue-signal SIGUSR1"
+
+ set test "single-step to handler"
+ gdb_test_multiple "stepi" $test {
+ -re "Program received signal SIGTRAP.*$gdb_prompt $" {
+ fail $test
+ }
+ -re "handler .*$gdb_prompt $" {
+ pass $test
+ }
+ }
+
+ # Check that the mainline PC points at the permanent
+ # breakpoint.
+ gdb_test "up 2" "test .*" "up to mainline code"
+
+ gdb_test "p /x \$pc" " = $address_bp" \
+ "mainline pc points at permanent breakpoint"
+
+ gdb_test "continue" "Breakpoint .*" \
+ "stop at permanent breakpoint, out of handler"
+ }
+
+ with_test_prefix "stepi signal with no handler" {
+ gdb_breakpoint "test_signal_no_handler"
+ gdb_continue_to_breakpoint "test_signal_no_handler"
+
+ gdb_test "continue" "Breakpoint .*" "stop at permanent breakpoint"
+
+ gdb_test "queue-signal SIGUSR1"
+
+ gdb_test "stepi" "after permanent bp .*" \
+ "single-step past permanent breakpoint"
+ }
+ }
+ }
+}
+
+foreach always_inserted {off on} {
+ foreach sw_watchpoint {0 1} {
+ with_test_prefix "always_inserted=$always_inserted, sw_watchpoint=$sw_watchpoint" {
+ test $always_inserted $sw_watchpoint
+ }
+ }
+}