blob: d8863ad28955323fac7ac5f4b1551f231390c249 [file]
# 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" ".*"
}
}