From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 56180 invoked by alias); 19 Aug 2015 06:53:39 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 56157 invoked by uid 89); 19 Aug 2015 06:53:38 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.6 required=5.0 tests=AWL,BAYES_00,KAM_LAZY_DOMAIN_SECURITY,RP_MATCHES_RCVD,SPF_HELO_PASS autolearn=no version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Wed, 19 Aug 2015 06:53:37 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (Postfix) with ESMTPS id C43D85BA3A for ; Wed, 19 Aug 2015 06:53:36 +0000 (UTC) Received: from pinnacle.lan ([10.3.113.17]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t7J6ra6G017575 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA256 bits=256 verify=NO) for ; Wed, 19 Aug 2015 02:53:36 -0400 Date: Wed, 19 Aug 2015 06:53:00 -0000 From: Kevin Buettner To: gdb-patches@sourceware.org Subject: [PATCH 0/8] Break at each iteration for breakpoints placed on a while statement Message-ID: <20150818235334.1afb0c85@pinnacle.lan> MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit X-IsSubscribed: yes X-SW-Source: 2015-08/txt/msg00491.txt.bz2 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 : jmp 0x4005af 0x00000000004005a0 : mov 0x200a8a(%rip),%eax # 0x601030 0x00000000004005a6 : add $0x1,%eax 0x00000000004005a9 : mov %eax,0x200a81(%rip) # 0x601030 0x00000000004005af : mov 0x200a7b(%rip),%eax # 0x601030 0x00000000004005b5 : cmp $0x2,%eax 0x00000000004005b8 : jle 0x4005a0 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.