|  | # Copyright 2019-2021 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/> . | 
|  |  | 
|  | # Print a 2 dimensional assumed shape array.  We pass different slices | 
|  | # of the array to a subroutine and print the array as recieved within | 
|  | # the subroutine.  This should exercise GDB's ability to handle | 
|  | # different strides for the different dimensions. | 
|  |  | 
|  | # Testing GDB's ability to print array (and string) slices, including | 
|  | # slices that make use of array strides. | 
|  | # | 
|  | # In the Fortran code various arrays of different ranks are filled | 
|  | # with data, and slices are passed to a series of show functions. | 
|  | # | 
|  | # In this test script we break in each of the show functions, print | 
|  | # the array slice that was passed in, and then move up the stack to | 
|  | # the parent frame and check GDB can manually extract the same slice. | 
|  | # | 
|  | # This test also checks that the size of the array slice passed to the | 
|  | # function (so as extracted and described by the compiler and the | 
|  | # debug information) matches the size of the slice manually extracted | 
|  | # by GDB. | 
|  |  | 
|  | if {[skip_fortran_tests]} { return -1 } | 
|  |  | 
|  | standard_testfile ".f90" | 
|  | load_lib fortran.exp | 
|  |  | 
|  | if {[build_executable ${testfile}.exp ${testfile} ${srcfile} \ | 
|  | {debug f90}]} { | 
|  | return -1 | 
|  | } | 
|  |  | 
|  | # Takes the name of an array slice as used in the test source, and extracts | 
|  | # the base array name.  For example: 'array (1,2)' becomes 'array'. | 
|  | proc array_slice_to_var { slice_str } { | 
|  | regexp "^(?:\\s*\\()*(\[^( \t\]+)" $slice_str matchvar varname | 
|  | return $varname | 
|  | } | 
|  |  | 
|  | proc run_test { repack } { | 
|  | global binfile gdb_prompt | 
|  |  | 
|  | clean_restart ${binfile} | 
|  |  | 
|  | # Avoid shared lib symbols. | 
|  | gdb_test_no_output "set auto-solib-add off" | 
|  |  | 
|  | if ![fortran_runto_main] { | 
|  | return -1 | 
|  | } | 
|  |  | 
|  | # Avoid libc symbols, in particular the 'array' type. | 
|  | gdb_test_no_output "nosharedlibrary" | 
|  |  | 
|  | gdb_test_no_output "set fortran repack-array-slices $repack" | 
|  |  | 
|  | # gdb_breakpoint [gdb_get_line_number "Display Message Breakpoint"] | 
|  | gdb_breakpoint [gdb_get_line_number "Display Element"] | 
|  | gdb_breakpoint [gdb_get_line_number "Display String"] | 
|  | gdb_breakpoint [gdb_get_line_number "Display Array Slice 1D"] | 
|  | gdb_breakpoint [gdb_get_line_number "Display Array Slice 2D"] | 
|  | gdb_breakpoint [gdb_get_line_number "Display Array Slice 3D"] | 
|  | gdb_breakpoint [gdb_get_line_number "Display Array Slice 4D"] | 
|  | gdb_breakpoint [gdb_get_line_number "Final Breakpoint"] | 
|  |  | 
|  | # We're going to print some reasonably large arrays. | 
|  | gdb_test_no_output "set print elements unlimited" | 
|  |  | 
|  | set found_final_breakpoint false | 
|  |  | 
|  | # We place a limit on the number of tests that can be run, just in | 
|  | # case something goes wrong, and GDB gets stuck in an loop here. | 
|  | set test_count 0 | 
|  | while { $test_count < 500 } { | 
|  | with_test_prefix "test $test_count" { | 
|  | incr test_count | 
|  |  | 
|  | set found_final_breakpoint false | 
|  | set expected_result "" | 
|  | set func_name "" | 
|  | gdb_test_multiple "continue" "continue" { | 
|  | -re ".*GDB = (\[^\r\n\]+)\r\n" { | 
|  | set expected_result $expect_out(1,string) | 
|  | exp_continue | 
|  | } | 
|  | -re "! Display Element" { | 
|  | set func_name "show_elem" | 
|  | exp_continue | 
|  | } | 
|  | -re "! Display String" { | 
|  | set func_name "show_str" | 
|  | exp_continue | 
|  | } | 
|  | -re "! Display Array Slice (.)D" { | 
|  | set func_name "show_$expect_out(1,string)d" | 
|  | exp_continue | 
|  | } | 
|  | -re "! Final Breakpoint" { | 
|  | set found_final_breakpoint true | 
|  | exp_continue | 
|  | } | 
|  | -re "$gdb_prompt $" { | 
|  | # We're done. | 
|  | } | 
|  | } | 
|  |  | 
|  | if ($found_final_breakpoint) { | 
|  | break | 
|  | } | 
|  |  | 
|  | # We want to take a look at the line in the previous frame that | 
|  | # called the current function.  I couldn't find a better way of | 
|  | # doing this than 'up', which will print the line, then 'down' | 
|  | # again. | 
|  | # | 
|  | # I don't want to fill the log with passes for these up/down | 
|  | # commands, so we don't report any.  If something goes wrong then we | 
|  | # should get a fail from gdb_test_multiple. | 
|  | set array_slice_name "" | 
|  | set unique_id "" | 
|  | array unset replacement_vars | 
|  | array set replacement_vars {} | 
|  | gdb_test_multiple "up" "up" { | 
|  | -re "\r\n\[0-9\]+\[ \t\]+call ${func_name} \\((\[^\r\n\]+)\\)\r\n$gdb_prompt $" { | 
|  | set array_slice_name $expect_out(1,string) | 
|  | } | 
|  | -re "\r\n\[0-9\]+\[ \t\]+call ${func_name} \\((\[^\r\n\]+)\\)\[ \t\]+! VARS=(\[^ \t\r\n\]+)\r\n$gdb_prompt $" { | 
|  | set array_slice_name $expect_out(1,string) | 
|  | set unique_id $expect_out(2,string) | 
|  | } | 
|  | } | 
|  | if {$unique_id != ""} { | 
|  | set str "" | 
|  | foreach v [split $unique_id ,] { | 
|  | set val [get_integer_valueof "${v}" "??"\ | 
|  | "get variable '$v' for '$array_slice_name'"] | 
|  | set replacement_vars($v) $val | 
|  | if {$str != ""} { | 
|  | set str "Str," | 
|  | } | 
|  | set str "$str$v=$val" | 
|  | } | 
|  | set unique_id " $str" | 
|  | } | 
|  | gdb_test_multiple "down" "down" { | 
|  | -re "\r\n$gdb_prompt $" { | 
|  | # Don't issue a pass here. | 
|  | } | 
|  | } | 
|  |  | 
|  | # Check we have all the information we need to successfully run one | 
|  | # of these tests. | 
|  | if { $expected_result == "" } { | 
|  | perror "failed to extract expected results" | 
|  | return 0 | 
|  | } | 
|  | if { $array_slice_name == "" } { | 
|  | perror "failed to extract array slice name" | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | # Check GDB can correctly print the array slice that was passed into | 
|  | # the current frame. | 
|  | set pattern [string_to_regexp " = $expected_result"] | 
|  | gdb_test "p array" "$pattern" \ | 
|  | "check value of '$array_slice_name'$unique_id" | 
|  |  | 
|  | # Get the size of the slice. | 
|  | set size_in_show \ | 
|  | [get_integer_valueof "sizeof (array)" "show_unknown" \ | 
|  | "get sizeof '$array_slice_name'$unique_id in show"] | 
|  | set addr_in_show \ | 
|  | [get_hexadecimal_valueof "&array" "show_unknown" \ | 
|  | "get address '$array_slice_name'$unique_id in show"] | 
|  |  | 
|  | # Now move into the previous frame, and see if GDB can extract the | 
|  | # array slice from the original parent object.  Again, use of | 
|  | # gdb_test_multiple to avoid filling the logs with unnecessary | 
|  | # passes. | 
|  | gdb_test_multiple "up" "up" { | 
|  | -re "\r\n$gdb_prompt $" { | 
|  | # Do nothing. | 
|  | } | 
|  | } | 
|  |  | 
|  | # Print the array slice, this will force GDB to manually extract the | 
|  | # slice from the parent array. | 
|  | gdb_test "p $array_slice_name" "$pattern" \ | 
|  | "check array slice '$array_slice_name'$unique_id can be extracted" | 
|  |  | 
|  | # Get the size of the slice in the calling frame. | 
|  | set size_in_parent \ | 
|  | [get_integer_valueof "sizeof ($array_slice_name)" \ | 
|  | "parent_unknown" \ | 
|  | "get sizeof '$array_slice_name'$unique_id in parent"] | 
|  |  | 
|  | # Figure out the start and end addresses of the full array in the | 
|  | # parent frame. | 
|  | set full_var_name [array_slice_to_var $array_slice_name] | 
|  | set start_addr [get_hexadecimal_valueof "&${full_var_name}" \ | 
|  | "start unknown"] | 
|  | set end_addr [get_hexadecimal_valueof \ | 
|  | "$start_addr + sizeof (${full_var_name})" \ | 
|  | "end unknown" \ | 
|  | "get end address of ${full_var_name}"] | 
|  |  | 
|  | # The Fortran compiler can choose to either send a descriptor that | 
|  | # describes the array slice to the subroutine, or it can repack the | 
|  | # slice into an array section and send that. | 
|  | # | 
|  | # We find the address range of the original array in the parent, | 
|  | # and the address of the slice in the show function, if the | 
|  | # address of the slice (from show) is in the range of the original | 
|  | # array then repacking has not occurred, otherwise, the slice is | 
|  | # outside of the parent, and repacking must have occurred. | 
|  | # | 
|  | # The goal here is to compare the sizes of the slice in show with | 
|  | # the size of the slice extracted by GDB.  So we can only compare | 
|  | # sizes when GDB's repacking setting matches the repacking | 
|  | # behaviour we got from the compiler. | 
|  | if { ($addr_in_show < $start_addr || $addr_in_show >= $end_addr) \ | 
|  | == ($repack == "on") } { | 
|  | gdb_assert {$size_in_show == $size_in_parent} \ | 
|  | "check sizes match" | 
|  | } elseif { $repack == "off" } { | 
|  | # GDB's repacking is off (so slices are left unpacked), but | 
|  | # the compiler did pack this one.  As a result we can't | 
|  | # compare the sizes between the compiler's slice and GDB's | 
|  | # slice. | 
|  | verbose -log "slice '$array_slice_name' was repacked, sizes can't be compared" | 
|  | } else { | 
|  | # Like the above, but the reverse, GDB's repacking is on, but | 
|  | # the compiler didn't repack this slice. | 
|  | verbose -log "slice '$array_slice_name' was not repacked, sizes can't be compared" | 
|  | } | 
|  |  | 
|  | # If the array name we just tested included variable names, then | 
|  | # test again with all the variables expanded. | 
|  | if {$unique_id != ""} { | 
|  | foreach v [array names replacement_vars] { | 
|  | set val $replacement_vars($v) | 
|  | set array_slice_name \ | 
|  | [regsub "\\y${v}\\y" $array_slice_name $val] | 
|  | } | 
|  | gdb_test "p $array_slice_name" "$pattern" \ | 
|  | "check array slice '$array_slice_name'$unique_id can be extracted, with variables expanded" | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | # Ensure we reached the final breakpoint.  If more tests have been added | 
|  | # to the test script, and this starts failing, then the safety 'while' | 
|  | # loop above might need to be increased. | 
|  | gdb_assert {$found_final_breakpoint} "ran all tests" | 
|  | } | 
|  |  | 
|  | foreach_with_prefix repack { on off } { | 
|  | run_test $repack | 
|  | } |