← All Posts

Debugging Linux Systems with GDB

debugginggdblinuxc
§

Disclaimer: The views expressed on this website are my own and do not necessarily reflect the views of Oracle. Nothing published here is written on behalf of, endorsed by, or reviewed by my employer. All technical content is written from public documentation and personal study, and describes general industry practice rather than any specific employer system, configuration, or product plan.

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.

The views expressed on this website are my own and do not necessarily reflect the views of Oracle. Nothing published here is written on behalf of, endorsed by, or reviewed by my employer.