binutils-gdb/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.exp
Andrew Burgess 65a33d75c2 gdb/testsuite: remove use of then keyword from gdb.base/*.exp
The canonical form of 'if' in modern TCL is 'if {} {}'.  But there's
still a bunch of places in the testsuite where we make use of the
'then' keyword, and sometimes these get copies into new tests, which
just spreads poor practice.

This commit removes all use of the 'then' keyword from the gdb.base/
test script directory.

There should be no changes in what is tested after this commit.
2022-11-28 21:04:09 +00:00

145 lines
5.5 KiB
Plaintext

# Copyright (C) 2021-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 test checks for an edge case when unwinding inline frames which
# occur towards the older end of the stack when the stack ends with a
# cycle. Consider this well formed stack:
#
# main -> normal_frame -> inline_frame
#
# Now consider that, for whatever reason, the stack unwinding of
# "normal_frame" becomes corrupted, such that the stack appears to be
# this:
#
# .-> normal_frame -> inline_frame
# | |
# '------'
#
# When confronted with such a situation we would expect GDB to detect
# the stack frame cycle and terminate the backtrace at the first
# instance of "normal_frame" with a message:
#
# Backtrace stopped: previous frame identical to this frame (corrupt stack?)
#
# However, at one point there was a bug in GDB's inline frame
# mechanism such that the fact that "inline_frame" was inlined into
# "normal_frame" would cause GDB to trigger an assertion.
#
# This text makes use of a Python unwinder which can fake the cyclic
# stack cycle, further the test sets up multiple levels of normal and
# inline frames. At the point of testing the stack looks like this:
#
# main -> normal_func -> inline_func -> normal_func -> inline_func -> normal_func -> inline_func
#
# Where "normal_func" is a normal frame, and "inline_func" is an inline frame.
#
# The python unwinder is then used to force a stack cycle at each
# "normal_func" frame in turn, we then check that GDB can successfully unwind
# the stack.
standard_testfile
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} {
return -1
}
# Skip this test if Python scripting is not enabled.
if { [skip_python_tests] } { continue }
if {![runto_main]} {
return 0
}
set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
# Run to the breakpoint where we will carry out the test.
gdb_breakpoint [gdb_get_line_number "Break here"]
gdb_continue_to_breakpoint "stop at test breakpoint"
# Load the script containing the unwinder, this must be done at the
# testing point as the script will examine the stack as it is loaded.
gdb_test_no_output "source ${pyfile}"\
"import python scripts"
# Check the unbroken stack.
gdb_test_sequence "bt" "backtrace when the unwind is left unbroken" {
"\\r\\n#0 \[^\r\n\]* inline_func \\(\\) at "
"\\r\\n#1 \[^\r\n\]* normal_func \\(\\) at "
"\\r\\n#2 \[^\r\n\]* inline_func \\(\\) at "
"\\r\\n#3 \[^\r\n\]* normal_func \\(\\) at "
"\\r\\n#4 \[^\r\n\]* inline_func \\(\\) at "
"\\r\\n#5 \[^\r\n\]* normal_func \\(\\) at "
"\\r\\n#6 \[^\r\n\]* main \\(\\) at "
}
with_test_prefix "cycle at level 5" {
# Arrange to introduce a stack cycle at frame 5.
gdb_test_no_output "python stop_at_level=5"
gdb_test "maint flush register-cache" \
"Register cache flushed\\."
gdb_test_lines "bt" "backtrace when the unwind is broken at frame 5" \
[multi_line \
"#0 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \
"#1 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \
"#2 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \
"#3 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \
"#4 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \
"#5 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \
"Backtrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)"]
}
with_test_prefix "cycle at level 3" {
# Arrange to introduce a stack cycle at frame 3.
gdb_test_no_output "python stop_at_level=3"
gdb_test "maint flush register-cache" \
"Register cache flushed\\."
gdb_test_lines "bt" "backtrace when the unwind is broken at frame 3" \
[multi_line \
"#0 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \
"#1 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \
"#2 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \
"#3 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \
"Backtrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)"]
}
with_test_prefix "cycle at level 1" {
# Arrange to introduce a stack cycle at frame 1.
gdb_test_no_output "python stop_at_level=1"
gdb_test "maint flush register-cache" \
"Register cache flushed\\."
gdb_test_lines "bt" "backtrace when the unwind is broken at frame 1" \
[multi_line \
"#0 \[^\r\n\]* inline_func \\(\\) at \[^\r\n\]+" \
"#1 \[^\r\n\]* normal_func \\(\\) at \[^\r\n\]+" \
"Backtrace stopped: previous frame identical to this frame \\(corrupt stack\\?\\)"]
}
# Flush the register cache (which also flushes the frame cache) so we
# get a full backtrace again, then switch on frame debugging and try
# to back trace. At one point this triggered an assertion.
gdb_test "maint flush register-cache" \
"Register cache flushed\\." ""
gdb_test_no_output "set debug frame 1"
gdb_test_multiple "bt" "backtrace with debugging on" {
-re "^$gdb_prompt $" {
pass $gdb_test_name
}
-re "\[^\r\n\]+\r\n" {
exp_continue
}
}
gdb_test "p 1 + 2 + 3" " = 6" \
"ensure GDB is still alive"