Leveling Up Your Debugging Skills with LLDB Commands in Xcode

Rohit Saini
CodeX
Published in
3 min readJul 16, 2024
Photo by Gabriel Heinzer on Unsplash

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 line
  • s (step): Step into a function call
  • finish: 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:

  1. Right-click on a breakpoint in Xcode
  2. Select “Edit Breakpoint”
  3. Click “Add Action” and choose “Debugger Command”
  4. 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!

--

--