← All Posts

Debugging Linux Systems with GDB

debugginggdblinuxc

Why GDB Still Matters

In an era of sophisticated IDE debuggers and observability tools, GDB remains the most versatile and lowest-level debugger available on Linux. It works everywhere — remote targets, embedded systems, core dumps, kernel modules — with zero dependencies.

Knowing GDB well separates engineers who can reproduce and fix bugs quickly from those who rely entirely on printf.

Essential Commands

Most GDB sessions involve a handful of commands used repeatedly:

(gdb) break main          # set breakpoint
(gdb) run                 # start the process
(gdb) next                # step over
(gdb) step                # step into
(gdb) finish              # run until function returns
(gdb) print var           # inspect a variable
(gdb) x/10xw 0xdeadbeef  # examine memory
(gdb) backtrace           # show call stack
(gdb) info registers      # dump CPU registers

Attaching to a Running Process

You do not always have the luxury of starting a process under GDB. Attaching to a running PID is straightforward:

gdb -p $(pgrep myprocess)

Once attached, the process is paused. Set breakpoints, then continue.

Debugging with Core Dumps

When a program crashes in production, a core dump is your best post-mortem tool. Enable them with:

ulimit -c unlimited

Then load the core alongside the binary:

gdb ./myprogram core
(gdb) backtrace

The backtrace command immediately shows you where the crash happened and the full call stack at that moment.

Conditional Breakpoints

Unconditional breakpoints in hot loops are unusable. Conditional breakpoints let you stop only when a specific condition is true:

(gdb) break process_packet if packet->type == 0x05

This is invaluable when debugging race conditions or rare error paths.

Watchpoints

Watchpoints halt execution whenever a memory location is read or written — perfect for tracking down memory corruption:

(gdb) watch *ptr          # break on write
(gdb) rwatch *ptr         # break on read
(gdb) awatch *ptr         # break on read or write

Hardware watchpoints (backed by debug registers) have no performance cost and can watch up to 4 locations simultaneously on x86.

Remote Debugging with gdbserver

For debugging across machines or in constrained environments:

# target machine
gdbserver :1234 ./myprogram

# host machine
gdb ./myprogram
(gdb) target remote 192.168.1.100:1234

This is the standard setup for debugging programs running inside virtual machines or embedded targets.