| # 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 | |
| set target_remote [gdb_is_target_remote] | |
| 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 | |
| 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 $" { | |
| fail "$test_message (start statement)" | |
| } | |
| -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 | |
| 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 } { | |
| gdb_test_multiple "next" "stepping out of recursion" { | |
| -re -wrap "NEXT OVER THIS RECURSION.*" { | |
| set step_out 0 | |
| } | |
| -re -wrap ".*" { | |
| 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" | |
| if {$target_remote} { | |
| # gdb doesn't record the change of return pointer for remote targets, | |
| # so we can't next forward over functions. | |
| setup_kfail gdb/29745 *-*-* | |
| } | |
| 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" | |
| if {$target_remote} { | |
| # Because of the above mentioned KFAIL, the inferior is now out of sync | |
| setup_kfail gdb/29745 *-*-* | |
| } | |
| gdb_test "step" ".*EXIT RECURSIVE FUNCTION.*" "enter recursive function" | |
| set seen_recursive_call 0 | |
| if {$target_remote} { | |
| # Because of the above mentioned KFAIL, the inferior is now out of sync | |
| # The fail state below will resync the inferior. | |
| setup_kfail gdb/29745 *-*-* | |
| } | |
| gdb_test_multiple "next" "step over recursion inside the recursion" { | |
| -re -wrap ".*RECURSIVE CALL.*" { | |
| incr seen_recursive_call | |
| 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 ".*" { | |
| 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" | |