LLDB 断点管理

3,566 阅读6分钟

设置断点:

breakpoint set

使用breakpoint 的子命令 set 设置断点

具体信息可以通过help breakpoint set 查询所有可选的选项(options)以及参数(arguments).

下面使用之前的可执行文件Greeter介绍一些常用的方法:

  1. 按方法名称设置--name(-n):
(lldb) breakpoint set -n greet
Breakpoint 2: where = Greeter`Greeter.Greeter.greet(personNamed: Swift.String) -> () + 127 at Greeter.swift:9:12, address = 0x00000001000034cf
  1. 按行数设置--line(-l):
(lldb) breakpoint set -l 18
Breakpoint 3: where = Greeter`main + 14 at Greeter.swift:18:15, address = 0x000000010000315e
  1. 按文件设置--file(-l):
(lldb) breakpoint set -f Greeter.swift -l 20
Breakpoint 6: where = Greeter`main + 34 at Greeter.swift:20:1, address = 0x0000000100003172
  1. 根据语言捕获异常--language-exception(-E):
(lldb) breakpoint set -E Objc
Breakpoint 7: no locations (pending).

可以通过--exception-typename(-O) 指定要捕获的异常类型:

(lldb)  breakpoint set -E Swift -O EnumErrorType
Breakpoint 8: no locations (pending).

获取断点列表:

breakpoint list

(lldb) breakpoint list
Current breakpoints:
1: name = 'sayHello', locations = 0 (pending)

2: name = 'greet', locations = 1
  2.1: where = Greeter`Greeter.Greeter.greet(personNamed: Swift.String) -> () + 127 at Greeter.swift:9:12, address = Greeter[0x00000001000034cf], unresolved, hit count = 0

3: file = '/Users/jackli/Desktop/Roborock/Greeter.swift', line = 18, exact_match = 0, locations = 1
  3.1: where = Greeter`main + 14 at Greeter.swift:18:15, address = Greeter[0x000000010000315e], unresolved, hit count = 0

4: file = 'main.swift', line = 20, exact_match = 0, locations = 0 (pending)

5: file = 'Greet.swift', line = 20, exact_match = 0, locations = 0 (pending)

6: file = 'Greeter.swift', line = 20, exact_match = 0, locations = 1
  6.1: where = Greeter`main + 34 at Greeter.swift:20:1, address = Greeter[0x0000000100003172], unresolved, hit count = 0

7: Exception breakpoint (catch: off throw: on) the correct runtime exception handler will be determined when you run

8: Swift Error breakpoint the correct runtime exception handler will be determined when you run

需要注意的是: 设置断点其实是设置了逻辑断点, 可能会对应一个或多个位置断点(location).

每个逻辑断点都有一个按照顺序从1开始分配的整型ID. 每一个位置断点又有一个自己的位置ID, 与逻辑断点的ID之间以.分隔, 用于定位位置断点. 示例如下:

2: name = 'greet', locations = 1
  2.1: where = Greeter`Greeter.Greeter.greet(personNamed: Swift.String) -> () + 127 at Greeter.swift:9:12, address = Greeter[0x00000001000034cf], unresolved, hit count = 0

逻辑断点是动态的, 如果程序载入了新的代码, 会自动载入新的位置断点.

修改断点:

breakpoint modify

使用这个命令修改逻辑断点或者位置断点, 需要传递逻辑断点或者位置断点的ID 作为参数, 具体参数可以通过help breakpoint modify 查看.

部分常用选项如下:

  • --condition(-c) 传入表达式参数, 只有表达式判断为true 才会执行断点.
  • ignore-count(-i) 指定跳过断点的次数, 在该断点处跳过指定次数后, 才会执行该断点.
  • --one-shot(-o) 执行断点一次后移除该断点.
  • --queue-name(-q) 指定队列名称, 该断点只有在指定队列上才会执行.
  • --thread-name(-T) 指定线程名称, 该断点只有在指定的线程上才会执行.
  • --thread-id(-t) 指定线程ID(TID), 该断点只有在指定的线程上才会执行.
  • --thread-index(-x) 指定线程index, 该断点只有在指定的线程上才会执行.

如下示例修改了断点ID为1的断点, 指定其在执行后,自动移除.

(lldb) breakpoint modify --one-shot 1

很多选项在breakpoint set 中就可以被指定的, 通过modify 可以重新修改选项.

在断点处执行命令

我们都知道, 当程序抵达断点处时, 程序会暂停执行, 此时可以执行lldb命令.

我们也可以通过执行breakpoint command add 命令, 让程序每次抵达断点处时, 自动执行我们添加的命令. 如:

在位置断点1.1处添加指令, 每行可以添加一个指令, Enter 跳转至下一指令, 输入DONE结束添加.

(lldb) breakpoint command add 1.1
Enter your debugger command(s). Type 'DONE' to end.
> thread backtrace
> DONE

可以将process continue 作为最后一个指令, 那么调试器就会在执行指令后, 自动继续执行程序, 这在记录Log的场景下使用是非常方便的.

(lldb) target create "Greeter"
Current executable set to (x86_64).
(lldb) breakpoint list
No breakpoints currently set.
(lldb) breakpoint set -n greet
Breakpoint 1: where = Greeter`Greeter.Greeter.greet(personNamed: Swift.String) -> () + 127 at Greeter.swift:9:12, address = 0x00000001000034cf
(lldb) breakpoint command add 1
Enter your debugger command(s).  Type 'DONE' to end.
> frame variable
> process continue
(lldb) process launch
Process 5997 launched: (x86_64)

// 下面的frame variable 和 process continue 都是自动执行的.
(lldb)  frame variable
(String) name = "Anton"
(Greeter.Greeter) self = 0x0000000100304540 {
  acquaintances = 0 values {}
}
(lldb)  process continue
Process 5997 resuming
Command #2 'process continue' continued the target.
Hello, Anton. Nice to meet you!

(lldb)  frame variable
(String) name = "Mei"
(Greeter.Greeter) self = 0x0000000100304540 {
  acquaintances = 1 value {
    [0] = "Anton"
  }
}
(lldb)  process continue
Process 5997 resuming
Command #2 'process continue' continued the target.
Hello, Mei. Nice to meet you!

(lldb)  frame variable
(String) name = "Anton"
(Greeter.Greeter) self = 0x0000000100304540 {
  acquaintances = 2 values {
    [0] = "Anton"
    [1] = "Mei"
  }
}
(lldb)  process continue
Process 5997 resuming
Command #2 'process continue' continued the target.
Hello again, Anton!

Process 5997 exited with status = 0 (0x00000000)

设置断点可用状态(Diable/Enable)

禁用断点: breakpoint disable

启用断点: breakpoint enable

以上命令都需要指定断点ID参数(支持通配符).

当逻辑断点设置为diable时, 它下方的所有位置断点都将不再生效.

(lldb) breakpoint disable 1
1 breakpoints disabled.
(lldb) breakpoint disable 2.1
1 breakpoints disabled.
(lldb) breakpoint enable 1
1 breakpoints enabled.
(lldb) breakpoint enable 2.1
1 breakpoints enabled.

还可用通配符*指定断点.

如下示例先是禁用了逻辑断点1下的所有位置断点, 而后启用了位置断点1.1

(lldb) breakpoint disable 1.*
2 breakpoint disabled*
(lldb) breakpoint enable 1.1
1 breakpoint enabled.

删除断点

(lldb) breakpoint delete 1
1 breakpoints deleted; 2 breakpoint locations disabled.

设置监视点(Watchpoint)

监视点(Watchpoint) 是一种设置在地址或者变量上的断点. 每次地址或变量被访问时就会执行.

监视点的数量收到硬件寄存器数量的显示.

可以通过watchpoint set variable 设置变量断点, 或者通过watchpoint set expression

传入返回地址的表达式参数设置地址断点.

(lldb) watchpoint set variable places
Watchpoint created: Watchpoint 1: addr = 0x100004a40 size = 8 state = enabled type = w
    declare @ 'main.swift:7'
    watchpoint spec = 'places'
    new value: 1 value
(lldb) watchpoint set expression -- (int *)$places + 8
Watchpoint created: Watchpoint 2: addr = 0x100005f33 size = 8 state = enabled type = w
    new value: 0x0000000000000000

获取监视点列表

watchpoint list

(lldb) watchpoint list
Current watchpoints:
Watchpoint 1: addr = 0x100004a50 size = 8 state = disabled type = w
    declare @ 'main.swift:7'
    watchpoint spec = 'places'

修改监视点

watchpoint modifybreakpoint modify 类似, 修改监视点的信息.

(lldb) watchpoint modify --condition !places.isEmpty
1 watchpoints modified.

在监视点处执行命令

watchpoint command addbreakpoint command add 类似, 在监视点出添加需要执行的命令.

(lldb) watchpoint command add 1
Enter your debugger command(s).  Type 'DONE' to end.
> bt
> DONE

删除监视点

由于监视点数量受到硬件条件限制, 在不再需要它的时候将其删除是很重要的.

(lldb) watchpoint delete 1
1 watchpoints deleted.

参考资料: Managing Breakpoints