C Mastery / Debugging with GDB and LLDB
Part 6 — Debugging and Optimization

Debugging with GDB and LLDB

This chapter covers interactive debugging with GDB and LLDB: breakpoints, stepping, inspecting state, and watchpoints.

Why This Matters

Print statements only take you so far. A debugger lets you stop execution, inspect variables and memory, and step through code — the most direct way to understand and fix bugs.

Prerequisites

Core Concept

A debugger controls a running program: it can set breakpoints, step through code, inspect registers/memory, and watch variables for changes. It relies on debug info (-g, DWARF) to map machine code back to source.

GDB Commands (also work in LLDB with small differences)

CommandEffect
break file.c:lineset breakpoint
break functionbreak at function entry
run / rstart program
next / nstep over
step / sstep into
continue / cresume
print expr / pprint value
print/x exprprint in hex
backtrace / btstack trace
frame Nselect frame
info registersshow registers
x/nfu addrexamine memory
watch varwatchpoint (stop on change)
listshow source

LLDB uses similar commands (b, n, s, c, p, bt, fr, register read, memory read).

Examples

A GDB session

gcc -g -O0 main.c -o app
gdb ./app
(gdb) break main
(gdb) run
(gdb) next
(gdb) print x
(gdb) backtrace
(gdb) continue

Conditional breakpoint

(gdb) break main.c:20 if x == 5

How It Works

The debugger uses ptrace (Linux) or equivalent to control the process, reads DWARF info to map addresses to source, and reads/writes memory and registers when stopped. Breakpoints are implemented by patching the instruction with a trap, and watchpoints use hardware debug registers or single-stepping.

Variations

LLDB vs. GDB

LLDB's command language is similar but not identical. Both are scriptable (Python). Pick the one your toolchain targets.

Core-file analysis

You can also debug a crashed program post-mortem via a core dump (c.debug.2).

Common Mistakes

away.

Undefined Behavior

Portability

portable; exact commands differ.

Under the Hood

The debugger reads DWARF sections (.debug_info, .debug_line) to map addresses to source lines and variables. It uses the symbol table for function names.

Practical Usage

Exercises

1. Compile a program with -g and set a breakpoint at main; step through. 2. Print a struct and an array, and inspect memory with x. 3. Set a watchpoint on a variable and find the line that modifies it. 4. Use a conditional breakpoint inside a loop.

Deep Challenge

Debug a use-after-free with GDB: set a breakpoint after free, inspect the heap, and use a watchpoint or ASan to pinpoint the invalid access. Explain your process.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.debug.gdb06
c.debug.lldb05