动态调试LLDB - wanshanhu79/Study GitHub Wiki

一、什么叫动态调试

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

二、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

Swift的高级技巧 - 动态注入和更改代码

⚠️ **GitHub.com Fallback** ⚠️