diff options
author | Pedro Alves <palves@redhat.com> | 2014-11-12 10:10:49 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2014-11-12 10:39:00 +0000 |
commit | af48d08f97fa678571a42be35a8a77b61e36d7d7 (patch) | |
tree | 37b1528627c562f4e573a258021fc3de92c7476e /gdb/testsuite/gdb.base/bp-permanent.c | |
parent | 1a853c5224e2b8fedfac6d029365522b83080b40 (diff) | |
download | gdb-af48d08f97fa678571a42be35a8a77b61e36d7d7.zip gdb-af48d08f97fa678571a42be35a8a77b61e36d7d7.tar.gz gdb-af48d08f97fa678571a42be35a8a77b61e36d7d7.tar.bz2 |
fix skipping permanent breakpoints
The gdb.arch/i386-bp_permanent.exp test is currently failing an
assertion recently added:
(gdb) stepi
../../src/gdb/infrun.c:2237: internal-error: resume: Assertion `sig != GDB_SIGNAL_0' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n)
FAIL: gdb.arch/i386-bp_permanent.exp: Single stepping past permanent breakpoint. (GDB internal error)
The assertion expects that the only reason we currently need to step a
breakpoint instruction is when we have a signal to deliver. But when
stepping a permanent breakpoint (with or without a signal) we also
reach this code.
The assertion is correct and the permanent breakpoints skipping code
is wrong.
Consider the case of the user doing "step/stepi" when stopped at a
permanent breakpoint. GDB's `resume' calls the
gdbarch_skip_permanent_breakpoint hook and then happily continues
stepping:
/* Normally, by the time we reach `resume', the breakpoints are either
removed or inserted, as appropriate. The exception is if we're sitting
at a permanent breakpoint; we need to step over it, but permanent
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);
}
But since gdbarch_skip_permanent_breakpoint already advanced the PC
manually, this ends up executing the instruction that is _after_ the
breakpoint instruction. The user-visible result is that a single-step
steps two instructions.
The gdb.arch/i386-bp_permanent.exp test is actually ensuring that
that's indeed how things work. It runs to an int3 instruction, does
"stepi", and checks that "leave" was executed with that "stepi". Like
this:
(gdb) b *0x0804848c
Breakpoint 2 at 0x804848c
(gdb) c
Continuing.
Breakpoint 2, 0x0804848c in standard ()
(gdb) disassemble
Dump of assembler code for function standard:
0x08048488 <+0>: push %ebp
0x08048489 <+1>: mov %esp,%ebp
0x0804848b <+3>: push %edi
=> 0x0804848c <+4>: int3
0x0804848d <+5>: leave
0x0804848e <+6>: ret
0x0804848f <+7>: nop
(gdb) si
0x0804848e in standard ()
(gdb) disassemble
Dump of assembler code for function standard:
0x08048488 <+0>: push %ebp
0x08048489 <+1>: mov %esp,%ebp
0x0804848b <+3>: push %edi
0x0804848c <+4>: int3
0x0804848d <+5>: leave
=> 0x0804848e <+6>: ret
0x0804848f <+7>: nop
End of assembler dump.
(gdb)
One would instead expect that a stepi at 0x0804848c stops at
0x0804848d, _before_ the "leave" is executed. This commit changes GDB
this way. Care is taken to make stepping into a signal handler when
the step starts at a permanent breakpoint instruction work correctly.
The patch adjusts gdb.arch/i386-bp_permanent.exp in this direction,
and also makes it work on x86_64 (currently it only works on i*86).
The patch also adds a new gdb.base/bp-permanent.exp test that
exercises many different code paths related to stepping permanent
breakpoints, including the stepping with signals cases. The test uses
"hack/trick" to make it work on all (or most) platforms -- it doesn't
really hard code a breakpoint instruction.
Tested on x86_64 Fedora 20, native and gdbserver.
gdb/
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.
gdb/testsuite/
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.
Diffstat (limited to 'gdb/testsuite/gdb.base/bp-permanent.c')
-rw-r--r-- | gdb/testsuite/gdb.base/bp-permanent.c | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.base/bp-permanent.c b/gdb/testsuite/gdb.base/bp-permanent.c new file mode 100644 index 0000000..53b3777 --- /dev/null +++ b/gdb/testsuite/gdb.base/bp-permanent.c @@ -0,0 +1,128 @@ +/* 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; +} |