diff options
-rw-r--r-- | gdb/infcmd.c | 39 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/until-trailing-insns.c | 35 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/until-trailing-insns.exp | 183 |
3 files changed, 257 insertions, 0 deletions
diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 48dda9b..0e1cfcb 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -1346,6 +1346,45 @@ until_next_command (int from_tty) tp->control.step_range_start = BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (func)); tp->control.step_range_end = sal.end; + + /* By setting the step_range_end based on the current pc, we are + assuming that the last line table entry for any given source line + will have is_stmt set to true. This is not necessarily the case, + there may be additional entries for the same source line with + is_stmt set false. Consider the following code: + + for (int i = 0; i < 10; i++) + loop_body (); + + Clang-13, will generate multiple line table entries at the end of + the loop all associated with the 'for' line. The first of these + entries is marked is_stmt true, but the other entries are is_stmt + false. + + If we only use the values in SAL, then our stepping range may not + extend to the end of the loop. The until command will reach the + end of the range, find a non is_stmt instruction, and step to the + next is_stmt instruction. This stopping point, however, will be + inside the loop, which is not what we wanted. + + Instead, we now check any subsequent line table entries to see if + they are for the same line. If they are, and they are marked + is_stmt false, then we extend the end of our stepping range. + + When we finish this process the end of the stepping range will + point either to a line with a different line number, or, will + point at an address for the same line number that is marked as a + statement. */ + + struct symtab_and_line final_sal + = find_pc_line (tp->control.step_range_end, 0); + + while (final_sal.line == sal.line && final_sal.symtab == sal.symtab + && !final_sal.is_stmt) + { + tp->control.step_range_end = final_sal.end; + final_sal = find_pc_line (final_sal.end, 0); + } } tp->control.may_range_step = 1; diff --git a/gdb/testsuite/gdb.base/until-trailing-insns.c b/gdb/testsuite/gdb.base/until-trailing-insns.c new file mode 100644 index 0000000..749695b --- /dev/null +++ b/gdb/testsuite/gdb.base/until-trailing-insns.c @@ -0,0 +1,35 @@ +/* Copyright 2022 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +int +main () +{ /* TAG: main prologue */ + asm ("main_label: .globl main_label"); + asm ("loop_start: .globl loop_start"); + int a, i; + i = 0; /* TAG: loop assignment */ + while (1) /* TAG: loop line */ + { + asm ("loop_condition: .globl loop_condition"); + if (i >= 10) break; /* TAG: loop condition */ + asm ("loop_code: .globl loop_code"); + a = i; /* TAG: loop code */ + asm ("loop_increment: .globl loop_increment"); + i++; /* TAG: loop increment */ + asm ("loop_jump: .globl loop_jump"); + } + asm ("main_return: .globl main_return"); + return 0; /* TAG: main return */ +} diff --git a/gdb/testsuite/gdb.base/until-trailing-insns.exp b/gdb/testsuite/gdb.base/until-trailing-insns.exp new file mode 100644 index 0000000..e87f4a3 --- /dev/null +++ b/gdb/testsuite/gdb.base/until-trailing-insns.exp @@ -0,0 +1,183 @@ +# Copyright 2022 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This test sets up debug information for a loop as we see in some cases +# from clang-13. In this situation, instructions at both the start and end +# of the loop are associated (in the line table), with the header line of +# the loop (line 10 in the example below). +# +# At the end of the loop we see some instructions marked as not a statement, +# but still associated with the same loop header line. For example, +# consider the following C code: +# +# 10: for (i = 0; i < 10; ++i) +# 11: loop_body (); +# 12: other_stuff (); +# +# Transformed into the following pseudo-assembler, with associated line table: +# +# 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 here 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 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 not to +# stop until they were outside of the loop. +# +# The fix is that, when the user issues the 'until' command, and GDB sets up +# the initial stepping range, GDB will check subsequent SALs 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 pushed out 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 test checks this behaviour using the DWARF assembler. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +if {![dwarf2_support]} { + unsupported "dwarf2 support required for this test" + return 0 +} + +if [get_compiler_info] { + return -1 +} + +# The DWARF assembler requires the gcc compiler. +if {!$gcc_compiled} { + unsupported "gcc is required for this test" + return 0 +} + +standard_testfile .c .S + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + declare_labels integer_label L + set int_size [get_sizeof "int" 4] + + # Find start address and length for our functions. + lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ + main_start main_len + set main_end "$main_start + $main_len" + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name until-trailing-isns.c} + {stmt_list $L DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {external 1 flag} + {name main} + {low_pc $main_start addr} + {high_pc $main_len DW_FORM_data4} + } + } + } + + lines {version 2 default_is_stmt 1} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate a line table program. This mimicks clang-13's behavior + # of adding some !is_stmt at the end of a loop line, making until + # not work properly. + program { + {DW_LNE_set_address $main_start} + {line [gdb_get_line_number "TAG: main prologue"]} + {DW_LNS_copy} + {DW_LNE_set_address loop_start} + {line [gdb_get_line_number "TAG: loop line"]} + {DW_LNS_copy} + {DW_LNE_set_address loop_condition} + {line [gdb_get_line_number "TAG: loop line"]} + {DW_LNS_negate_stmt} + {DW_LNS_copy} + {DW_LNE_set_address loop_code} + {line [gdb_get_line_number "TAG: loop code"]} + {DW_LNS_negate_stmt} + {DW_LNS_copy} + {DW_LNE_set_address loop_increment} + {line [gdb_get_line_number "TAG: loop line"]} + {DW_LNS_copy} + {DW_LNE_set_address loop_jump} + {line [gdb_get_line_number "TAG: loop line"]} + {DW_LNS_negate_stmt} + {DW_LNS_copy} + {DW_LNE_set_address main_return} + {line [gdb_get_line_number "TAG: main return"]} + {DW_LNS_negate_stmt} + {DW_LNS_copy} + {DW_LNE_set_address $main_end} + {line [expr [gdb_get_line_number "TAG: main return"] + 1]} + {DW_LNS_copy} + {DW_LNE_end_sequence} + } + } + +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug} ] } { + return -1 +} + +if ![runto_main] { + return -1 +} + +gdb_test "next" ".* TAG: loop code .*" "inside the loop" +gdb_test "next" ".* TAG: loop line .*" "ending of loop" +gdb_test "until" ".* TAG: main return .*" "left loop" |