Examining the Stack

Viewing the Backtrace

If we set a breakpoint in g and run our program, we can see the current stack with the where command:

(gdb) where
#0  g () at example.c:4
#1  0x000055fc5f7d1184 in f () at example.c:9
#2  0x000055fc5f7d11b3 in main (argc=1, argv=0x7fff3464e5f8) at example.c:14

The frame at #0 is the current execution context, while #1 is its caller, and so on. backtrace and bt are synonyms for where.

If all we are trying to do is track down a segmentation fault, we might not need to do anything other than run your program in GDB and use where to find the offending statement.

Changing the Current Function View

Depending on the specific instruction causing an error, you might need to look in the the calling frame. The up command lets you change the active frame (the function being examined) by switching to the current function’s caller. You can call this multiple times. The reverse of this is down.

For example, if we run up at the previous breakpoint, we see

(gdb) up
#1  0x000055fc5f7d1184 in f () at example.c:9
9      g();

This tells us that we’re now in frame #1, which is line 9 of example.c, in the function f, and we’ve called g(). Running it again gives us

(gdb) up
#2  0x000055fc5f7d11b3 in main (argc=1, argv=0x7fff3464e5f8) at example.c:14
14     f();

which is the point in main where we called f().

Running down twice takes us back to #0 (g):

(gdb) down
#1  0x000055fc5f7d1184 in f () at example.c:9
9      g();
(gdb) down
#0  g () at example.c:4
4      printf("in g()\n");

Inspecting Frame Information

Every function call has an associated stack frame, which holds function arguments, local variables, and any information we need to address these or unwind to the calling function’s frame. We can see this information with info frame. Both the info command and the frame subcommand can be shortened to single characters, so i frame, info f, and i f all behave the same.

Here’s what we see in g:

(gdb) info frame
Stack level 0, frame at 0x7fff3464e4b0:
 rip = 0x55fc5f7d1151 in g (example.c:4); saved rip = 0x55fc5f7d1184
 called by frame at 0x7fff3464e4c0
 source language c.
 Arglist at 0x7fff3464e4a0, args: 
 Locals at 0x7fff3464e4a0, Previous frame's sp is 0x7fff3464e4b0
 Saved registers:
  rbp at 0x7fff3464e4a0, rip at 0x7fff3464e4a8

This tells us where the frame begins (0x7fff3464e4b0), and all valid stack addresses for this frame will have lower values than this (because the stack grows downward). It also tells us the current value of rip, the instruction pointer (pc on ARM platforms), which is the address of the next instruction for the CPU to execute, as well as the saved rip from the caller, which is the address of the next instruction in the caller after this function returns. On AMD64 (the most common 64-bit x86-family architecture), we store the base and instruction pointers for the caller in our frame. For ARM, these are typically only stored if this function is going to call another one.

info frame also takes an optional integer argument, which equates to the frame level as shown by where. That is, info frame 0 will show us information about the stack frame of g, info frame 1 will show us information about the stack frame of f, and info frame 2 will show us information about the stack frame of main.

Synonyms