This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH 0/8] Break at each iteration for breakpoints placed on a while statement


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.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]