1 # Copyright 2019-2024 Free Software Foundation, Inc. 2 3 # This program is free software; you can redistribute it and/or modify 4 # it under the terms of the GNU General Public License as published by 5 # the Free Software Foundation; either version 3 of the License, or 6 # (at your option) any later version. 7 # 8 # This program is distributed in the hope that it will be useful, 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 # GNU General Public License for more details. 12 # 13 # You should have received a copy of the GNU General Public License 14 # along with this program. If not, see <http://www.gnu.org/licenses/> . 15 16 # Print a 2 dimensional assumed shape array. We pass different slices 17 # of the array to a subroutine and print the array as received within 18 # the subroutine. This should exercise GDB's ability to handle 19 # different strides for the different dimensions. 20 21 # Testing GDB's ability to print array (and string) slices, including 22 # slices that make use of array strides. 23 # 24 # In the Fortran code various arrays of different ranks are filled 25 # with data, and slices are passed to a series of show functions. 26 # 27 # In this test script we break in each of the show functions, print 28 # the array slice that was passed in, and then move up the stack to 29 # the parent frame and check GDB can manually extract the same slice. 30 # 31 # This test also checks that the size of the array slice passed to the 32 # function (so as extracted and described by the compiler and the 33 # debug information) matches the size of the slice manually extracted 34 # by GDB. 35 36 require allow_fortran_tests 37 38 # This test relies on output from the inferior. 39 require {!target_info exists gdb,noinferiorio} 40 41 standard_testfile ".f90" 42 load_lib fortran.exp 43 44 if {[build_executable ${testfile}.exp ${testfile} ${srcfile} \ 45 {debug f90}]} { 46 return -1 47 } 48 49 # Takes the name of an array slice as used in the test source, and extracts 50 # the base array name. For example: 'array (1,2)' becomes 'array'. 51 proc array_slice_to_var { slice_str } { 52 regexp "^(?:\\s*\\()*(\[^( \t\]+)" $slice_str matchvar varname 53 return $varname 54 } 55 56 proc run_test { repack } { 57 global binfile gdb_prompt 58 59 clean_restart ${binfile} 60 61 # Avoid shared lib symbols. 62 gdb_test_no_output "set auto-solib-add off" 63 64 if ![fortran_runto_main] { 65 return -1 66 } 67 68 # Avoid libc symbols, in particular the 'array' type. 69 gdb_test_no_output "nosharedlibrary" 70 71 gdb_test_no_output "set fortran repack-array-slices $repack" 72 73 # gdb_breakpoint [gdb_get_line_number "Display Message Breakpoint"] 74 gdb_breakpoint [gdb_get_line_number "Display Element"] 75 gdb_breakpoint [gdb_get_line_number "Display String"] 76 gdb_breakpoint [gdb_get_line_number "Display Array Slice 1D"] 77 gdb_breakpoint [gdb_get_line_number "Display Array Slice 2D"] 78 gdb_breakpoint [gdb_get_line_number "Display Array Slice 3D"] 79 gdb_breakpoint [gdb_get_line_number "Display Array Slice 4D"] 80 gdb_breakpoint [gdb_get_line_number "Final Breakpoint"] 81 82 # We're going to print some reasonably large arrays. 83 gdb_test_no_output "set print elements unlimited" 84 85 set found_final_breakpoint false 86 87 # We place a limit on the number of tests that can be run, just in 88 # case something goes wrong, and GDB gets stuck in an loop here. 89 set test_count 0 90 while { $test_count < 500 } { 91 with_test_prefix "test $test_count" { 92 incr test_count 93 94 set found_final_breakpoint false 95 set expected_result "" 96 set func_name "" 97 set found_prompt false 98 gdb_test_multiple "continue" "continue" { 99 -i $::inferior_spawn_id 100 101 -re ".*GDB = (\[^\r\n\]+)\r\n" { 102 set expected_result $expect_out(1,string) 103 if {!$found_prompt} { 104 exp_continue 105 } 106 } 107 108 -i $::gdb_spawn_id 109 110 -re "! Display Element" { 111 set func_name "show_elem" 112 exp_continue 113 } 114 -re "! Display String" { 115 set func_name "show_str" 116 exp_continue 117 } 118 -re "! Display Array Slice (.)D" { 119 set func_name "show_$expect_out(1,string)d" 120 exp_continue 121 } 122 -re "! Final Breakpoint" { 123 set found_final_breakpoint true 124 exp_continue 125 } 126 -re "$gdb_prompt $" { 127 set found_prompt true 128 129 if {$found_final_breakpoint 130 || ($expected_result != "" && $func_name != "")} { 131 # We're done. 132 } else { 133 exp_continue 134 } 135 } 136 } 137 138 if ($found_final_breakpoint) { 139 break 140 } 141 142 # We want to take a look at the line in the previous frame that 143 # called the current function. I couldn't find a better way of 144 # doing this than 'up', which will print the line, then 'down' 145 # again. 146 # 147 # I don't want to fill the log with passes for these up/down 148 # commands, so we don't report any. If something goes wrong then we 149 # should get a fail from gdb_test_multiple. 150 set array_slice_name "" 151 set unique_id "" 152 array unset replacement_vars 153 array set replacement_vars {} 154 gdb_test_multiple "up" "up" { 155 -re "\r\n\[0-9\]+\[ \t\]+call ${func_name} \\((\[^\r\n\]+)\\)\r\n$gdb_prompt $" { 156 set array_slice_name $expect_out(1,string) 157 } 158 -re "\r\n\[0-9\]+\[ \t\]+call ${func_name} \\((\[^\r\n\]+)\\)\[ \t\]+! VARS=(\[^ \t\r\n\]+)\r\n$gdb_prompt $" { 159 set array_slice_name $expect_out(1,string) 160 set unique_id $expect_out(2,string) 161 } 162 } 163 if {$unique_id != ""} { 164 set str "" 165 foreach v [split $unique_id ,] { 166 set val [get_integer_valueof "${v}" "??"\ 167 "get variable '$v' for '$array_slice_name'"] 168 set replacement_vars($v) $val 169 if {$str != ""} { 170 set str "Str," 171 } 172 set str "$str$v=$val" 173 } 174 set unique_id " $str" 175 } 176 gdb_test_multiple "down" "down" { 177 -re "\r\n$gdb_prompt $" { 178 # Don't issue a pass here. 179 } 180 } 181 182 # Check we have all the information we need to successfully run one 183 # of these tests. 184 if { $expected_result == "" } { 185 perror "failed to extract expected results" 186 return 0 187 } 188 if { $array_slice_name == "" } { 189 perror "failed to extract array slice name" 190 return 0 191 } 192 193 # Check GDB can correctly print the array slice that was passed into 194 # the current frame. 195 set pattern [string_to_regexp " = $expected_result"] 196 gdb_test "p array" "$pattern" \ 197 "check value of '$array_slice_name'$unique_id" 198 199 # Get the size of the slice. 200 set size_in_show \ 201 [get_integer_valueof "sizeof (array)" "show_unknown" \ 202 "get sizeof '$array_slice_name'$unique_id in show"] 203 set addr_in_show \ 204 [get_hexadecimal_valueof "&array" "show_unknown" \ 205 "get address '$array_slice_name'$unique_id in show"] 206 207 # Now move into the previous frame, and see if GDB can extract the 208 # array slice from the original parent object. Again, use of 209 # gdb_test_multiple to avoid filling the logs with unnecessary 210 # passes. 211 gdb_test_multiple "up" "up" { 212 -re "\r\n$gdb_prompt $" { 213 # Do nothing. 214 } 215 } 216 217 # Print the array slice, this will force GDB to manually extract the 218 # slice from the parent array. 219 gdb_test "p $array_slice_name" "$pattern" \ 220 "check array slice '$array_slice_name'$unique_id can be extracted" 221 222 # Get the size of the slice in the calling frame. 223 set size_in_parent \ 224 [get_integer_valueof "sizeof ($array_slice_name)" \ 225 "parent_unknown" \ 226 "get sizeof '$array_slice_name'$unique_id in parent"] 227 228 # Figure out the start and end addresses of the full array in the 229 # parent frame. 230 set full_var_name [array_slice_to_var $array_slice_name] 231 set start_addr [get_hexadecimal_valueof "&${full_var_name}" \ 232 "start unknown"] 233 set end_addr [get_hexadecimal_valueof \ 234 "$start_addr + sizeof (${full_var_name})" \ 235 "end unknown" \ 236 "get end address of ${full_var_name}"] 237 238 # The Fortran compiler can choose to either send a descriptor that 239 # describes the array slice to the subroutine, or it can repack the 240 # slice into an array section and send that. 241 # 242 # We find the address range of the original array in the parent, 243 # and the address of the slice in the show function, if the 244 # address of the slice (from show) is in the range of the original 245 # array then repacking has not occurred, otherwise, the slice is 246 # outside of the parent, and repacking must have occurred. 247 # 248 # The goal here is to compare the sizes of the slice in show with 249 # the size of the slice extracted by GDB. So we can only compare 250 # sizes when GDB's repacking setting matches the repacking 251 # behaviour we got from the compiler. 252 if { ($addr_in_show < $start_addr || $addr_in_show >= $end_addr) \ 253 == ($repack == "on") } { 254 gdb_assert {$size_in_show == $size_in_parent} \ 255 "check sizes match" 256 } elseif { $repack == "off" } { 257 # GDB's repacking is off (so slices are left unpacked), but 258 # the compiler did pack this one. As a result we can't 259 # compare the sizes between the compiler's slice and GDB's 260 # slice. 261 verbose -log "slice '$array_slice_name' was repacked, sizes can't be compared" 262 } else { 263 # Like the above, but the reverse, GDB's repacking is on, but 264 # the compiler didn't repack this slice. 265 verbose -log "slice '$array_slice_name' was not repacked, sizes can't be compared" 266 } 267 268 # If the array name we just tested included variable names, then 269 # test again with all the variables expanded. 270 if {$unique_id != ""} { 271 foreach v [array names replacement_vars] { 272 set val $replacement_vars($v) 273 set array_slice_name \ 274 [regsub "\\y${v}\\y" $array_slice_name $val] 275 } 276 gdb_test "p $array_slice_name" "$pattern" \ 277 "check array slice '$array_slice_name'$unique_id can be extracted, with variables expanded" 278 } 279 } 280 } 281 282 # Ensure we reached the final breakpoint. If more tests have been added 283 # to the test script, and this starts failing, then the safety 'while' 284 # loop above might need to be increased. 285 gdb_assert {$found_final_breakpoint} "ran all tests" 286 } 287 288 foreach_with_prefix repack { on off } { 289 run_test $repack 290 } 291