From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id AYyyFlOHqWlhjxYAWB0awg (envelope-from ) for ; Thu, 05 Mar 2026 08:38:27 -0500 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=VsQ6pNez; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 570701E089; Thu, 05 Mar 2026 08:38:27 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 3980C1E089 for ; Thu, 05 Mar 2026 08:38:26 -0500 (EST) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id BCB5D4BA23DF for ; Thu, 5 Mar 2026 13:38:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BCB5D4BA23DF Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=VsQ6pNez Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 01CEA4BA23CF for ; Thu, 5 Mar 2026 13:37:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 01CEA4BA23CF Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 01CEA4BA23CF Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1772717852; cv=none; b=fTrUYcXCXaQzE6jar8Nn2vNCvgxTX9ByQQ78FfZbeW95l/KYbO00e2e0nr8NYeDlEruE9GuZHUxIm5JsGRTjRkLNOMJFmP5ksWvaU56kMRVgemsfNwwgGtgY5A3spYgcTv1vgorQdrtvG2hOCNCqnHswHSPf4vpSl7YxSH/QtqY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1772717852; c=relaxed/simple; bh=Z6uPlXhWM0lyolrl/HlO41H5/WntDyThTBQrtA8MraM=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=hMFI5h9JziTVFgAsdHERzhD2epFxI/uBzGiKvU/tM4shRUrGzdztYtADA5UkG6j+jHAbEZhaJ/6AiguDkykDiStrtDS7R9Bj++ua5STOfpR60Ekt+D3DsX6c7whuyKxtCfTXAC/+cKq98XxADaPwV+xPjr0HYSAL9bmDc1oRG/4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 01CEA4BA23CF DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1772717851; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=g/ozssOjhZZee/5qxcIT51W3VUzcBvZxrvFxQqstTlM=; b=VsQ6pNez9C/hc0CXcoO4cj3NVOHsJXwmVn9WvQ7DdopdFm8IcWn3Aj4rg/EY5Qv+YFKmUt HEX799Iplp3nY+KbQFCusN7Wcw0eeotDP2Hh+hjXGDALf+o+EY3NwmwlUSgeEGtSiULjzB AFkUY3lSgOHFn3pVk9evtwpw0T+sfo0= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-513-Q2YtexgDPdG8336-RH0dSA-1; Thu, 05 Mar 2026 08:37:29 -0500 X-MC-Unique: Q2YtexgDPdG8336-RH0dSA-1 X-Mimecast-MFC-AGG-ID: Q2YtexgDPdG8336-RH0dSA_1772717848 Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-439b3b408f9so3920340f8f.0 for ; Thu, 05 Mar 2026 05:37:29 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772717848; x=1773322648; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=g/ozssOjhZZee/5qxcIT51W3VUzcBvZxrvFxQqstTlM=; b=Zz8txPu0LkRJzwxyV11s5g4E/afMewCe5qq4eyyNZQ+V/CynfYSTdhpPJWlijyyU1A DJm8/onz+lVS/qW3u2YSFpzVG5u7JsWk/2sSjzL1RD/+0pC+uQ5ulHfb7Bf6vaeyopnA eJiShuuVaVSj8iZyRoCnCamwdvN+LFPu3p4idb5F3rEBDZ0VJsq1dubbzXAKWm7CoorT oa8GM9yNLn7Z9cFLQBmb8khb5wK/MqJ4WvjfM6r1sst6hD6V/6CdmZm6ttkANtFBulcz tl7t46Uds+qLTy3IEPgkfHR8UTzjtduOslYHOe9c7VdTaEyQNE8KLsw2zueHLQ9K6wo/ 78Mg== X-Gm-Message-State: AOJu0YxTVUo9K5B9t8w9JwW4peEmNSSgRsNJpaC9xWly+7cspBdaXQTj Xeyo5xEdv8oL32rBNMFE6KPqG/dTPnbhlQVqbtGHufdBlyL5wGLMjXZ6L+CQ1ZIwbur9xNyZdc9 E1U+SC0Pu+QeRvpzipQJ1Oo4KD4nsoDtCUZM48a/XCpOPELbyFoXJnVjEuppfhaBF1x42g4g7qS 73vqsgV65gxupI+A6k6Dg+ZhBmtrC+BmvnEAfTljEL3DLDbLE= X-Gm-Gg: ATEYQzykf5U8zqPQ5XpHLGppXb6tRkcvrhd7ZhCtw1WfAPaSA3gSg7Q3+/ljz0+yQPr /sOzWGZDbt0MtwlrEhiJspPLc15omI8GeY87kywoCsnbaMo8eV26cqRI+NRFZTb8BgQvmrDDLCC 7vQ55+wj0FcD5kLA7UXLax3VCC4MJHRWqzeKtK7lFluANWMR+zw3ul4MKcWp/yGlHyVWBTt8sr4 RiI682yzO5gwK8eBQLA/9pvXBev1HHKHFvhGE5DPd94k5grb0ChWvMs9iDRlL6KdVoXJs5LBhFt E4e+0Flv+TwrblImBm6nkWNX13/TesCsQzMSvEhTan2N124Db6lmCtzx572KqwWz67lSdzIF3NC 0Z0HVtye2SXdsJJqI X-Received: by 2002:a5d:588c:0:b0:439:beee:43a6 with SMTP id ffacd0b85a97d-439c7f79edbmr11267124f8f.18.1772717847566; Thu, 05 Mar 2026 05:37:27 -0800 (PST) X-Received: by 2002:a5d:588c:0:b0:439:beee:43a6 with SMTP id ffacd0b85a97d-439c7f79edbmr11267055f8f.18.1772717846948; Thu, 05 Mar 2026 05:37:26 -0800 (PST) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439c1fc577bsm19592134f8f.19.2026.03.05.05.37.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Mar 2026 05:37:26 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Eli Zaretskii Subject: [PATCHv2 3/4] gdb/python: don't allow FinishBreakpoints for inline frames Date: Thu, 5 Mar 2026 13:37:17 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: nqF3NDd8skhKZuq9MirvXBNUwp57Ue7KaRhU5ZcXi-k_1772717848 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Creating a Python gdb.FinishBreakpoint for an inline frame doesn't work. If we look at the 'finish' command, in the finish_command function (infcmd.c) then we see that GDB handles inline frames very different to non-inline frames. For non-inline frames GDB creates a temporary breakpoint and then resumes the inferior until the breakpoint is hit. But for inline frames, GDB steps forward until we have left the inline frame. When it comes to gdb.FinishBreakpoint we only have the "create a temporary breakpoint" mechanism; that is, after all, what the FinishBreakpoint is, it's a temporary breakpoint placed at the return address in the caller. Currently, when a FinishBreakpoint is created within an inline frame, GDB ends up creating the breakpoint at the current $pc. As a result the breakpoint will not be hit before the current function exits (unless there's a loop going on, but that's not the point). We could imagine what a solution to this problem would look like, GDB would need to figure out the set of addresses for all possible exit points from the inline function, and place a breakpoint at each of these locations. I don't propose doing that in this commit. Instead, I plan to update the docs to note that creating a FinishBreakpoint within an inline frame is not allowed, and I will catch this case within bpfinishpy_init (python/py-finishbreakpoint.c) and throw an error. Though the error is new, all I'm doing is raising an error for a case that never worked. There's a new test to cover this case. Reviewed-By: Eli Zaretskii --- gdb/doc/python.texi | 3 + gdb/python/py-finishbreakpoint.c | 8 +- .../gdb.python/py-finish-breakpoint-inline.c | 58 +++++++ .../py-finish-breakpoint-inline.exp | 147 ++++++++++++++++++ .../gdb.python/py-finish-breakpoint-inline.py | 27 ++++ 5 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint-inline.c create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint-inline.exp create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint-inline.py diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 009ac7a3f9f..2df3b7c0423 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -7282,6 +7282,9 @@ Finish Breakpoints in Python Finish breakpoints are thread specific and must be create with the right thread selected. +It is not possible to create a @code{gdb.FinishBreakpoint} for an +inline frame (@pxref{Inline Functions}). + @defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]}) Create a finish breakpoint at the return address of the @code{gdb.Frame} object @var{frame}. If @var{frame} is not provided, this defaults to the diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c index 4af8ec6c75f..834f85037c0 100644 --- a/gdb/python/py-finishbreakpoint.c +++ b/gdb/python/py-finishbreakpoint.c @@ -192,10 +192,16 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs) PyErr_SetString (PyExc_ValueError, _("Invalid ID for the `frame' object.")); } + else if (get_frame_type (frame) == INLINE_FRAME) + { + PyErr_SetString + (PyExc_ValueError, + _("Unable to create FinishBreakpoint for inline frame.")); + } else { prev_frame = get_prev_frame (frame); - if (prev_frame == 0) + if (prev_frame == nullptr) { PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" not " diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-inline.c b/gdb/testsuite/gdb.python/py-finish-breakpoint-inline.c new file mode 100644 index 00000000000..c955917a873 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-inline.c @@ -0,0 +1,58 @@ +/* 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 . */ + +/* 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. */ +} diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-inline.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint-inline.exp new file mode 100644 index 00000000000..d1c3f3de8f0 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-inline.exp @@ -0,0 +1,147 @@ +# 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 . + +# 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 + } +} diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-inline.py b/gdb/testsuite/gdb.python/py-finish-breakpoint-inline.py new file mode 100644 index 00000000000..e493dfa4017 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-inline.py @@ -0,0 +1,27 @@ +# 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 . + + +import gdb + + +class MyFinishBreakpoint(gdb.FinishBreakpoint): + def stop(self): + print("Stopped at MyFinishBreakpoint") + print("Return value is {}".format(self.return_value)) + return True + + +print("Python script imported") -- 2.25.4