| # Copyright 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/>. */ |
| |
| # Single step through a simple (empty) function that was compiled |
| # without DWARF debug information. |
| # |
| # At each instruction check that the frame-id, and frame base address, |
| # are calculated correctly. |
| # |
| # Additionally, check we can correctly unwind to the previous frame, |
| # and that the previous stack-pointer value, and frame base address |
| # value, can be calculated correctly. |
| |
| standard_testfile .c -foo.c |
| |
| if {[prepare_for_testing_full "failed to prepare" \ |
| [list ${testfile} debug \ |
| $srcfile {debug} $srcfile2 {nodebug}]]} { |
| return -1 |
| } |
| |
| if ![runto_main] then { |
| return 0 |
| } |
| |
| # Return a two element list, the first element is the stack-pointer |
| # value (from the $sp register), and the second element is the frame |
| # base address (from the 'info frame' output). |
| proc get_sp_and_fba { testname } { |
| with_test_prefix "get \$sp and frame base $testname" { |
| set sp [get_hexadecimal_valueof "\$sp" "*UNKNOWN*"] |
| |
| set fba "" |
| gdb_test_multiple "info frame" "" { |
| -re -wrap ".*Stack level ${::decimal}, frame at ($::hex):.*" { |
| set fba $expect_out(1,string) |
| } |
| } |
| |
| return [list $sp $fba] |
| } |
| } |
| |
| # Return the frame-id of the current frame, collected using the 'maint |
| # print frame-id' command. |
| proc get_fid { } { |
| set fid "" |
| gdb_test_multiple "maint print frame-id" "" { |
| -re -wrap ".*frame-id for frame #${::decimal}: (.*)" { |
| set fid $expect_out(1,string) |
| } |
| } |
| return $fid |
| } |
| |
| # Record the current stack-pointer, and the frame base address. |
| lassign [get_sp_and_fba "in main"] main_sp main_fba |
| set main_fid [get_fid] |
| |
| # Now enter the foo function. |
| gdb_breakpoint "*foo" |
| gdb_continue_to_breakpoint "enter foo" |
| |
| # Figure out the range of addresses covered by this function. |
| set last_addr_in_foo "" |
| |
| # The disassembly of foo on PowerPC looks like: |
| # Dump of assembler code for function foo: |
| # => 0x00000000100006dc <+0>: std r31,-8(r1) |
| # 0x00000000100006e0 <+4>: stdu r1,-48(r1) |
| # 0x00000000100006e4 <+8>: mr r31,r1 |
| # 0x00000000100006e8 <+12>: nop |
| # 0x00000000100006ec <+16>: addi r1,r31,48 |
| # 0x00000000100006f0 <+20>: ld r31,-8(r1) |
| # 0x00000000100006f4 <+24>: blr |
| # 0x00000000100006f8 <+28>: .long 0x0 |
| # 0x00000000100006fc <+32>: .long 0x0 |
| # 0x0000000010000700 <+36>: .long 0x1000180 |
| # End of assembler dump. |
| # |
| # The last instruction in function foo is blr. Need to ignore the .long |
| # entries following the blr instruction. |
| |
| gdb_test_multiple "disassemble foo" "" { |
| -re "^disassemble foo\r\n" { |
| exp_continue |
| } |
| |
| -re "^Dump of assembler code for function foo:\r\n" { |
| exp_continue |
| } |
| |
| -re "^...($hex) \[<>+0-9:\s\t\]*\.long\[\s\t\]*\[^\r\n\]*\r\n" { |
| exp_continue |
| } |
| |
| -re "^...($hex) \[^\r\n\]+\r\n" { |
| set last_addr_in_foo $expect_out(1,string) |
| exp_continue |
| } |
| |
| -wrap -re "^End of assembler dump\\." { |
| gdb_assert { ![string equal $last_addr_in_foo ""] } \ |
| "found some addresses in foo" |
| pass $gdb_test_name |
| } |
| } |
| |
| # Record the current stack-pointer, and the frame base address. |
| lassign [get_sp_and_fba "in foo"] foo_sp foo_fba |
| set foo_fid [get_fid] |
| |
| for { set i_count 1 } { true } { incr i_count } { |
| with_test_prefix "instruction ${i_count}" { |
| |
| # The current stack-pointer value can legitimately change |
| # throughout the lifetime of a function, so we don't check the |
| # current stack-pointer value. But the frame base address |
| # should not change, so we do check for that. |
| lassign [get_sp_and_fba "for foo"] sp_value fba_value |
| gdb_assert { $fba_value == $foo_fba } |
| |
| # The frame-id should never change within a function, so check |
| # that now. |
| set fid [get_fid] |
| gdb_assert { [string equal $fid $foo_fid] } \ |
| "check frame-id matches" |
| |
| # Check that the previous frame is 'main'. |
| gdb_test "bt 2" "\r\n#1\\s+\[^\r\n\]+ in main \\(\\) .*" |
| |
| # Move up the stack (to main). |
| gdb_test "up" \ |
| "\r\n#1\\s+\[^\r\n\]+ in main \\(\\) .*" |
| |
| # Check we can unwind the stack-pointer and the frame base |
| # address correctly. |
| lassign [get_sp_and_fba "for main"] sp_value fba_value |
| gdb_assert { $sp_value == $main_sp } |
| gdb_assert { $fba_value == $main_fba } |
| |
| # Check we have a consistent value for main's frame-id. |
| set fid [get_fid] |
| gdb_assert { [string equal $fid $main_fid] } |
| |
| # Move back to the inner most frame. |
| gdb_test "frame 0" ".*" |
| |
| set pc [get_hexadecimal_valueof "\$pc" "*UNKNOWN*"] |
| if { $pc == $last_addr_in_foo } { |
| break |
| } |
| |
| if { $i_count > 100 } { |
| # We expect a handful of instructions, if we reach 100, |
| # something is going wrong. Avoid an infinite loop. |
| fail "exceeded max number of instructions" |
| break |
| } |
| |
| gdb_test "stepi" ".*" |
| } |
| } |