GDB quick reference

What is gdb? It’s a command-line debugger for C and C++ (although it does significantly more than that). That means it helps you look at the state of the program while the program is running. The idea is similar to the lc3sim-tk tool, but in a terminal/command window. lc3sim is essentially a stripped-down version of gdb.

In the following guide, if two lines are given without commentary describing the difference between them, they’re equivalent statements (aliases).

⚠️ Note
THIS IS NOT A COMPREHENSIVE GUIDE

If you want to do extra reading about gdb and the things it can do (e.g. running code in reverse), the man page (what is a man page?) is one starting point. CS 225 has a slightly more detailed quick reference here (as of fall 2023). Here is a more detailed cheat sheet that shows up in a search engine (link permanence not guaranteed), but which is also not exhaustive.

Note: At any point, the screen can be cleared by typing Ctrl+l (lowercase L).

Suppose the current working directory is foo, and that there is an executable file, bar, which is located in foo.

From folder foo, to run gdb on bar one can do:

gdb ./bar //if bar requires no arguments or the arguments will be defined with the 'run' command
gdb --args ./bar arg1 arg2 //if bar requires arguments

which will give the gdb prompt. To get the program running do:

run //no args or args defined when calling gdb
r //no args or args defined when calling gdb
run arg1 arg2 //program requires args that were not defined when gdb was called

Note: If no breakpoints are defined, this will run the program straight to the end (or until the program crashes). Breakpoints should be defined before running (or while the program is paused in gdb at a preexisting breakpoint).

Setting a breakpoint at the beginning of function baz can be done as:

break baz
b baz

or for setting a breakpoint at line 24 of file qux.c:

break qux.c:24
b qux.c:24

Note: Depending on the level of optimization used by the compiler, line 24 in qux.c in the compiled program may not match line 24 of qux.c in the actual file. In some cases the difference may be large, and may include reordering the lines of code. To prevent the compiler from doing this, compilation can be performed without optimization by using the -O0 command (minus oh zero). This is distinct from the output flag -o.

Once debugging is done, to remove a breakpoint that is at line 24 of file qux.c one can do:

clear qux.c:24

Or to progress by one line, without seeing the functions that that line may call:

next
n

Similarly, to “step into” the function called by the current line, or, if none, to progress to the next line, having run the current line do:

step
s

whereas to run until the next line is reached (e.g. you’re at the last line of a loop and only wanted to go through it yourself once, but want it to run all the way through without you seeing) do:

until
u

Other commands include finish to run until the current function finishes:

finish
fin

or to keep going until you hit your next breakpoint, or the end of the program, whichever comes first:

continue
c

and to print the value of a variable that is in the current scope (assume the variable is named corge):

print corge
p corge

or to monitor the status of variable (or expression) corge and have it automatically print each time gdb pauses:

display corge

Similarly, to display a few lines of the code (before and after) the line you're currently on:

list

or to show the code so you can see the file (in this case, bar.c) that's being executed:

layout bar.c

to see the list of functions that called other functions, etc, to where you currently are in the program:

backtrace

Note: this command can also be abbreviated as bt.

Backtrace will give you an output that looks like this:

#0 0x00001234 in main() at bar.c:12
#1 0x00004567 in foo() at bar.c:47
#2 0x00009876 in baz() at bar.c:56

This can be interpreted as function main() called foo() at line 12, and foo() called baz() at line 56. The number after # indicates the frame number of a function. Note that if the execution has finished, printing variable values will not work. Additionally, if the execution is in frame 2 - baz(), "print" will not work for variables in foo() or main(). To go back to the context of foo() and print variables in that function:

frame 1

and to run a function called grault that is in the program:

call grault() //if grault takes no arguments
call grault(arg1, arg2, ...) //if grault takes arguments

and finally to exit:

quit
q

gdbinit files (slightly more advanced)

gdbinit files are useful if you are opening and closing gdb many times, and have many breakpoints. Using a gdbinit file means not having to retype your breakpoints each time. (Some students find gdbinit files useful starting around MP8.)

In your home directory (the folder that you start in when you open a fresh terminal window), create a file called .gdbinit (no quote marks, yes period).

In this file, put the line:

add-auto-load-safe-path <path to folder where you’re working>

Save and close it. (The path named in this file should be edited when you want to use gdbinit files for another project.)

In the directory where you're working, create another .gdbinit file. This local gdbinit file is where you describe the breakpoints and commands for gdb. If there is no gdbinit file in the directory where you're working, gdb will run as if no gdbinit files were in use.

Example contents for local gdbinit file for program foo:

set print pretty #makes gdb output about structs easier to read
b baz #break at function baz
set args 4 25 #set arguments for program foo
run #run foo

To then run gdb on the program:

gdb ./foo

Terminal User Interface (TUI)

gdb also can do a slightly fancier interface on most modern terminals (which supports the curses library). You can read more about the TUI interface here.

gdbtui ./bar
gdb ./bar --tui

Depending on which layout was loaded up you will generally see at least two windows in the terminal. One of these will be the standard gdb prompt where all the commands mentioned above can be used. The other will be either the source, assembly, or register window. To cycle between layout issue at the gdb prompt

layout next

Hint: Simply hit enter at the prompt to reissue the command.

To switch between the focused windows within the terminal use C-x o (this means to first press the Ctrl + x combination followed by the letter o).

Alternative 1 - lldb

Typically we stick to the gcc/gdb pair but on MacOS, XCode will install its own clang compiler which comes with a debugger called lldb. If you are interested in using that, you can read more about the differences between the two here.

IMO: lldb has a prettier TUI than gdb (which need not translate to more functional).

Alternative 2 - gdb dashboard

Yet another alternative, if you have become familiar with .dotfiles is to use an advanced gdbinit file that will implement a TUI interface for you. You can explore this approach at this link 🔗

All rights reserved by ECE220@UIUC. Design by Asher Mai & Ivan Abraham.
Last modified: January 09, 2024. Website built with Franklin.jl and the Julia programming language.