--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+/* Global used to create filler code within functions. */
+volatile int global_var = 1;
+
+static int __attribute__ ((noinline, noclone))
+baz (int arg)
+{
+ arg += global_var;
+ return arg;
+}
+
+static inline int __attribute__ ((__always_inline__))
+bar (int arg)
+{
+ arg += global_var;
+ arg = baz (arg); /* Finish location. */
+ arg -= global_var;
+ return arg;
+}
+
+static inline int __attribute__ ((__always_inline__))
+foo (int arg)
+{
+ arg += global_var;
+ arg = bar (arg);
+ arg -= global_var;
+ return arg;
+}
+
+int
+main (void)
+{
+ int ans;
+
+ ++global_var;
+ ++global_var;
+ ans = foo (42);
+ ++global_var;
+ ++global_var;
+ ans += global_var;
+ return ans; /* Final breakpoint. */
+}
--- /dev/null
+# 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 attempting to create a FinishBreakpoint within an inline
+# function will fail.
+#
+# For inline functions the 'finish' command steps forward until we are
+# outside the inline function.
+#
+# For FinishBreakpoints though we need to pick an address an place a
+# breakpoint there. Currently GDB doesn't know where to place such a
+# breakpoint for an inline function, so our solution is to prevent
+# creation of FinishBreakpoints for inline frames.
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile]} {
+ return
+}
+
+if {![runto_main]} {
+ return
+}
+
+# Source the Python script.
+set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+gdb_test "source $pyfile" "Python script imported" "import python scripts"
+
+# Breakpoint locations needed for this test.
+gdb_breakpoint foo
+gdb_breakpoint bar
+gdb_breakpoint baz
+set final_lineno [gdb_get_line_number "Final breakpoint."]
+gdb_breakpoint $final_lineno
+
+# Depending on how the code is compiled, and exactly where the finish
+# breakpoint is placed, the breakpoint could potentially be reported
+# on either of these lines.
+set lineno_1 [gdb_get_line_number "Finish location."]
+set lineno_2 [expr {$lineno_1 + 1}]
+set lineno_re "(?:$lineno_1|$lineno_2)"
+
+# Run to 'foo', which is an inline function called from a normal
+# function, and try to create a MyFinishBreakpoint. This should fail.
+gdb_continue_to_breakpoint "breakpoint in foo"
+gdb_test "python MyFinishBreakpoint(gdb.selected_frame())" \
+ "Error occurred in Python: Unable to create FinishBreakpoint for inline frame\\." \
+ "try to create FinishBreakpoint for inline frame, caller is a normal frame"
+
+# Continue to 'bar', which is an inline function called from another
+# inline function, and try to create a MyFinishBreakpoint. This
+# should fail.
+gdb_continue_to_breakpoint "breakpoint in bar"
+gdb_test "python MyFinishBreakpoint(gdb.selected_frame())" \
+ "Error occurred in Python: Unable to create FinishBreakpoint for inline frame\\." \
+ "try to create FinishBreakpoint for inline frame, caller is an inline frame"
+
+# Continue to 'baz', which is a normal function called from an inline
+# function, and create a MyFinishBreakpoint, which we expect to succeed.
+gdb_continue_to_breakpoint "breakpoint in baz"
+gdb_test "python MyFinishBreakpoint(gdb.selected_frame())" \
+ "Temporary breakpoint $decimal at $hex: file \[^\r\n\]+/$srcfile, line $lineno_re\\." \
+ "create FinishBreakpoint normal function, caller is an inline frame"
+
+# Continue and make sure we hit the MyFinishBreakpoint.
+set saw_finish_breakpoint false
+set saw_return_value false
+set saw_breakpoint_location false
+set saw_source_line false
+gdb_test_multiple "continue" "continue to finish breakpoint" {
+ -re "^Stopped at MyFinishBreakpoint\r\n" {
+ set saw_finish_breakpoint true
+ exp_continue
+ }
+ -re "^Return value is 51\r\n" {
+ set saw_return_value true
+ exp_continue
+ }
+ -re "^Breakpoint $decimal, ($hex in )?bar \\(arg=$decimal\\) at \[^\r\n\]+/$srcfile:$lineno_re\r\n" {
+ set saw_breakpoint_location true
+ exp_continue
+ }
+ -re "^$lineno_re\\s+\[^\r\n\]+\r\n" {
+ set saw_source_line true
+ exp_continue
+ }
+ -re "^$gdb_prompt $" {
+ gdb_assert {$saw_finish_breakpoint \
+ && $saw_return_value \
+ && $saw_breakpoint_location \
+ && $saw_source_line } $gdb_test_name
+ }
+ -re "^\[^\r\n\]*\r\n" {
+ exp_continue
+ }
+}
+
+# Continue to the final breakpoint location. We don't expect to see
+# any of the MyFinishBreakpoint output here. If we do then we've hit
+# an unexpected FinishBreakpoint.
+set saw_finish_breakpoint false
+set saw_return_value false
+set saw_breakpoint_location false
+set saw_source_line false
+gdb_test_multiple "continue" "continue to final breakpoint" {
+ -re "^Stopped at MyFinishBreakpoint\r\n" {
+ set saw_finish_breakpoint true
+ exp_continue
+ }
+ -re "^Return value is 51\r\n" {
+ set saw_return_value true
+ exp_continue
+ }
+ -re "^Breakpoint $decimal, main \\(\\) at \[^\r\n\]+/$srcfile:$final_lineno\r\n" {
+ set saw_breakpoint_location true
+ exp_continue
+ }
+ -re "^$final_lineno\\s+\[^\r\n\]+\r\n" {
+ set saw_source_line true
+ exp_continue
+ }
+ -re "^$gdb_prompt $" {
+ gdb_assert {!$saw_finish_breakpoint \
+ && !$saw_return_value \
+ && $saw_breakpoint_location \
+ && $saw_source_line } $gdb_test_name
+ }
+ -re "^\[^\r\n\]*\r\n" {
+ exp_continue
+ }
+}