forked from Imagelibrary/binutils-gdb
When testing using reverse-stepi to fully step through a function, the code checks for an infinite loop by seeing if we land on the line that contains the return statement multiple times. This assumption only works if there is only one instruction associated with that line, which is how GCC handles line information, but other compilers may handle it differently. Clang-15, for instance, associates 6. Because of this, the inferior used to get seriously out of sync with the test expectations, and result in 13 spurious failures. The same issue occurs with gdb.reverse/step-precsave.exp. This commit changes the test so that we check for PC instead of line number. The test still only happens when the same line is detected, to simplify the resulting log. With this change, no new failures are emitted when using clang. Approved-By: Tom Tromey <tom@tromey.com>
344 lines
8.7 KiB
Plaintext
344 lines
8.7 KiB
Plaintext
# Copyright 2008-2023 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 file is part of the GDB testsuite. It tests reverse stepping.
|
|
# Lots of code borrowed from "step-test.exp".
|
|
|
|
#
|
|
# Test step and next in reverse
|
|
#
|
|
|
|
require supports_reverse
|
|
|
|
standard_testfile
|
|
|
|
if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
|
|
return -1
|
|
}
|
|
|
|
runto_main
|
|
|
|
if [supports_process_record] {
|
|
# Activate process record/replay
|
|
gdb_test_no_output "record" "turn on process record"
|
|
}
|
|
|
|
# plain vanilla step/next (no count)
|
|
|
|
gdb_test "next" ".*NEXT TEST 1.*" "next test 1"
|
|
gdb_test "step" ".*STEP TEST 1.*" "step test 1"
|
|
|
|
# step/next with count
|
|
|
|
gdb_test "next 2" ".*NEXT TEST 2.*" "next test 2"
|
|
gdb_test "step 3" ".*STEP TEST 2.*" "step test 2"
|
|
|
|
# Next through a recursive function call.
|
|
gdb_test "next 2" "NEXT OVER THIS CALL.*" "next over recursion"
|
|
|
|
# step over call
|
|
|
|
gdb_test "next" ".*STEP INTO THIS CALL.*" "next over call"
|
|
|
|
# step into call
|
|
|
|
gdb_test "step" ".*ARRIVED IN CALLEE.*" "step into call"
|
|
|
|
# finish out of call
|
|
|
|
set test_message "finish out of fn call"
|
|
gdb_test_multiple "finish" "$test_message" {
|
|
-re "FINISH TEST.*$gdb_prompt $" {
|
|
pass "$test_message"
|
|
}
|
|
-re "STEP INTO THIS CALL.*$gdb_prompt $" {
|
|
send_gdb "step\n"
|
|
exp_continue
|
|
}
|
|
}
|
|
|
|
# stepi over flat code (no calls)
|
|
|
|
set test_message "simple stepi"
|
|
gdb_test_multiple "stepi" "$test_message" {
|
|
-re "STEPI TEST.*$gdb_prompt $" {
|
|
pass "$test_message"
|
|
}
|
|
-re "FINISH TEST.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "NEXTI TEST.*$gdb_prompt $" {
|
|
fail "$test_message (too far)"
|
|
}
|
|
}
|
|
|
|
# stepi into a function call
|
|
|
|
set alphanum_re "\[a-zA-Z0-9\]"
|
|
set pic_thunk_re "__$alphanum_re*\\.get_pc_thunk\\.$alphanum_re* \\(\\)"
|
|
set test_message "stepi into function call"
|
|
gdb_test_multiple "stepi" "$test_message" {
|
|
-re "ARRIVED IN CALLEE.*$gdb_prompt $" {
|
|
pass "$test_message"
|
|
}
|
|
-re "NEXTI TEST.*$gdb_prompt $" {
|
|
fail "$test_message (too far)"
|
|
}
|
|
-re "RETURN FROM CALLEE.*$gdb_prompt $" {
|
|
fail "$test_message (too far)"
|
|
}
|
|
-re "ENTER CALLEE.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "STEPI TEST.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "$pic_thunk_re.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
}
|
|
|
|
# stepi thru return of a function call
|
|
|
|
set test_message "stepi back from function call"
|
|
gdb_test_multiple "stepi" "$test_message" {
|
|
-re -wrap "NEXTI TEST.*" {
|
|
pass "$test_message"
|
|
}
|
|
-re "ARRIVED IN CALLEE.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "RETURN FROM CALLEE.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "STEPI TEST.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "ENTER CALLEE.*$gdb_prompt $" {
|
|
fail "$test_message (too far)"
|
|
}
|
|
}
|
|
|
|
###
|
|
###
|
|
###
|
|
|
|
# Set reverse execution direction
|
|
gdb_test_no_output "set exec-dir reverse" "set reverse execution"
|
|
|
|
# stepi backward thru return and into a function
|
|
|
|
set stepi_location [gdb_get_line_number "ARRIVED IN CALLEE" "$srcfile"]
|
|
set test_message "reverse stepi thru function return"
|
|
gdb_test_multiple "stepi" "$test_message" {
|
|
-re "NEXTI TEST.*$gdb_prompt $" {
|
|
fail "$test_message (start statement)"
|
|
}
|
|
-re "RETURN FROM CALLEE.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "$hex\[ \t\]*$stepi_location.*ARRIVED IN CALLEE.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "ARRIVED IN CALLEE.*$gdb_prompt $" {
|
|
pass "$test_message"
|
|
}
|
|
-re "ENTER CALLEE.*$gdb_prompt $" {
|
|
fail "$test_message (too far)"
|
|
}
|
|
-re "STEPI TEST.*$gdb_prompt $" {
|
|
fail "$test_message (too far)"
|
|
}
|
|
}
|
|
|
|
# stepi backward out of a function call
|
|
|
|
# When testing stepi, we dont want to infinitely step if we're not moving
|
|
# so we store the starting PC, in case we land on the same line as above
|
|
set start_pc [get_hexadecimal_valueof "\$pc" 0 "get PC at start"]
|
|
set get_pc_count 0
|
|
set stepi_location [gdb_get_line_number "STEPI TEST" "$srcfile"]
|
|
set test_message "reverse stepi from a function call"
|
|
gdb_test_multiple "stepi" "$test_message" {
|
|
-re "ARRIVED IN CALLEE.*$gdb_prompt $" {
|
|
incr get_pc_count
|
|
if { [get_hexadecimal_valueof "\$pc" 0 "get PC again $get_pc_count"] == $start_pc } {
|
|
fail "$test_message (start statement)"
|
|
} else {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
}
|
|
-re "ENTER CALLEE.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "$pic_thunk_re.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "${hex} in main .*:$stepi_location.*STEPI TEST.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "STEPI TEST.*$gdb_prompt $" {
|
|
pass "$test_message"
|
|
}
|
|
-re "STEP INTO THIS CALL.*$gdb_prompt $" {
|
|
fail "$test_message (too far)"
|
|
}
|
|
}
|
|
|
|
# stepi backward over flat code (no calls)
|
|
|
|
set stepi_location [gdb_get_line_number "FINISH TEST" "$srcfile"]
|
|
set test_message "simple reverse stepi"
|
|
gdb_test_multiple "stepi" "$test_message" {
|
|
-re "STEPI TEST.*$gdb_prompt $" {
|
|
fail "$test_message (start statement)"
|
|
}
|
|
-re "$hex\[ \t\]*$stepi_location.* FINISH TEST.*$gdb_prompt $" {
|
|
send_gdb "stepi\n"
|
|
exp_continue
|
|
}
|
|
-re "$stepi_location.* FINISH TEST.*$gdb_prompt $" {
|
|
pass "$test_message"
|
|
}
|
|
-re "STEP INTO THIS CALL.*$gdb_prompt $" {
|
|
fail "$test_message (too far)"
|
|
}
|
|
}
|
|
|
|
# step backward into function (thru return)
|
|
|
|
gdb_test "step" "(RETURN FROM CALLEE|ARRIVED IN CALLEE).*" \
|
|
"reverse step into fn call"
|
|
|
|
# step backward out of called function (thru call)
|
|
|
|
set test_message "reverse step out of called fn"
|
|
gdb_test_multiple "step" "$test_message" {
|
|
-re "STEP INTO THIS CALL.*.*$gdb_prompt $" {
|
|
pass "$test_message"
|
|
}
|
|
-re "ARRIVED IN CALLEE.*$gdb_prompt $" {
|
|
send_gdb "step\n"
|
|
exp_continue
|
|
}
|
|
-re "ENTER CALLEE.*$gdb_prompt $" {
|
|
send_gdb "step\n"
|
|
exp_continue
|
|
}
|
|
}
|
|
|
|
# Next backward over call.
|
|
|
|
gdb_test "next" ".*NEXT OVER THIS CALL.*" "reverse next over call"
|
|
|
|
set step_out 0
|
|
set max_iterations 1000
|
|
gdb_test_multiple "next" "reverse next over recursion" {
|
|
-re -wrap ".*NEXT OVER THIS RECURSION.*" {
|
|
pass "$gdb_test_name"
|
|
}
|
|
-re -wrap ".*RECURSIVE CALL.*" {
|
|
fail "$gdb_test_name"
|
|
set step_out 1
|
|
}
|
|
}
|
|
if { "$step_out" == 1 } {
|
|
set iterations 0
|
|
gdb_test_multiple "next" "stepping out of recursion" {
|
|
-re -wrap "^main.*NEXT OVER THIS RECURSION.*" {
|
|
set step_out 0
|
|
pass "$gdb_test_name"
|
|
}
|
|
-re -wrap "^\[0-9\].*" {
|
|
incr iterations
|
|
if { $iterations == $max_iterations } {
|
|
fail "$gdb_test_name (reached $max_iterations iterations)"
|
|
return
|
|
}
|
|
|
|
send_gdb "next\n"
|
|
exp_continue
|
|
}
|
|
}
|
|
}
|
|
|
|
# Step forward over recursion again so we can test stepping over calls
|
|
# inside the recursion itself.
|
|
gdb_test_no_output "set exec-dir forward" "forward again to test recursion"
|
|
gdb_test "next" "NEXT OVER THIS CALL.*" "reverse next over recursion again"
|
|
gdb_test_no_output "set exec-dir reverse" "reverse again to test recursion"
|
|
|
|
gdb_test "step" ".*EXIT RECURSIVE FUNCTION.*" "enter recursive function"
|
|
set seen_recursive_call 0
|
|
set iterations 0
|
|
gdb_test_multiple "next" "step over recursion inside the recursion" {
|
|
-re -wrap ".*RECURSIVE CALL.*" {
|
|
incr seen_recursive_call
|
|
incr iterations
|
|
if { $iterations == $max_iterations } {
|
|
fail "$gdb_test_name (reached $max_iterations iterations)"
|
|
return
|
|
}
|
|
|
|
send_gdb "next\n"
|
|
exp_continue
|
|
}
|
|
-re -wrap ".*NEXT OVER THIS RECURSION.*" {
|
|
gdb_assert {"$seen_recursive_call" == 1} \
|
|
"step over recursion inside the recursion"
|
|
}
|
|
-re -wrap "^\[0-9\].*" {
|
|
incr iterations
|
|
if { $iterations == $max_iterations } {
|
|
fail "$gdb_test_name (reached $max_iterations iterations)"
|
|
return
|
|
}
|
|
|
|
send_gdb "next\n"
|
|
exp_continue
|
|
}
|
|
}
|
|
|
|
# step/next backward with count
|
|
|
|
gdb_test "step 3" ".*REVERSE STEP TEST 1.*" "reverse step test 1"
|
|
gdb_test "next 2" ".*REVERSE NEXT TEST 1.*" "reverse next test 1"
|
|
|
|
# step/next backward without count
|
|
|
|
gdb_test "step" ".*STEP TEST 1.*" "reverse step test 2"
|
|
gdb_test "next" ".*NEXT TEST 1.*" "reverse next test 2"
|
|
|
|
|
|
|
|
# Finish test by running forward to the end.
|
|
# FIXME return to this later...
|
|
# gdb_test_no_output "set exec-dir forward" "set forward execution"
|
|
# gdb_continue_to_end "step-reverse.exp"
|
|
|