Print stack frames from stack (GDB)

Scenario

Application code

#include <cstdio>

int add(int n1, int n2)
{
    int sum = n1 + n2;
    printf("%d", sum);
    return sum;
}

int main()
{
    int n1 = 3;
    int n2 = 8;
    int sum = add(n1, n2);
    printf("%d", sum);
    return 0;
}

Print frame script

import gdb

class StackframePrinter(gdb.Command):
    def __init__(self):
        super().__init__("print-stackframe", gdb.COMMAND_USER)

    def invoke(self, arg, from_tty):
        cur = gdb.selected_frame()

        try:
            sp = int(cur.read_register("rsp"))
            print(f"[Debug] Current SP: 0x{sp:x}")
        except gdb.error as e:
            print(f"[Error] Cannot read current SP: {e}")
            return

        prev_sp = None
        caller = cur.older()
        if caller:
            saved = gdb.selected_frame()
            try:
                prev_sp = int(caller.read_register("rsp"))
                print(f"[Debug] Caller SP: 0x{prev_sp:x}")
            except gdb.error as e:
                print(f"[Warn] Could not read caller SP: {e}")

        if prev_sp and sp < prev_sp:
            count = (prev_sp - sp) // 8
            addr = sp
            print(f"[Debug] Print {count} stack entries at 0x{addr:x}")
            try:
                gdb.execute(f"x/{count}gx {addr}")
            except gdb.error as e:
                print(f"[Error] Memory dump failed: {e}")
        elif prev_sp == sp:
            print("[Info] Frame optimized.")
        else:
            print("[Error] Corrupted stack.")

StackframePrinter()

Analysis

GDB session

$ lscpu | grep Arch
Architecture:                         x86_64
$ g++ -g main.cpp
$ gdb a.out 
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
...
(gdb) b 7
Breakpoint 1 at 0x1164: file main.cpp, line 7.
(gdb) r
Starting program: /home/max.sperling/Develop/test/a.out 

Breakpoint 1, add (n1=3, n2=8) at main.cpp:7
7	    return sum;
(gdb) info frame
Stack level 0, frame at 0x7fffffffe270:
 rip = 0x555555555164 in add (main.cpp:7); saved rip = 0x55555555518e
 called by frame at 0x7fffffffe290
 source language c++.
 Arglist at 0x7fffffffe260, args: n1=3, n2=8
 Locals at 0x7fffffffe260, Previous frame's sp is 0x7fffffffe270
 Saved registers:
  rbp at 0x7fffffffe260, rip at 0x7fffffffe268
(gdb) info frame 1
Stack frame at 0x7fffffffe290:
 rip = 0x55555555518e in main (main.cpp:14); saved rip = 0x7ffff7e01d7a
 caller of frame at 0x7fffffffe270
 source language c++.
 Arglist at 0x7fffffffe280, args: 
 Locals at 0x7fffffffe280, Previous frame's sp is 0x7fffffffe290
 Saved registers:
  rbp at 0x7fffffffe280, rip at 0x7fffffffe288
(gdb) source stackframe_printer.py 
(gdb) print-stackframe
[Debug] Current SP: 0x7fffffffe240
[Debug] Caller SP: 0x7fffffffe270
[Debug] Print 6 stack entries at 0x7fffffffe240
0x7fffffffe240:	0x0000555555554040	0x0000000300000008
0x7fffffffe250:	0x0000000000000000	0x0000000b00000000
0x7fffffffe260:	0x00007fffffffe280	0x000055555555518e

Frame 0

0x7fffffffe240:	0x0000555555554040	0x0000000300000008
0x7fffffffe250:	0x0000000000000000	0x0000000b00000000
0x7fffffffe260:	0x00007fffffffe280	0x000055555555518e

Unused preallocated uninitialized memory
Local Var 2
Local Var 1
Local Var 0
Caller RBP
Caller RIP