Debugging & Tooling

a debugger (gdb / lldb)

/ gdb -> GEE-dee-bee; lldb -> ell-ell-DEE-bee /

Picture watching a film, except you hold the remote: you can pause on any frame, step forward one frame at a time, rewind your attention to earlier scenes, and freeze the picture to read tiny details you would never catch at full speed. A debugger is that remote control for a running program. Normally a program races through millions of instructions per second and you only see the final result; a debugger lets you stop it mid-run, look at exactly where it is, inspect every variable, and move it forward in tiny controlled steps.

Concretely, a debugger is a separate program that takes control of your program as it runs. It uses help from the operating system (on Linux, the ptrace mechanism) to pause the target, read and write its memory and CPU registers, and resume it. You tell the debugger things like 'stop when you reach line 42' (a breakpoint), 'now run the next line' (stepping), or 'show me what x holds right now' (printing a variable). gdb is the long-standing GNU debugger common on Linux; lldb is the LLVM project's debugger, common on macOS and with Clang. They differ in command spelling but do the same job: gdb uses commands like break, run, next, print; lldb uses breakpoint set, run, next, print. To make this readable, you compile your program with debug information so the debugger can map raw addresses back to your source lines and variable names.

Why it matters: a debugger turns 'the program crashed somewhere' into 'the program stopped here, with these exact values', which is the difference between guessing and knowing. It shines when a bug is hard to reach by adding print statements - deep in a library, in a crash you cannot predict, or in code you cannot easily recompile. The honest limit: a debugger pauses time, which can hide bugs that only appear at full speed or under specific timing (a heisenbug), and on heavily optimized builds the mapping back to source gets blurry - so debug builds, or at least debug info, make a debugger far more useful.

$ gcc -g -O0 buggy.c -o buggy then $ gdb ./buggy then inside gdb: break main, run, next, print x. gdb stops at main, runs one line at a time, and shows you the value of x at each step.

Compile with -g, launch under gdb, set a breakpoint, step, and inspect a variable.

A debugger does not fix bugs and rarely names the root cause by itself - it gives you precise observations, and you still have to reason about what they mean. Running under a debugger can also slightly change timing, so it is not always faithful to a race that only shows up at full speed.

Also called
interactive debuggerGNU Debugger (gdb)LLDB互動式除錯器