Comprehensive guide to LLDB debugger for macOS/iOS development. Covers LLDB vs GDB differences, Swift/Objective-C debugging, Xcode integration, command-line usage, breakpoint expressions, watchpoints, Python scripting, and performance improvements (50x faster step-over in 2025).
Last Updated: 2025-10-26
LLDB is the default debugger for macOS, iOS, and Apple platforms. It's the backend for Xcode's debugger and provides superior Swift/Objective-C debugging compared to GDB. As of 2025, LLDB features 50x faster step-over performance for optimized Swift code.
| Feature | GDB | LLDB | |---------|-----|------| | Swift debugging | Limited | Native | | Objective-C | Basic | Excellent | | macOS integration | Poor | Native | | Xcode backend | No | Yes | | Command syntax | Abbreviated | Structured | | Performance (2025) | Baseline | 50x faster step-over |
Debuggee (App/Binary)
↓
debugserver (macOS/iOS)
↓
LLDB (debugger)
↓
Xcode UI / Command Line
# Check LLDB version
lldb --version
# lldb-1600.0.0 (2025 version)
# Check Xcode Command Line Tools
xcode-select -p
# Install if missing
xcode-select --install
# Launch LLDB with binary
lldb ./my_program
# Launch with arguments
lldb -- ./my_program arg1 arg2
# Attach to running process
lldb -p <pid>
lldb -n ProcessName
# Load and run immediately
lldb -o "run" ./my_program
LLDB uses structured syntax: <command> <subcommand> [options]
GDB → LLDB
────────────────────────────────────────────────
break main → breakpoint set -n main
break file.c:42 → breakpoint set -f file.c -l 42
continue → continue (same)
step → thread step-in
next → thread step-over
finish → thread step-out
backtrace → thread backtrace
print var → expr var / p var
run → process launch / run
attach <pid> → process attach -p <pid>
info breakpoints → breakpoint list
LLDB provides GDB-compatible aliases:
(lldb) run # Alias for process launch
(lldb) r # Short form
(lldb) step # Alias for thread step-in
(lldb) s # Short form
(lldb) next # Alias for thread step-over
(lldb) n # Short form
(lldb) continue # Same as GDB
(lldb) c # Short form
Function breakpoints:
# Swift function
(lldb) breakpoint set -n MyClass.myMethod
(lldb) br s -n MyClass.myMethod
(lldb) b MyClass.myMethod
# Objective-C method
(lldb) breakpoint set -n "-[MyClass myMethod:]"
(lldb) b -[MyClass myMethod:]
# C function
(lldb) breakpoint set -n main
(lldb) b main
# Regex breakpoint
(lldb) breakpoint set -r 'MyClass.*'
(lldb) rb 'calculate.*'
File and line breakpoints:
# Break at file:line
(lldb) breakpoint set -f ViewController.swift -l 42
(lldb) br s -f ViewController.swift -l 42
(lldb) b ViewController.swift:42
# Break in current file
(lldb) breakpoint set -l 100
Conditional breakpoints:
# Break if condition true
(lldb) breakpoint set -n process_data -c 'count > 100'
(lldb) br s -n myMethod -c 'userName == "admin"'
# Modify existing breakpoint
(lldb) breakpoint modify -c 'index >= 5' 1
# List all breakpoints
(lldb) breakpoint list
(lldb) br l
# Disable/enable breakpoint
(lldb) breakpoint disable 1
(lldb) breakpoint enable 1
# Delete breakpoint
(lldb) breakpoint delete 1
(lldb) br del 1
# Delete all breakpoints
(lldb) breakpoint delete
# Ignore breakpoint N times
(lldb) breakpoint modify -i 10 1
Monitor memory changes:
# Watch variable
(lldb) watchpoint set variable myVariable
(lldb) watch set var myVariable
# Watch memory address
(lldb) watchpoint set expression -- 0x7ffeefbff5b0
(lldb) watch set expr -- 0x7ffeefbff5b0
# Watch with condition
(lldb) watchpoint set variable count
(lldb) watchpoint modify -c 'count > 100' 1
# List watchpoints
(lldb) watchpoint list
# Delete watchpoint
(lldb) watchpoint delete 1
# Launch program
(lldb) process launch
(lldb) run
(lldb) r
# Launch with arguments
(lldb) process launch -- arg1 arg2
(lldb) run arg1 arg2
# Set arguments before launch
(lldb) settings set target.run-args arg1 arg2
(lldb) run
# Launch with environment variables
(lldb) env VAR=value
(lldb) run
# Launch with input redirection
(lldb) process launch -i input.txt
# Step into (enter functions)
(lldb) thread step-in
(lldb) step
(lldb) s
# Step over (don't enter functions)
(lldb) thread step-over
(lldb) next
(lldb) n
# Step out (finish current function)
(lldb) thread step-out
(lldb) finish
# Step one instruction
(lldb) thread step-inst
(lldb) stepi
(lldb) si
# Continue until line
(lldb) thread until 50
# Continue execution
(lldb) process continue
(lldb) continue
(lldb) c
Context: As of 2025, LLDB step-over is 50x faster for optimized Swift code compared to 2020 versions.
Usage:
# Fast step-over in Swift (2025 LLDB)
(lldb) thread step-over # 50x faster than older LLDB
# Enable performance optimizations
(lldb) settings set target.skip-prologue true
(lldb) settings set target.process.optimization-warnings false
Benchmark (2025 data):
Stepping through 1000 lines of optimized Swift:
- LLDB 2020: ~25 seconds
- LLDB 2025: ~0.5 seconds (50x improvement)
# Full backtrace
(lldb) thread backtrace
(lldb) bt
# Backtrace with arguments
(lldb) bt all
# Backtrace all threads
(lldb) thread backtrace all
# Limited backtrace
(lldb) thread backtrace -c 10
# Show current frame
(lldb) frame info
# Select frame
(lldb) frame select 2
(lldb) f 2
# Move up/down
(lldb) up
(lldb) down
# Show frame variables
(lldb) frame variable
(lldb) fr v
# Show specific variable
(lldb) frame variable count
(lldb) fr v count
# Evaluate expression
(lldb) expression count + 1
(lldb) expr count + 1
(lldb) p count + 1
# Assign variable
(lldb) expr count = 42
# Call function
(lldb) expr -l swift -- print("Debug message")
(lldb) po print("Debug message") # Swift
# Objective-C call
(lldb) expr -l objc -- (void)[self printDebugInfo]
(lldb) po [self description]
Print Swift objects:
# Print description (CustomStringConvertible)
(lldb) po myObject
# Print detailed info
(lldb) frame variable -D myObject
(lldb) fr v -D myObject
# Print type
(lldb) type lookup MyClass
Swift-specific commands:
# Import Swift module
(lldb) expr import MyFramework
# Swift REPL (exit with :quit)
(lldb) repl
Print Objective-C objects:
# Print description
(lldb) po myObject
(lldb) expr -O -- myObject
# Call method
(lldb) po [myArray count]
(lldb) po [myString uppercaseString]
# Print ivar
(lldb) po myObject->_privateIvar
# Read memory
(lldb) memory read 0x7ffeefbff5b0
(lldb) x 0x7ffeefbff5b0
# Read with format
(lldb) memory read -f x -c 10 0x7ffeefbff5b0 # 10 hex values
(lldb) x/10xw 0x7ffeefbff5b0 # GDB syntax also works
# Read string
(lldb) memory read -f s 0x100003f80
(lldb) x/s 0x100003f80
# Write memory (dangerous!)
(lldb) memory write 0x7ffeefbff5b0 0x42
Access LLDB console in Xcode:
Common Xcode workflows:
# In Xcode console, all LLDB commands work
# Print variable
(lldb) po self.userName
# Modify variable
(lldb) expr self.count = 10
# Continue
(lldb) continue
# Force unwrap Swift optional
(lldb) po myOptional!
# Break on exception
(lldb) breakpoint set -E swift
(lldb) br s -E objc # Objective-C exceptions
MyClass.myMethod or -[MyClass myMethod:]count > 10Xcode View Debugger (3D UI hierarchy):
LLDB view commands:
# Print view hierarchy (UIKit)
(lldb) po UIApplication.shared.keyWindow?.rootViewController?.view.recursiveDescription()
# Print responder chain
(lldb) po [UIResponder.firstResponder]
# Modify view
(lldb) expr ((UIView *)0x7f8a3d4050f0).backgroundColor = UIColor.red
Load Python script:
(lldb) command script import ~/lldb_scripts/my_script.py
my_script.py:
import lldb
def hello_command(debugger, command, result, internal_dict):
"""Print hello message."""
print(f"Hello, {command}!")
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add -f my_script.hello_command hello')
print("Custom commands loaded")
Swift pretty printer:
import lldb
def swift_array_summary(valobj, internal_dict):
"""Custom summary for Swift Array."""
count = valobj.GetChildAtIndex(0).GetValueAsUnsigned()
return f"Array with {count} elements"
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('type summary add -F my_script.swift_array_summary "Swift.Array"')
~/.lldbinit (global) or ./.lldbinit (project):
# Settings
settings set target.skip-prologue false
settings set target.process.extra-startup-command QSetLogging:bitmask=LOG_ALL;
# Aliases
command alias bfl breakpoint set -f %1 -l %2
command alias bp breakpoint set -n %1
# Auto-import scripts
command script import ~/lldb_scripts/swift_helpers.py
# Break on Swift errors
breakpoint set -E swift
# Stop on Objective-C exceptions
breakpoint set -E objc
# Create alias
(lldb) command alias bfl breakpoint set -f %1 -l %2
# Use alias
(lldb) bfl ViewController.swift 42
# Create regex command
(lldb) command regex jump 's/(.+)/thread jump --by %1/'
# Use regex
(lldb) jump 5 # Jump forward 5 lines
Xcode handles this automatically, but for manual setup:
# Connect device via USB
# Start debugserver on device (requires jailbreak or Xcode)
# On Mac:
(lldb) platform select remote-ios
(lldb) platform connect connect://localhost:6666
(lldb) file /path/to/MyApp.app/MyApp
(lldb) run
Start debugserver on remote Mac:
# Remote Mac
/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/debugserver localhost:2345 ./my_program
Connect from local Mac:
# Local Mac
(lldb) platform select remote-macosx
(lldb) platform connect connect://192.168.1.100:2345
(lldb) continue
# Break on Swift error throw
(lldb) breakpoint set -E swift
(lldb) br s -E swift
# Break on specific error type
(lldb) breakpoint set -E swift -O "MyError"
# Inspect type metadata
(lldb) type lookup MyStruct
# Dump type layout
(lldb) type lookup --show-layout MyClass
# Print protocol conformances
(lldb) expr -l swift -- import Foundation
(lldb) expr -l swift -- print(type(of: myObject))
# Launch Swift REPL
(lldb) repl
# In REPL:
1> import Foundation
2> let url = URL(string: "https://example.com")
3> print(url)
4> :quit
# Instance method
(lldb) breakpoint set -n "-[MyClass myMethod:]"
(lldb) b -[MyClass myMethod:]
# Class method
(lldb) breakpoint set -n "+[MyClass classMethod]"
(lldb) b +[MyClass classMethod]
# All methods in class
(lldb) breakpoint set -r '\[MyClass .*\]'
# Trace all messages to object
(lldb) expr (void)instrumentObjcMessageSends(YES)
# Check /tmp/msgSends-<pid> for log
# Enable fast step-over (2025 feature)
(lldb) settings set target.skip-prologue true
# Reduce output verbosity
(lldb) settings set thread.format "thread #${thread.index}: tid = ${thread.id}\n"
# Disable auto-summary for large collections
(lldb) type summary delete "Swift.Array"
# Use hardware breakpoints (limited, but faster)
(lldb) breakpoint set -n main -H
# Profile LLDB command execution
time lldb -b -o "run" -o "bt" -o "quit" ./my_program
# Enable LLDB logging
(lldb) log enable lldb all
(lldb) log enable gdb-remote packets
# macOS: core dumps disabled by default
ulimit -c unlimited
# Set core file location
sudo sysctl -w kern.corefile=/tmp/core.%P
# Check settings
ulimit -c
sysctl kern.corefile
# Load core dump
lldb -c /tmp/core.12345 ./my_program
# Or within LLDB:
(lldb) target create ./my_program
(lldb) target core /tmp/core.12345
# Analyze
(lldb) bt
(lldb) frame info
(lldb) thread backtrace all
# List threads
(lldb) thread list
# Select thread
(lldb) thread select 2
# Backtrace all threads
(lldb) thread backtrace all
# Apply command to all threads
(lldb) thread apply all bt
# Step only current thread
(lldb) thread step-over
# Run until crash
(lldb) run
# Examine crash
(lldb) thread backtrace
(lldb) frame variable
(lldb) po self
# Check for force-unwrap crash
(lldb) fr v myOptional # Check if nil
# Print view hierarchy
(lldb) po UIApplication.shared.keyWindow?.rootViewController?.view.recursiveDescription()
# Modify view background
(lldb) expr myView.backgroundColor = .red
# Continue to see change
(lldb) continue
Use Instruments instead of LLDB:
LLDB memory debugging:
# Print retain count (Objective-C)
(lldb) po [myObject retainCount]
# Print all instances of class
(lldb) expr -l objc -- (void)[NSClassFromString(@"MyClass") instancesRespondToSelector:@selector(description)]
❌ NEVER: Use GDB for Swift/Objective-C debugging
→ LLDB has native support, GDB doesn't
❌ NEVER: Ignore 50x faster step-over (2025 LLDB)
→ Update Xcode for performance gains
❌ NEVER: Debug optimized Swift without -Onone
→ Variables optimized out, inaccurate stepping
❌ NEVER: Use `po` for non-printable types
→ Use `frame variable` instead
❌ NEVER: Attach to system processes without SIP disabled
→ macOS System Integrity Protection blocks this
❌ NEVER: Modify memory in production apps
→ Undefined behavior, crashes
✅ ALWAYS: Use LLDB for macOS/iOS development
✅ ALWAYS: Update Xcode for latest LLDB features (50x perf)
✅ ALWAYS: Use `po` for Swift/ObjC objects, `fr v` for primitives
✅ ALWAYS: Set symbolic breakpoints in Xcode for convenience
✅ ALWAYS: Use Instruments for memory/performance debugging
✅ ALWAYS: Save .lldbinit for project-specific commands
LLDB is the standard debugger for macOS/iOS. Key capabilities:
po, expression evaluationbreakpoint set, thread step-in, process launchQuick start:
lldb ./my_program
(lldb) breakpoint set -n main
(lldb) run
(lldb) thread step-over
(lldb) po myVariable
(lldb) thread backtrace
Master LLDB for efficient debugging on Apple platforms.