好用的 LLDB 调试一

2018-09-14  本文已影响20人  厨子

一句话介绍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 printhelp 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 -- objectse -O -- 的简写是: po

打印四

可以为 print 指定不同的打印format,语法:print <fmt> or p <fmt>. 查看完整的 fmts 列表

(lldb) p/x 16
(int) $0 = 0x00000010
(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)

finish

finish 命令会从函数调用中跳出。如果通过 step in 进入某个函数调用,那么,finishstep 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 后,其断点后的代码都不会执行,而是直接返回。从上图可以看出:

管理断点

创建断点

(lldb) breakpoint set -f main.m -l 28
Breakpoint 4: where = TestEasy`main + 70 at main.m:28, address = 0x000000010a71a5c6
(lldb) b main.m:24
Breakpoint 5: where = TestEasy`main + 27 at main.m:24, address = 0x00000001053f458b
(lldb) b isEven
Breakpoint 6: where = TestEasy`isEven + 16 at main.m:13, address = 0x00000001053f4640
(lldb) br s -F isEven
Breakpoint 7: where = TestEasy`isEven + 16 at main.m:13, address = 0x00000001053f4640
符号断点

有两种添加 Symbol Breakpoint 的方式:LLDBUI

通过LLDB
(lldb) breakpoint set -F "-[NSArray objectAtIndex:]"
Breakpoint 8: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x00000001066cb330
(lldb) b -[NSArray objectAtIndex:]
Breakpoint 9: where = CoreFoundation`-[NSArray objectAtIndex:], address = 0x00000001066cb330
通过UI

在图形界面上添加断点 如图一:

图一
点击 Symbolic Breakpoint 出现图二. 图二
UI上的断点

选择 UI 上的断点,双击编辑,会出现下图的编辑弹窗:

Breakpoint Action

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) 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)
上一篇下一篇

猜你喜欢

热点阅读