一句话介绍LLDB
:内置在 Xcode 中的调试器。能在特定的位置暂停程序运行,并观察变量的值,也可以执行自定义指令。
好处:通过 LLDB
对程序做了修改,而不需要重新运行项目,就可以看到效果
目录
- Basic
- 帮助
- 打印一
- expression
- 打印二
- 打印三
- 打印四
- 声明变量
- flow control
- finish
- frame info
- thread return
- Breakpoint
- 管理断点
- 创建断点
- 为 OC API 添加断点
- 符号断点
- 通过LLDB
- 通过UI
- UI上的断点
- Breakpoint Action
- LLDB Debugger 缺点
帮助
借助 help
查看某个命令,比如,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
or p
. 查看完整的 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
置灰某个断点。简写:br dis
-
breakpoint enable
使某个断点可用。简写:br en
-
breakpoint delete
删掉某个断点。简写: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)