文章目录
现LLDB调试器已经成为Xcode工程中默认的调试器。它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。LLDB为Xcode提供了底层调试环境,其中包括内嵌在Xcode IDE中的位于调试区域的控制面板。
一、LLDB命令结构
<noun> <verb> [-options [option-value]] [argument [argument...]]
<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
- <command>(命令)和<subcommand>(子命令):LLDB调试器对象的名称。命令和子命令按层级结构来排列:一个特定的命令对象为跟随其的子命令对象创建一个上下文,子命令又为其子命令创建一个上下文,依此类推。
- <action>:我们想在组合的调试器对象(前面的命令序列)的上下文中执行的一些操作。
- <options>:行为修改器(action modifiers)。通常带有一些值。
- <argument>:根据使用的命令的上下文来表示各种不同的东西。例如,使用process launch命令启动进程,该上下文中的参数就是您在命令行中输入的参数,就好像你在调试会话之外调用进程一样。
LLBD命令行解析是在命令执行之前完成的,因此在所有命令中都是统一的。基本命令的命令语法非常简单,参数、选项和选项值都用空格分隔,并且双引号用于保护参数中的空格。如果需要在参数中使用反斜杠或双引号,可以在参数中使用反斜杠。这使得命令语法更加规范
如下所示:
(lldb) command [subcommand] -option "some \"quoted\" string"
(lldb) command [subcommand] -option 'some "quoted" string'
二、命令选项
LLDB中的命令选项有规范形式(canonical,也称为“discoverable”)和缩写形式(abbreviated)。
规范形式:
expression –object-description – foo
缩写形式:
e -0 – foo
别名:
po foo
选项可以放在命令行的任何位置,但如果参数以“-”开头,则必须通过添加选项终止来告诉LLDB您已经完成了当前命令的选项:“–”,例如,如果要启动进程并提供“进程启动”“command the”–stop at entry“选项,但是您希望将要启动的进程使用参数”-program_arg value“启动,您将键入:
(lldb) process launch --stop-at-entry -- -program_arg value
三、基础
执行命令(Execution Commands)
流程控制
从左到右,四个按钮分别是:continue,step over,step into,step out。
- continue
取消程序的暂停,允许程序正常执行 (要么一直执行下去,要么到达下一个断点)。
LLDB中使用process continue命令。
(lldb) process continue
(lldb) continue
(lldb) c
- step over
以黑盒的方式执行一行代码。如果所在这行代码是一个函数调用,那么就不会跳进这个函数,而是会执行这个函数,然后继续。
LLDB中使用thread step-over命令。
(lldb) thread step-over
(lldb) next
(lldb) n
- step in
如果你确实想跳进一个函数调用来调试或者检查程序的执行情况。当前行不是函数调用时,next和step效果是一样的。
在LLDB中使用thread step-in命令。
(lldb) thread step-in
(lldb) step
(lldb) s
- step out
如果你曾经不小心跳进一个函数,但实际上你想跳过它,常见的反应是重复的运行step over直到函数返回。其实这种情况,step out会继续执行到下一个返回语句 (直到一个堆栈帧结束) 然后再次停止。
在LLDB中使用thread step-out命令。
(lldb) thread step-out
(lldb) finish
process
使用参数启动进程
(lldb) process launch -- <args>
(lldb) run <args>
(lldb) r <args>
取消程序的暂停
(lldb) process continue
(lldb) continue
(lldb) c
thread
在当前选定的线程中执行源级单步。
(lldb) thread step-in
(lldb) step
(lldb) s
在当前选定的线程中执行源级别单步操作。
(lldb) thread step-over
(lldb) next
(lldb) n
在当前选定的线程中执行指令级单步。(遇到汇编函数,会进入汇编函数内部)
(lldb) thread step-inst
(lldb) si
在当前选定的线程中执行指令级别的单步操作。
(lldb) thread step-inst-over
(lldb) ni
走出当前选择的帧的。
(lldb) thread step-out
(lldb) finish
立即从当前选定的帧返回,并带有可选的返回值。
(lldb) thread return <RETURN EXPRESSION>
运行直到我们达到第12行或控制离开当前函数(行数只能在该函数之内)
(lldb) thread until 12
断点命令(Breakpoint Commands)
管理断点
在Xcode的左侧面板,有一组按钮。其中一个看起来像断点。点击它打开断点导航,这是一个可以快速管理所有断点的面板。
在LLDB中使用breakpoint list命令。
(lldb) breakpoint list
(lldb) br l
可以点击单个断点来开启或关闭。
在LLDB中使用breakpoint enable 和breakpoint disable 。
(lldb) breakpoint enable <breakpointID>
(lldb) br e <breakpointID>
(lldb) breakpoint disable <breakpointID>
(lldb) br disable <breakpointID>
显示断点详细信息
(lldb) breakpoint list -verbose
(lldb) br l -v
实例
(lldb) br l
Current breakpoints:
1: name = 'UIApplicationMain', locations = 1, resolved = 1, hit count = 1
1.1: where = UIKitCore`UIApplicationMain, address = 0x0000000108d52791, resolved, hit count = 1
2: names = {'objc_exception_throw', '__cxa_throw'}, locations = 2, resolved = 2, hit count = 0
2.1: where = libobjc.A.dylib`objc_exception_throw, address = 0x000000010548a705, resolved, hit count = 0
2.2: where = libc++abi.dylib`__cxa_throw, address = 0x00000001076df089, resolved, hit count = 0
3: file = '/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m', line = 23, exact_match = 0, locations = 1, resolved = 1, hit count = 1
3.1: where = lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:] + 70 at AppDelegate.m:23, address = 0x0000000104b6c5e6, resolved, hit count = 1
4: file = '/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m', line = 29, exact_match = 0, locations = 1, resolved = 1, hit count = 0
4.1: where = lldbTest`-[AppDelegate testExecutionCommands] + 23 at AppDelegate.m:29, address = 0x0000000104b6c647, resolved, hit count = 0
解释一下输出的信息
3: file = '/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m', line = 23, exact_match = 0, locations = 1, resolved = 1, hit count = 1
1.冒号前面的数字(3)是断点组的组号。
2.file = ‘/Users/lixingdong/workSpace/lldbTest/lldbTest/AppDelegate.m’, line = 29 断点的生成方式.
3.locations = 1 该组有几个断点。
4.resolved = 1 该组可用的断点的数量。
5.hit count = 1 该组断点被段过的次数。每一次程序在断点中暂停一次这个计数+1。
3.1: where = lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:] + 70 at AppDelegate.m:23, address = 0x0000000104b6c5e6, resolved, hit count = 1
- 3.1,3组号,1是组里编号,这个断点是第三组中的第一个断点。
- lldbTest是工程名称。
- -[AppDelegate application:didFinishLaunchingWithOptions:] 是断点所在类的函数。
-
- 70指的是该断点所在指令在其所在栈空间的偏移量(十进制)。
- AppDelegate.m:23 表示该断点所在文件行数。
- address = 0x0000000104b6c5e6 表示该断点所在的物理地址。
- resolved 表示是否可用,不可用是unresolved。
- hit count = 1 表示该断点拦截次数。
疑问
LLDB的断点命令都是临时生效?
user级别断点和project级别断点,使用breakpoint list查看不出区别?
操作断点
在Xcode中可以通过在源码页面器的滚槽上点击来创建断点。通过把断点拖拽出滚槽,然后释放鼠标来删除断点。
也可以点击断点栏左侧的 + 按钮创建断点,在断点导航页选择断点,然后按下删除键删除。
创建
在LLDB中使用breakpoint set命令。
可以使用缩写形式 br。虽然 b 是一个完全不同的命令 (_regexp-break 的缩写),但恰好也可以实现和上面同样的效果。
给某个函数设置断点(私有方法亦可设置)
(lldb) breakpoint set --name main
(lldb) br s -n main
(lldb) b main
(lldb) breakpoint set --name "-[NSString stringWithFormat:]"
(lldb) b -[NSString stringWithFormat:]
某个文件中的某行设置一个断点
(lldb) breakpoint set --file test.c --line 12
(lldb) br s -f test.c -l 12
(lldb) b test.c:12
给C++中所有命名为main的方法设置断点
(lldb) breakpoint set --method main
(lldb) br s -M main
Objective-C中所有命名为count的选择器设置断点
(lldb) breakpoint set --selector count
(lldb) br s -S count
使用–shlib
(lldb) breakpoint set --shlib foo.dylib --name foo
通过函数名称上的正则表达式设置断点
(lldb) breakpoint set --func-regex regular-expression
(lldb) br s -r regular-expression
编辑
- Symbol:
符号断点。填入想设置断点的方法 (例如:-[NSException raise],-号是实例方法,+号是类方法) - Module
填入要设置断点的方法或函数是否在位于dylib中,默认不填。 - Condition
填入条件。返回值必须为BOOL类型。确定类类型isKindOfClass:,确定对象在继承体系中的位置的isMemberOfClass:,判断一个对象是否能接收某个特定消息的respondsToSelector:,判断一个对象是否遵循某个协议的conformsToProtocol:,以及提供方法实现地址的methodForSelector: - Ignore
忽略前n次不执行断点,优先级在condition之上 - Action
- AppleScript
内置的一种功能强大的脚本语言。 - Capture GPU Frame
捕捉动画帧速。 - Debugger Command
调试器命令。
可输入LLDB相关命令。 - Log Message
日志信息。
%B会打印断点的名字,%H会打印断点的调用次数,@@中间可以输入表达式。打印内容直接输入。 - Shell Command
终端命令。 - Sound
播放声音。
- Options
勾选Automatically continue after evaluating actions之后程序会在断点产生后继续运行。这个属性是相当有用的,可以输入调试信息至于不暂停程序。
删除
在LLDB中使用breakpoint delete命令。
(lldb) breakpoint delete 1
(lldb) br del 1
breakpoint
在名为main的所有函数上设置断点。
(lldb) breakpoint set --name main
(lldb) br s -n main
(lldb) b main
在文件test.c的第12行中设置断点。
(lldb) breakpoint set --file test.c --line 12
(lldb) br s -f test.c -l 12
(lldb) b test.c:12
给C++中所有命名为main的方法设置断点。
(lldb) breakpoint set --method main
(lldb) br s -M main
在Objective-C函数-[NSString stringWithFormat:]中设置断点。
(lldb) breakpoint set --name "-[NSString stringWithFormat:]"
(lldb) b -[NSString stringWithFormat:]
在其选择器为count的所有Objective-C方法中设置断点
(lldb) breakpoint set --selector count
(lldb) br s -S count
通过函数名称上的正则表达式设置断点。
(lldb) breakpoint set --func-regex regular-expression
(lldb) br s -r regular-expression
通过源文件内容上的正则表达式设置断点。
(lldb) breakpoint set --name foo --condition '(int)strcmp(y,"hello") == 0'
(lldb) br s -n foo -c '(int)strcmp(y,"hello") == 0'
列出所有断点。
(lldb) breakpoint list
(lldb) br l
删除断点。
(lldb) breakpoint delete 1
(lldb) br del 1
观察点命令(Watchpoint Commands)
设置观察点
有时候我们会关心类的某个属性什么时候被人修改了,最简单的方法当然就是在setter的方法打断点,或者在@property的属性生命行打上断点。这样当对象的setter方法被调用时,就会触发这个断点。
当然这么做是有缺点的,对于直接访问内存地址的修改,setter方法的断点并没有办法监控得到,因此我们需要用到watchpoint命令。
_watchPointInteger = 8;
watchpoint命令在XCode的GUI中也可以直接使用,当程序暂停时,我们能对当前程序栈中的变量设置watchpoint。值得注意的是,watchpoint是直接设置到该变量所在的内存地址上的,所以当这个变量释放了后,watchpoint仍然是对这个地址的内存生效的。
受限于CPU的支持程度,在intel平台上,提供了4个slot供watchpoint使用,所以同时只可设置最多4个watchpoint,arm上是2个。
在LLDB中使用用watchpoint命令(不能使用点语法,因为是getter函数)
(lldb) wa set variable self->_watchPointInteger
Watchpoint created: Watchpoint 1: addr = 0x6000033da7e8 size = 8 state = enabled type = w
watchpoint spec = 'self->_watchPointInteger'
new value: 1
(lldb) wa modify -c 'self->_watchPointInteger == 5'
Watchpoint 1 hit:
old value: 1
new value: 5
(lldb) wa list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x6000033da7e8 size = 8 state = enabled type = w
watchpoint spec = 'self->_watchPointInteger'
old value: 1
new value: 5
condition = 'self->_watchPointInteger == 5'
watchpoint
在写入变量时在变量上设置观察点。
(lldb) watchpoint set variable global_var
(lldb) wa s v global_var
在写入时在内存位置设置观察点。 如果未指定“-x byte_size”,则要监视的区域的大小默认为指针大小。 此命令采用原始输入,作为表达式计算,返回指向区域开头的无符号整数,位于’–'选项终止符之后。
(lldb) watchpoint set expression -- my_ptr
(lldb) wa s e -- my_ptr
在观察点上设置条件。
(lldb) watch set var global
(lldb) watchpoint modify -c '(global==5)'
(lldb) c
...
(lldb) bt
* thread #1: tid = 0x1c03, 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16, stop reason = watchpoint 1
frame #0: 0x0000000100000ef5 a.out`modify + 21 at main.cpp:16
frame #1: 0x0000000100000eac a.out`main + 108 at main.cpp:25
frame #2: 0x00007fff8ac9c7e1 libdyld.dylib`start + 1
(lldb) frame var global
(int32_t) global = 5
列出所有观察点。
(lldb) watchpoint list
(lldb) watch l
删除观察点。
(lldb) watchpoint delete 1
(lldb) watch del 1
检查变量(Examining Variables)
查看调用栈状态
检查帧参数和本地变量的最简便的方式是使用frame variable命令
(lldb) frame variable
(lldb) fr v
如果没有指定任何变量名,则会显示所有参数和本地变量。
(lldb) frame variable
(AppDelegate *) self = 0x000060000123ba00
(SEL) _cmd = "application:didFinishLaunchingWithOptions:"
(UIApplication *) application = 0x00007f8cb6c02780
(NSDictionary *) launchOptions = nil
如果指定参数名或变量名,则只打印指定的值。
(lldb) frame variable self
(AppDelegate *) self = 0x000060000123ba00
frame variable命令不是一个完全的表达式解析器,但它支持一些简单的操作符,如&,*,->,[]。这个数组括号可用于指针,以将指针作为数组处理。
frame variable命令会在变量上执行”对象打印”操作。目前,LLDB只支持Objective-C打印,使用的是对象的description方法。
(lldb) frame variable *self
(AppDelegate) *self = {
_window = 0x00007f8cb6c2a620
_stepInString = 0x0000000108c74070 @"stepInString"
_stepOutString = 0x0000000108c740b0 @"12345"
_watchPointInteger = 5
_watchPointView = 0x00007f8cb8904320
}
(lldb) frame variable launchOptions[0]
(NSObject) launchOptions[0] = <parent is NULL>
如果想查看另外一帧,可以使用frame select命令
(lldb) frame select 1
frame #1: 0x000000010ce50bde UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 280
UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]:
0x10ce50bde <+280>: cmpb $0x0, -0x29(%rbp)
0x10ce50be2 <+284>: setne %r13b
0x10ce50be6 <+288>: andb %al, %r13b
0x10ce50be9 <+291>: movl -0x38(%rbp), %r15d
frame
显示当前帧的参数和局部变量。
(lldb) frame variable
(lldb) fr v
显示当前帧的局部变量。
(lldb) frame variable --no-args
(lldb) fr v -a
显示局部变量“bar”的内容。
(lldb) frame variable bar
(lldb) fr v bar
(lldb) p bar
显示格式为十六进制的局部变量“bar”的内容。
(lldb) frame variable --format x bar
(lldb) fr v -f x bar
target
显示全局变量“baz”的内容。
(lldb) target variable baz
(lldb) ta v baz
显示当前源文件中定义的全局/静态变量。
(lldb) target variable
(lldb) ta v
每次停止时显示变量“argc”和“argv”。
(lldb) target stop-hook add --one-liner "frame variable argc argv"
(lldb) ta st a -o "fr v argc argv"
(lldb) display argc
(lldb) display argv
仅当你在main函数中停止时,才显示变量“argc”和“argv”。
(lldb) target stop-hook add --name main --one-liner "frame variable argc argv"
(lldb) ta st a -n main -o "fr v argc argv"
仅当您在名为MyClass的c类中停止时,才显示变量“* this”。
(lldb) target stop-hook add --classname MyClass --one-liner "frame variable *this"
(lldb) ta st a -c MyClass -o "fr v *this"
评价表达式(Evaluating Expressions)
打印
打印值很简单;只要试试 print 命令:
(lldb) expr (int) printf ("Print nine: %d.", 4 + 5)
or using the print alias:
(lldb) print (int) printf ("Print nine: %d.", 4 + 5)
结果中有个 1 。 实 际 上 你 可 以 使 用 它 来 指 向 这 个 结 果 。 另 外 1。实际上你可以使用它来指向这个结果。另外 1。实际上你可以使用它来指向这个结果。另外后面的数值是递增的,每打印一个与对象相关的命令,这个值都会加1
(lldb) p self.stepOutString
(__NSCFConstantString *) $1 = 0x0000000106999090 @"stepOutString"
赋值
如果想改变一个值怎么办?你或许会猜 modify。其实这时候我们要用到的是 expression 这个方便的命令。
(lldb) print self.watchPointInteger
(NSInteger) $2 = 1
(lldb) expression self.watchPointInteger=8
(NSInteger) $3 = 8
(lldb) print self.watchPointInteger
(NSInteger) $4 = 8
打印对象
上面的print命令会打印出对象的很多信息,如果我们只想查看对象的值的信息,则可以使用po(print object的缩写)命令。
(lldb) expr -o -- [SomeClass returnAnObject]
or using the po alias:
(lldb) po [SomeClass returnAnObject]
打印字符串
(lldb) po self.stepOutString
12345
(lldb) p self.stepOutString
(__NSCFConstantString *) $6 = 0x00000001069990b0 @"12345"
(lldb)
打印数组
(lldb) p @[ @"foo", @"bar" ]
(__NSArrayI *) $7 = 0x00006000018d56c0 @"2 elements"
(lldb) po $7
<__NSArrayI 0x6000018d56c0>(
foo,
bar
)
expr
评估当前帧中的广义表达式。
(lldb) expr (int) printf ("Print nine: %d.", 4 + 5)
or using the print alias:
(lldb) print (int) printf ("Print nine: %d.", 4 + 5)
为便利变量创建和赋值。
(lldb) expr unsigned int $foo = 5
打印对象的ObjC的"description(描述)"。
(lldb) expr -o -- [SomeClass returnAnObject]
or using the po alias:
(lldb) po [SomeClass returnAnObject]
打印表达式结果的动态类型。
(lldb) expr -d 1 -- [SomeClass returnAnObject]
(lldb) expr -d 1 -- someCPPObjectPtrOrReference
or set dynamic type printing to be the default: (lldb) settings set target.prefer-dynamic run-target
调用函数使您可以在函数中的断点处停止。
(lldb) expr -i 0 -- function_with_a_breakpoint()
调用崩溃的函数,并在函数崩溃时停止。
(lldb) expr -u 0 -- function_which_crashes()
检查线程状态(Examining Thread State)
查看线程状态
在进程停止后,LLDB会选择一个当前线程和线程中当前帧(frame)。很多检测状态的命令可以用于这个线程或帧。
为了检测进程的当前状态,可以从以下命令开始:
(lldb) thread list
Process 59154 stopped
* thread #1: tid = 0x56c99e, 0x0000000106997294 lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:](self=0x0000600000dea280, _cmd="application:didFinishLaunchingWithOptions:", application=0x00007fb230c00bf0, launchOptions=0x0000000000000000) at AppDelegate.m:28, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
thread #3: tid = 0x56ca07, 0x00000001099b15be libsystem_kernel.dylib`__workq_kernreturn + 10
thread #4: tid = 0x56ca09, 0x00000001099b31b2 libsystem_kernel.dylib`__psynch_cvwait + 10, name = 'JavaScriptCore bmalloc scavenger'
thread #5: tid = 0x56ca0a, 0x00000001099afc2a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'WebThread'
thread #6: tid = 0x56ca0b, 0x00000001099afc2a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
thread #7: tid = 0x56ca0d, 0x00000001099f2dec libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell + 268, queue = 'com.apple.root.utility-qos'
(lldb)
星号(*)表示thread #1为当前线程。为了获取线程的跟踪栈,可以使用以下命令:
(lldb) thread backtrace
(lldb) bt
例如
(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
* frame #0: 0x0000000106997294 lldbTest`-[AppDelegate application:didFinishLaunchingWithOptions:](self=0x0000600000dea280, _cmd="application:didFinishLaunchingWithOptions:", application=0x00007fb230c00bf0, launchOptions=0x0000000000000000) at AppDelegate.m:28
frame #1: 0x000000010ab75bde UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 280
frame #2: 0x000000010ab775cb UIKitCore`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3979
frame #3: 0x000000010ab7cc2f UIKitCore`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1623
frame #4: 0x000000010a39b4e9 UIKitCore`__111-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:]_block_invoke + 866
frame #5: 0x000000010a3a429c UIKitCore`+[_UICanvas _enqueuePostSettingUpdateTransactionBlock:] + 153
frame #6: 0x000000010a39b126 UIKitCore`-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:] + 233
frame #7: 0x000000010a39bae0 UIKitCore`-[__UICanvasLifecycleMonitor_Compatability activateEventsOnly:withContext:completion:] + 1085
frame #8: 0x000000010a399cb5 UIKitCore`__82-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:]_block_invoke + 795
frame #9: 0x000000010a39995f UIKitCore`-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:] + 435
frame #10: 0x000000010a39ea90 UIKitCore`__125-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:]_block_invoke + 584
frame #11: 0x000000010a39f80e UIKitCore`_performActionsWithDelayForTransitionContext + 100
frame #12: 0x000000010a39e7ef UIKitCore`-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:] + 221
frame #13: 0x000000010a3a393a UIKitCore`-[_UICanvas scene:didUpdateWithDiff:transitionContext:completion:] + 392
frame #14: 0x000000010ab7b44e UIKitCore`-[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 515
frame #15: 0x000000010a71fd09 UIKitCore`-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 357
frame #16: 0x00000001133b42da FrontBoardServices`-[FBSSceneImpl _didCreateWithTransitionContext:completion:] + 448
frame #17: 0x00000001133bf443 FrontBoardServices`__56-[FBSWorkspace client:handleCreateScene:withCompletion:]_block_invoke_2 + 271
frame #18: 0x00000001133beb3a FrontBoardServices`__40-[FBSWorkspace _performDelegateCallOut:]_block_invoke + 53
frame #19: 0x0000000109616602 libdispatch.dylib`_dispatch_client_callout + 8
frame #20: 0x0000000109619b78 libdispatch.dylib`_dispatch_block_invoke_direct + 301
frame #21: 0x00000001133f3ba8 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
frame #22: 0x00000001133f3860 FrontBoardServices`-[FBSSerialQueue _performNext] + 457
frame #23: 0x00000001133f3e40 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 45
frame #24: 0x0000000107c7d721 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #25: 0x0000000107c7cf93 CoreFoundation`__CFRunLoopDoSources0 + 243
frame #26: 0x0000000107c7763f CoreFoundation`__CFRunLoopRun + 1263
frame #27: 0x0000000107c76e11 CoreFoundation`CFRunLoopRunSpecific + 625
frame #28: 0x00000001103f81dd GraphicsServices`GSEventRunModal + 62
frame #29: 0x000000010ab7e81d UIKitCore`UIApplicationMain + 140
frame #30: 0x0000000106997200 lldbTest`main(argc=1, argv=0x00007ffee9267fd0) at main.m:14
frame #31: 0x000000010968c575 libdyld.dylib`start + 1
(lldb)
如果想查看所有线程的调用栈,则可以使用以下命令:
(lldb) thread backtrace all
(lldb) bt all
thread
列出程序中的线程。
(lldb) thread list
选择线程1作为后续命令的默认线程。
(lldb) thread select 1
(lldb) t 1
显示当前线程的堆栈回溯。
(lldb) thread backtrace
(lldb) bt
显示所有线程的堆栈回溯。
(lldb) thread backtrace all
(lldb) bt all
回溯当前线程的前五帧。
(lldb) thread backtrace -c 5
(lldb) bt 5 (lldb-169 and later)
(lldb) bt -c 5 (lldb-168 and earlier)
frame
按索引为当前线程选择不同的堆栈帧。
(lldb) frame select 12
(lldb) fr s 12
(lldb) f 12
列出有关当前线程中当前所选帧的信息。
(lldb) frame info
选择调用当前堆栈帧的堆栈帧。
(lldb) up
(lldb) frame select --relative=1
选择当前堆栈帧调用的堆栈帧。
(lldb) down
(lldb) frame select --relative=-1
(lldb) fr s -r-1
使用相对偏移量选择不同的堆栈帧。
(lldb) frame select --relative 2
(lldb) fr s -r2
(lldb) frame select --relative -3
(lldb) fr s -r-3
可执行和共享库查询命令(Executable and Shared Library Query Commands)
image
image命令的用法也挺多,首先可以用它来查看工程中使用的库,如下所示:
(lldb) image list
[ 0] D96F1640-B0C5-33FC-A8BB-3290BAA51E88 0x0000000106996000 /Users/lixingdong/Library/Developer/Xcode/DerivedData/lldbTest-fkaqwvfpyrrqthbnxsglafxirqyy/Build/Products/Debug-iphonesimulator/lldbTest.app/lldbTest
[ 1] D6387150-2FB8-3066-868D-72E1B1C43982 0x000000010e532000 /usr/lib/dyld
[ 2] C3514384-926E-3813-BF0C-69FFC704E283 0x00000001069a2000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim
[ 3] E5391C7B-0161-33AF-A5A7-1E18DBF9041F 0x0000000106c8b000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation
[ 4] 177A61B3-9E02-3A09-9A98-C1C3C9AB7958 0x00000001072b1000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib
......
我们还可以用它来查找可执行文件或共享库的原始地址,这一点还是很有用的,当我们的程序崩溃时,我们可以使用这条命令来查找崩溃所在的具体位置,如下所示:
NSArray *array = @[@1, @2];
NSLog(@"item 3: %@", array[2]);
这段代码在运行后会抛出如下异常:
2019-08-06 22:00:34.724090+0800 lldbTest[59567:5748056] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndexedSubscript:]: index 2 beyond bounds [0 .. 1]'
*** First throw call stack:
(
0 CoreFoundation 0x000000010143d1bb __exceptionPreprocess + 331
1 libobjc.A.dylib 0x00000001009db735 objc_exception_throw + 48
2 CoreFoundation 0x00000001013894ec _CFThrowFormattedException + 194
3 CoreFoundation 0x00000001014bfb00 +[__NSArrayI allocWithZone:] + 0
4 lldbTest 0x00000001000bc311 -[AppDelegate testImageCommands] + 241
5 lldbTest 0x00000001000bc0ae -[AppDelegate application:didFinishLaunchingWithOptions:] + 110
......
(lldb) image lookup --address 0x1ec4
(lldb) im loo -a 0x1ec4
根据以上信息,我们可以判断崩溃位置是在AppDelegate.m文件中,要想知道具体在哪一行,可以使用以下命令:
(lldb) image lookup --address 0x00000001000bc311
Address: lldbTest[0x0000000100001311] (lldbTest.__TEXT.__text + 945)
Summary: lldbTest`-[AppDelegate testImageCommands] + 241 at AppDelegate.m:45
(lldb) image lookup --address 0x00000001014bfb00
Address: CoreFoundation[0x00000000001aab00] (CoreFoundation.__TEXT.__text + 1741536)
Summary: CoreFoundation`+[__NSArrayI allocWithZone:]
可以使用image lookup命令来查看具体的类型
(lldb) image lookup --type NSURL
Best match found in /Users/**/Library/Developer/Xcode/DerivedData/test-byjqwkhxixddxudlnvqhrfughkra/Build/Products/Debug/test:
id = {0x100000157}, name = "NSURL", byte-size = 40, decl = NSURL.h:17, clang_type = "@interface NSURL : NSObject{
NSString * _urlString;
NSURL * _baseURL;
void * _clients;
void * _reserved;
}
@property ( readonly,getter = absoluteString,setter = <null selector>,nonatomic ) NSString * absoluteString;
@property ( readonly,getter = relativeString,setter = <null selector>,nonatomic ) NSString * relativeString;
@property ( readonly,getter = baseURL,setter = <null selector>,nonatomic ) NSURL * baseURL;
@property ( readonly,getter = absoluteURL,setter = <null selector>,nonatomic ) NSURL * absoluteURL;
@property ( readonly,getter = scheme,setter = <null selector>,nonatomic ) NSString * scheme;
@property ( readonly,getter = resourceSpecifier,setter = <null selector>,nonatomic ) NSString * resourceSpecifier;
@property ( readonly,getter = host,setter = <null selector>,nonatomic ) NSString * host;
@property ( readonly,getter = port,setter = <null selector>,nonatomic ) NSNumber * port;
@property ( readonly,getter = user,setter = <null selector>,nonatomic ) NSString * user;
@property ( readonly,getter = password,setter = <null selector>,nonatomic ) NSString * password;
@property ( readonly,getter = path,setter = <null selector>,nonatomic ) NSString * path;
@property ( readonly,getter = fragment,setter = <null selector>,nonatomic ) NSString * fragment;
@property ( readonly,getter = parameterString,setter = <null selector>,nonatomic ) NSString * parameterString;
@property ( readonly,getter = query,setter = <null selector>,nonatomic ) NSString * query;
@property ( readonly,getter = relativePath,setter = <null selector>,nonatomic ) NSString * relativePath;
@property ( readonly,getter = fileSystemRepresentation,setter = <null selector> ) const char * fileSystemRepresentation;
@property ( readonly,getter = isFileURL,setter = <null selector>,readwrite ) BOOL fileURL;
@property ( readonly,getter = standardizedURL,setter = <null selector>,nonatomic ) NSURL * standardizedURL;
@property ( readonly,getter = filePathURL,setter = <null selector>,nonatomic ) NSURL * filePathURL;
@end"
补充
- lldb导入UIKit
expr @import UIKit
在lldbinit中设置全局自动导入UIKit
echo display @import UIKit >> ~/.lldbinit
摘要
https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/gdb_to_lldb_transition_guide/document/Introduction.html
http://lldb.llvm.org/use/tutorial.html
https://southpeak.github.io/2015/01/25/tool-lldb/
https://huos3203.github.io/2018/09/05/调试/使用LLDB独立调试APP/
https://www.jianshu.com/p/e48b9912b011
https://www.jianshu.com/p/39fb3d4a1368
https://www.bbsmax.com/A/6pdDEQLlzw/
https://blog.csdn.net/xuhen/article/details/77747456
https://www.jianshu.com/p/2d4083d27653
https://www.jianshu.com/p/d6a0a5e39b0e
https://objccn.io/issue-19-2/
http://openfibers.github.io/blog/2016/02/24/auto-import-frameworks-in-xcode-lldb/