The line pointed to by the arrow is the next line to be executed.
Short Commands
r: run
c: continue
b: break
n: next
Enter: repeat last command
s (step): step into a function
q: quit
How to Use LLDB
-
Generate source-level debug information:
gcc -g hello.c -
Run LLDB on the executable:
lldb a.out -
Run the program:
(lldb) run
LLDB vs GDB
LLDB is the default debugger in the LLVM project (Clang, LLVM, and related tools). GDB is the GNU debugger. Both debug C, C++, and other compiled languages, but they differ in architecture and features:
| Feature | LLDB | GDB |
|---|---|---|
| Default compiler | Clang | GCC |
| Startup speed | Fast (pre-compiled debug info parser) | Slower for large binaries |
| Python scripting | Native Python REPL in debugger | Built-in Python (GDB 7+) |
| Expression parsing | Clang AST (C++11/14/17/20 aware) | GCC expression parser |
| macOS support | First-class (Xcode default) | Requires install, limited |
| Linux support | Full | Full |
| Remote debugging | Built-in (gdbserver-compatible) | Built-in |
| Reverse debugging | Via plugins | Native reverse-step, reverse-next |
| Syntax | LLDB command style | GDB command style |
| Plugin ecosystem | Smaller | Larger (Extensive GDB plugins) |
LLDB is the default on macOS (Xcode) and is gaining ground on Linux with Clang’s growing adoption. GDB remains more common on Linux for GCC-compiled code.
Command Equivalent Map
| Task | GDB | LLDB |
|---|---|---|
| Run | run |
run |
| Break at line | break file.c:10 |
b file.c:10 |
| Break at function | break func |
b func |
| Next line | next |
next or n |
| Step into | step |
step or s |
| Continue | continue |
continue or c |
| Print variable | print x |
p x |
| Backtrace | backtrace |
bt |
| List breakpoints | info breakpoints |
br list |
| Delete breakpoint | delete 1 |
br del 1 |
Setting Breakpoints
(lldb) breakpoint set -f demo.cpp -l 10
(lldb) br s -f demo.cpp -l 10
(lldb) b demo.cpp:10
Breakpoints with Symbols
On a function:
(lldb) b square
On a class method:
(lldb) b Demo::demo
Inside a namespace:
(lldb) b LLDBDemo::add
Conditional Breakpoints
Break only when a condition is true:
(lldb) br s -f demo.cpp -l 10 --condition 'x > 5'
(lldb) b demo.cpp:10 -c 'i == 42'
Conditions use the same expression syntax as p. The breakpoint stops only when the expression evaluates to true. This avoids thousands of manual stops in a loop.
Breakpoint Commands
Run one or more LLDB commands automatically when a breakpoint is hit:
(lldb) br command add 1
> p x
> p y
> continue
> DONE
This is useful for logging variable values without stopping the program flow.
Manipulating Breakpoints
Listing breakpoints:
(lldb) br list
Deleting breakpoints:
(lldb) br del 1
(lldb) br del
Disable and re-enable without deleting:
(lldb) br disable 1
(lldb) br enable 1
Stepping Around
Step over:
(lldb) next
(lldb) n
Step into:
(lldb) step
(lldb) s
Step out:
(lldb) finish
Continue:
(lldb) continue
(lldb) c
Step by instruction (assembly level):
(lldb) si # step instruction
(lldb) ni # next instruction
Inspecting Variables
Print variable contents:
(lldb) p varname
(lldb) print varname
Print with type and format:
(lldb) p/x varname # hex
(lldb) p/f varname # float
(lldb) p/d varname # decimal
(lldb) p/s varname # string
Frame variables (see all visible variables):
(lldb) frame variable
(lldb) fr v
Current line:
(lldb) frame select
Expression Evaluation
LLDB evaluates C++ expressions using the Clang AST. This means you can call functions, create objects, and evaluate complex expressions directly in the debugger:
(lldb) expr myVector.size()
(lldb) expr myString.substr(0, 5)
(lldb) p sqrt((double)i + j)
# Assign values
(lldb) expr myVar = 42
(lldb) expr *ptr = 0
This is more powerful than GDB’s expression engine because LLDB uses the full Clang parser, which handles C++ templates, overload resolution, and lambdas.
Backtrace and Frames
Backtrace:
(lldb) bt
Show all frames with arguments:
(lldb) bt all
(lldb) frame variable # shows arguments and locals in current frame
Switching frames:
(lldb) frame select 0
(lldb) f 2
Using Watchpoints
Program must be running to set watchpoints.
Global variable:
(lldb) watchpoint set variable globalVariable
(lldb) watchpoint set variable -w read | write | read_write globalVariable
Member variable:
(lldb) b main
(lldb) run
(lldb) w s v d.memberVar
Watchpoint Types
| Type | Command | Triggers When |
|---|---|---|
| Write | watchpoint set variable -w write var |
Variable value changes |
| Read | watchpoint set variable -w read var |
Variable is read |
| Read/Write | watchpoint set variable -w read_write var |
Variable is read or written |
| Address | watchpoint set expression -w write -- 0x7fff5fbff700 |
Memory at a specific address changes |
Watchpoints are hardware-limited on most CPUs (typically 4-8 simultaneous watchpoints). LLDB falls back to software watchpoints when hardware watchpoints are exhausted, which is far slower.
Thread Debugging
LLDB provides full multi-threaded debugging support:
# List all threads
(lldb) thread list
# Switch to a specific thread
(lldb) thread select 3
# Show backtrace for all threads
(lldb) bt all
# Step a specific thread while others run
(lldb) thread step-in -t 3
# Set a breakpoint that only stops a specific thread
(lldb) br s -f demo.cpp -l 10 --thread-id 3
Debugging a deadlock:
(lldb) thread list
# Each thread waiting on a mutex — compare frame 1 of each thread
(lldb) thread select 1
(lldb) bt
(lldb) thread select 2
(lldb) bt
The backtrace for each blocked thread typically shows pthread_mutex_lock as the blocking call. Check the mutex address across threads to confirm a circular wait.
Core Dump Analysis
Core dumps capture the full memory state of a crashed program. Enable them:
ulimit -c unlimited
./myprogram # run until it crashes
# [1] 12345 segmentation fault (core dumped)
Analyze the core dump:
lldb myprogram core
(lldb) bt
# * frame #0: 0x00007ffff7a3b4f0 libc.so.6`memcpy
# frame #1: 0x00005555555551a0 myprogram`process_data
# frame #2: 0x0000555555555230 myprogram`main
(lldb) frame select 1
(lldb) fr v
Core dump analysis is essential for production crashes that you cannot reproduce interactively. The core dump includes all threads, all variables, and the full call stack at the moment of the crash.
Core Dump Configuration
# Systemd core dump storage
coredumpctl list
coredumpctl info 12345
coredumpctl debug 12345 # opens lldb directly
On modern Ubuntu with systemd, core dumps are managed by systemd-coredump. You do not need to enable core dumps manually; systemd already collects them.
Memory Debugging with ASAN
AddressSanitizer (ASAN) is a compiler instrumentation that detects memory errors at runtime. It catches buffer overflows, use-after-free, double-free, and memory leaks.
Compile with ASAN
gcc -fsanitize=address -g -o myprogram main.c
./myprogram
ASAN prints a detailed report when a memory error occurs, including the allocation and deallocation stack traces. LLDB can also be used alongside ASAN:
gcc -fsanitize=address -g -o myprogram main.c
lldb myprogram
(lldb) run
LLDB stops at the ASAN error location, allowing you to inspect variables and backtrace interactively.
Other Sanitizers
# Undefined behaviour: signed overflow, null pointer arithmetic
gcc -fsanitize=undefined -g main.c
# Thread safety: data races
gcc -fsanitize=thread -g main.c -lpthread
# Memory leaks
gcc -fsanitize=leak -g main.c
Reverse Debugging
Reverse debugging lets you step backward through program execution — useful for understanding how a variable reached an unexpected value.
LLDB supports reverse debugging when running under a simulator or VM that supports it (like rr — record and replay):
# Install rr
apt install rr
# Record execution
rr record ./myprogram
# Replay in lldb
rr replay
(lldb) b main
(lldb) continue
(lldb) reverse-step # step backward
(lldb) reverse-next # go to previous line
(lldb) reverse-continue # continue backward to the last breakpoint
Reverse debugging is invaluable for intermittent bugs. You record the program once, then replay the execution as many times as needed, moving backward to identify exactly where the state became corrupted.
Python Scripting in LLDB
LLDB has a built-in Python interpreter. You can write Python scripts that interact with the debugger:
(lldb) script
>>> import lldb
>>> target = lldb.debugger.GetSelectedTarget()
>>> process = target.GetProcess()
>>> for thread in process:
... print(thread)
Custom Python Commands
Create ~/lldb/find-bad-ptr.py:
import lldb
def find_nulls(debugger, command, result, internal_dict):
target = debugger.GetSelectedTarget()
process = target.GetProcess()
frame = process.GetSelectedThread().GetFrameAtIndex(0)
for var in frame.GetVariables(True, True, True, True):
if var.GetValue() == '0x0':
print(f"NULL: {var.GetName()}")
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add -f find_bad_ptr.find_nulls find_nulls')
Load it and use it:
(lldb) command script import ~/lldb/find-bad-ptr.py
(lldb) find_nulls
This is useful for writing domain-specific debugging commands that inspect your application’s data structures.
Pretty Printers
LLDB uses Python type formatters to display complex C++ objects readably:
(lldb) type summary add -F my_formatter.format_my_type MyType
(lldb) type synthetic add -l my_synthetic.MySyntheticProvider MyContainer
Formatters for STL containers (std::vector, std::map, std::string) are built in. For custom types, you can register a Python formatter that extracts and formats internal fields.
LLDB Init File Customization
LLDB reads ~/.lldbinit on startup. Use it to set preferences, define aliases, and load scripts:
# ~/.lldbinit
# Settings
settings set stop-line-count-before 5
settings set stop-line-count-after 5
# Aliases
command alias bv frame variable
command alias btall thread backtrace -c 999
command alias bp breakpoint set -f %1 -l %2
# Colors
settings set use-color true
# Auto-load a project-specific script
script import os
script os.chdir('/home/user/myproject')
# Load command scripts
command script import ~/lldb/pretty-printers.py
Aliases save keystrokes during long debugging sessions. For example, bv becomes frame variable, and bp demo.cpp:42 becomes breakpoint set -f demo.cpp -l 42.
Terminating
Kill process:
(lldb) kill
Exiting:
(lldb) quit
(lldb) q
IDE Integration
VS Code
LLDB is the default debugger for C/C++ in VS Code with the CodeLLDB extension:
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "lldb",
"request": "launch",
"program": "${workspaceFolder}/myprogram",
"args": ["--verbose"],
"cwd": "${workspaceFolder}",
"preLaunchTask": "build"
}
]
}
CLion
CLion supports LLDB via the bundled LLDB-MI. Select Settings → Build, Execution, Deployment → Toolchains and choose LLDB as the debugger.
Neovim
Use nvim-dap with the mfussenegger/nvim-dap plugin and LLDB adapter:
local dap = require('dap')
dap.adapters.lldb = {
type = 'executable',
command = '/usr/bin/lldb-vscode',
name = 'lldb'
}
Additional Tips
- Repeat last command: Press
Enter - Set breakpoint at function:
b mainorb func_name - Print variable:
print varnameorp varname - Print all variables:
fr v - Show help:
helporhelp <command> - Register format:
p/x $rax(view registers in hex) - Disassembly:
dis -f -c 20(disassemble current function, 20 lines)
References
- LLDB Tutorial
- LLDB Command Map
- LLDB Python API
- AddressSanitizer Documentation
- rr — Record and Replay Debugging
Comments