动态调试 LLDB
一、什么叫动态调试
将程序运行起来,通过下断点、打印等方式,查看参数、返回值、函数调用流程等。
二、Xcode 的动态调试原理
Xcode 里包含有调试器 LLDB,debugserver 一开始存放在 Mac 的 Xcode 里,路径为 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/9.1/DeveloperDiskImage.dmg/usr/bin/debugserver
。当 Xcode 识别到手机设备时,Xcode 会自动将 debugserver 安装到 iPhone 上。
一般情况下,只能调试通过 Xcode 安装的 APP
三、动态调试任意 APP
debugserver 的权限问题
默认情况下,/Develop/usr/bin/debugserver
缺少一定的权限,只能调试通过Xcode安装的APP ,无法调试其他APP。如果希望调试其他APP,需要对debugserver重新签名,签上2个调试相关的权限。
- get-task-allow
- task_for_pid_allow
如何给debugserver签上权限
- iPhone上的
/Developer
目录是只读的,无法直接对它签名,需要先把debugserver复制到Mac - 通过ldid命令导出文件以前的签名权限
ldid -e debugserver > debugserver.entitlements
给debugserver.plist文件加上get-task-allow 和 task_for_pid_allow 权限
- 通过ldid命令重新签名
ldid -S debugserver.entitlements debugserver
- 将已经签好权限的debugserver放到
/usr/bin
目录,便于找到debugserver指令 - 关于权限的签名,也可以使用codesign
# 查看权限信息 codesign -d --entitlements - debugserver # 签名权限 codesign -f -s --entitlements debugserver.entitlements debugserver #或者简写为 codesign -fs --entitlements debugserver.entitlements debugserver
让debugserver附加到某个APP进程
debugserver *:端口号 -a 进程
- *: 端口号 :使用iPhone的某个端口启动debugserver服务(只要不是保留端口号就行)
- -a 进程 :输入APP的进程信息(进程ID或者进程名称)
在Mac上启动LLDB,远程连接iPhone上的debugserver服务
- 启动lldb
lldb
- 连接debugserver服务
(lldb) process connect connect://手机IP地址:debugserver服务端口号
- 使用LLDB的c命令让程序先继续运行
(lldb) c
- 接下来就可以使用lldb指令调试APP
通过debugserver启动APP
debugserver -x auto *:端口号 APP的可执行文件路径
四、常用LLDB指令
- 指令的格式是
<command> [<subcommand> [<subcommand...>]] <action> [-options [option-value]] [argument [argument...]]
- : 命令
- : 子命令
- : 命令操作
- : 命令选项
- : 命令参数
- [] : 表示可以没有
breakpoint set -n test
- 给test函数设置断点
- breakpoint 是 * set是 * -n是
- test是
- breakpoint 是 * set是 * -n是
- help
- 查看指令的用法
- 比如help breakpoint、help breakpoint set
- expression --
- 执行一个表达式
* <cmd-options> : 命令选项 * -\- : 命令选项结束符,表示所有的命令选项已经设置完毕,如果没有命令选项,-\-可以省略 * <expr> : 需要执行的表达式
expression self.view.backgroundColor = [UIColor redColor]
- expression、expression -- 和指令print、p、call的效果一样
- expression -O -- 和指令po的效果一样
- 使用 OC 的代码在Swift 的框架中:
expression -l objc -O -- <expr>
-expression -l objc -O -- [self.view recursiveDescription]
打印 self.view 的子视图 - Swift中修改值unsafeBitCast(point, to: type)
expression unsafeBitCast(0x7fa94cb015c0, to: UIButton.self).frame.origin.x = 100
- 刷新视图暂存区:expression CATransaction.flush()
- 执行一个表达式
- thread
- thread backtrace
- 打印线程的堆栈信息
- 和指令bt的效果一样
- thread return []
- 让函数直接返回某个值,不会执行断点后面的代码
- hread continue、continue、c : 程序继续运行
- thread step-over、next、n : 单步运行,把子函数当做整体一步执行
- thread step-in、step、s : 单步运行,遇到子函数会进入子函数
- thread step-out、finsh : 直接执行完当前函数的所有代码,返回到上一个函数
- si、ni和s、n类似 * s、n是源码级别 * si(thread step-inst 、stepi)、ni(thread step-inst-over、nexti)是汇编指令级别
- thread backtrace
- frame variable []
- 打印当前栈帧的变量
- breakpoint set
- 设置断点
- breakpoint set -a 函数地址
- breakpoint set -n 函数名
breakpoint set -n test
: C语言函数breakpoint set -n touchesBegan:withEvent:
: OC方法名,没有指定类,是所有的
breakpoint set -n "-[ViewController touchesBegan:withEvent:"
- breakpoint set -r 正则表达式 : 会对所有符合这个条件的地方设置断点
- breakpoint set -s 动态库 -n 函数名
- breakpoint list : 列出所有的断点(每个断点都有自己的编号) * breakpoint disable 断点编号 :禁用断点 * breakpoint enable 断点编号 : 启用断点 * breakpoint delete 断点编号 : 删除断点
- image lookup
- image lookup -t 类型 : 查找某个类型的信息
- image lookup -a 地址 : 根据内存地址查找在模块中的位置
- image lookup -n 符号或者函数名 : 查找某个符号或者函数的位置
- image list
- 列出所加载的模块信息
- image list -o -f : 打印出模块的偏移地址(ASLR)、全路径
- 小技巧
- 敲Enter,会自动执行上次的命令
- 绝大部分指令都可以使用缩写
- 设置别名
command alias poc expression -l objc -O --
command alias flush expression -l objc -- (void)[CATransaction flush]
五、LLDB其它用法
创建全新的方法和类
po
Enter expressions, then terminate with an empty line to evaluate:
1 class $BreakpointUtils {
2 static var $counter = 0
3 }
4 func $increaseCounter() {
5 $BreakpointUtils.$counter += 1
6 print("Times I've hit this breakpoint: \($BreakpointUtils.$counter)")
7 }
使用美元符号表示这些属性和方法属于lldb,而不是实际代码。
lldb 插件 chisel
v
避免po
动态
v myProperty
(Int) myProperty = 1
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论