一、概述
LLDB
(Low Lever Debug
这里的low
指轻量级)默认内置于Xcode
中的动态调试工具。标准的 LLDB
提供了一组广泛的命令,旨在与老版本的 GDB
命令兼容。 除了使用标准配置外,还可以很容易地自定义 LLDB
以满足实际需要。
二、LLDB语法
<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
-
command
:命令 -
subcommand
:子命令 -
action
:执行命令的操作 -
options
:命令选项 -
arguement
:参数 -
[]
:表示可选
例子:
//command action option arguement
breakpoint set -n test1
唯一匹配原则:根据n
个字母已经能唯一匹配到某个命令,则只写n
个字母等效于完整的命令(大小写敏感)。也就是说只要能识别出来命令唯一就可以:
br s -n test1
help
直接在LLDB
中输入help
可以查看所有的LLDB
命令。
查看某一个命令help <command-name>
/help <command> <subcommand>
:
help breakpoint
help breakpoint set
apropos
apropos
可以用来搜索命令相关信息。
//将所有breakpoint命令搜索出来
apropos breakpoint
三、lldb常用命令
3.1 lldb断点设置
3.1.1 breakpoint
breakpoint set
-
set
是子命令 -
-n
是选项--name
的缩写。
根据方法名设置断点
breakpoint set -n test1
,相当于对符号test1
下断点,所有的test1
都会被断住:
Breakpoint 4: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010be80d00
这和在Xcode
中设置符号断点是一样的:
区别是前者重新启动后就失效了。
设置组断点
breakpoint set -n "[ViewController click1:]" -n "[ViewController click2:]" -n "[ViewController click3:]"
相当于下了一组断点:
(lldb) breakpoint set -n "[ViewController click1:]" -n "[ViewController click2:]" -n "[ViewController click3:]"
Breakpoint 6: 3 locations.
(lldb) breakpoint list 6
6: names = {'[ViewController click1:]', '[ViewController click1:]', '[ViewController click1:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click3:]', '[ViewController click3:]', '[ViewController click3:]'}, locations = 3, resolved = 3, hit count = 0
6.1: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00, resolved, hit count = 0
6.2: where = LLDBTest`-[ViewController click2:] at ViewController.m:35, address = 0x000000010be80e40, resolved, hit count = 0
6.3: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80, resolved, hit count = 0
可以同时启用和禁用组断点。
使用-f
指定文件
(lldb) br set -f ViewController.m -n click1:
Breakpoint 12: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00
使用-l
指定某一行设置断点
(lldb) br set -f ViewController.m -l 40
Breakpoint 13: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80
使用-c
设置条件断点
只要计算的结果是个bool
型或整型数值就可以。test2:
方法接收一个布尔值参数,则当传入的值为YES
时才断住:
(lldb) br set -n test2: -c enable==YES
Breakpoint 1: where = LLDBTest`-[ViewController test2:] at ViewController.m:45, address = 0x0000000100dc1e80
使用-F
设置函数全名
(lldb) br set -F test1
Breakpoint 1: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010762ecb0
使用-a
设置地址断点
(lldb) br set -a 0x000000010762ecb0
Breakpoint 2: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010762ecb0
使用--selector
设置断点
(lldb) br set -n touchesBegan:withEvent:
Breakpoint 2: 97 locations.
(lldb) br set --selector touchesBegan:withEvent:
Breakpoint 3: 97 locations.
--selector
在这里和-n
等价都是全部匹配。不过-n
是针对符号,--selector
针对OC
的方法。
使用-r
模糊匹配
(lldb) br set -f ViewController.m -r test
Breakpoint 4: 2 locations.
(lldb) br list
Current breakpoints:
4: regex = 'test', locations = 2, resolved = 2, hit count = 0
4.1: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010762ecb0, resolved, hit count = 0
4.2: where = LLDBTest`-[ViewController test2:] at ViewController.m:45, address = 0x000000010762ee80, resolved, hit count = 0
使用-i
设置忽略次数
(lldb) br set -f ViewController.m -r test -i 3
Breakpoint 1: 2 locations.
这里的次数是这组所有断点加起来的次数。
breakpoint list
查看断点列表:
(lldb) br l
Current breakpoints:
7: names = {'[ViewController click1:]', '[ViewController click1:]', '[ViewController click1:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click3:]', '[ViewController click3:]', '[ViewController click3:]'}, locations = 3, resolved = 3, hit count = 0
7.1: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00, resolved, hit count = 0
7.2: where = LLDBTest`-[ViewController click2:] at ViewController.m:35, address = 0x000000010be80e40, resolved, hit count = 0
7.3: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80, resolved, hit count = 0
查看某一个/某一组断点:
(lldb) br l
Current breakpoints:
7: names = {'[ViewController click1:]', '[ViewController click1:]', '[ViewController click1:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click3:]', '[ViewController click3:]', '[ViewController click3:]'}, locations = 3, resolved = 3, hit count = 0
7.1: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00, resolved, hit count = 0
7.2: where = LLDBTest`-[ViewController click2:] at ViewController.m:35, address = 0x000000010be80e40, resolved, hit count = 0
7.3: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80, resolved, hit count = 0
8: name = 'test1', locations = 1, resolved = 1, hit count = 0
8.1: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010be80d00, resolved, hit count = 0
(lldb) br l 8
8: name = 'test1', locations = 1, resolved = 1, hit count = 0
8.1: where = LLDBTest`test1 at ViewController.m:17, address = 0x000000010be80d00, resolved, hit count = 0
(lldb) br l 7
7: names = {'[ViewController click1:]', '[ViewController click1:]', '[ViewController click1:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click2:]', '[ViewController click3:]', '[ViewController click3:]', '[ViewController click3:]'}, locations = 3, resolved = 3, hit count = 0
7.1: where = LLDBTest`-[ViewController click1:] at ViewController.m:31, address = 0x000000010be80e00, resolved, hit count = 0
7.2: where = LLDBTest`-[ViewController click2:] at ViewController.m:35, address = 0x000000010be80e40, resolved, hit count = 0
7.3: where = LLDBTest`-[ViewController click3:] at ViewController.m:40, address = 0x000000010be80e80, resolved, hit count = 0
breakpoint disable/enable/delete
breakpoint disable
(lldb) br dis 7.1
1 breakpoints disabled.
image.png
breakpoint enable
(lldb) br en 7.1
1 breakpoints enabled.
breakpoint delete
只能删除指定组断点,不能删除组里面的某一个。
(lldb) br del 7.2
0 breakpoints deleted; 1 breakpoint locations disabled.
(lldb) br del 7
1 breakpoints deleted; 0 breakpoint locations disabled.
breakpoint delete
删除所有断点
(lldb) breakpoint delete
About to delete all breakpoints, do you want to do that?: [Y/n] y
All breakpoints removed. (3 breakpoints)
⚠️breakpoint
组在一次运行过程中是一直递增的。多次添加断点只会断住一次。
3.1.2 watchpoint 内存断点/地址断点
breakpoint
是对方法生效的断点,watchpoint
是对地址生效的断点。如果地址里中的数据改变了,就让程序中断。
watchpoint set
watchpoint set variable
(lldb) watchpoint set variable item1->_name
Watchpoint created: Watchpoint 1: addr = 0x600002ce8868 size = 8 state = enabled type = w
declare @ '/Users/zaizai/LLDBTest/LLDBTest/ViewController.m:28'
watchpoint spec = 'item1->_name'
new value: 0x000000010ad37038
改变name
值的时候就会断住(即使值没有变):
Watchpoint 1 hit:
old value: 0x0000000109319038
new value: 0x0000000109319038
watchpoint set variable
传入的是变量名,不接受方法。所以不能使用watchpoint set variable item1.name
。
watchpoint set expression
(lldb) p item1->_name
(__NSCFConstantString *) $0 = 0x000000010a0d0038 @"1"
(lldb) p &item1->_name
(NSString **) $1 = 0x00006000033bec48
(lldb) watchpoint set expression 0x00006000033bec48
Watchpoint created: Watchpoint 1: addr = 0x6000033bec48 size = 8 state = enabled type = w
new value: 4463591480
和breakpoint
类似,watchpoint
也有watchpoint list
、watchpoint disable
、watchpoint enable
、watchpoint delete
。
3.2 lldb代码执行 expression、p、print、call、po
expression
执行一个表达式,并将表达式返回的结果输出。
expression <cmd-options> -- <expr>
-
cmd-options
:命令选项,一般使用默认。 -
--
:命令选项结束符。 -
expr
:表达式。
p
、print
、call
都是expression --
的别名:
-
print
: 打印某个东西,可以是变量/表达式,p
是print
的缩写。 -
call
: 调用某个方法。
po
是expression -O --
的别名。调用的是description
或者debugDescription
方法。
进制转换p/x、p/o、p/t
p
除了打印还有常量的进制转换功能。
//默认10进制打印
(lldb) p 100
(int) $0 = 100
//16进制打印
(lldb) p/x 100
(int) $1 = 0x00000064
//8进制打印
(lldb) p/o 100
(int) $2 = 0144
//2进制打印
(lldb) p/t 100
(int) $3 = 0b00000000000000000000000001100100
//字符串转换为10进制
(lldb) p/d 'A'
(char) $4 = 65
//10进制转换为字符
(lldb) p/c 65
(int) $5 = A\0\0\0
浮点数转换
(lldb) p/x (double) 180.0
(double) $6 = 0x4066800000000000
(lldb) p/f 0x4066800000000000
(long) $1 = 180
(lldb) e -f f -- 0x4066800000000000
(long) $2 = 180
x/nuf<addr>
(lldb) x self
0x600002c12180: 29 8a 00 00 01 80 1d 00 00 00 00 00 00 00 00 00 )...............
0x600002c12190: 0e 00 00 00 00 00 00 00 00 5e 75 01 00 60 00 00 .........^u..`..
(lldb) x/4gx self
0x600002c12180: 0x001d800100008a29 0x0000000000000000
0x600002c12190: 0x000000000000000e 0x0000600001755e00
(lldb) x/4gw self
0x600002c12180: 0x00008a29 0x001d8001 0x00000000 0x00000000
x/nuf<addr>:
x
就是 memory read
内存读取。
-
n
:n
表示要打印的内存单元的个数。 -
u
:u
表示一个地址单元的长度。iOS
是小端模式。-
b
表示单字节 -
h
表示双字节 -
w
表示四字节 -
g
表示八字节。
-
-
f
:f
表示显示方式。-
x
按十六进制格式显示变量 -
d
按十进制显示 -
u
按十进制显示无符号整形 -
o
按八进制显示变量 -
t
按二进制显示变量 -
a
按十六进制显示变量 -
i
指令地址格式 -
c
按字符格式显示变量 -
f
按浮点数格式显示变量
-
-
addr
:地址/数据。
3.3查看堆栈信息
bt(thread backtrace)
thread #1, queue = 'com.apple.main-thread', stop reason = step over
* frame #0: 0x000000010e60fa06 LLDBTest`-[ViewController touchesBegan:withEvent:](self=0x00007fce43806030, _cmd="touchesBegan:withEvent:", touches=1 element, event=0x0000600002168540) at ViewController.m:46:23
frame #1: 0x00007fff246ca70f UIKitCore`forwardTouchMethod + 321
frame #2: 0x00007fff246ca5bd UIKitCore`-[UIResponder touchesBegan:withEvent:] + 49
frame #3: 0x00007fff246d95b5 UIKitCore`-[UIWindow _sendTouchesForEvent:] + 622
frame #4: 0x00007fff246db6c7 UIKitCore`-[UIWindow sendEvent:] + 4774
frame #5: 0x00007fff246b5466 UIKitCore`-[UIApplication sendEvent:] + 633
frame #6: 0x00007fff24745f04 UIKitCore`__processEventQueue + 13895
frame #7: 0x00007fff2473c877 UIKitCore`__eventFetcherSourceCallback + 104
frame #8: 0x00007fff2039038a CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #9: 0x00007fff20390282 CoreFoundation`__CFRunLoopDoSource0 + 180
frame #10: 0x00007fff2038f764 CoreFoundation`__CFRunLoopDoSources0 + 248
frame #11: 0x00007fff20389f2f CoreFoundation`__CFRunLoopRun + 878
frame #12: 0x00007fff203896d6 CoreFoundation`CFRunLoopRunSpecific + 567
frame #13: 0x00007fff2c257db3 GraphicsServices`GSEventRunModal + 139
frame #14: 0x00007fff24696cf7 UIKitCore`-[UIApplication _run] + 912
frame #15: 0x00007fff2469bba8 UIKitCore`UIApplicationMain + 101
frame #16: 0x000000010e60fff2 LLDBTest`main(argc=1, argv=0x00007ffee15efd20) at main.m:17:12
frame #17: 0x00007fff2025a3e9 libdyld.dylib`start + 1
up、down、frame select
(lldb) up
frame #3: 0x00007fff246d95b5 UIKitCore`-[UIWindow _sendTouchesForEvent:] + 622
UIKitCore`-[UIWindow _sendTouchesForEvent:]:
-> 0x7fff246d95b5 <+622>: lea rax, [rip + 0x628a699c] ; UIApp
0x7fff246d95bc <+629>: mov rdi, qword ptr [rax]
0x7fff246d95bf <+632>: mov rsi, qword ptr [rbp - 0x170]
0x7fff246d95c6 <+639>: mov rdx, r12
0x7fff246d95c9 <+642>: mov rcx, rbx
0x7fff246d95cc <+645>: mov r8, r14
0x7fff246d95cf <+648>: call r13
0x7fff246d95d2 <+651>: mov ecx, 0x1
(lldb) down
frame #2: 0x00007fff246ca5bd UIKitCore`-[UIResponder touchesBegan:withEvent:] + 49
UIKitCore`-[UIResponder touchesBegan:withEvent:]:
-> 0x7fff246ca5bd <+49>: mov rdi, rbx
0x7fff246ca5c0 <+52>: pop rbx
0x7fff246ca5c1 <+53>: pop r12
0x7fff246ca5c3 <+55>: pop r14
0x7fff246ca5c5 <+57>: pop r15
0x7fff246ca5c7 <+59>: pop rbp
0x7fff246ca5c8 <+60>: jmp qword ptr [rip + 0x5bf063e2] ; (void *)0x00007fff2018f760: objc_release
UIKitCore`forwardTouchMethod:
0x7fff246ca5ce <+0>: push rbp
(lldb) frame select 10
frame #10: 0x00007fff2038f764 CoreFoundation`__CFRunLoopDoSources0 + 248
CoreFoundation`__CFRunLoopDoSources0:
-> 0x7fff2038f764 <+248>: mov r13d, eax
0x7fff2038f767 <+251>: jmp 0x7fff2038f7dc ; <+368>
0x7fff2038f769 <+253>: xor r13d, r13d
0x7fff2038f76c <+256>: jmp 0x7fff2038f802 ; <+406>
0x7fff2038f771 <+261>: mov rbx, r14
0x7fff2038f774 <+264>: mov rdi, qword ptr [rbp - 0x38]
0x7fff2038f778 <+268>: call 0x7fff20312bcc ; CFArrayGetCount
0x7fff2038f77d <+273>: mov r15, rax
这3个命令只是方便我们查看堆栈信息,寄存器还是在断点处。
frame variable
查看当前frame
参数
(lldb) frame variable
(ViewController *) self = 0x00007f8c59405600
(SEL) _cmd = "touchesBegan:withEvent:"
(BOOL) enable = NO
在已经执行过的frame
中修改参数不会影响后面的结果。
thread return
thread return
可以接受一个表达式,调用命令之后直接从当前的frame
返回表达式的值。直接返回不执行后面的代码。相当于回滚(相当于直接到bl
跳转的下一行汇编代码)。当然修改pc
寄存器的值也能达到相同的效果。
3.4 command指令
给断点添加命令的命令。
breakpoint command add
(lldb) b test2:
Breakpoint 1: where = LLDBTest`-[ViewController test2:] at ViewController.m:65, address = 0x0000000103e7db40
(lldb) br command add 1
Enter your debugger command(s). Type 'DONE' to end.
> frame variable
> DONE
当断点断住的时候执行frame variable
指令。
当然也可以只添加一条指令:
br command add -o "po self" 1
多次对同一个断点添加命令,后面命令会将前面命令覆盖
breakpoint command list
查看某个断点已有的命令(list 后必须有断点编号)
(lldb) breakpoint command list 1
Breakpoint 1:
Breakpoint commands:
po self
3.5 target stop-Hook指令
target stop-hook
命令可以在每次stop
的时候去执行一些命令
(lldb) target stop-hook add -o "frame variable"
Stop hook #1 added.
与command
不同的是它对所有断点生效。相当于对程序下钩子。
与display
命令等价:
(lldb) display frame variable
Stop hook #2 added.
target stop-hook
只对breakpoint
和watchpoint
的stop
生效,直接点击Xcode
上的pause
或者debug view hierarchy
不会生效
target stop-hook list
(lldb) target stop-hook list
Hook: 1
State: enabled
Commands:
frame variable
Hook: 2
State: enabled
Commands:
expr -- frame variable
target stop-hook disable / enable
暂时让某个stop-hook
失效/生效,不传id
则代表全部。
target stop-hook delete / undisplay
删除stop-hook
(lldb) target stop-hook delete
Delete all stop hooks?: [Y/n] y
delete
可以不传id
,undisplay
必须传id
。
3.6 image(target modules)指令
image lookup --address
查找某个地址具体对应的文件位置,可以使用image lookup --address
(image lookup -a
)
比如有一个crash
:
2021-05-19 18:19:45.833183+0800 LLDBTest[41719:24239029] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndexedSubscript:]: index 5 beyond bounds [0 .. 2]'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff20421af6 __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007fff20177e78 objc_exception_throw + 48
2 CoreFoundation 0x00007fff2049e77f _CFThrowFormattedException + 194
3 CoreFoundation 0x00007fff20320825 -[__NSArrayM removeAllObjects] + 0
4 LLDBTest 0x0000000107c469f7 -[ViewController touchesBegan:withEvent:] + 151
从上面的堆栈可以看到是-[ViewController touchesBegan:withEvent:]
中的调用发生了crash
,但是并不知道在ViewController.m
的哪一行。使用image lookup -a
就可以具体定位到确定的行数:
(lldb) image lookup -a 0x0000000107c469f7
Address: LLDBTest[0x00000001000019f7] (LLDBTest.__TEXT.__text + 807)
Summary: LLDBTest`-[ViewController touchesBegan:withEvent:] + 151 at ViewController.m:48:28
image lookup --name
查找方法或者符号的信息可以使用image lookup --name
(image lookup -n
):
(lldb) image lookup -n test2:
1 match found in /Users/zaizai/Library/Developer/Xcode/DerivedData/LLDBTest-enxwhkxlnnynraafdlfrcoxaibzm/Build/Products/Debug-iphonesimulator/LLDBTest.app/LLDBTest:
Address: LLDBTest[0x0000000100001b30] (LLDBTest.__TEXT.__text + 1120)
Summary: LLDBTest`-[ViewController test2:] at ViewController.m:65
image lookup --type
可以使用image lookup --type
(image lookup -t
)查看类型:
(lldb) image lookup -t ViewController
Best match found in /Users/zaizai/Library/Developer/Xcode/DerivedData/LLDBTest-enxwhkxlnnynraafdlfrcoxaibzm/Build/Products/Debug-iphonesimulator/LLDBTest.app/LLDBTest:
id = {0x100000033}, name = "ViewController", byte-size = 16, decl = ViewController.h:10, compiler_type = "@interface ViewController : UIViewController{
NSMutableArray * _items;
}
@property(nonatomic, readwrite, getter = items, setter = setItems:) NSMutableArray *items;
@end"
3.7 .lldbinit
LLDB
有了一个启动时加载的文件~/.lldbinit
,每次启动都会加载。一些初始化的操作可以添加在.lldbinit
中。由于这时候程序还没有真正运行,也有部分操作无法完成,比如设置断点等。
chisel
和lldb
插件就是在.lldbinit
初始化的。
直接在.lldbinit
中添加:
target stop-hook add -o "frame variable"
这样每次运行都会自动添加stop-hook
Stop hook #1 added.
(lldb) b -n test2:
Breakpoint 1: where = LLDBTest`-[ViewController test2:] at ViewController.m:65, address = 0x000000010c519b40
(ViewController *) self = 0x00007ffd95406160
(SEL) _cmd = "touchesBegan:withEvent:"
(BOOL) enable = NO
3.8 其它命令
-
image list
: 查看某块列表 -
register read
: 读取寄存器 -
register write
: 写入寄存器 -
Memory read
: 读取内存值
四、流程控制
image.png-
c
(continue
/thread continue
) :继续执行。 -
n
(next
/thread step-over
):单步运行,将子函数当做整体一步执行。
ni
:单步运行汇编级别。 -
s
(step
/thread step-in
):单步运行,遇到子函数会进去。
si
:单步运行可跳转指令内部,汇编级别。 -
finish
(step-out
):表示直接走完当前方法,返回到上层frame
。
网友评论