对于苹果开发者而言,
LLDB
是无人不知的调试工具,然而此知非彼知,相信有相当规模的开发者对LLDB
的了解仍然停留于几个基础命令的使用,今天让我们来重新认识一下既熟悉又陌生的LLDB
,看看它那些你不曾用过的强大功能,以及如何提高我们的开发效率。
一、什么叫动态调试
将程序运行起来,通过下断点、打印等⽅方式,查看参数、返回值、函数调⽤用流程等
二、Xcode 的动态调试原理
关于 GCC、LLVM、GDB、LLDB
debugserver一开始存放在Mac的Xcode⾥面
1
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/9.1/DeveloperDiskImage.dmg/usr/bin/debugserver
当Xcode识别到手机设备时,Xcode会自动将debugserver安装到iPhone上
1
/Developer/usr/bin/debugserver
Xcode调试的局限性
- 一般情况下,只能调试通过 Xcode 安装的 App
三、动态调试任意 App
1、debugserver 的权限问题
- 默认情况下,
/Developer/usr/bin/debugserver
缺少一定的权限,只能调试通过 Xcode 安装的 APP,无法调试其他 App (⽐如来⾃自 App Store 的 App) - 如果希望调试其他App,需要对
debugserver
重新签名,签上2个调试相关的权限- get-task-allow
- task_for_pid-allow
2、如何给 debugserver 签上权限
- iPhone上的
/Developer
⽬录是只读的,无法直接对/Developer/usr/bin/debugserver
⽂件签名,需要先把debugserver
复制到 Mac 上 - 通过
ldid
命令导出⽂件以前的签名权限1
ldid -e debugserver > debugserver.entitlements
- 给 debugserver.plist 文件加上
get-task-allow
和task_for_pid-allow
权限 - 通过
ldid
命令重新签名1
ldid -S debugserver.entitlements debugserver
- 将已经签好权限的
debugserver
放到/usr/bin
目录,便于找到debugserver
指令 debugserver
添加执行权限1
xhmod +x /usr/bin/debugserver
3、让 debugserver 附加到某个 App 进程
执行下面的命令:
1
debugserver *:端口号 -a 进程
*:端口号:使用 iPhone 的某个端口启动 debugserver 服务(只要不是保留端⼝号就行)
-a 进程:输入 App 的进程信息(进程 ID 或者进程名称)
4、在 Mac 上启动 LLDB,远程连接 iPhone 上的 debugserver 服务
启动LLDB
1
2lldb
(lldb)连接 debugserver 服务
1
(lldb) process connect connect://手机IP地址:debugserver服务端⼝号
使用 LLDB 的
c
命令让程序先继续运⾏1
(lldb) c
接下来就可以使⽤用 LLDB 命令调试App
5、通过debugserver启动App
执行下面的命令:
1
debugserver -x auto *:端⼝号 APP的可执⾏文件路径
四、常用LLDB指令
指令的格式是
1
<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
<command>
:命令<subcommand>
:子命令<action>
:命令操作,想在前面的命令序列的上下文中执行的一些操作<options>
:命令选项,行为修改器(action modifiers),通常带有一些值。<argument>
:命令参数,据使用的命令的上下文来表示各种不同的东西[]
:表示命令是可选的,可以有也可以没有。举个例子:给
test
函数设置断点1
breakpoint set -n test
breakpoint 是
<command>
, set 是<action>
, -n 是<options>
,test 是<argument>
可以查看指令的用法:
help<command>
,比如1
2help breakpoint
help breakpoint set执行⼀个表达式:
expression<cmd-options> -- <expr>
1
expression self.view.backgroundColor = [UIColor redColor]
<cmd-options>
: 命令选项--
: 命令选项结束符,表示所有的命令选项已经设置完毕,如果没有命令选项,--
可以省略
<expr>
: 需要执⾏的表达式expression
、expression --
和指令print
、p
、call
的效果一样expression -O --
和指令po
的效果⼀样
打印线程的堆栈信息,和指令
bt
的效果一样1
thread backtrace
让函数直接返回某个值,不会执行断点后面的代码
1
thread return [<expr>]
打印当前栈帧的变量量
1
frame variable [<variable name>]
程序继续运行
1
2
3thread continue
continue
c单步运⾏,把子函数当做整体一步执⾏
1
2
3thread step-over
next
n单步运行,遇到⼦函数会进入子函数
1
2
3thread step-in
step
s直接执⾏完当前函数的所有代码,返回到上一个函数
1
2thread step-out
finish设置断点
1
2
3
4
5
6
7breakpoint set -a 函数地址
breakpoint set -n 函数名
breakpoint set -n test
breakpoint set -n touchesBegan:withEvent:
breakpoint set -n "-[ViewController-touchesBegan:withEvent:]"
breakpoint set -r 正则表达式
breakpoint set -s 动态库 -n 函数名列出所有的断点(每个断点都有⾃己的编号)
1
breakpoint list
禁用、启用、删除断点
1
2
3breakpoint disable 断点编号
breakpoint enable 断点编号
breakpoint delete 断点编号给断点预先设置需要执行的命令,到触发断点时,就会按顺序执行
1
breakpoint command add 断点编号
查看某个断点设置的命令
1
breakpoint command list 断点编号*
删除某个断点设置的命令
1
breakpoint command delete 断点编号
查找某个类型的信息
1
image lookup -t 类型
根据内存地址查找在模块中的位置
1
image lookup -a 地址
查找某个符号或者函数的位置
1
image lookup -n 符号或者函数名
列出所加载的模块信息
1
image list
打印出模块的偏移地址、全路径
1
image list -o -f