Debugging Swift code with LLDB

Where should we go first?

  1. Explore variables values: expression, e, print, po, p
  2. Get overall app’s state + language specific commands: bugreport, frame, language
  3. Control app’s execution flow: process, breakpoint, thread, watchpoint
  4. Honorable mentions: command, platform, gui
Download full size version with this link — https://www.dropbox.com/s/9sv67e7f2repbpb/lldb-commands-map.png?dl=0

1. Explore variables value and state

(lldb) e <variable>
(lldb) e <expression>
(lldb) e sum 
(Int) $R0 = 6 // You can also use $R0 to refer to this variable in the future (during current debug session)
(lldb) e sum = 4 // Change value of sum variable(lldb) e sum
(Int) $R2 = 4 // sum variable will be "4" till the end of debugging session
(lldb) expression <some flags> -- <variable>
> lldb
> (lldb) help # To explore all available commands
> (lldb) help expression # To explore all expressions related sub-commands
  • -D <count> (--depth <count>) — Set the max recurse depth when dumping aggregate types (default is infinity).
  • -O (--object-description) — Display using a language-specific description API, if possible.
  • -T (--show-types) — Show variable types when dumping values.
  • -f <format> (--format <format>) –– Specify a format to be used for display.
  • -i <boolean> (--ignore-breakpoints <boolean>) — Ignore breakpoint hits while running expressions
(lldb) e -D 1 -- logger(LLDB_Debugger_Exploration.Logger) $R5 = 0x0000608000087e90 {
currentClassName = "ViewController"
debuggerStruct ={...}
}
(lldb) e -- logger(LLDB_Debugger_Exploration.Logger) $R6 = 0x0000608000087e90 {
currentClassName = "ViewController"
debuggerStruct = (methodName = "name", lineNumber = 2, commandCounter = 23)
}
(lldb) po logger<Logger: 0x608000087e90>

2. Get overall app’s state + language specific commands

(lldb) bugreport unwind --outfile <path to output file>
Example of bugreport command output
(lldb) frame infoframe #0: 0x000000010bbe4b4d LLDB-Debugger-Exploration`ViewController.valueOfLifeWithoutSumOf(a=2, b=2, self=0x00007fa0c1406900) -> Int at ViewController.swift:96
(lldb) language swift refcount loggerrefcount data: (strong = 4, weak = 0)

3. Control app’s execution flow

(lldb) process statusProcess 27408 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
frame #0: 0x000000010bbe4889 LLDB-Debugger-Exploration`ViewController.viewDidLoad(self=0x00007fa0c1406900) -> () at ViewController.swift:69
66
67 let a = 2, b = 2
68 let result = valueOfLifeWithoutSumOf(a, and: b)
-> 69 print(result)
70
71
72
(lldb) process continue(lldb) c // Or just type "c" which is the same as previous command
(lldb) breakpoint listCurrent breakpoints:
1: file = '/Users/Ahmed/Desktop/Recent/LLDB-Debugger-Exploration/LLDB-Debugger-Exploration/ViewController.swift', line = 95, exact_match = 0, locations = 1, resolved = 1, hit count = 1
1.1: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.valueOfLifeWithoutSumOf (Swift.Int, and : Swift.Int) -> Swift.Int + 27 at ViewController.swift:95, address = 0x0000000107f3eb3b, resolved, hit count = 12: file = '/Users/Ahmed/Desktop/Recent/LLDB-Debugger-Exploration/LLDB-Debugger-Exploration/ViewController.swift', line = 60, exact_match = 0, locations = 1, resolved = 1, hit count = 12.1: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.viewDidLoad () -> () + 521 at ViewController.swift:60, address = 0x0000000107f3e609, resolved, hit count = 1
(lldb) breakpoint set -f ViewController.swift -l 96Breakpoint 3: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.valueOfLifeWithoutSumOf (Swift.Int, and : Swift.Int) -> Swift.Int + 45 at ViewController.swift:96, address = 0x0000000107f3eb4d
(lldb) b ViewController.swift:96
(lldb) breakpoint set --func-regex valueOfLifeWithoutSumOf(lldb) b -r valueOfLifeWithoutSumOf // Short version of the command above
(lldb) breakpoint set --one-shot -f ViewController.swift -l 90(lldb) br s -o -f ViewController.swift -l 91 // Shorter version of the command above
(lldb) b ViewController.swift:96 // Let's add a breakpoint firstBreakpoint 2: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.valueOfLifeWithoutSumOf (Swift.Int, and : Swift.Int) -> Swift.Int + 45 at ViewController.swift:96, address = 0x000000010c555b4d(lldb) breakpoint command add 2 // Setup some commands Enter your debugger command(s).  Type 'DONE' to end.
> p sum // Print value of "sum" variable
> p a + b // Evaluate a + b
> DONE
(lldb) breakpoint command list 2Breakpoint 2:
Breakpoint commands:
p sum
p a + b
Process 36612 resuming
p sum
(Int) $R0 = 6
p a + b
(Int) $R1 = 4
(lldb) breakpoint command add 2 // Setup some commandsEnter your debugger command(s).  Type 'DONE' to end.
> p sum // Print value of "sum" variable
> p a + b // Evaluate a + b
> continue // Resume right after first hit
> DONE
p sum
(Int) $R0 = 6
p a + b
(Int) $R1 = 4
continue
Process 36863 resuming
Command #3 'continue' continued the target.
(lldb) thread step-over
(lldb) next // The same as "thread step-over" command
(lldb) n // The same as "next" command
(lldb) thread step-in
(lldb) step // The same as "thread step-in"
(lldb) s // The same as "step"
(lldb) thread info thread #1: tid = 0x17de17, 0x0000000109429a90 LLDB-Debugger-Exploration`ViewController.sumOf(a=2, b=2, self=0x00007fe775507390) -> Int at ViewController.swift:90, queue = 'com.apple.main-thread', stop reason = step in
(lldb) thread listProcess 50693 stopped* thread #1: tid = 0x17de17, 0x0000000109429a90 LLDB-Debugger-Exploration`ViewController.sumOf(a=2, b=2, self=0x00007fe775507390) -> Int at ViewController.swift:90, queue = 'com.apple.main-thread', stop reason = step in  thread #2: tid = 0x17df4a, 0x000000010daa4dc6  libsystem_kernel.dylib`kevent_qos + 10, queue = 'com.apple.libdispatch-manager'  thread #3: tid = 0x17df4b, 0x000000010daa444e libsystem_kernel.dylib`__workq_kernreturn + 10  thread #5: tid = 0x17df4e, 0x000000010da9c34a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'

Honorable mentions

thread info // Show current thread info
br list // Show all breakpoints
(lldb) command source /Users/Ahmed/Desktop/lldb-test-scriptExecuting commands in '/Users/Ahmed/Desktop/lldb-test-script'.thread info
thread #1: tid = 0x17de17, 0x0000000109429a90 LLDB-Debugger-Exploration`ViewController.sumOf(a=2, b=2, self=0x00007fe775507390) -> Int at ViewController.swift:90, queue = 'com.apple.main-thread', stop reason = step in
br list
Current breakpoints:
1: file = '/Users/Ahmed/Desktop/Recent/LLDB-Debugger-Exploration/LLDB-Debugger-Exploration/ViewController.swift', line = 60, exact_match = 0, locations = 1, resolved = 1, hit count = 0
1.1: where = LLDB-Debugger-Exploration`LLDB_Debugger_Exploration.ViewController.viewDidLoad () -> () + 521 at ViewController.swift:60, address = 0x0000000109429609, resolved, hit count = 0
(lldb) command import ~/Desktop/script.pyThe "print_hello" python command has been installed and is ready for use.(lldb) print_helloHello Debugger!
(lldb) platform statusPlatform: ios-simulator
Triple: x86_64-apple-macosx
OS Version: 10.12.5 (16F73)
Kernel: Darwin Kernel Version 16.6.0: Fri Apr 14 16:21:16 PDT 2017; root:xnu-3789.60.24~6/RELEASE_X86_64
Hostname: 127.0.0.1
WorkingDir: /
SDK Path: "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk"
Available devices:
614F8701-3D93-4B43-AE86-46A42FEB905A: iPhone 4s
CD516CF7-2AE7-4127-92DF-F536FE56BA22: iPhone 5
0D76F30F-2332-4E0C-9F00-B86F009D59A3: iPhone 5s
3084003F-7626-462A-825B-193E6E5B9AA7: iPhone 6
...
(lldb) gui// You'll see this error if you try to execute gui command in Xcode
error: the gui command requires an interactive terminal.
This is how LLDB GUI mode looks like

Conclusion:

References and useful articles on LLDB

  • Official LLDB site — you’ll find here all possible materials related to LLDB. Documentation, guides, tutorials, sources and much more.
  • LLDB Quick Start Guide by Apple — as usual, Apple has a great documentation. This guide will help you to get started with LLDB really quickly. Also, they’ve described how to do debugging with LLDB without Xcode.
  • How debuggers work: Part 1 — Basics — I enjoyed this series of articles a lot. It’s Just fantastic overview how debuggers really work. Article describes all underlying principles using code of hand-made debugger written in C. I strongly encourage you to read all parts of these great series (Part 2, Part 3).
  • WWDC14 Advanced Swift Debugging in LLDB — great overview what’s new in LLDB in terms of Swift debugging. And how LLDB helps you be more productive with an overall debugging process using built-in functions and features.
  • Introduction To LLDB Python Scripting — the guide on Python scripting for LLDB which allows you to start really quickly.
  • Dancing in the Debugger. A Waltz with LLDB — a clever introduction to some LLDB basics. Some information is a bit outdated (like (lldb) thread return command, for example. Unfortunately, it doesn't work with Swift properly because it can potentially bring some damage to reference counting). Still, it’s a great article to start your LLDB journey.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store