This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH 0/8] Break at each iteration for breakpoints placed on a while statement
- From: Kevin Buettner <kevinb at redhat dot com>
- To: gdb-patches at sourceware dot org
- Date: Mon, 18 Jan 2016 09:48:00 -0700
- Subject: Re: [PATCH 0/8] Break at each iteration for breakpoints placed on a while statement
- Authentication-results: sourceware.org; auth=none
- References: <20150818235334 dot 1afb0c85 at pinnacle dot lan>
Ping.
On Tue, 18 Aug 2015 23:53:34 -0700
Kevin Buettner <kevinb@redhat.com> wrote:
> This patch set changes the current behavior of breakpoints placed on
> while loops. (It actually restores past behavior; see below.)
>
> Consider the following code:
>
> 7 v = 0;
> 8
> 9 while (v < 3) /* Loop 1 condition */
> 10 {
> 11 v++; /* Loop 1 increment */
> 12 }
>
> This example is taken from the new test case, loop-break.c.
>
> Let's place breakpoints at lines 9 and 11:
>
> (gdb) b 9
> Breakpoint 1 at 0x4005af: file gdb.base/loop-break.c, line 9.
> (gdb) b 11
> Breakpoint 2 at 0x4005a0: file gdb.base/loop-break.c, line 11.
>
> We'll run the program and then continue to get to breakpoint #2:
>
> (gdb) run
> Starting program: gdb.base/loop-break
>
> Breakpoint 1, loop_test ()
> at gdb.base/loop-break.c:9
> 9 while (v < 3) /* Loop 1 condition */
> (gdb) c
> Continuing.
>
> Breakpoint 2, loop_test ()
> at gdb.base/loop-break.c:11
> 11 v++; /* Loop 1 increment */
>
> So far, so good. Now, watch what happens when we continue again:
>
> (gdb) c
> Continuing.
>
> Breakpoint 2, loop_test ()
> at /ironwood1/sourceware-git/mesquite-native-5509943/bld/../../binutils-gdb/gdb/testsuite/gdb.base/loop-break.c:11
> 11 v++; /* Loop 1 increment */
>
> GDB has somehow missed the breakpoint placed on line 9. The user is
> unable to examine state prior to evaluation of the loop condition.
>
> The compiler is implementing this looping construct in the following
> fashion. An unconditional branch is placed at the start of the loop,
> branching to an address immediately after the loop body. At that point,
> the condition is evaluated. If the condition evaluates as true, a
> conditional branch causes execution to continue at the first instruction
> of the loop body, placed immediately after the unconditional branch.
>
> GDB is placing its breakpoint on the unconditional branch. This is fine
> for the first iteration of the loop, but does not work for subsequent
> iterations.
>
> This is the code that gcc generates on x86_64:
>
> 0x000000000040059e <loop_test+14>: jmp 0x4005af <loop_test+31>
> 0x00000000004005a0 <loop_test+16>: mov 0x200a8a(%rip),%eax # 0x601030 <v>
> 0x00000000004005a6 <loop_test+22>: add $0x1,%eax
> 0x00000000004005a9 <loop_test+25>: mov %eax,0x200a81(%rip) # 0x601030 <v>
> 0x00000000004005af <loop_test+31>: mov 0x200a7b(%rip),%eax # 0x601030 <v>
> 0x00000000004005b5 <loop_test+37>: cmp $0x2,%eax
> 0x00000000004005b8 <loop_test+40>: jle 0x4005a0 <loop_test+16>
>
> The breakpoint is being placed on 0x40059e (loop_test+14). As such, it
> gets hit only once. If we could arrange to have the breakpoint placed at
> the branch target, it would stop at each iteration of the loop. I.e.
> it would behave like this:
>
> (gdb) b 9
> Breakpoint 1 at 0x4005af: file gdb.base/loop-break.c, line 9.
> (gdb) b 11
> Breakpoint 2 at 0x4005a0: file gdb.base/loop-break.c, line 11.
> (gdb) command 1
> Type commands for breakpoint(s) 1, one per line.
> End with a line saying just "end".
> >p v
> >end
> (gdb) run
> Starting program: gdb.base/loop-break
>
> Breakpoint 1, loop_test ()
> at gdb.base/loop-break.c:9
> 9 while (v < 3) /* Loop 1 condition */
> $1 = 0
> (gdb) c
> Continuing.
>
> Breakpoint 2, loop_test ()
> at gdb.base/loop-break.c:11
> 11 v++; /* Loop 1 increment */
> (gdb) c
> Continuing.
>
> Breakpoint 1, loop_test ()
> at gdb.base/loop-break.c:9
> 9 while (v < 3) /* Loop 1 condition */
> $2 = 1
>
> This change introduces a new gdbarch method for potentially following
> an unconditional branch. If the circumstances are right, it uses
> this method to cause the breakpoint to be placed on the branch target
> instead of the branch itself.
>
> This fixes the problem described above and causes no regressions.
> The new test case includes the loop shown above. It also contains
> several other loops which verify that GDB stops at the the correct
> places in each. Only while loops of the form shown above are
> affected by this patch; other looping constructs continue to work
> as they had before.
>
> Of particular interest is the test which uses an explicit goto; this
> mimics the code that the compiler generates for a while loop.
> However, in this example, placing a breakpoint on the goto should
> cause execution to stop on the goto. My initial attempt at a fix
> for this problem caused that explicit goto to be followed, which is
> definitely not correct.
>
> Lastly, I'll note that, in the distant past, GCC used to output code
> for the condition at the top of the loop. GDB from either then or now
> will stop at the evaluation of the condition each time through the
> loop.
>
> I was able to locate a compiler that I could run from circa 1998. While
> I was able to run the compiler, I was not able to run the linker; it
> died with an internal error in collect2, undoubtedly due to not having
> compatible libraries. But I was able to use -S in order to see
> what the assembly code looked like. Here is the assembly code for
> our example loop, compiled by gcc 2.9, targeting i386-pc-linux-gnu:
>
> movl $0,v
> .stabn 68,0,9,.LM3-loop_test
> .LM3:
> .align 4
> .L2:
> movl v,%eax
> cmpl $2,%eax
> jle .L4
> jmp .L3
> .align 4
> .L4:
> .stabn 68,0,11,.LM4-loop_test
> .LM4:
> movl v,%eax
> leal 1(%eax),%edx
> movl %edx,v
> .stabn 68,0,12,.LM5-loop_test
> .LM5:
> jmp .L2
> .L3:
>
> The loop begins with the evaluation of the condition at .L2. The jle
> branches to instructions forming the body of the loop; the jmp instruction
> immediately following the jle gets us out of the loop.
>
> This code isn't very efficient, but it is a straightforward translation
> of the corresponding C code. A breakpoint placed at the beginning of
> line 9 (which is both .LM3 and .L2) will stop on each iteration through
> the loop.
>
> My patch-set restores this behavior for while loops implemented in
> a more efficient manner.