EECS 280 Tutorials

GDB Tutorial

GDB is a command line debugger. It is a good choice on Linux or WSL. On macOS, use LLDB instead (LLDB Tutorial).

GDB is harder to learn compared to most visual debuggers. However, you might want to use GDB in situations like these:

GDB uses commands. Here’s a handy reference card.

Quick Start

Compile your program with the -g flag and start GDB in text user interface (TUI) mode. EECS 280 project Makefiles include -g by default.

$ g++ -g --std=c++17 main.cpp -o main.exe
$ make main.exe  # If you have a Makefile
$ gdb -tui main.exe
b main breakpoint on main function
b 13 breakpoint on line 13 of current file
b main.cpp:13 breakpoint on line 13 of main.cpp
r run or rerun
l list a few lines of code
n step over (next)
s step into
up step out (up)
c continue
p myvar print variable myvar
refresh refresh TUI view
bt backtrace, useful for segfaults
q quit

Install

You should already have g++ and gdb installed from the WSL tutorial. Your versions might be different.

$ g++ --version
g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
$ gdb --version
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2

Compile and Run

GDB uses an executable you build at the command line.

First, compile and run your executable at the command line.

$ g++ -g --std=c++17 main.cpp -o main.exe
$ make main.exe  # If you have a Makefile
$ ./main.exe
Hello World!

Pitfall: GDB debugging will be very hard to understand if there are no debugging symbols. If you’re using a Makefile, double check the output of make and verify that you see -g.

Run with GDB. You now see the GDB prompt. GDB’s interface is similar to your command line shell, where you enter commands and press Enter (Return).

$ gdb -tui main.exe
Reading symbols from main.exe...
(gdb)

The r command runs the program. You can ignore any warnings about “separate debuginfos”.

(gdb) r
Starting program: /n/higgins/z/awdeorio/gdbeg/main.exe
Hello World!
[Inferior 1 (process 95933) exited normally]

Quit with q.

(gdb) q

Sanitizers

We recommend enabling the address sanitizer and undefined behavior sanitizer. These will help you find memory errors like going off the end of an array or vector.

First, edit your Makefile and add the CXXFLAGS recommended by the ASAN Quick Start.

Input redirection

If you’re unfamiliar with input redirection, first read the CLI tutorial section on input redirection.

Run with input redirection. Make sure to add the name of your input file (main_test.in in this example).

$ gdb main.exe
...
(gdb) r < main_test.in
...

Arguments and options

Arguments and options are inputs to a program typed at the command line. Here’s an example from EECS 280 Project 5:

$ ./main.exe train_small.csv test_small.csv --debug

To run a program with options or arguments in LLDB, inc

$ gdb main.exe
(gdb) r train_small.csv test_small.csv --debug

Debug

In this section, we’ll set a breakpoint, which pauses the debugger. Then, we’ll cover some of the options to continue execution.

n Next / Step Over: Run one line of code, stepping over any function calls by running the whole function in one step.

s Step / Step Into: Run one line of code, stepping into any function calls to execute them line-by-line.

up Up / Step Out: Run the program until it returns from the current function (or until the next breakpoint).

c Continue: Run the program until the next breakpoint.

q Quit: Quit GDB.

Example code

To get started, copy this example main.cpp into your editor.

#include <iostream>
#include <vector>
using namespace std;

double sum (const vector<double> &data) {
  double total = 0;
  for (size_t i = 0; i < data.size(); ++i) {
    total += data[i];
  }
  return total;
}

int main() {
  vector<double> data;
  data.push_back(10);
  data.push_back(20);
  data.push_back(30);
  cout << "sum(data) = " << sum(data) << endl;
}

Start GDB

Start GDB. The -tui option makes it easier to see your code (optional).

$ gdb -tui main.exe
   ┌──main.cpp─────────────────────────────────────────────────────────────────┐
   │13      int main() {                                                       │
   │14        vector<double> data;                                             │
   │15        data.push_back(10);                                              │
   │16        data.push_back(20);                                              │
   │17        data.push_back(30);                                              │
   │18        cout << "sum(data) = " << sum(data) << endl;                     │
   │19      }                                                                  │
   │20                                                                         │
   │21                                                                         │
   │22                                                                         │
   │23                                                                         │
   │24                                                                         │
   │25                                                                         │
   └───────────────────────────────────────────────────────────────────────────┘
exec No process In:                                                L??   PC: ?? 

(gdb) 

Pro-tip: If the user interface looks messed up, try

(gdb) refresh

Breakpoint

Set a breakpoint on the main function.

   ┌──main.cpp─────────────────────────────────────────────────────────────────┐
   │13      int main() {                                                       │
b+ │14        vector<double> data;                                             │
   │15        data.push_back(10);                                              │
   │16        data.push_back(20);                                              │
   │17        data.push_back(30);                                              │
   │18        cout << "sum(data) = " << sum(data) << endl;                     │
   │19      }                                                                  │
   │20                                                                         │
   │21                                                                         │
   │22                                                                         │
   │23                                                                         │
   │24                                                                         │
   │25                                                                         │
   └───────────────────────────────────────────────────────────────────────────┘
exec No process In:                                                L??   PC: ?? 

(gdb) b	main
Breakpoint 1 at 0x400c66: file main.cpp, line 14.

Pro-tip: There are several ways to set breakpoints.

b main breakpoint on main function
b 13 breakpoint on line 13 of current file
b main.cpp:13 breakpoint on line 13 of main.cpp

Run

Run the program being debugged. The program pauses at the breakpoint. Ignore any error about “Missing separate debuginfos”.

   ┌──main.cpp─────────────────────────────────────────────────────────────────┐
   │13      int main() {                                                       │
B+>│14        vector<double> data;                                             │
   │15        data.push_back(10);                                              │
   │16        data.push_back(20);                                              │
   │17        data.push_back(30);                                              │
   │18        cout << "sum(data) = " << sum(data) << endl;                     │
   │19      }                                                                  │
   │20                                                                         │
   │21                                                                         │
   │22                                                                         │
   │23                                                                         │
   │24                                                                         │
   │25                                                                         │
   └───────────────────────────────────────────────────────────────────────────┘
native process 95465 In: main                                L14   PC: 0x400c66 
Breakpoint 1 at 0x400c66: file main.cpp, line 14.
(gdb) r
Starting program: /n/higgins/z/awdeorio/gdbeg/main.exe
Breakpoint 1, main () at main.cpp:14

Pro-tip: start is a shortcut for b main followed by r.

(gdb) start

List

List a few lines of surrounding code with l (that’s a lowercase L). This command is only useful when you’re not using TUI mode.

(gdb) l

Step over

Enter n (Next / Step Over) a few times until you reach the highlighted line of code.

(gdb) n
   ┌──main.cpp─────────────────────────────────────────────────────────────────┐
   │10        return total;                                                    │
   │11      }                                                                  │
   │12                                                                         │
   │13      int main() {                                                       │
B+ │14        vector<double> data;                                             │
   │15        data.push_back(10);                                              │
   │16        data.push_back(20);                                              │
   │17        data.push_back(30);                                              │
  >│18        cout << "sum(data) = " << sum(data) << endl;                     │
   │19      }                                                                  │
   │20                                                                         │
   │21                                                                         │
   │22                                                                         │
   └───────────────────────────────────────────────────────────────────────────┘
native process 95465 In: main                                L18   PC: 0x400cd2 

(gdb) n
(gdb) n
(gdb) n
(gdb) n

Pro-tip: Hit Enter to repeat your previous command.

Inspect

Print the value of a variable with p. If you have trouble viewing the contents of a vector, see the Pretty Printing STL Containers with gdb section. (Here’s an explanation of why the capacity is 4 when the length is 3.)

(gdb) p data
$1 = std::vector of length 3, capacity 4 = {10,	20, 30}

Step into

Enter s (Step / Step Into). The cursor enters the sum() function.

   ┌──main.cpp─────────────────────────────────────────────────────────────────┐
   │1       #include <iostream>                                                │
   │2       #include <vector>                                                  │
   │3       using namespace std;                                               │
   │4                                                                          │
   │5       double sum (const vector<double> &data) {                          │
  >│6         double total = 0;                                                │
   │7         for (size_t i = 0; i < data.size(); ++i) {                       │
   │8           total += data[i];                                              │
   │9         }                                                                │
   │10        return total;                                                    │
   │11      }                                                                  │
   │12                                                                         │
   │13      int main() {                                                       │
   └───────────────────────────────────────────────────────────────────────────┘
native process 95465 In: sum                                 L6    PC: 0x400c02 

(gdb) s

Step out

Enter up (Step Out). The sum() function completes, and the program pauses again.

   ┌──main.cpp─────────────────────────────────────────────────────────────────┐
   │13      int main() {                                                       │
B+ │14        vector<double> data;                                             │
   │15        data.push_back(10);                                              │
   │16        data.push_back(20);                                              │
   │17        data.push_back(30);                                              │
  >│18        cout << "sum(data) = " << sum(data) << endl;                     │
   │19      }                                                                  │
   │20                                                                         │
   │21                                                                         │
   │22                                                                         │
   │23                                                                         │
   │24                                                                         │
   │25                                                                         │
   └───────────────────────────────────────────────────────────────────────────┘
native process 95465 In: main                                L18   PC: 0x400cf0 

(gdb) up

Continue

Enter c (Continue) to run the program to the next breakpoint, or the end, whichever comes first.

(gdb) c
Continuing.
sum(data) = 60
[Inferior 1 (process 95465) exited normally]

Quit

Quit GDB. Pro-tip: Control-D will quit.

(gdb) q

Pro-tips

Use the up arrow key to cycle through previously used commands.

Use TAB completion to automatically complete the name of a command or a variable.

Use Emacs keyboard shortcuts to enter and edit your current command.

Pretty-printing STL Containers with gdb

Some installations of gdb don’t ship with pretty printing support for STL containers like vector.

If you have this problem, you’ll see something like this when you try to view the contents of a vector while debugging in GDB or VS Code (some VS Code configurations use GDB under the hood).

Install Subversion and Python.

Get the GDB STL pretty printers. These are written in Python.

$ mkdir ~/.gdb.d/
$ svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python ~/.gdb.d/python

Use this command to create a .gdbinit file in your home directory.

$ echo -e 'python\nimport os\nimport sys\nsys.path.insert(0, os.path.expanduser("~/.gdb.d/python"))\nfrom libstdcxx.v6.printers import register_libstdcxx_printers\nregister_libstdcxx_printers (None)\nend' > ~/.gdbinit

Double check everything:

$ cat ~/.gdbinit
python
import os
import sys
sys.path.insert(0, os.path.expanduser("~/.gdb.d/python"))
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end
$ ls -R ~/.gdb.d/
/Users/awdeorio/.gdb.d/:
python

/Users/awdeorio/.gdb.d/python:
Makefile.am  Makefile.in  hook.in  libstdcxx

/Users/awdeorio/.gdb.d/python/libstdcxx:
__init__.py  v6

/Users/awdeorio/.gdb.d/python/libstdcxx/v6:
__init__.py  printers.py  xmethods.py

Finally, restart GDB or VS Code and try to check the contents of vector again.

Acknowledgments

Original document written by Andrew DeOrio awdeorio@umich.edu.

This document is licensed under a Creative Commons Attribution-NonCommercial 4.0 License. You’re free to copy and share this document, but not to sell it. You may not share source code provided with this document.