Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Tom de Vries <tdevries@suse.de>
To: gdb-patches@sourceware.org
Subject: [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative)
Date: Wed, 21 Jan 2026 13:25:43 +0100	[thread overview]
Message-ID: <20260121122543.4129049-1-tdevries@suse.de> (raw)

From: Andrew Burgess <aburgess@redhat.com>

With test-case gdb.base/inline-frame-cycle-unwind.exp on s390x-linux, I run
into:
...
(gdb) bt
 #0  inline_func () at inline-frame-cycle-unwind.c:49
 #1  normal_func () at inline-frame-cycle-unwind.c:32
 #2  0x000000000100065c in inline_func () at inline-frame-cycle-unwind.c:45
 #3  normal_func () at inline-frame-cycle-unwind.c:32
 Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) FAIL: $exp: bt: cycle at level 5: backtrace when the unwind is broken \
  at frame 5
...

In contrast, on x86_64-linux, I get:
...
(gdb) bt
 #0  inline_func () at inline-frame-cycle-unwind.c:49
 #1  normal_func () at inline-frame-cycle-unwind.c:32
 #2  0x0000000000401157 in inline_func () at inline-frame-cycle-unwind.c:45
 #3  normal_func () at inline-frame-cycle-unwind.c:32
 #4  0x0000000000401157 in inline_func () at inline-frame-cycle-unwind.c:45
 #5  normal_func () at inline-frame-cycle-unwind.c:32
 Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) PASS: $exp: bt: cycle at level 5: backtrace when the unwind is broken \
  at frame 5
...

Let's first see what happens on x86_64-linux.

The test-case installs a custom unwinder, which gets triggered with
pending_frame.level() == 5.

The responsibility of the unwinder at that point is to:
- calculate a frame ID for the pending frame, and
- provided the register values for the previous frame (with level
  pending_frame.level() + 1), as saved in the pending frame.

However, the custom unwinder does some else.  While it does calculate the
frame ID for the pending frame, instead it provides the register values for
the pending frame, which causes the unwinder to stop.

After adding some debugging prints, we can see that the frame ID calculated by
the custom unwinder:
...
LEVEL: 5
FrameID: sp: 7fffffffcd50, pc: 401116
...
matches the frame ID as calculated by GDB:
...
(gdb) maint print frame-id 5
frame-id for frame #5: {stack=0x7fffffffcd50,code=0x0000000000401116,!special}
...

Now back to s390x-linux.

This time, the frame ID calculated by the custom unwinder:
...
LEVEL: 5
FrameID: sp: 3ffffffe8d0, pc: 1000608
...
does not match the frame ID as calculated by GDB:
...
(gdb) maint print frame-id 5
frame-id for frame #5: {stack=0x3ffffffe970,code=0x0000000001000608,!special}
...

Instead, it matches the frame ID for frame #3:
...
(gdb) maint print frame-id 3
frame-id for frame #3: {stack=0x3ffffffe8d0,code=0x0000000001000608,!special}
...

Fix this by using "maint print frame-id" to get all frame-ids, and using those
instead.

Tested on x86_64-linux and s390x-linux.

Co-Authored-By: Tom de Vries <tdevries@suse.de>
---
 .../gdb.base/inline-frame-cycle-unwind.exp    |  3 +
 .../gdb.base/inline-frame-cycle-unwind.py     | 58 +++++++++++++------
 2 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.exp b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.exp
index cc9585ee325..fd9cba78f9a 100644
--- a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.exp
+++ b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.exp
@@ -72,6 +72,9 @@ gdb_continue_to_breakpoint "stop at test breakpoint"
 gdb_test_no_output "source ${pyfile}"\
     "import python scripts"
 
+# Print the captured frame IDs.
+gdb_test "python print_frame_ids()"
+
 # Test with and without filters.
 foreach bt_cmd { "bt" "bt -no-filters" } {
     with_test_prefix "$bt_cmd" {
diff --git a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py
index 80cfb864f21..09ddfb7e536 100644
--- a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py
+++ b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py
@@ -13,6 +13,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import re
+
 import gdb
 from gdb.unwinder import Unwinder
 
@@ -21,10 +23,14 @@ from gdb.unwinder import Unwinder
 # was written for.
 stop_at_level = None
 
-# Set this to the stack frame size of frames 1, 3, and 5.  These
-# frames will all have the same stack frame size as they are the same
-# function called recursively.
-stack_adjust = None
+# List of FrameId instances, one for each stack frame.
+frame_ids = []
+
+
+def print_frame_ids():
+    for i in range(len(frame_ids)):
+        fid = frame_ids[i]
+        print("frame-id for frame #%s: {stack=0x%x,code=0x%x}" % (i, fid.sp, fid.pc))
 
 
 class FrameId(object):
@@ -49,16 +55,25 @@ class TestUnwinder(Unwinder):
         if stop_at_level is None or pending_frame.level() != stop_at_level:
             return None
 
-        if stack_adjust is None:
-            raise gdb.GdbError("invalid stack_adjust")
+        if len(frame_ids) < stop_at_level:
+            raise gdb.GdbError("not enough parsed frame-ids")
 
         if stop_at_level not in [1, 3, 5]:
             raise gdb.GdbError("invalid stop_at_level")
 
-        sp_desc = pending_frame.architecture().registers().find("sp")
-        sp = pending_frame.read_register(sp_desc) + stack_adjust
-        pc = (gdb.lookup_symbol("normal_func"))[0].value().address
-        unwinder = pending_frame.create_unwind_info(FrameId(sp, pc))
+        # We're unwinding frame #n (n == pending_frame.level()), and are
+        # expected to return:
+        # - a frame ID for frame #n, and
+        # - the register values for frame #(n+1), as saved in frame #n
+        #
+        # Instead, we return the frame ID for frame #n, and the register
+        # values for frame #n.
+        #
+        # This will cause the backtrace stop at level #n, meaning:
+        # - the entry for frame #n will be printed.
+        # - the entry for frame #(n+1) will not be printed, instead
+        #   "Backtrace stopped: <reason>" will be printed.
+        unwinder = pending_frame.create_unwind_info(frame_ids[stop_at_level])
 
         for reg in pending_frame.architecture().registers("general"):
             val = pending_frame.read_register(reg)
@@ -76,11 +91,18 @@ gdb.unwinder.register_unwinder(None, TestUnwinder(), True)
 #
 #   main -> normal_func -> inline_func -> normal_func -> inline_func -> normal_func -> inline_func
 #
-# Compute the stack frame size of normal_func, which has inline_func
-# inlined within it.
-f0 = gdb.newest_frame()
-f1 = f0.older()
-f2 = f1.older()
-f0_sp = f0.read_register("sp")
-f2_sp = f2.read_register("sp")
-stack_adjust = f2_sp - f0_sp
+# Iterate through frames 0 to 6, parse their frame-id and store it
+# into the global FRAME_IDS list.
+for i in range(7):
+    # Get the frame-id in a verbose text form.
+    output = gdb.execute("maint print frame-id %d" % i, to_string=True)
+
+    # Parse the frame-id in OUTPUT, find the stack and code addresses.
+    match = re.search(r"stack=(0x[0-9a-fA-F]+).*?code=(0x[0-9a-fA-F]+)", output)
+    if not match:
+        raise gdb.GdbError("Could not parse frame-id for frame #%d" % i)
+
+    # Create the FrameId object.
+    sp_addr = int(match.group(1), 16)
+    pc_addr = int(match.group(2), 16)
+    frame_ids.append(FrameId(sp_addr, pc_addr))

base-commit: 5606bf89ba974005089bb39bd23e944973fe60eb
-- 
2.51.0


             reply	other threads:[~2026-01-21 12:26 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-21 12:25 Tom de Vries [this message]
2026-01-21 17:10 ` Andrew Burgess
2026-01-21 17:21   ` Tom de Vries
2026-01-26 13:47     ` Andrew Burgess
2026-01-24 23:52 ` Kevin Buettner
2026-01-28 19:52 ` [PATCHv3] gdb: fix gdb.base/inline-frame-cycle-unwind.exp for s390x Andrew Burgess
2026-01-29  8:11   ` Tom de Vries

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260121122543.4129049-1-tdevries@suse.de \
    --to=tdevries@suse.de \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox