Xcode is incredibly useful for debugging iOS apps, especially with the updates released in the last few years. Sometimes that isn’t enough. Sometimes you want to find the exact error that caused an exception throw, or only activate a breakpoint with a certain method previously in the call stack. These cases, which are hard to debug with Xcode’s standard toolset, are easy to debug with LLDB. This talk will walk through the foundations of debugging in LLDB for Objective-C and Swift, as well as introduce some more advanced features for those really tough bugs.
17. (lldb) p *self.myCar
(Car) $1 = {
NSObject = {
isa = Car
}
_running = NO
_make = 0x000000010077d088 @"Tesla"
_model = 0x000000010077d0a8 @"S"
_year = 2014
_color = 0x00007fd531eb9e30
_gear = Park
}
18. (lldb) po self.myCar
<SwiftCars.Car: 0x7fe8bb456a60>
(lldb) p *self.myCar
error: <EXPR>:1:1: error: '*' is not a prefix unary operator
*self.myCar
^
40. settings set prompt [lldb]$
command alias bd breakpoint disable
command alias be breakpoint enable
command alias bdel breakpoint delete
command alias bcommand breakpoint command add
command alias commands breakpoint command list
58. (lldb) thread return true
error: Error returning from frame 0 of thread 1: We only support
setting simple integer and float return types at present..
100. (lldb) watch set var self.speed
error: "self" is a pointer and . was used to attempt to
access "speed". Did you mean "self->speed"?
(lldb) watch set var self->speed
error: "speed" is not a member of "(Car *const) self"
(lldb) watch set var _speed
101. (lldb) watch set var self.speed
Watchpoint created: Watchpoint 1: addr = 0x6080000a4698 size = 8
state = enabled type = w
declare @ ‘/Projects/SwiftCars/ViewController.swift:28’
watchpoint spec = 'self.speed'
103. (lldb) watch set var _speed
Watchpoint created: Watchpoint 1: addr = 0x7fb9c145d768 size
= 8 state = enabled type = w
watchpoint spec = ‘_speed'
new value: 0
(lldb) watch modify -c '(_speed==15)'
(lldb) c
Process 2150 resuming
Watchpoint 1 hit:
old value: 0
new value: 15
(lldb)
104. (lldb) watch set var self.speed
Watchpoint created: Watchpoint 1: addr = 0x6080000a4698 size = 8
state = enabled type = w
declare @ ‘/Projects/SwiftCars/ViewController.swift:28’
watchpoint spec = 'self.speed'
(lldb) watch modify -c ‘(self.speed==15)’ 1
(lldb) c
Watchpoint 1 hit:
(lldb) po self.speed
6
❌
105. (lldb) watch command add 1
Enter your debugger command(s). Type 'DONE' to end.
> p _speed
> continue
> DONE
(lldb) c
Process 2252 resuming
(NSInteger) $16 = 15
Process 2252 resuming
Command #2 'continue' continued the target.
114. (lldb) help script
Pass an expression to the script interpreter for evaluation and
return the results. Drop into the interactive interpreter if no
expression is given. This command takes 'raw' input (no need to
quote stuff).
Syntax: script [<script-expression-for-evaluation>]
120. find_in_stack = ['-[Car changeGearTo:]']
def continue_ignored(frame, bp_loc, dict):
global find_in_stack
names = set([frame.GetFunctionName() for frame
in frame.GetThread()])
all_ignored = set(find_in_stack)
ignored_here = all_ignored.intersection(names)
if len(ignored_here) == 0:
return False
quit()
find_in_stack = ['-[Car changeGearTo:]']
def continue_ignored(frame, bp_loc, dict):
global find_in_stack
names = set([frame.GetFunctionName() for frame
in frame.GetThread()])
all_ignored = set(find_in_stack)
ignored_here = all_ignored.intersection(names)
if len(ignored_here) == 0:
return False
quit()
121. find_in_stack = ['-[Car changeGearTo:]']
def continue_ignored(frame, bp_loc, dict):
global find_in_stack
names = set([frame.GetFunctionName() for frame
in frame.GetThread()])
all_ignored = set(find_in_stack)
ignored_here = all_ignored.intersection(names)
if len(ignored_here) == 0:
return False
quit()
find_in_stack = ['-[Car changeGearTo:]']
def continue_ignored(frame, bp_loc, dict):
global find_in_stack
names = set([frame.GetFunctionName() for frame
in frame.GetThread()])
all_ignored = set(find_in_stack)
ignored_here = all_ignored.intersection(names)
if len(ignored_here) == 0:
return False
quit()
stack symbol
function declaration
global accessor
all functions in stack
check if symbol we want
is in this stack
if it isn’t, continue
122. find_in_stack = ['-[Car changeGearTo:]']
def continue_ignored(frame, bp_loc, dict):
global find_in_stack
names = set([frame.GetFunctionName() for frame
in frame.GetThread()])
all_ignored = set(find_in_stack)
ignored_here = all_ignored.intersection(names)
if len(ignored_here) == 0:
return False
quit()
125. (lldb) br command add -s python 2
Enter your Python command(s). Type 'DONE' to end.
def function(frame,bp_loc,internal_dict):
"""frame: the SBFrame for the location at which you stopped
bp_loc: an SBBreakpointLocation for the breakpoint
location information
internal_dict: an LLDB support object not to be used"""
global find_in_stack
find_in_stack = ['-[Car changeGearTo:]']
names = set([frame.GetFunctionName() for frame in
frame.GetThread()])
all_ignored = set(find_in_stack)
ignored_here = all_ignored.intersection(names)
if len(ignored_here) == 0:
return False
DONE
135. More Use Cases
• Only break after another breakpoint has
been hit
• Check multiple threads for a symbol
• Data formatters for everything in Objc
• Custom LLDB commands