Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Debugging with LLDB

LLDB is the debugger for macOS, part of the LLVM project. If you’re coming from Linux and used to GDB, LLDB will feel familiar but different. This chapter covers practical LLDB usage with command equivalents for GDB users.

LLDB vs GDB

FeatureGDBLLDB
PlatformLinux, othersmacOS, iOS, LLVM
ProjectGNULLVM
On macOSAvailable via HomebrewBuilt-in
ScriptingPython, GuilePython
Default on macOSNoYes

LLDB is the only debugger that works seamlessly with macOS code signing, entitlements, and System Integrity Protection.

Getting Started

Launching LLDB

# Debug an executable
$ lldb ./myprogram

# Debug with arguments
$ lldb -- ./myprogram arg1 arg2

# Attach to running process
$ lldb -p 12345

# Attach by name
$ lldb -n Safari

Compiling for Debugging

# Include debug symbols
$ clang -g -O0 program.c -o program

# -g: Generate debug info
# -O0: No optimization (easier debugging)

Essential Commands

Running

TaskLLDBGDB
Run programrun or rrun
Run with argsrun arg1 arg2run arg1 arg2
Continuecontinue or ccontinue
Step overnext or nnext
Step intostep or sstep
Step outfinishfinish
StopCtrl+CCtrl+C
Quitquit or qquit

Breakpoints

# Set breakpoint at function
(lldb) breakpoint set --name main
(lldb) b main                        # Shorthand

# Set breakpoint at file:line
(lldb) breakpoint set --file main.c --line 42
(lldb) b main.c:42                   # Shorthand

# Set breakpoint at address
(lldb) breakpoint set --address 0x100003f00

# Conditional breakpoint
(lldb) breakpoint set --name foo --condition 'x > 10'
(lldb) b foo -c 'x > 10'

# List breakpoints
(lldb) breakpoint list
(lldb) br l

# Delete breakpoint
(lldb) breakpoint delete 1
(lldb) br del 1

# Disable/enable breakpoint
(lldb) breakpoint disable 1
(lldb) breakpoint enable 1

# Delete all breakpoints
(lldb) breakpoint delete

Watchpoints

# Watch variable for write
(lldb) watchpoint set variable myvar

# Watch expression
(lldb) watchpoint set expression -- &myvar

# Watch for read
(lldb) watchpoint set variable -w read myvar

# Watch for read or write
(lldb) watchpoint set variable -w read_write myvar

# List watchpoints
(lldb) watchpoint list

# Delete watchpoint
(lldb) watchpoint delete 1

Examining Data

# Print variable
(lldb) print myvar
(lldb) p myvar

# Print with format
(lldb) print/x myvar          # Hex
(lldb) print/d myvar          # Decimal
(lldb) print/t myvar          # Binary
(lldb) print/c myvar          # Character

# Print expression
(lldb) print 2 + 2
(lldb) print strlen("hello")

# Print pointer contents
(lldb) print *ptr
(lldb) print ptr[0]

# Print array
(lldb) print myarray
(lldb) parray 10 myarray      # Print 10 elements

# Print struct
(lldb) print mystruct
(lldb) print mystruct.field

# Frame variables (local variables)
(lldb) frame variable
(lldb) fr v

# Specific variable
(lldb) frame variable myvar
(lldb) fr v myvar

Memory Examination

# Read memory (x command)
(lldb) memory read 0x100003f00
(lldb) x 0x100003f00

# With format and count
(lldb) memory read --size 4 --count 10 --format x 0x100003f00
(lldb) x -s4 -c10 -fx 0x100003f00

# Read as string
(lldb) memory read --format s 0x100003f00
(lldb) x -fs 0x100003f00

# Read variable's memory
(lldb) memory read &myvar

# Common formats: x (hex), d (decimal), s (string), c (char), i (instruction)

Stack Frames

# Show backtrace
(lldb) thread backtrace
(lldb) bt

# Full backtrace (all threads)
(lldb) thread backtrace all
(lldb) bt all

# Select frame
(lldb) frame select 2
(lldb) f 2

# Frame info
(lldb) frame info

# Up/down frames
(lldb) up
(lldb) down

Threads

# List threads
(lldb) thread list

# Select thread
(lldb) thread select 2

# Thread backtrace
(lldb) thread backtrace

# Continue specific thread
(lldb) thread continue

GDB to LLDB Command Map

TaskGDBLLDB
Runrunrun
Break at functionbreak mainb main
Break at linebreak file:lineb file:line
Continuecontinuecontinue
Step overnextnext
Step intostepstep
Step outfinishfinish
Printprint varprint var
Print hexprint/x varp/x var
Backtracebacktracebt
List breakpointsinfo breakpointsbr list
Delete breakpointdelete 1br del 1
Examine memoryx/10x addrx -c10 -fx addr
Local variablesinfo localsfr v
Disassembledisasdisassemble
Registersinfo registersregister read
Attachattach pidattach -p pid
Set variableset var x=10expr x=10

Advanced Features

Expressions

# Evaluate expression
(lldb) expression 2 + 2
(lldb) expr 2 + 2

# Call function
(lldb) expr (int)printf("Hello\n")

# Modify variable
(lldb) expr myvar = 42

# Cast
(lldb) expr (char *)myptr

# Create variable for session
(lldb) expr int $myval = 42
(lldb) print $myval

Disassembly

# Disassemble current function
(lldb) disassemble
(lldb) di

# Disassemble function by name
(lldb) disassemble --name main
(lldb) di -n main

# Disassemble at address
(lldb) disassemble --start-address 0x100003f00

# Show mixed source and assembly
(lldb) disassemble --mixed
(lldb) di -m

# Disassemble bytes
(lldb) disassemble --bytes
(lldb) di -b

Register Access

# Read all registers
(lldb) register read

# Read specific register
(lldb) register read rax
(lldb) register read x0    # ARM64

# Write register
(lldb) register write rax 0x42

# Read register in different format
(lldb) register read --format binary rax

Process Control

# Process info
(lldb) process status

# Kill process
(lldb) process kill

# Detach
(lldb) process detach

# Signal handling
(lldb) process handle SIGINT --stop false --pass true

Source Code

# Show source
(lldb) source list
(lldb) l

# Show specific lines
(lldb) source list --line 42
(lldb) l -l 42

# Show function
(lldb) source list --name main

macOS-Specific Features

Debugging System Programs

SIP restricts debugging system processes:

# This may fail
$ lldb /usr/bin/some_system_tool
error: process exited with status -1 (attach failed (Not allowed to attach to process. ...))

For system process debugging, disable SIP (not recommended) or debug copies.

Code Signing for Debugging

To debug your own signed apps:

# Check entitlements
$ codesign -d --entitlements - /path/to/app

# May need com.apple.security.get-task-allow entitlement

Debugging Universal Binaries

# Specify architecture
$ lldb --arch x86_64 ./universal_binary
$ lldb --arch arm64 ./universal_binary

Working with dSYM Files

# Debug symbols are in dSYM bundles
$ dsymutil myprogram         # Generate dSYM
$ ls myprogram.dSYM/

# LLDB finds dSYM automatically if nearby
# Or specify manually:
(lldb) target symbols add myprogram.dSYM

Crash Log Analysis

# Symbolicate crash log
$ lldb
(lldb) target create --core /path/to/crashlog

# Or use atos for quick symbolication
$ atos -o myprogram.dSYM/Contents/Resources/DWARF/myprogram -l 0x100000000 0x100003f00

Scripting with Python

Interactive Python

(lldb) script
>>> print(lldb.frame.GetFunctionName())
main
>>> exit()

Python Command

(lldb) script print(lldb.debugger.GetSelectedTarget().GetExecutable())

Custom Commands

Create ~/.lldbinit:

command script import ~/lldb_scripts/mycommands.py

Example script (mycommands.py):

import lldb

def hello_command(debugger, command, result, internal_dict):
    print("Hello from LLDB!")

def __lldb_init_module(debugger, internal_dict):
    debugger.HandleCommand('command script add -f mycommands.hello_command hello')

Configuration

.lldbinit File

Create ~/.lldbinit:

# Custom settings
settings set target.x86-disassembly-flavor intel
settings set stop-line-count-after 5
settings set stop-line-count-before 5

# Aliases
command alias bpl breakpoint list
command alias bpc breakpoint clear

# Auto-load scripts
command script import ~/.lldb/custom.py

# Type formatters
type summary add --summary-string "${var.name} (${var.age} years)" Person

Useful Settings

# Show settings
(lldb) settings list

# Change setting
(lldb) settings set target.run-args arg1 arg2
(lldb) settings set target.env-vars DEBUG=1

# Disassembly flavor
(lldb) settings set target.x86-disassembly-flavor intel

# Auto-confirm
(lldb) settings set auto-confirm true

GUI Mode

LLDB has a curses-based GUI:

(lldb) gui

In GUI mode:

  • Tab: Switch panes
  • Arrow keys: Navigate
  • Enter: Select
  • Esc: Back/close
  • h: Help

Integration with Xcode

LLDB is Xcode’s built-in debugger:

  • Breakpoints set in Xcode use LLDB
  • Debug console is LLDB
  • Can use all LLDB commands in Xcode’s debug console
  • Variable inspection uses LLDB

Common Debugging Scenarios

Debugging Segfault

$ lldb ./crashy_program
(lldb) run
Process stopped
* thread #1, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)

(lldb) bt
* frame #0: crashy_program`bad_function at crashy.c:15
  frame #1: crashy_program`main at crashy.c:42

(lldb) frame select 0
(lldb) frame variable
(lldb) print ptr      # See what's null

Finding Memory Leaks

# Use with MallocStackLogging
$ MallocStackLogging=1 lldb ./program
(lldb) run

# When done, check leaks
$ leaks program_pid

Debugging Deadlocks

(lldb) process interrupt    # Ctrl+C
(lldb) thread list
(lldb) bt all               # See all thread stacks

Summary

CategoryKey Commands
Runningrun, continue, next, step, finish
Breakpointsb function, b file:line, br list, br del
Inspectionprint var, frame variable, bt, memory read
Threadsthread list, thread select, bt all
Memoryx address, memory read
Registersregister read, register write
Controlprocess kill, process detach, quit

LLDB is powerful and integrates deeply with macOS. While the command syntax differs from GDB, the concepts are the same. Use the GDB-to-LLDB mapping table while learning, and soon LLDB’s syntax will feel natural.