LLDB

LLDB 是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它,你可以查看变量的值,执行自定的指令,并且按照你所认为合适的步骤来操作程序的进展

基本语法

<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
  • <command>(命令)和<subcommand>(子命令):LLDB调试命令的名称。命令和子命令按层级结构来排列:一个命令对象为跟随其的子命令对象创建一个上下文,子命令又为其子命令创建一个上下文,依此类推。
  • <action>执行命令的操作
  • <options>命令选项
  • <arguement>命令的参数
  • []表示命令是可选的,可以有也可以没有

GDB to LLDB 参考是一个非常好的调试器可用命令的总览。也可以安装 Chisel,它是一个开源的 LLDB 插件合辑,这会使调试变得更加有趣

例: breakpoint set -n main

这个命令对应到上面的语法就是:

command: breakpoint 表示断点命令
action: set 表示设置断点
option: -n 表示根据方法name设置断点
arguement: mian 表示方法名为mian

快捷键

快捷键功能 命令
暂停/继续 cmd + ctrl + Y
控制台显示/隐藏 cmd + Y
光标切换到控制台 cmd + shift + C
清空控制台 cmd + K
step over fn6
step into fn7
step out fn8

唯一匹配原则

LLDB有个很省事的特性,如果输入的字母已经能匹配到某个命令,就可以直接执行,等于输入了完整的命令

help

最简单命令是 help,它会列举出所有的命令。如果你忘记了一个命令是做什么的,或者想知道更多的话,你可以通过 help <command>来了解更多细节,例如 help print 或者 help thread。如果你甚至忘记了 help 命令是做什么的,你可以试试 help help。不过你如果知道这么做,那就说明你大概还没有忘光这个命令

apropos

有的时候,可能并不能完全记得某个命令,如果只记得命令中的某个关键字。这时候可以使用apropos搜索相关命令信息

(lldb) apropos stop-hook
The following built-in commands may relate to 'stop-hook':
  _regexp-display          -- Add an expression evaluation stop-hook.
  _regexp-undisplay        -- Remove an expression evaluation stop-hook.
  target stop-hook         -- A set of commands for operating on debugger
                              target stop-hooks.
  target stop-hook add     -- Add a hook to be executed when the target stops.
  target stop-hook delete  -- Delete a stop-hook.
  target stop-hook disable -- Disable a stop-hook.
  target stop-hook enable  -- Enable a stop-hook.
  target stop-hook list    -- List all stop-hooks.

$

任何以美元符开头的东西都是存在于 LLDB 的命名空间的,它们是为了帮助你进行调试而存在的

p、po、print、call

print: 打印某个东西,可以是变量和表达式 p: 可以看做是print的简写 call: 调用某个方法

查询变量一般用p与po命令。
po (print object 的缩写)的作用为打印对象,事实上,我们可以通过help po得知,po是expression -O --的简写,我们可以通过它打印出对象,而不是打印对象的指针。而值得一提的是,在 help expression 返回的帮助信息中,我们可以知道,po命令会尝试调用对象的 description 方法来取得对象信息,因此我们也可以重载某个对象的description方法,使我们调试的时候能获得可读性更强,更全面的信息

expression

expression 可简写为e,作用为执行一个表达式,可以用来查询当前堆栈变量的值

打印变量

默认的格式

(lldb) p 16
16

十六进制:

(lldb) p/x 16
0x10

二进制 (t 代表 two):

(lldb) p/t 16
0b00000000000000000000000000010000
(lldb) p/t (char)16
0b00010000

也可以使用 p/c 打印字符,或者 p/s 打印以空终止的字符串 (译者注:以 '\0' 结尾的字符串)

可以在 C 语言中用 int a = 0 来声明一个变量一样,也可以在 LLDB 中做同样的事情。不过为了能使用声明的变量,变量必须以美元$符开头

(lldb) e int $a = 2
(lldb) p $a * 19
38
(lldb) e NSArray *$array = @[ @"Saturday", @"Sunday", @"Monday" ]
(lldb) p [$array count]
2
(lldb) po [[$array objectAtIndex:0] uppercaseString]
SATURDAY
(lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
error: no known method '-characterAtIndex:'; cast the message send to the method's return type
error: 1 errors parsing expression

breakpoint

一般对breakpoint命令使用得不多,而是在XCode的GUI界面中直接添加断点。除了直接触发程序暂停供调试外,可以进行进一步的配置

断点创建

(lldb) breakpoint set -f main.m -l 16
Breakpoint 1: where = DebuggerDance`main + 27 at main.m:16, address = 0x000000010a3f6cab

也可以使用缩写形式 br。虽然 b 是一个完全不同的命令 (_regexp-break 的缩写),但恰好也可以实现和上面同样的效果

watchpoint

有时候会关心类的某个属性什么时候被人修改了,最简单的方法当然就是在setter的方法打断点,或者在@property的属性生命行打上断点。这样当对象的setter方法被调用时,就会触发这个断点,当然这么做是有缺点的,对于直接访问内存地址的修改,setter方法的断点并没有办法监控得到,因此需要用到watchpoint命令

watchpoint命令在XCode的GUI中也可以直接使用,当程序暂停时,我们能对当前程序栈中的变量设置watchpoint。值得注意的是,watchpoint是直接设置到该变量所在的内存地址上的,所以当这个变量释放了后,watchpoint仍然是对这个地址的内存生效的

watchpoint set self->testVar     //为该变量地址设置watchpoint
watchpoint set expression 0x00007fb27b4969e0 //为该内存地址设置watchpoint,内存地址可从前文提及的`p`命令获取
watchpoint command add -o 'frame info' 1  //为watchpoint 1号加上子命令 `frame info`
watchpoint list //列出所有watchpoint
watchpoint delete // 删除所有watchpoint

thread、bt

thread backtrace [-c <count>] [-s <frame-index>] [-e <boolean>]

-c:设置打印堆栈的帧数(frame) -s:设置从哪个帧(frame)开始打印 -e:是否显示额外的回溯

bt即是thread backtrace,作用是打印出当前线程的堆栈信息。当程序发生了crash后,我们可以用该命令打印出发生crash的当前的程序堆栈,查询出发生crash的调用路径。由于比较常用,所以LLDB直接给它一个特殊的bt别名。 thread另一个比较常用的用法是 thread return,调试的时候,我们希望在当前执行的程序堆栈直接返回一个自己想要的值,可以执行该命令直接返回。 thread return <expr> 在这个断点中,我们可以执行 thread return NO让该函数调用直接返回NO ,在调试中轻松覆盖任何函数的返回路径。

c、n、s、finish

  • c/ continue/ thread continue: 这三个命令效果都等同于上图中第一个按钮的。表示程序继续运行
  • n/ next/ thread step-over: 这三个命令效果等同于上图第二个按钮。表示单步运行
  • s/ step/ thread step-in: 这三个命令效果等同于上图第三个按钮。表示进入某个方法
  • finish/ step-out: 这两个命令效果等同于第四个按钮。表示直接走完当前方法,返回到上层frame

thread其他不常用的命令

thread jump: 直接让程序跳到某一行。由于ARC下编译器实际插入了不少retain,release命令。跳过一些代码不执行很可能会造成对象内存混乱发生crash。 thread list: 列出所有的线程 thread select: 选择某个线程 thread until: 传入一个line的参数,让程序执行到这行的时候暂停 thread info: 输出当前线程的信息

frame

frame即是帧,其实就是当前的程序堆栈,我们输入bt命令,打印出来的其实是当前线程frame。 frame info查看当前frame的信息 在调试中,一般我们比较关心当前堆栈的变量值,我们可以使用frame variable来获取全部变量值。当然也可以输入特定变量名,来获取单独的变量值,如frame v self-> testVar来获取testVar的值

(lldb) frame info
frame #0: 0x0000000103a11210 Test`-[AppDelegate application:didFinishLaunchingWithOptions:](self=0x000060000002d8c0, _cmd="application:didFinishLaunchingWithOptions:", application=0x00007f8d2e100160, launchOptions=0x0000000000000000) at AppDelegate.m:21

(lldb) frame variable
(AppDelegate *) self = 0x000060000002d8c0
(SEL) _cmd = "application:didFinishLaunchingWithOptions:"
(UIApplication *) application = 0x00007f8d2e100160
(NSDictionary *) launchOptions = nil

target

target modules lookup(image lookup)

对于target这个命令,我们用得最多的可能就是target modules lookup。由于LLDB给target modules取了个别名image,所以这个命令我们又可以写成image lookup。

image lookup –address

当有一个地址,想查找这个地址具体对应的文件位置,可以使用image lookup --address,简写为image lookup -a e.g: 当我们发生一个crash

(lldb) image lookup -a 0x000000010a1c3e36

image lookup –name

当想查找一个方法或者符号的信息,比如所在文件位置等。我们可以使用image lookup --name,简写为image lookup -n

(lldb) image lookup -n dictionaryWithXMLString:

image lookup –type

当我们想查看一个类型的时候,可以使用image lookup --type,简写为image lookup -t:

查看Model的类型:

(lldb) image lookup -t Model

target stop-hook

target stop-hook命令就是让在每次stop的时候去执行一些命令

target stop-hook只对breakpoint和watchpoint的程序stop生效,直接点击Xcode上的pause或者debug view hierarchy不会生效

target stop-hook add & display

假如想在每次程序stop的时候,都用命令打印当前frame的所有变量。我们可以添加一个stop-hook:

(lldb) target stop-hook add -o "frame variable"
Stop hook #4 added.

target stop-hook add表示添加stop-hook,-o的全称是--one-liner,表示添加一条命令

大多情况下,在stop的时候可能想要做的是打印一个东西。正常情况需要用target stop-hook add -o "p xxx",LLDB提供了一个更简便的命令display, 下面2行代码效果相同

(lldb) target stop-hook add -o "p self.view"
(lldb) display self.view

target stop-hook list

查看当前所有的stop-hook

target stop-hook delete & undisplay

有添加的命令,当然也就有删除的命令。使用target stop-hook delete可以删除stop-hook,也可以用undispla

(lldb) target stop-hook delete 4
(lldb) undisplay 5

target stop-hook disable/enable 暂时想让某个stop-hook失效的时候,可以使用target stop-hook disable

(lldb) target stop-hook disable 8

让stop-hook生效

(lldb) target stop-hook enable 8

想让所有的stop-hook失效/生效,只需不传入stop-hookid即可

target symbols add(add-dsym)

add-dsym ~/.../XXX.dSYM

说这个命令之前,先简单解释一下dSYM文件。程序运行的时候,都会编译成二进制文件。因为计算机只识别二进制文件,在编译的时候Xcode会生成dSYM文件,dSYM文件记录了哪行代码对应着哪些二进制,对代码打断点就会对应到二进制上

当Xcode找不着dSYM文件的时候,就无法对代码打断点,进行调试。target symbols add命令的作用就是可以手动的将dSYM文件添加上去。LLBD对这个命令起了一个别名: add-dsym

results matching ""

    No results matching ""