Xcode 动态调试原理
- debugserver依附在 iOS 的 APP 进程上,时刻监听 APP 的运行状态,并有控制 APP 执行的能力;
- lldb在PC端和debugserver建立连接并控制App行为
- 在真机调试的时候,Xcode将debugserver加入到APP中,通过lldb来调试APP
debugserver 权限修改
Xcode 的 lldb 默认只能调试通过 Xcode 安装的 App 需要给 debugserver 授权才能调试任何 App
- 从 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/De viceSupport/14.5/DeveloperDiskImage.dmg/usr/bin/debugserver 获取 debugserver 程序
- 生成授权entitlements文件
ldid -e debugserver > debugserver.entitlements
- 删除 entitlements 其他权限,修改为如下四个权限
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.springboard.debugapplications</key>
<true/>
<key>run-unsigned-code</key>
<true/>
<key>get-task-allow</key>
<true/>
<key>task_for_pid-allow</key>
<true/>
</dict>
</plist>
- 重新授权 debugserver 程序
ldid -Sdebugserver.entitlements debugserver
- 移动 debugserver 程序到 iOS 设备上
scp debugserver root@10.100.170.85:/usr/bin/
启动 debugserver 和 lldb 服务
打开 App 进程, 如设置App(Preferences), 将 debugserver 附加到 iOS 进程上:
ssh root@10.100.170.85
/usr/bin/debugserver localhost:12345 –a Preferences
通过 USB
连接 iOS 设备, 新建 Terminal 窗口使用 iproxy(brew install mutmuxd) 映射端口
iproxy 12345 12345
新建第三个 Terminal 窗口运行 lldb,连接 debugserver
lldb
process connect connect://localhost:12345
process interrupt // 暂停程序
这样就连接上了,可使用lldb来调试越狱设备上的应用。
LLDB指令
查看指令帮助
help image list
help br
help breakpoint set
打印
// 查看函数调用堆栈信息
bt
// 线程列表
thread list
// 打印当前的栈帧变量, 可以带参数表示打印具体变量名
frame variable
// 查看当前执行代码信息
frame info
// 打印 OC 对象
po self
// 查看类的方法信息
po [className _shortMethodDescription]
打印和修改界面
// 打印所有界面层次
po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
po [(UITableView*)0x13e051800 setBackgroundColor:[UIColor yellowColor]]
// 需要手动刷新下界面
e (void)[CATransaction flush]
进程和动态库查询
// 查看 App 进程加载的二进制和动态库列表和偏移地址
image list -o -f
image list -o -f | grep Demo
// 在所有模块中查找指定符号或者地址
image lookup
image lookup -t 类型 : 查找某个类型的信息
image lookup -a 内存地址 : 根据内存地址查找在模块中的位置
image lookup -n 符号名 : 查找某个符号或者函数的位置
内存操作
memory read 0x1bce000f8 // 读取内存值
memory write 0x1bce000f8 5 // 修改内存值
寄存器
// 显示所有寄存器的值
register read
register read x0
register write
po $x1 打印方法调用者
x/s $x1:打印方法名
po $x2:打印参数(以此类推,x3、x4也可能是参数)
断点
- 函数断点
breakpoint set --name tableView:didSelectRowAtIndexPath:
br s -n tableView:didSelectRowAtIndexPath:
br set -n "-[PSUIPrefsListController tableView:didSelectRowAtIndexPath:]"
// 设置内存断点
br set -a 0x00000001008ca7e4
断点结果
Breakpoint 1: 261 locations. (1:表示断点序号, 261: 生成了 261 个断点)
Breakpoint 2: where = PreferencesUI`-[PSUIPrefsListController
tableView:didSelectRowAtIndexPath:], address = 0x00000001d991d36c
-
断点流程
- c(continue): 继续运行
- n(next): 单步执行
- s(step in): 进入函数内部执行
- finish: 执行到函数尾部
-
断点操作
br list // 列出所有断点
br delete 2 // 通过序号删除断点
br delete // 删除所有断点
br disable // 禁用断点
br enable // 启用断点
watchpoint 监听内存值变化
在指定内存存储的数据发生改变的时候触发
watchpoint set variable 变量
watchpoint set variable self->age
watchpoint set expression 内存地址
watchpoint set expression &(self->_age)
watchpoint list