| # Copyright 2022-2023 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. |
| require dwarf2_support |
| |
| 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" |