| # Copyright 2018-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/>. |
| |
| # This tests GDB's frame selection as used by the 'frame', |
| # 'select-frame', and 'info frame' commands. |
| |
| standard_testfile |
| |
| if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} { |
| return -1 |
| } |
| |
| runto_main |
| gdb_breakpoint frame_2 |
| gdb_continue_to_breakpoint frame_2 |
| |
| gdb_test "bt" "#0 frame_2.*#1 $hex in frame_1.*#2 $hex in main.*" "backtrace at breakpoint" |
| |
| # Perform "info frame" to extract the frame's address. |
| proc get_frame_address { {testname ""} } { |
| global hex gdb_prompt |
| |
| set frame_address "unknown" |
| set testname "get_frame_address: ${testname}" |
| gdb_test_multiple "info frame" $testname { |
| -re ", frame at ($hex):\r\n.*\r\n$gdb_prompt $" { |
| set frame_address $expect_out(1,string) |
| pass $testname |
| } |
| } |
| |
| return $frame_address |
| } |
| |
| # Passed a list of addresses. Return a new address that is not in the |
| # list by sorting the addresses and adding 0x10 to the highest |
| # address. |
| proc get_new_address { {addresses {}} } { |
| # Find the highest element in the list. |
| set elem [lindex [lsort -integer -decreasing $addresses] 0] |
| |
| # Return a new address as a hex formatted string. |
| return [format "%#x" [expr $elem + 0x10]] |
| } |
| |
| |
| # Check that the current frame is at stack depth LEVEL, at frame |
| # address ADDRESS, and is in FUNCTION. |
| proc check_frame { level address function } { |
| global hex gdb_prompt |
| |
| set re [multi_line \ |
| "Stack level ${level}, frame at ($address):" \ |
| ".* = $hex in ${function} \(\[^\r\n\]*\); saved .* = $hex" \ |
| ".*\r\n$gdb_prompt $" ] |
| |
| set testname "check frame level ${level}" |
| gdb_test_multiple "info frame" $testname { |
| -re $re { |
| pass $testname |
| } |
| } |
| } |
| |
| # Select frame using level, but relying on this being the default |
| # action, so "frame 0" performs "frame level 0". |
| gdb_test "frame 0" "#0 frame_2.*" |
| set frame_0_address [ get_frame_address "frame 0" ] |
| gdb_test "frame 1" "#1 $hex in frame_1.*" |
| set frame_1_address [ get_frame_address "frame 1" ] |
| gdb_test "frame 2" "#2 $hex in main.*" |
| set frame_2_address [ get_frame_address "frame 2" ] |
| gdb_test "frame 3" "No frame at level 3\." |
| |
| # Find an address that matches no frame. |
| set no_frame_address [ get_new_address [list $frame_0_address \ |
| $frame_1_address \ |
| $frame_2_address] ] |
| |
| # Select frame using 'level' specification. |
| gdb_test "frame level 0" "#0 frame_2.*" |
| gdb_test "frame level 1" "#1 $hex in frame_1.*" |
| gdb_test "frame level 2" "#2 $hex in main.*" |
| gdb_test "frame level 3" "No frame at level 3\." |
| |
| # Select frame by address. |
| gdb_test "frame address ${frame_0_address}" "#0 frame_2.*" \ |
| "select frame 0 by address" |
| gdb_test "frame address ${frame_1_address}" "#1 $hex in frame_1.*" \ |
| "select frame 1 by address" |
| gdb_test "frame address ${frame_2_address}" "#2 $hex in main.*" \ |
| "select frame 2 by address" |
| gdb_test "frame address ${no_frame_address}" \ |
| "No frame at address ${no_frame_address}\." \ |
| "attempt to select a frame at an invalid address" |
| |
| # Select frame by function. |
| gdb_test "frame function frame_2" "#0 frame_2.*" |
| gdb_test "frame function frame_1" "#1 $hex in frame_1.*" |
| gdb_test "frame function main" "#2 $hex in main.*" |
| |
| # Check for a distinction between a known function not in the stack |
| # trace, and an unknown function. |
| gdb_test "frame function recursive" "No frame for function \"recursive\"." |
| gdb_test "frame function foo" "Function \"foo\" not defined." |
| |
| |
| with_test_prefix "select-frame, no keyword" { |
| gdb_test_no_output "select-frame 0" |
| check_frame "0" "${frame_0_address}" "frame_2" |
| gdb_test_no_output "select-frame 1" |
| check_frame "1" "${frame_1_address}" "frame_1" |
| gdb_test_no_output "select-frame 2" |
| check_frame "2" "${frame_2_address}" "main" |
| gdb_test "select-frame 3" "No frame at level 3\." |
| } |
| |
| with_test_prefix "select-frame, keyword=level" { |
| gdb_test_no_output "select-frame level 0" |
| check_frame "0" "${frame_0_address}" "frame_2" |
| gdb_test_no_output "select-frame level 1" |
| check_frame "1" "${frame_1_address}" "frame_1" |
| gdb_test_no_output "select-frame level 2" |
| check_frame "2" "${frame_2_address}" "main" |
| gdb_test "select-frame level 3" "No frame at level 3\." |
| } |
| |
| with_test_prefix "select-frame, keyword=address" { |
| gdb_test_no_output "select-frame address ${frame_0_address}" \ |
| "select frame 0 by address" |
| check_frame "0" "${frame_0_address}" "frame_2" |
| gdb_test_no_output "select-frame address ${frame_1_address}" \ |
| "select frame 1 by address" |
| check_frame "1" "${frame_1_address}" "frame_1" |
| gdb_test_no_output "select-frame address ${frame_2_address}" \ |
| "select frame 2 by address" |
| check_frame "2" "${frame_2_address}" "main" |
| gdb_test "select-frame address ${no_frame_address}" \ |
| "No frame at address ${no_frame_address}\." \ |
| "select-frame for an invalid address" |
| } |
| |
| with_test_prefix "select-frame, keyword=function" { |
| gdb_test_no_output "select-frame function frame_2" |
| check_frame "0" "${frame_0_address}" "frame_2" |
| gdb_test_no_output "select-frame function frame_1" |
| check_frame "1" "${frame_1_address}" "frame_1" |
| gdb_test_no_output "select-frame function main" |
| check_frame "2" "${frame_2_address}" "main" |
| } |
| |
| # Check for a distinction between a known function not in the stack |
| # trace, and an unknown function. |
| gdb_test "select-frame function recursive" \ |
| "No frame for function \"recursive\"." |
| gdb_test "select-frame function foo" \ |
| "Function \"foo\" not defined." |
| |
| # Now continue until we hit the breakpoint again. |
| with_test_prefix "second frame_2 breakpoint" { |
| gdb_continue_to_breakpoint frame_2 |
| gdb_test "bt" \ |
| "#0 frame_2.*#1 $hex in recursive.*#2 $hex in recursive.*#3 $hex in recursive.*#4 $hex in main.*" \ |
| "backtrace at breakpoint with recursive frames" |
| |
| # Check "frame function" when a function name occurs multiple times in |
| # the stack. The inner most (lowest level) should always be selected. |
| gdb_test "frame function frame_2" "#0 frame_2.*" |
| gdb_test "frame function recursive" "#1 $hex in recursive.*" \ |
| "select frame for function recursive, first attempt" |
| gdb_test "frame function recursive" "#1 $hex in recursive.*" \ |
| "select frame for function recursive, second attempt" |
| gdb_test "frame function main" "#4 $hex in main.*" |
| gdb_test "frame function recursive" "#1 $hex in recursive.*" \ |
| "select frame for function recursive, third attempt" |
| } |