# gdb ## What is GDB? **GDB** (the GNU Debugger) lets you inspect and control a running program: pause it at a chosen line, print the value of any variable, walk the call stack, and step through execution one instruction or one source line at a time. It works on compiled C, C++, and Fortran binaries by reading the debug symbols the compiler embedded during compilation. Without a debugger, diagnosing a crash means reasoning backward from a core dump or scattering `printf` statements through the code and recompiling after every guess. GDB replaces that loop with direct inspection: you attach to the process at the moment it misbehaves and ask it questions directly, what is this pointer, how did we get here, what did the caller pass in. GDB requires debug information to be useful. A binary compiled with `-O0 -g` gives GDB an exact mapping from machine instructions back to source lines and variable names; a binary compiled with `-O3` and no `-g` gives it almost nothing, since the optimiser has already reordered, inlined, and eliminated the very structure GDB would need to explain. ## Install On Debian and Ubuntu: ```bash sudo apt install gdb ``` On Fedora and RHEL: ```bash sudo dnf install gdb ``` Compile the target with debug symbols and without optimisation for the most reliable debugging experience: ```bash gcc -O0 -g -o app app.c ``` ## Practice Start GDB with the binary as an argument, set a breakpoint, and run: ```bash gdb ./app (gdb) break main (gdb) run (gdb) next (gdb) print my_variable (gdb) continue ``` `break` sets a breakpoint by function name or `file.c:line`. `run` starts the program, which executes normally until it hits a breakpoint. `next` steps over the current line (executing any function call in one step); `step` steps into it. `print` evaluates a C expression in the current scope, which can be as simple as a variable name or as complex as `array[i].field->next`. To debug a crash after the fact, inspect the core dump it left behind: ```bash ulimit -c unlimited # enable core dumps for this shell ./app # crashes, writes core file gdb ./app core (gdb) bt # backtrace at the moment of the crash ``` ## Concepts ### Breakpoints and watchpoints A breakpoint stops execution at a location; a watchpoint stops execution when a value changes, regardless of where in the code that happens. Watchpoints are the tool of choice for "this variable is getting corrupted somewhere and I don't know where": ``` (gdb) watch my_struct.count (gdb) continue ``` GDB halts the instant `my_struct.count` changes and reports the old and new value, which pins down the exact line responsible even if it's in code you weren't looking at. ### Backtrace and frame navigation `bt` prints the call stack, each entry a **frame** representing one function call still in progress. `frame N` (or `up`/`down`) switches the context GDB uses for `print` to that frame's locals and arguments, which is how you inspect a caller's state after a crash deep in a callee: ``` (gdb) bt #0 vector_push (v=0x..., x=42) at vector.c:31 #1 process_item (item=0x...) at process.c:88 #2 main () at main.c:15 (gdb) frame 1 (gdb) print item->id ``` ### Conditional breakpoints A breakpoint can carry a condition, useful for a bug that only shows up on, say, the 10,000th loop iteration: ``` (gdb) break process_item if i == 9999 ``` Without the condition, hitting `continue` 9999 times by hand would be impractical. GDB evaluates the condition every time the breakpoint location is reached and only actually stops when it's true. ### TUI mode `gdb -tui` (or `Ctrl-X Ctrl-A` inside a running session) splits the terminal into a source-code pane and a command pane, so stepping through code shows the current line highlighted in context rather than requiring a separate `list` command after every step. ```bash gdb -tui ./app ``` ### Attaching to a running process GDB can attach to an already-running process by PID, which is the only option once a program is already hung or stuck rather than freshly launched: ```bash gdb -p $(pgrep app) ``` This is common for diagnosing a deadlock: attach, then `bt` on each thread with `thread apply all bt` to see exactly where every thread is blocked.