blob: 6a266abd790fdbc0313e9e9aefffe499afd02728 [file]
# Copyright (C) 2026 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/>.
# Check that FinishBreakpoints are reached when set for a tailcall
# frame.
#
# Frame #0 is never a tailcall frame. As such, we need to do more
# than just stop in a tailcall function and create a finish
# breakpoint. Instead, we need to stop in a function called from a
# tail call function, then walk back up the stack and create the
# finish breakpoint on the tail call frame.
#
# At one point, GDB would create the breakpoint in the correct
# location, but set the frame-id on the breakpoint for the wrong
# frame, with the result that the breakpoint would never trigger.
load_lib gdb-python.exp
require allow_python_tests
standard_testfile
if {[build_executable "failed to build" $testfile $srcfile \
{debug optimize=-O2}]} {
return
}
# For remote host testing.
set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
# Run to 'normal_function' and then try to create a FinishBreakpoint.
#
# When USE_PARENT_FRAME_P is true we use the parent frame, which is
# for tailcall_function, to create the FinishBreakpoint.
#
# When USE_PARENT_FRAME_P is false we use the frame of normal_function
# to create the FinishBreakpoint.
#
# In both cases the finish breakpoint should be placed back in main,
# which is where the inferior should stop when resumed.
proc run_test { use_parent_frame_p } {
clean_restart $::testfile
if {![runto normal_function]} {
return
}
gdb_test "bt" \
[multi_line \
"#0\\s+normal_function\[^\r\n\]+" \
"#1\\s+(?:$::hex in )?tailcall_function\[^\r\n\]+" \
"#2\\s+(?:$::hex in )?main\[^\r\n\]+"] \
"check call stack"
gdb_test "source $::pyfile" "Python script imported" "import python scripts"
set lineno [gdb_get_line_number "Temporary breakpoint here."]
if { $use_parent_frame_p } {
gdb_test_no_output "python frame=gdb.selected_frame().older()" \
"store a reference to the parent frame"
} else {
gdb_test_no_output "python frame=gdb.selected_frame()" \
"store a reference to the current frame"
}
gdb_test "python MyFinishBreakpoint(frame)" \
"Temporary breakpoint $::decimal at $::hex: file \[^\r\n\]+/$::srcfile, line $lineno\\." \
"create finish breakpoint"
set saw_stopped_message false
set saw_breakpoint_line false
set saw_source_line false
set saw_return_value false
gdb_test_multiple "continue" "" {
-re "^Stopped at MyFinishBreakpoint\r\n" {
set saw_stopped_message true
exp_continue
}
-re "^Return value is 43\r\n" {
set saw_return_value true
exp_continue
}
-re "^Breakpoint $::decimal, main \\(\\) at \[^\r\n\]+/$::srcfile:$lineno\r\n" {
set saw_breakpoint_line true
exp_continue
}
-re "^$lineno\\s+\[^\r\n\]+\r\n" {
set saw_source_line true
exp_continue
}
-re "^$::gdb_prompt $" {
gdb_assert { $saw_stopped_message \
&& $saw_breakpoint_line \
&& $saw_source_line \
&& $saw_return_value } $gdb_test_name
}
-re "^\[^\r\n\]*\r\n" {
exp_continue
}
}
}
foreach_with_prefix parent_frame { true false } {
run_test $parent_frame
}