1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
# 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.
if {![dwarf2_support]} {
unsupported "dwarf2 support 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"
|