2020 December 20,

CS 311: Debugger Tutorial

A debugger is a program that lets you examine the state of a program while it is running. As the name suggests, a debugger can greatly accelerate your process of diagnosing and fixing errors.

This page explains the basics of using a debugger for C programs. It simultaneously treats Clang/LLVM/LLDB (which students in macOS are probably using) and GCC/GDB (which students in Ubuntu Linux are probably using). Many features go unmentioned. To learn more, search the Web for bigger tutorials.

Starting and Stopping the Debugger

First, you need to compile your program with the -g flag (in addition to any other flags and linking that you need). This flag causes the compiler to embed a bunch of debugging information into the compiled code. As always, the compiler gives you an executable file, which by default is a.out. Then you run LLDB or GDB on this executable file. Now you're in the debugger. To quit the debugger, enter quit.

Clang/LLVM/LLDBGCC/GDB
compile ordinarilyclang example.ccc example.c
compile for debuggingclang -g example.ccc -g example.c
run ordinarily./a.out./a.out
run in debuggerlldb a.outgdb a.out
quit debuggerquitquit

The remaining sections of this tutorial explain some of the things that you can do while you're in the debugger.

Break Points

A break point is a place in your code, where you'd like to pause execution and examine the variables. Usually, the first thing you do in a debugger is set some break points. You can set a break point on a particular line of your C file, or at the start of a particular function. By default, the break point is set in your main file, where your main function is. You can also set break points in other files by specifying them explicitly. You can also delete break points that you don't want anymore.

LLDBGDB
set break point at line 13b 13break 13
...in file helper.cb helper.c:13break helper.c:13
set break point at function foob foobreak foo
...in file helper.cb helper.c:foobreak helper.c:foo
list all current break pointsbr listinfo break
delete break point #6br del 6delete 6
delete all break pointsbr del

Running and Stepping

Once you have some break points set up, run the program by entering run. It runs until it hits the first break point. Then it pauses. Enter continue to run to the next break point and pause there. Enter continue again to run to the next break point.

Maybe you'd like finer control. Instead of pausing at the next break point, maybe you'd like to pause at the next line of code. You can take such tiny steps by entering next or step. To put it vaguely, step pauses more often than next, which pauses more often than continue.

LLDBGDB
start runningrunrun
run to the next break pointcontinuecontinue
step (over) to the next linenextnext
step (into) to the next linestepstep

To actually understand the distinction between step and next, imagine that you're stepping through a function foo, and you come to a line where it calls another function bar. Do you want to step into bar and go through that function line-by-line? Then use step. Or do you want to regard bar as a single, indivisible step, step over it, and stay in foo? Then use next.

Examining Variables

Thus far, we haven't done anything useful. We've just practiced a complicated way of making a program run slowly. The power of the debugger is that it lets you examine the state of the program while it is paused. Here are some options.

LLDBGDB
show backtrace (call stack)btbt
show arguments to functionframe variableinfo args
show other local variablesframe variableinfo locals
print value of variable bazp bazp baz
set value of variable baz to 5expr baz = 5set var baz=5

(You can also alter your break points while the program is paused.)