Home | History | Annotate | Line # | Download | only in gdb.reverse
      1 # Copyright (C) 2015-2024 Free Software Foundation, Inc.
      2 #
      3 # This program is free software; you can redistribute it and/or modify
      4 # it under the terms of the GNU General Public License as published by
      5 # the Free Software Foundation; either version 3 of the License, or
      6 # (at your option) any later version.
      7 #
      8 # This program is distributed in the hope that it will be useful,
      9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11 # GNU General Public License for more details.
     12 #
     13 # You should have received a copy of the GNU General Public License
     14 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15 
     16 require supports_reverse !use_gdb_stub
     17 
     18 standard_testfile
     19 
     20 if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
     21 	 [list debug]]} {
     22     return -1
     23 }
     24 if {![runto_main]} {
     25     return
     26 }
     27 
     28 # Read function name from testcases[N].
     29 
     30 proc read_testcase { n } {
     31     global gdb_prompt
     32 
     33     set result -1
     34     gdb_test_multiple "print testcases\[${n}\]" "read name of test case ${n}" {
     35 	-re "\[$\].*= .*<(.*)>.*$gdb_prompt $" {
     36 	    set result $expect_out(1,string)
     37 	}
     38 	-re "$gdb_prompt $" { }
     39     }
     40 
     41     return $result
     42 }
     43 
     44 # In each function FUNC, GDB turns on process record, and single step
     45 # until program goes to the end of the function.  Then, single step
     46 # backward.  In each of forward single step and backward single step,
     47 # the contents of registers are saved, and test compares them.  If
     48 # there is any differences, a FAIL is emitted.
     49 
     50 proc test { func testcase_nr } {
     51     global hex decimal
     52     global gdb_prompt
     53 
     54     with_test_prefix "$func" {
     55 	gdb_start_cmd $testcase_nr
     56 	gdb_test "" "" "wait for prompt"
     57 
     58 	gdb_breakpoint $func
     59 	gdb_test "continue"
     60 
     61 	set last_insn ""
     62 	set test "disassemble $func"
     63 	gdb_test_multiple $test $test {
     64 	    -re ".*($hex) <\\+$decimal>:\[^\r\n\]+\r\nEnd of assembler dump\.\r\n$gdb_prompt $" {
     65 		set last_insn $expect_out(1,string)
     66 	    }
     67 	}
     68 	if { $last_insn == "" } {
     69 	    fail "find the last instruction of function $func"
     70 	}
     71 
     72 	# Activate process record/replay
     73 	gdb_test_no_output "record" "turn on process record"
     74 
     75 	# Registers contents before each forward single step.
     76 	set count 0
     77 	set insn_addr ""
     78 	for {} {$count < 500} {incr count} {
     79 	    set prev_insn_addr $insn_addr
     80 	    set insn_addr ""
     81 	    gdb_test_multiple "x/i \$pc" "" {
     82 		-re ".* ($hex) <.*>:\[ \t\]*(.*)\r\n$gdb_prompt $" {
     83 		    set insn_addr $expect_out(1,string)
     84 		    set insn_array($count) $expect_out(2,string)
     85 		}
     86 	    }
     87 
     88 	    if { $insn_addr == "" } {
     89 		break
     90 	    }
     91 
     92 	    if { $last_insn == $insn_addr } {
     93 		break
     94 	    }
     95 
     96 	    if { $prev_insn_addr == $insn_addr } {
     97 		# Failed to make progress, might have run into SIGILL.
     98 		fail "no progress at: $expect_out(2,string)"
     99 		# Ignore the last instruction recorded
    100 		incr count -1
    101 		break
    102 	    }
    103 
    104 	    set pre_regs($count) [capture_command_output "info all-registers" ""]
    105 	    gdb_test -nopass "si"
    106 	}
    107 
    108 	# Registers contents after each backward single step.
    109 	for {set i [expr $count - 1]} {$i >= 0} {incr i -1} {
    110 	    gdb_test -nopass "reverse-stepi"
    111 	    set post_regs($i) [capture_command_output "info all-registers" ""]
    112 	}
    113 
    114 	# Compare the register contents.
    115 	for {set i 0} {$i < $count} {incr i} {
    116 	    if { ![gdb_assert { [string compare $pre_regs($i) $post_regs($i)] == 0 } \
    117 		      "compare registers on insn $i:$insn_array($i)"] } {
    118 
    119 		foreach pre_line [split $pre_regs($i) \n] post_line [split $post_regs($i) \n] {
    120 		    if { [string compare $pre_line $post_line] } {
    121 			verbose -log " -:$pre_line"
    122 			verbose -log " +:$post_line"
    123 		    }
    124 		}
    125 	    }
    126 	}
    127 	gdb_test "record stop"
    128     }
    129 }
    130 
    131 set n_testcases [get_integer_valueof "n_testcases" 0]
    132 
    133 if { ${n_testcases} == 0 } {
    134     untested "no test"
    135     return 1
    136 }
    137 
    138 for { set i 0 } { ${i} < ${n_testcases} } { incr i } {
    139     set testcase [read_testcase $i]
    140 
    141     test $testcase $i
    142 }
    143