一句话介绍LLDB:内置在 Xcode 中的调试器。能在特定的位置暂停程序运行,并观察变量的值,也可以执行自定义指令。
好处:通过 LLDB 对程序做了修改,而不需要重新运行项目,就可以看到效果
目录
- Basic
- 帮助
- 打印一
- expression
- 打印二
- 打印三
- 打印四
- 声明变量
- flow control
- finish
- frame info
- thread return
- Breakpoint
- 管理断点
- 创建断点
- 为 OC API 添加断点
- 符号断点
- 通过LLDB
- 通过UI
- UI上的断点
- Breakpoint Action
- LLDB Debugger 缺点
帮助
借助 help <command> 查看某个命令,比如,help print 、help thread。
打印一
如图所示,print count 会输出一个以 $ 开头的变量以及一个值,这个变量可以作为这个值的引用。所以当打印 print $0 + 7 输出的结果是106.

LLDB 支持前缀字符匹配,prin, pri, or p 都代表一个意思。
expression
如果要修改这个值,可以使用 expression,它修改的不仅是LLDB中的值,连带程序里面的值也修改掉!它的简写是:e

打印二
我们打印 print count = 30 并再次打印 print count,会发现它的作用和 expression 一样。它们之间的区别是:expression 命令可以跟参数,print 命令不跟参数。
来看一个命令:e -h +17。它会产生歧义:是把 -h 当做标记,只计算 +17呢?还是计算17与h的差值呢?连字符-会给人造成困扰。
对于这种情况,可以使用 -- 应对。-- 表示标记位的结束,输入的开始。
因此,就有下面两种输入格式:
// 使用 `-h` 做标记
1. e -h -- +17
// 计算它们的差值
2. e -- -h +17
e -- 就是 print。
可以通过 help print 查看它的定义:'print' is an abbreviation for 'expression --'.
打印三
(lldb) p objects
(lldb) (__NSCFConstantString *) $0 = 0x000000010ee9a158 @"abcd"
(lldb) po objects
(lldb) @"abcd"
p objects 输出包含了很多内容,如果我们只想看值abcd,请使用:e -O -- objects 。e -O -- 的简写是: po
打印四
可以为 print 指定不同的打印format,语法:print <fmt> or p <fmt>. 查看完整的 fmts 列表
- 打印16进制:
(lldb) p/x 16
(int) $0 = 0x00000010
- 打印2进制:
(lldb) p/t 16
(int) $1 = 0b00000000000000000000000000010000
(lldb) p/t (char)16
(char) $2 = 0b00010000
声明变量
可以在LLDB中声明自定义变量,这些变量名前需要加上美元$符号。
(lldb) e int $a = 2
(lldb) p $a
(int) $a = 2
(lldb) p $a * 19
(int) $3 = 38
(lldb) e NSArray *$array = @[@"a",@"b",@"c"]
(lldb) p [$array count]
(NSUInteger) $4 = 3
(lldb) po [[$array objectAtIndex:0] uppercaseString]
A
(lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
error: no known method '-characterAtIndex:'; cast the message send to the method's return type
最后的出错信息,时有发生,可以给 LLDB 些提示信息,使其正确输出结果:
(lldb) p (char)[[$array objectAtIndex:$a] characterAtIndex:0]
(char) $6 = 'c'
(lldb) p/d (char)[[$array objectAtIndex:$a] characterAtIndex:0]
(char) $7 = 99
Flow control
程序运行到断点处,在Debug 窗口,会有下图中的几个按钮,从左到右→_→依次是:继续(continue),单步执行(step over),跳入(step in),跳出(step out)

-
continue后,程序会继续执行,直到下一个断点处。
它的三种等价命令:process continue==continue==c -
step over执行后,跳到下一行可执行代码处,也就是一行一行执行。
它的三种等价命令:thread step-over==next==n -
step in执行后,跳入函数调用内部。前提是处于调试的那行代码是对某个函数的调用。如果不是函数调用,它和step over没有区别。
它的三种等价命令:thread step-in==step==s -
step out执行后,迅速执行完这个函数,从其内部跳出。
命令:thread step-out.- 跳出后,程序继续执行,直到碰到
return,然后会停在这个地方,等待下一步调试。
- 跳出后,程序继续执行,直到碰到
finish
finish 命令会从函数调用中跳出。如果通过 step in 进入某个函数调用,那么,finish 和 step out 效果相同
frame info
frame info 命令会打印出当前调试行的 行数 以及 文件信息:
(lldb) frame info
frame #0: 0x00000001026525b1 TestEasy`main(argc=1, argv=0x00007ffeed5ad0c0) at main.m:25
thread return
thread return 命令后面可以跟一个可选参数,用来表示函数返回的结果。比如,在一个包含有 return 的函数中,执行thread return xxx 那么这个函数的返回结果就是 xxx。

另外注意一点,执行 thread return 后,其断点后的代码都不会执行,而是直接返回。从上图可以看出:
- 执行
s后进入函数调用的 13 行 - 在这里执行
thread return YES,13行之后的代码都不会执行,同事返回值被设置为了YES
管理断点
-
breakpoint list列出所有断点信息。简写:br li -
breakpoint disable <breakpointID>置灰某个断点。简写:br dis <id> -
breakpoint enable <breakpointID>使某个断点可用。简写:br en -
breakpoint delete <breakpointID>删掉某个断点。简写:br del
创建断点
-
breakpoint set创建断点。-f断点所在文件,-l第几行
(lldb) breakpoint set -f main.m -l 28
Breakpoint 4: where = TestEasy`main + 70 at main.m:28, address = 0x000000010a71a5c6
-
b 文件名:第几行创建断点
(lldb) b main.m:24
Breakpoint 5: where = TestEasy`main + 27 at main.m:24, address = 0x00000001053f458b
-
b 函数名创建断点。 断点会定位在函数执行的第一行,函数名必须是项目中已知的。
(lldb) b isEven
Breakpoint 6: where = TestEasy`isEven + 16 at main.m:13, address = 0x00000001053f4640
-
br s -F 函数名创建断点。
(lldb) br s -F isEven
Breakpoint 7: where = TestEasy`isEven + 16 at main.m:13, address = 0x00000001053f4640
符号断点
有两种添加 Symbol Breakpoint 的方式:LLDB 和 UI
通过LLDB
-
breakpoint set -F "OC API声明"下面命令给CoreFoundation提供的数组 API 添加断点
(lldb) breakpoint set -F "-[NSArray objectAtIndex:]"
Breakpoint 8: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x00000001066cb330
b API声明
(lldb) b -[NSArray objectAtIndex:]
Breakpoint 9: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x00000001066cb330
通过UI
在图形界面上添加断点 如图一:

点击
Symbolic Breakpoint 出现图二.

- 在
Symbol处可以输入要断点的OC方法,比如"-[NSArray objectAtIndex:]",效果等同于breakpoint set -F "OC API声明" -
Module填写方法所在的模块,比如CoreFoundation
UI上的断点
选择 UI 上的断点,双击编辑,会出现下图的编辑弹窗:

-
Condition断点执行条件 -
Ignore满足断点执行条件的前提下, X 次之后再执行此断点 -
Action下面单独介绍
Breakpoint Action
Action 可以根据类型,添加多个。
-
Debugger Command类型,输入命令po i,满足断点条件时,就会执行该命令 -
Log Message类型,满足条件时输出message或者 朗读message -
Automatically continue after evaluating actions底部复选框, 意义是 所有的Actions执行完后,执行continue。相当于在最后一个Action后面,又加了个continue命令的Action。复选框把这一步简化了。
可以在 LLDB 命令下添加 Debugger Command 类型的 Action:
(lldb) br s -F isEven
Breakpoint 3: where = TestEasy`isEven + 16 at main.m:13, address = 0x000000010c34c640
(lldb) br modify -c 'i == 110' 3 /// -c 代表 condition
(lldb) br command add 3
Enter your debugger command(s). Type 'DONE' to end.
> p i
> DONE
(lldb) br li 3
3: name = 'isEven', locations = 1, resolved = 1, hit count = 0
Breakpoint commands:
p i
Condition: i == 110
3.1: where = TestEasy`isEven + 16 at main.m:13, address = 0x000000010c34c640, resolved, hit count = 0
LLDB Debugger 缺点
LLDB 支持 C/Objective-C/C++/Swift 命令调试,但也有不足之处:
-
LLDB Debugger不能创建新的函数,也就意味着不能在LLDB中创建类,block,函数,含有虚函数的C++类等
除此之外,它都可以做。
我们可以申请分配一些字节:
(lldb) e char *$str = (char *)malloc(8)
(lldb) e (void)strcpy($str,"munkeys")
(lldb) e $str[1]
(char) $0 = 'u'
(lldb) e $str[1] = 'o'
(char) $1 = 'o'
(lldb) p $str
(char *) $str = 0x000060000001d4b0 "monkeys"
x 命令读取内存,每次读取4个字节(x 是读取内存的缩写):
// c 作为character输出
(lldb) x/1c $str
0x604000010a10: monk
向后偏移3位读取:
(lldb) x/1c '$str+3'
0x604000010a13: keys
用完要释放掉,不然会有内存泄露:
(lldb) e (void)free($str)