动态调试 LLDB

发布于 2021-03-04 13:09:17 字数 5783 浏览 1503 评论 0

一、什么叫动态调试

将程序运行起来,通过下断点、打印等方式,查看参数、返回值、函数调用流程等。

二、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
  • 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)是汇编指令级别
  • 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文