aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.base/frame-selection.exp
blob: 3b4503acb33c9a3bbee3fae56f7ba5d17d16a801 (plain)
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# Copyright 2018-2022 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"
}