Simplifying the watchpoint.c code down to: void func() {} int main() { int a; for (a = 0; a != 2; ++a) { func(); } return 0; } And running this through clang (assembly attached) and gdb, the following behavior is observed: => 0x00000000004005af <main+15>: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp) (gdb) until => 0x00000000004005c3 <main+35>: e8 c8 ff ff ff callq 0x400590 <func> (gdb) => 0x00000000004005c8 <main+40>: 8b 45 f8 mov -0x8(%rbp),%eax 0x00000000004005cb <main+43>: 05 01 00 00 00 add $0x1,%eax 0x00000000004005d0 <main+48>: 89 45 f8 mov %eax,-0x8(%rbp) (gdb) => 0x00000000004005c3 <main+35>: e8 c8 ff ff ff callq 0x400590 <func> so 'until' doesn't cause the loop to be skipped at all - continuing to run 'until' will just behave as though the user is stepping through the entire loop. I believe the issue here is that Clang keeps the loop condition at the top of the loop, whereas GCC puts it at the end, the theory being 'until' really looks for an instruction with a higher PC than the /last/ instruction on the line you started at, not the specific instruction you started at. (eg: while I ran "until" from <main+40>, I went through 40, 43, 48, then <main+51> jumped up to the top of the loop (+22) but on the same line according to the line table, continued on to 29, and then to 35 - since 35 is on a distinct line from 29 and 29 > 35, GDB stopped here, even though 40 !> 35) And GCC puts the condition at the end of the loop, so you go from increment, to condition, then jump up from the condition to the body - changing lines and decreasing the PC, so that causes until to skip that and keep going... until eventually it gets out of the loop.
ping
*** Bug 27196 has been marked as a duplicate of this bug. ***
I believe I have a solution for this problem. The issue wasn't that the loop condition was at the top, but rather that there were lines at the bottom that weren't marked as is_stmt. This way, when calculating the maximum PC to execute to, it landed on that !is_stmt instruction, assumed the inferior was out of the block and GDB stopped at the next is_stmt instruction, the one inside the loop My patch fixes this by searching the first instance of an is_stmt instruction, or the first instruction that happens on a different line, and sets that as the stopping PC The patch is available here: https://sourceware.org/pipermail/gdb-patches/2022-January/184799.html
The master branch has been updated by Andrew Burgess <aburgess@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9ab50efc463ff723b8e9102f1f68a6983d320517 commit 9ab50efc463ff723b8e9102f1f68a6983d320517 Author: Bruno Larsen <blarsen@redhat.com> Date: Wed Jan 26 10:08:13 2022 -0300 gdb: fix until behavior with trailing !is_stmt lines When using the command "until", it is expected that GDB will exit a loop if the current instruction is the last one related to that loop. However, if there were trailing non-statement instructions, "until" would just behave as "next". This was noticeable in clang-compiled code, but might happen with gcc-compiled as well. PR gdb/17315 relates to this problem, as running gdb.base/watchpoint.exp with clang would fail for this reason. To better understand this issue, consider the following source code, with line numbers marked on the left: 10: for (i = 0; i < 10; ++i) 11: loop_body (); 12: other_stuff (); If we transform this to pseudo-assembler, and generate a line table, we could end up with something like this: Address | Pseudo-Assembler | Line | Is-Statement? 0x100 | i = 0 | 10 | Yes 0x104 | loop_body () | 11 | Yes 0x108 | i = i + 1 | 10 | Yes 0x10c | if (i < 10): | 10 | No 0x110 | goto 0x104 | 10 | No 0x114 | other_stuff () | 12 | Yes Notice the two non-statement instructions at the end of the loop. The problem is that when we reach address 0x108 and use 'until', hoping to leave the loop, GDB sets up a stepping range that runs from the start of the function (0x100 in our example) to the end of the current line table entry, that is 0x10c in our example. GDB then starts stepping forward. When 0x10c is reached GDB spots that we have left the stepping range, that the new location is not a statement, and that the new location is associated with the same source line number as the previous stepping range. GDB then sets up a new stepping range that runs from 0x10c to 0x114, and continues stepping forward. Within that stepping range the inferior hits the goto (at 0x110) and loops back to address 0x104. At 0x104 GDB spots that we have left the previous stepping range, that the new address is marked as a statement, and that the new address is for a different source line. As a result, GDB stops and returns control to the user. This is not what the user was expecting, they expected GDB to exit the loop. The fix proposed in this patch, is that, when the user issues the 'until' command, and GDB sets up the initial stepping range, GDB will check subsequent SALs (symtab_and_lines) to see if they are non-statements associated with the same line number. If they are then the end of the initial stepping range is extended to the end of the non-statement SALs. In our example above, the user is at 0x108 and uses 'until', GDB now sets up a stepping range from the start of the function 0x100 to 0x114, the first address associated with a different line. Now as GDB steps around the loop it never leaves the initial stepping range. It is only when GDB exits the loop that we leave the stepping range, and the stepping finishes at address 0x114. This patch also adds a test case that can be run with gcc to test that this functionality is not broken in the future. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=17315
Sorry for the delay in closing this. The above-mentioned commit should fix this issue