好用的 LLDB 调试一

一句话介绍LLDB:内置在 Xcode 中的调试器。能在特定的位置暂停程序运行,并观察变量的值,也可以执行自定义指令。

好处:通过 LLDB 对程序做了修改,而不需要重新运行项目,就可以看到效果

目录

- Basic
   - 帮助
   - 打印一
   - expression
   - 打印二
   - 打印三
   - 打印四
   - 声明变量
   - flow control
      - finish
      - frame info
      - thread return
- Breakpoint
   - 管理断点
   - 创建断点
      - 为 OC API 添加断点
      - 符号断点
         - 通过LLDB
         - 通过UI
      - UI上的断点
         - Breakpoint Action
   - LLDB Debugger 缺点

帮助

借助 help 查看某个命令,比如,help printhelp thread

打印一

如图所示,print count 会输出一个以 $ 开头的变量以及一个值,这个变量可以作为这个值的引用。所以当打印 print $0 + 7 输出的结果是106.

好用的 LLDB 调试一_第1张图片

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 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 进入某个函数调用,那么,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

好用的 LLDB 调试一_第2张图片

另外注意一点,执行 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 的方式:LLDBUI

通过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

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

好用的 LLDB 调试一_第3张图片
图一

点击 Symbolic Breakpoint 出现图二.

好用的 LLDB 调试一_第4张图片
图二
  • Symbol 处可以输入要断点的 OC 方法,比如 "-[NSArray objectAtIndex:]",效果等同于 breakpoint set -F "OC API声明"
  • Module 填写方法所在的模块,比如 CoreFoundation
UI上的断点

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

好用的 LLDB 调试一_第5张图片
  • 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)

你可能感兴趣的:(好用的 LLDB 调试一)