Leveling Up Your Debugging Skills with LLDB Commands in Xcode
As iOS developers, we often rely on Xcode’s graphical debugging interface. However, mastering LLDB (Low Level Debugger) commands can significantly enhance your debugging capabilities. This article will explore some powerful LLDB commands and demonstrate how to use them effectively in Xcode’s console.
Getting Started with LLDB
LLDB is the default debugger in Xcode. To access it, set a breakpoint in your code and run the app. When the debugger stops at the breakpoint, you’ll see the LLDB prompt in Xcode’s console:
(lldb)
From here, you can start entering LLDB commands.
Essential LLDB Commands
1. Print Variables (p, po)
The p
command prints the description of a variable, while po
(print object) provides a more detailed, human-readable description.
let name = "John Doe"
let age = 30
In LLDB:
(lldb) p name
(String) $R0 = "John Doe"
(lldb) po name
John Doe
(lldb) p age
(Int) $R1 = 30
2. Examining Memory (x)
The x
command allows you to examine memory directly:
This command displays 4 giant words (g) in hexadecimal (x) starting from the address of name
.
3. Backtrace (bt)
The bt
command shows the current call stack:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x... YourApp`ViewController.viewDidLoad() -> ()
frame #1: 0x... UIKitCore`UIViewController.view
frame #2: 0x... UIKitCore`-[UIViewController loadViewIfRequired]
...
4. Stepping Through Code (n, s, finish)
n
(next): Step over the current lines
(step): Step into a function callfinish
: Step out of the current function
5. Watchpoints
Watchpoints allow you to stop execution when a variable changes:
(lldb) watchpoint set variable age
Watchpoint created: Watchpoint 1: addr = 0x... size = 8 state = enabled type = w
declare @ '/path/to/your/file.swift:10'
watchpoint spec = 'age'
new value: 30
6. Custom LLDB Commands
You can create custom commands to streamline your debugging workflow. For example, to print the view hierarchy:
(lldb) command alias vh expression -l objc -O -- [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
Now you can use vh
to print the view hierarchy:
(lldb) vh
<UIWindow: 0x...>
| <UIView: 0x...>
| | <UILabel: 0x...>
| | <UIButton: 0x...>
...
Advanced Techniques
1. Modifying Variables on the Fly
You can change variable values during runtime:
(lldb) expr age = 31
(Int) $R2 = 31
2. Calling Methods
LLDB allows you to call methods on objects:
class Person {
var name: String
func sayHello() { print("Hello, I'm \(name)") }
init(name: String) { self.name = name }
}
let person = Person(name: "Alice")
In LLDB:
(lldb) expr person.sayHello()
Hello, I'm Alice
3. Creating and Manipulating Objects
You can create new objects and manipulate them:
(lldb) expr let newPerson = Person(name: "Bob")
(lldb) expr newPerson.name = "Charlie"
(lldb) po newPerson
▿ Person
- name: "Charlie"
4. Using Breakpoint Commands
You can add LLDB commands to breakpoints, which will execute automatically when the breakpoint is hit:
- Right-click on a breakpoint in Xcode
- Select “Edit Breakpoint”
- Click “Add Action” and choose “Debugger Command”
- Enter your LLDB command(s)
For example, to automatically print a variable when a breakpoint is hit:
po myVariable
continue
This will print myVariable
and continue execution each time the breakpoint is hit.
5. Regular Expressions in Breakpoints
You can use regex to set breakpoints on multiple methods at once:
(lldb) breakpoint set -r '\[MyClass \w+]'
This sets breakpoints on all Objective-C methods of MyClass
.
6. Python Scripting in LLDB
LLDB has built-in Python scripting capabilities. You can write complex debug actions using Python:
def print_view_controllers(debugger, command, result, internal_dict):
debugger.HandleCommand('po [[[UIApplication sharedApplication] keyWindow] rootViewController]')
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add -f my_script.print_view_controllers pvcs')
Save this as my_script.py
, then in LLDB:
(lldb) command script import /path/to/my_script.py
(lldb) pvcs
7. Symbolicate Crash Logs
Load your app’s debug symbols:
(lldb) target create /path/to/MyApp.app/MyApp
Load the crash log:
(lldb) target symbols add /path/to/crash.log
Symbolicate an address:
(lldb) image lookup --address 0x000000010001c8f6
Conclusion
Mastering LLDB commands can significantly enhance your debugging skills, allowing you to inspect and manipulate your app’s state with precision. While Xcode’s graphical interface is useful, the power and flexibility of LLDB commands can help you tackle complex debugging scenarios more efficiently.
Remember, practice makes perfect. Start incorporating these commands into your daily debugging routine, and you’ll soon find yourself navigating through code issues with greater ease and confidence.
Happy debugging!