EMZETT.
Login

Debuggers gdb and lldb: Examining programs step by step

In short: A debugger halts a running program, shows variables and call chain and executes it line by line; gdb and lldb are the standard tools for C, C++ and Rust.

In more detail: Instead of scattering printf everywhere, you can watch the program work. The debugger starts it under control, stops at breakpoints and lets you examine memory and registers. Even after a crash (segmentation fault) it helps with a core file.

In Depth

Preparation

Compile with debug symbols and without strong optimisation:

gcc -g -O0 program.c -o program      # C
g++ -g -O0 program.cpp -o program    # C++
cargo build                          # Rust (debug profile)

Without -g line and variable names are missing. -O0 prevents the compiler from rearranging code.

Important gdb commands

gdb ./program
(gdb) break main            # breakpoint (also: break file.c:42)
(gdb) run                  # start (run arg1 arg2)
(gdb) next    (n)          # next line (without entering functions)
(gdb) step    (s)          # step into function
(gdb) continue (c)         # run on to the next breakpoint
(gdb) print variable       # show value (print *pointer, print arr[0]@5)
(gdb) display variable     # show after every step
(gdb) backtrace (bt)       # call chain
(gdb) frame 2              # switch stack frame
(gdb) watch variable       # stop when the value changes
(gdb) info locals          # local variables
(gdb) quit

lldb (default on macOS/LLVM) knows similar commands (b main, r, n, s, p var, bt).

Analysing crashes

For a segmentation fault gdb ./program, run, after the crash bt and frame N to see the location and variable values. With core files (ulimit -c unlimited, gdb program core) crashes can be examined after the fact. In addition Valgrind and AddressSanitizer (-fsanitize=address) find memory errors (overflows, use-after-free).

Convenience and other languages

Graphical front ends: VS Code (C/C++ extension, CodeLLDB), CLion, gdb -tui and gdbgui. For Python there are pdb/breakpoint(), for JavaScript the browser developer tools/node --inspect, for Java the IDE’s debugger. The principle is the same everywhere: set a breakpoint, look at the state, step on, test a hypothesis.

See also: Debugging, Reading a stack trace, Valgrind and sanitizers, Segmentation fault