Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative)
@ 2026-01-21 12:25 Tom de Vries
  2026-01-21 17:10 ` Andrew Burgess
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Tom de Vries @ 2026-01-21 12:25 UTC (permalink / raw)
  To: gdb-patches

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


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative)
  2026-01-21 12:25 [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative) Tom de Vries
@ 2026-01-21 17:10 ` Andrew Burgess
  2026-01-21 17:21   ` Tom de Vries
  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
  2 siblings, 1 reply; 7+ messages in thread
From: Andrew Burgess @ 2026-01-21 17:10 UTC (permalink / raw)
  To: Tom de Vries, gdb-patches

Tom de Vries <tdevries@suse.de> writes:

> 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.

Sort of.  Passing a value from #n to #(n+1) unmodified isn't itself
wrong, or bad, it's just saying that a register hasn't changed between
frames.

In this case, by passing $sp and $pc up the stack unmodified, frame
#(n+1) is going to appear to be an exact duplicate of frame #n, but the
custom unwinder isn't going to trigger from #(n+1), so the standard
DWARF unwinder will kick in instead.

The DWARF unwinder should then compute the exact same frame-id that our
custom unwinder installed for frame #n, which will trigger the frame
cycle which is at the core of this test.

>
> 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}
> ...

So the problem for s390 is the difference in relationship between the
Call Frame Address (CFA), as used in the frame-id, and $sp value for a
function.

On x86-64, for this specific test, the difference between $sp and the
CFA for frames 1, 3, and 5 is the same as the difference between $sp
values between these frames.  There's no reason why this has to be the
case, it just is, and the original form of this test depended on this
fact.

Remember, our custom unwinder, when triggered for frame #n, wants to
generate the actual, real, correct, frame-id for frame #n.  But when the
test was written 'maint print frame-id' wasn't a thing, so the hack that
I used to calculate the frame-id noticed that the $sp to CFA for a frame
was the same as $sp to $sp between frames.  So I calculate the
difference between $sp values (called STACK_ADJUST), and then add this
to the $sp value in frame #n to try and recreate the frame-id.

On s390 however, my hack doesn't work.  The CFA for a frame is 2x the
frame size (on this test, on the machine and compiler I tested with).
And so, when we try to create the frame-id for #n in the custom
unwinder, instead of calculating the correct CFA, we calculate a
different frame-id that happens to be the same as an earlier frame, and
that is why the frame is truncated where it is.

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

I did make an attempt at fixing this, but having read your commit
message, and having written the above reply, I think your approach here
is probably the right one.  It might be worth folding some of the above
text into the commit message though, what do you think?

I'm happy to do this if you'd like me too.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative)
  2026-01-21 17:10 ` Andrew Burgess
@ 2026-01-21 17:21   ` Tom de Vries
  2026-01-26 13:47     ` Andrew Burgess
  0 siblings, 1 reply; 7+ messages in thread
From: Tom de Vries @ 2026-01-21 17:21 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 1/21/26 6:10 PM, Andrew Burgess wrote:
> I did make an attempt at fixing this, but having read your commit
> message, and having written the above reply, I think your approach here
> is probably the right one.  It might be worth folding some of the above
> text into the commit message though, what do you think?
> 

Hi Andrew,

thank you so much for digging into all this.  I agree that your 
observations are worthwhile to record, but ...

> I'm happy to do this if you'd like me too.

I'd prefer it if you would do this, I feel your understanding of the 
subject at hand is at a better/more detailed level than mine.

Thanks,
- Tom


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative)
  2026-01-21 12:25 [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative) Tom de Vries
  2026-01-21 17:10 ` 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
  2 siblings, 0 replies; 7+ messages in thread
From: Kevin Buettner @ 2026-01-24 23:52 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom de Vries, Andrew Burgess

Tom, Andrew,

FWIW, I'm fine with the approach taken here.  I prefer it over my
attempt to fix these failures for s390x.

Regarding the commit log - I'd like to see it written up in a way
where all of the context is supplied, including a description of the
problem being solved.  (I see that Andrew has offered to work on
the commit log and that Tom has accepted - that'd be great, IMO.)

Kevin

On Wed, 21 Jan 2026 13:25:43 +0100
Tom de Vries <tdevries@suse.de> wrote:

> 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
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative)
  2026-01-21 17:21   ` Tom de Vries
@ 2026-01-26 13:47     ` Andrew Burgess
  0 siblings, 0 replies; 7+ messages in thread
From: Andrew Burgess @ 2026-01-26 13:47 UTC (permalink / raw)
  To: Tom de Vries, gdb-patches

Tom de Vries <tdevries@suse.de> writes:

> On 1/21/26 6:10 PM, Andrew Burgess wrote:
>> I did make an attempt at fixing this, but having read your commit
>> message, and having written the above reply, I think your approach here
>> is probably the right one.  It might be worth folding some of the above
>> text into the commit message though, what do you think?
>> 
>
> Hi Andrew,
>
> thank you so much for digging into all this.  I agree that your 
> observations are worthwhile to record, but ...
>
>> I'm happy to do this if you'd like me too.
>
> I'd prefer it if you would do this, I feel your understanding of the 
> subject at hand is at a better/more detailed level than mine.

No problem Tom.  I'll take your patch and update the description.  I'll
get this done this week.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCHv3] gdb: fix gdb.base/inline-frame-cycle-unwind.exp for s390x
  2026-01-21 12:25 [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative) Tom de Vries
  2026-01-21 17:10 ` Andrew Burgess
  2026-01-24 23:52 ` Kevin Buettner
@ 2026-01-28 19:52 ` Andrew Burgess
  2026-01-29  8:11   ` Tom de Vries
  2 siblings, 1 reply; 7+ messages in thread
From: Andrew Burgess @ 2026-01-28 19:52 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Tom de Vries

Tom,

Here's what I propose we push to resolve this.  The code changes are
basically as you posted in v2 with some tweaks to the comments.  The
commit message is updated based on my feedback to your v2.

I'll push this next week, or sooner if you give a +1.

Thanks,
Andrew

---

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
  ...

To understand what's going wrong here, we first need to understand
what this test was trying to do.

The test tries to create a frame-cycle using a custom Python
unwinder.  A frame-cycle occurs when a frame in the backtrace has the
same frame-id as an earlier frame.  As soon as GDB finds a frame with
a frame-id that it has seen before then the backtrace will be
terminated with the message:

   Backtrace stopped: previous frame identical to this frame (corrupt stack?)

A Python frame unwinder does two jobs:

  - It provides the frame-id for a frame #n, and

  - it provides the unwound register values for the previous (#n + 1)
    frame.

For a frame not claimed by a Python frame unwinder, GDB will compute
the frame-id based off of the register values.  Particularly, the $pc
and $sp, or $fp registers (or the architecture's equivalent).

In this test then, our frame unwinder does something a little
strange.  When we want to stop a frame #5, the frame unwinder claims
frame #5.  The frame unwinder then tries to give frame #5 its "normal"
frame-id, but, instead of unwinding the register values, we provide
frame #6 with all of the register values from frame #5 unmodified.

The frame unwinder does not claim frame #6, instead GDB uses its
"normal" logic to compute the frame-id.  As the registers in frame #6
are identical to the values in frame #5, GDB computes the same
frame-id for frame #6 as we supplied for frame #5, and thus a frame
cycle is created.

Notice I said that we try to give frame #5 its "normal" frame-id.

When this test was originally written there was no easy way to access
the frame-id for a frame.  So instead, the test was written to try and
recreate the frame-id based on the frame stack pointers, and the frame
size (difference between subsequent frames).  And this worked fine for
a bunch of common architectures, like x86-64 and AArch64.

But unfortunately this frame-id computation doesn't work on s390.

For this explanation we only care about two parts of the frame-id, the
code address, and the stack address.  The code address part is usually
the start of the function for a particular frame.  Our computation of
this was fine.

The stack address in the frame-id isn't the actual $sp value.
Instead, this is usually the Call Frame Address (CFA) as defined in
the DWARF.  How this is calculated really depends on the DWARF, but is
often influenced by the architectures ABI.

On x86-64 and AArch64 the CFA is usually the $sp immediately on entry
to a frame, before any stack adjustment is performed.  Thus, if we
take the frame size (difference between $sp in two frames), and add
this to the current $sp value, we successfully calculate the CFA,
which we can use in the frame-id.

On s390 though, this is not the case, the calculation of the CFA is
more complex as the s390 ABI requires that caller frames allocate a
160 byte Register Save Area below the stack pointer.  Because of this,
when the Python unwinder ties to calculate the real frame-id for a
frame, it gets the CFA wrong, and inadvertently ends up calculating a
frame-id which matches an earlier frame-id.  In the example above,
frame #5 ends up matching frame #3.  Because frame #4 is an inline
frame GDB ends up terminating the backtrace after frame #3.

The fix for this is actually pretty simple.  Since this test was
originally written GDB has gained the 'maint print frame-id' command.
So instead of trying to calculate the frame-id we can just capture the
frame-ids that GDB generates, and then, once the Python frame unwinder
is in use, we can repeat the previously captured frame-id back to
GDB.

This fixes the issues seen on s390, and doesn't impact testing on the
other architectures.

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     | 74 ++++++++++++++-----
 2 files changed, 59 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..1a3fc5517ad 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,17 @@ 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.  This list is
+# populated when this file is sourced into GDB.
+frame_ids = []
+
+
+# To aid debugging, print the captured FrameId instances.
+def print_frame_ids():
+    for level, fid in enumerate(frame_ids):
+        print(
+            "frame-id for frame #%s: {stack=0x%x,code=0x%x}" % (level, fid.sp, fid.pc)
+        )
 
 
 class FrameId(object):
@@ -49,17 +58,39 @@ 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))
+        # Set the frame-id for this frame to its actual, expected
+        # frame-id, which we captured in the FRAME_IDS list.
+        unwinder = pending_frame.create_unwind_info(frame_ids[stop_at_level])
 
+        # Provide the register values for the caller frame, that is,
+        # the frame at 'STOP_AT_LEVEL + 1'.
+        #
+        # We forward all of the register values unchanged from this
+        # frame.
+        #
+        # What this means is that, as far as GDB is concerned, the
+        # caller frame will appear to be identical to this frame.  Of
+        # particular importance, we send $pc and $sp unchanged to the
+        # caller frame.
+        #
+        # Because the caller frame has the same $pc and $sp as this
+        # frame, GDB will compute the same frame-id for the caller
+        # frame as we just supplied for this frame (above).  This
+        # creates the artificial frame cycle which is the whole point
+        # of this test.
+        #
+        # NOTE: Forwarding all registers unchanged like this to the
+        # caller frame is not how you'd normally write a frame
+        # unwinder.  Some registers might indeed be unmodified between
+        # frames, but we'd usually expect the $sp and/or the $pc to
+        # change.  This test is deliberately doing something weird in
+        # order to force a cycle, and so test GDB.
         for reg in pending_frame.architecture().registers("general"):
             val = pending_frame.read_register(reg)
             # Having unavailable registers leads to a fall back to the standard
@@ -76,11 +107,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: c02c23175e47f29907a0a3af3f6e6a404bc99719
-- 
2.25.4


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCHv3] gdb: fix gdb.base/inline-frame-cycle-unwind.exp for s390x
  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
  0 siblings, 0 replies; 7+ messages in thread
From: Tom de Vries @ 2026-01-29  8:11 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 1/28/26 8:52 PM, Andrew Burgess wrote:
> Tom,
> 
> Here's what I propose we push to resolve this.  The code changes are
> basically as you posted in v2 with some tweaks to the comments.  The
> commit message is updated based on my feedback to your v2.
> 
> I'll push this next week, or sooner if you give a +1.
> 

Hi Andrew,

I read through this, and other than one typo noted below, LGTM.

So, +1.

> Thanks,
> Andrew
> 
> ---
> 
> 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
>    ...
> 
> To understand what's going wrong here, we first need to understand
> what this test was trying to do.
> 
> The test tries to create a frame-cycle using a custom Python
> unwinder.  A frame-cycle occurs when a frame in the backtrace has the
> same frame-id as an earlier frame.  As soon as GDB finds a frame with
> a frame-id that it has seen before then the backtrace will be
> terminated with the message:
> 
>     Backtrace stopped: previous frame identical to this frame (corrupt stack?)
> 
> A Python frame unwinder does two jobs:
> 
>    - It provides the frame-id for a frame #n, and
> 
>    - it provides the unwound register values for the previous (#n + 1)
>      frame.
> 
> For a frame not claimed by a Python frame unwinder, GDB will compute
> the frame-id based off of the register values.  Particularly, the $pc
> and $sp, or $fp registers (or the architecture's equivalent).
> 
> In this test then, our frame unwinder does something a little
> strange.  When we want to stop a frame #5, the frame unwinder claims

a -> at

Thanks,
- Tom

> frame #5.  The frame unwinder then tries to give frame #5 its "normal"
> frame-id, but, instead of unwinding the register values, we provide
> frame #6 with all of the register values from frame #5 unmodified.
> 
> The frame unwinder does not claim frame #6, instead GDB uses its
> "normal" logic to compute the frame-id.  As the registers in frame #6
> are identical to the values in frame #5, GDB computes the same
> frame-id for frame #6 as we supplied for frame #5, and thus a frame
> cycle is created.
> 
> Notice I said that we try to give frame #5 its "normal" frame-id.
> 
> When this test was originally written there was no easy way to access
> the frame-id for a frame.  So instead, the test was written to try and
> recreate the frame-id based on the frame stack pointers, and the frame
> size (difference between subsequent frames).  And this worked fine for
> a bunch of common architectures, like x86-64 and AArch64.
> 
> But unfortunately this frame-id computation doesn't work on s390.
> 
> For this explanation we only care about two parts of the frame-id, the
> code address, and the stack address.  The code address part is usually
> the start of the function for a particular frame.  Our computation of
> this was fine.
> 
> The stack address in the frame-id isn't the actual $sp value.
> Instead, this is usually the Call Frame Address (CFA) as defined in
> the DWARF.  How this is calculated really depends on the DWARF, but is
> often influenced by the architectures ABI.
> 
> On x86-64 and AArch64 the CFA is usually the $sp immediately on entry
> to a frame, before any stack adjustment is performed.  Thus, if we
> take the frame size (difference between $sp in two frames), and add
> this to the current $sp value, we successfully calculate the CFA,
> which we can use in the frame-id.
> 
> On s390 though, this is not the case, the calculation of the CFA is
> more complex as the s390 ABI requires that caller frames allocate a
> 160 byte Register Save Area below the stack pointer.  Because of this,
> when the Python unwinder ties to calculate the real frame-id for a
> frame, it gets the CFA wrong, and inadvertently ends up calculating a
> frame-id which matches an earlier frame-id.  In the example above,
> frame #5 ends up matching frame #3.  Because frame #4 is an inline
> frame GDB ends up terminating the backtrace after frame #3.
> 
> The fix for this is actually pretty simple.  Since this test was
> originally written GDB has gained the 'maint print frame-id' command.
> So instead of trying to calculate the frame-id we can just capture the
> frame-ids that GDB generates, and then, once the Python frame unwinder
> is in use, we can repeat the previously captured frame-id back to
> GDB.
> 
> This fixes the issues seen on s390, and doesn't impact testing on the
> other architectures.
> 
> 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     | 74 ++++++++++++++-----
>   2 files changed, 59 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..1a3fc5517ad 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,17 @@ 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.  This list is
> +# populated when this file is sourced into GDB.
> +frame_ids = []
> +
> +
> +# To aid debugging, print the captured FrameId instances.
> +def print_frame_ids():
> +    for level, fid in enumerate(frame_ids):
> +        print(
> +            "frame-id for frame #%s: {stack=0x%x,code=0x%x}" % (level, fid.sp, fid.pc)
> +        )
>   
>   
>   class FrameId(object):
> @@ -49,17 +58,39 @@ 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))
> +        # Set the frame-id for this frame to its actual, expected
> +        # frame-id, which we captured in the FRAME_IDS list.
> +        unwinder = pending_frame.create_unwind_info(frame_ids[stop_at_level])
>   
> +        # Provide the register values for the caller frame, that is,
> +        # the frame at 'STOP_AT_LEVEL + 1'.
> +        #
> +        # We forward all of the register values unchanged from this
> +        # frame.
> +        #
> +        # What this means is that, as far as GDB is concerned, the
> +        # caller frame will appear to be identical to this frame.  Of
> +        # particular importance, we send $pc and $sp unchanged to the
> +        # caller frame.
> +        #
> +        # Because the caller frame has the same $pc and $sp as this
> +        # frame, GDB will compute the same frame-id for the caller
> +        # frame as we just supplied for this frame (above).  This
> +        # creates the artificial frame cycle which is the whole point
> +        # of this test.
> +        #
> +        # NOTE: Forwarding all registers unchanged like this to the
> +        # caller frame is not how you'd normally write a frame
> +        # unwinder.  Some registers might indeed be unmodified between
> +        # frames, but we'd usually expect the $sp and/or the $pc to
> +        # change.  This test is deliberately doing something weird in
> +        # order to force a cycle, and so test GDB.
>           for reg in pending_frame.architecture().registers("general"):
>               val = pending_frame.read_register(reg)
>               # Having unavailable registers leads to a fall back to the standard
> @@ -76,11 +107,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: c02c23175e47f29907a0a3af3f6e6a404bc99719


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-01-29  8:12 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-21 12:25 [PATCH v2] [gdb/testsuite] Fix gdb.base/inline-frame-cycle-unwind.exp for s390x (alternative) Tom de Vries
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox