fix skipping permanent breakpoints
authorPedro Alves <palves@redhat.com>
Wed, 12 Nov 2014 10:10:49 +0000 (10:10 +0000)
committerPedro Alves <palves@redhat.com>
Wed, 12 Nov 2014 10:39:00 +0000 (10:39 +0000)
commitaf48d08f97fa678571a42be35a8a77b61e36d7d7
tree37b1528627c562f4e573a258021fc3de92c7476e
parent1a853c5224e2b8fedfac6d029365522b83080b40
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.
gdb/ChangeLog
gdb/infrun.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/i386-bp_permanent.c [new file with mode: 0644]
gdb/testsuite/gdb.arch/i386-bp_permanent.exp
gdb/testsuite/gdb.base/bp-permanent.c [new file with mode: 0644]
gdb/testsuite/gdb.base/bp-permanent.exp [new file with mode: 0644]