Debugging with GDB

GDB (GNU Debugger) lets you pause a running program, inspect its state, and step through code line by line. It’s the primary tool for diagnosing segfaults, logic errors, and memory corruption in C programs.

Why It Matters

printf debugging doesn’t scale. When a program segfaults inside a library call, or corrupts memory that only crashes later, you need to inspect the actual state — registers, stack frames, memory contents. GDB gives you x-ray vision into a running process.

Setup

gcc -g -O0 program.c -o program   # -g = debug symbols, -O0 = no optimization
gdb ./program                      # launch

Always compile with -g. Optimization (-O2) can reorder code and eliminate variables — use -O0 for debugging.

Essential Commands

CommandShortWhat It Does
break mainb mainBreakpoint at function entry
break file.c:42b file.c:42Breakpoint at specific line
runrStart (or restart) program
run arg1 arg2Start with arguments
nextnStep over (execute line, don’t enter functions)
stepsStep into function call
finishfinRun until current function returns
continuecResume execution until next breakpoint
print xp xPrint variable value
print *ptrDereference and print
print arr[0]@10Print 10 elements starting at arr[0]
backtracebtShow call stack (most useful after crash)
info localsShow all local variables
listlShow source code around current line
quitqExit GDB

Watchpoints

Break when a variable changes, not at a specific line:

(gdb) watch counter        # break when counter changes
(gdb) watch *0x7ffff000    # break when memory at address changes
(gdb) rwatch buffer[0]     # break on READ of buffer[0]

Useful for catching “who modified this variable?” bugs.

Conditional Breakpoints

(gdb) break process_item if i == 99    # only stop on 100th iteration
(gdb) break parser.c:50 if len > 1024  # stop when buffer looks suspicious

Examining Memory

The x command (examine) reads raw memory:

(gdb) x/4xw &variable    # 4 words in hex
(gdb) x/16xb ptr         # 16 bytes in hex
(gdb) x/s string_ptr     # as null-terminated string
(gdb) x/10i $pc          # 10 instructions at program counter

Format: x/<count><format><size> where format is x(hex), d(decimal), s(string), i(instruction) and size is b(byte), h(halfword), w(word), g(giant/8-byte).

Debugging a Crash

Typical workflow when a program segfaults:

$ gdb ./program
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x00401234 in parse_line (line=0x0) at parser.c:47

(gdb) bt
#0  parse_line (line=0x0) at parser.c:47        ← NULL pointer!
#1  process_file (path=0x7fff...) at main.c:23
#2  main (argc=2, argv=0x7fff...) at main.c:8

(gdb) frame 1                    # switch to caller's frame
(gdb) print path                 # inspect what was passed
(gdb) info locals                # see all locals in this frame

Core Dumps

Debug a crash after the fact:

ulimit -c unlimited              # enable core dumps
./program                        # crashes, produces core file
gdb ./program core               # load core dump
(gdb) bt                         # see where it crashed

TUI Mode

(gdb) tui enable                 # split-screen: source + command
(gdb) layout split               # source + assembly
# Ctrl-x a to toggle TUI on/off