EECS 280 Tutorials
LLDB Tutorial
LLDB is a command line debugger. It is the default debugger on macOS, and is used by Xcode behind the scenes.
LLDB is harder to learn compared to most visual debuggers. However, you might want to use LLDB instead of VS Code or Xcode for several reasons:
- Zero setup. Fast and easy debugging once you’ve learned.
- Command line only, work on a remote server without a GUI
- Support for threads
- Support for connecting to a remote target like an operating system that has crashed so badly you can’t use the keyboard or monitor
- Integration with Vim and Emacs
LLDB uses commands. Here’s a handy list of LLDB and GDB commands.
Quick Start
Compile your program with the -g
flag and start LLDB. 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
$ lldb 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 |
n |
step over (next) |
s |
step into |
up |
step out (up) |
c |
continue |
p myvar |
print variable myvar |
bt |
backtrace, useful for segfaults |
q |
quit |
Install
You should already have g++
and lldb
installed from the main macOS tutorial. Your versions might be different.
$ g++ --version
Apple clang version 12.0.0 (clang-1200.0.32.28)
$ lldb --version
lldb-1200.0.44.2
Compile and Run
LLDB 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: LLDB 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 LLDB. You now see the LLDB prompt. LLDB’s interface is similar to your command line shell, where you enter commands and press Enter (Return).
$ lldb main.exe
(lldb) target create "./main.exe"
Current executable set to '/Users/awdeorio/src/eecs280/p1-stats/main.exe' (arm64).
The r
command runs the program.
(lldb) r
Process 76550 launched: '/Users/awdeorio/gdbeg/main.exe' (arm64)
Hello World!
Process 76550 exited with status = 0 (0x00000000)
Quit with q
. Pro-tip: Control-D
will also quit at any time.
(lldb) 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.
Command-Line Arguments and Options
Inputs to a program may be provided when it is initially run via command-line arguments or options. Here’s an example from EECS 280 Project 1:
$ ./two_sample.exe HCMST_ver_3.04.tsv q24_met_online 1 0 ppage
./two_sample.exe
is used to run the program- Each of
HCMST_ver_3.04.tsv
,q24_met_online
,1
,0
,ppage
are passed to it as arguments
The arguments above specify the name of a data file, coulumns, and filter values for the program to use.
To run a program with options or arguments in LLDB, include them after r
.
$ lldb main.exe
(lldb) r HCMST_ver_3.04.tsv q24_met_online 1 0 ppage
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).
$ lldb main.exe
...
(lldb) settings set target.input-path main_test.in
(lldb) r
...
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 LLDB.
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 LLDB
Start LLDB.
$ lldb main.exe
(lldb) target create "main.exe"
Current executable set to '/Users/awdeorio/src/eecs280/p1-stats/main.exe' (arm64).
Breakpoint
Set a breakpoint on the main function.
(lldb) b main
Breakpoint 1: where = main.exe`main + 20 at main.cpp:14:18, address = 0x0000000100003e48
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.
(lldb) r
Process 72957 launched: '/Users/awdeorio/src/eecs280/p1-stats/main.exe' (arm64)
Process 72957 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100003e48 main.exe`main at main.cpp:14:18
11 }
12
13 int main() {
-> 14 vector<double> data;
15 data.push_back(10);
16 data.push_back(20);
17 data.push_back(30);
Target 0: (main.exe) stopped.
Step over
Enter n
(Next / Step Over) a few times until you reach the highlighted line of code.
(lldb) n
Process 72957 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
frame #0: 0x0000000100003ea4 main.exe`main at main.cpp:18:8
15 data.push_back(10);
16 data.push_back(20);
17 data.push_back(30);
-> 18 cout << "sum(data) = " << sum(data) << endl;
19 }
Pro-tip: Hit Return to repeat your previous command.
Inspect
Print the value of a variable with p
.
(lldb) p data
(std::vector<double, std::allocator<double> >) $0 = size=3 {
[0] = 10
[1] = 20
[2] = 30
}
Step into
Enter s
(Step / Step Into). The cursor enters the sum()
function.
(lldb) s
Process 72957 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
frame #0: 0x0000000100003d7c main.exe`sum(data=size=3) at main.cpp:6:10
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 }
Step out
Enter up
(Step Out). The sum()
function completes, and the program pauses again.
(lldb) up
frame #1: 0x0000000100003eb8 main.exe`main at main.cpp:18:29
15 data.push_back(10);
(lldb) up
frame #1: 0x0000000100003eb8 main.exe`main at main.cpp:18:29
15 data.push_back(10);
16 data.push_back(20);
17 data.push_back(30);
-> 18 cout << "sum(data) = " << sum(data) << endl;
19 }
Continue
Enter c
(Continue) to run the program to the next breakpoint, or the end, whichever comes first.
(lldb) c
Process 72957 resuming
sum(data) = 60
Process 72957 exited with status = 0 (0x00000000)
Quit
Quit LLDB. Pro-tip: Control-D
will quit.
(lldb) q
Pro-tips
Use the up and down arrow keys to cycle through previous commands similar to your command line.
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.
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.