aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.base/frame-selection.exp
diff options
context:
space:
mode:
authorAndrew Burgess <andrew.burgess@embecosm.com>2015-09-10 13:06:16 +0100
committerAndrew Burgess <andrew.burgess@embecosm.com>2018-09-28 11:59:34 +0100
commitf67ffa6a785bee26bc23550670f85c6db578641f (patch)
treebc989c15856f0c9cb628d899d0ca54f8617a4911 /gdb/testsuite/gdb.base/frame-selection.exp
parent5f9aecea0dfd9e8b0a728c332e8ddb105bae6054 (diff)
downloadgdb-f67ffa6a785bee26bc23550670f85c6db578641f.zip
gdb-f67ffa6a785bee26bc23550670f85c6db578641f.tar.gz
gdb-f67ffa6a785bee26bc23550670f85c6db578641f.tar.bz2
gdb: Change how frames are selected for 'frame' and 'info frame'.
The 'frame' command, and thanks to code reuse the 'info frame' and 'select-frame' commands, currently have an overloaded mechanism for selecting a frame. These commands take one or two parameters, if it's one parameter then we first try to use the parameter as an integer to select a frame by level (or depth in the stack). If that fails then we treat the parameter as an address and try to select a stack frame by stack-address. If we still have not selected a stack frame, or we initially had two parameters, then GDB allows the user to view a stack frame that is not part of the current backtrace. Internally, a new frame is created with the given stack and pc addresses, and this is shown to the user. The result of this is that a typo by the user, entering the wrong stack frame level for example, can result in a brand new frame being viewed rather than an error. The purpose of this commit is to remove this overloading, while still offering the same functionality through some new sub-commands. By making the default behaviour of 'frame' (and friends) be to select a stack frame by level index, it is hoped that enough backwards-compatibility is maintained that users will not be overly inconvenienced. The 'frame', 'select-frame', and 'info frame' commands now all take a frame specification string as an argument, this string can be any of the following: (1) An integer. This is treated as a frame level. If a frame for that level does not exist then the user gets an error. (2) A string like 'level <LEVEL>', where <LEVEL> is a frame level as in option (1) above. (3) A string like 'address <STACK-ADDRESS>', where <STACK-ADDRESS> is a stack-frame address. If there is no frame for this address then the user gets an error. (4) A string like 'function <NAME>', where <NAME> is a function name, the inner most frame for function <NAME> is selected. If there is no frame for function <NAME> then the user gets an error. (5) A string like 'view <STACK-ADDRESS>', this views a new frame with stack address <STACK-ADDRESS>. (6) A string like 'view <STACK-ADDRESS> <PC-ADDRESS>', this views a new frame with stack address <STACK-ADDRESS> and the pc <PC-ADDRESS>. This change assumes that the most common use of the commands like 'frame' is to select a frame by frame level, it is for this reason that this is the behaviour that is kept for backwards compatibility. Any of the alternative behaviours, which are assumed to be less used, now require a change in user behaviour. The MI command '-stack-select-frame' has not been changed. This ensures that we maintain backwards compatibility for existing frontends. gdb/ChangeLog: (NEWS): Mention changes to frame related commands. * cli/cli-decode.c (add_cmd_suppress_notification): New function. (add_prefix_cmd_suppress_notification): New function. (add_com_suppress_notification): Call add_cmd_suppress_notification. * command.h (add_cmd_suppress_notification): Declare. (add_prefix_cmd_suppress_notification): Declare. * mi/mi-cmd-stack.c: Add 'safe-ctype.h' include. (parse_frame_specification): Moved from stack.c, with simplification to handle a single argument. (mi_cmd_stack_select_frame): Use parse_frame_specification, the switch to the selected frame. Add a header comment. * stack.c: Remove 'safe-ctype.h' include. (find_frame_for_function): Add declaration. (find_frame_for_address): New function. (parse_frame_specification): Moved into mi/mi-cmd-stack.c. (frame_selection_by_function_completer): New function. (info_frame_command): Rename to... (info_frame_command_core): ...this, and update parameter types. (select_frame_command): Rename to... (select_frame_command_core): ...this, and update parameter types. (frame_command): Rename to... (frame_command_core): ...this, and update parameter types. (class frame_command_helper): New class to wrap implementations of frame related sub-commands. (frame_apply_cmd_list): New static global. (frame_cmd_list): Make static. (select_frame_cmd_list): New global for sub-commands. (info_frame_cmd_list): New global for sub-commands. (_initialize_stack): Register sub-commands for 'frame', 'select-frame', and 'info frame'. Update 'frame apply' commands to use frame_apply_cmd_list. Move function local static frame_apply_list to file static frame_apply_cmd_list for consistency. * stack.h (select_frame_command): Delete declarationn. (select_frame_for_mi): Declare new function. gdb/doc/ChangeLog: * gdb.texinfo (Frames): Rewrite the description of 'frame number' to highlight that the number is also the frame's level. (Selection): Rewrite documentation for 'frame' and 'select-frame' commands. (Frame Info): Rewrite documentation for 'info frame' command. gdb/testsuite/ChangeLog: * gdb.base/frame-selection.exp: New file. * gdb.base/frame-selection.c: New file.
Diffstat (limited to 'gdb/testsuite/gdb.base/frame-selection.exp')
-rw-r--r--gdb/testsuite/gdb.base/frame-selection.exp188
1 files changed, 188 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
new file mode 100644
index 0000000..2dde33f
--- /dev/null
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -0,0 +1,188 @@
+# Copyright 2018 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"
+}