LLDB - Low Lever Debugger, XCode 5 之后 替代GDB 成为XCode的默认调试器.
作为调试器, 应该有两个主要功能: 流程控制 和 状态获取.
在代码中设置断点(Breakpoints)后, 当程序运行到断点处时, LLDB会暂停程序运行.
此时, 通过调试器可以观察或者修改变量, 或者通过单步调试/跳入函数内部/跳出函数来控制程序断点位置.
为了使用LLDB调试程序, 我们先生成一个简单的可执行程序.
创建Greet.swift
并通过swiftc -g Greet.swift
生成可执行文件.
class Greeter {
private var acquaintances: Set<String> = []
func hasMet(personNamed name: String) -> Bool {
return acquaintances.contains(name)
}
func greet(personNamed name: String) {
if hasMet(personNamed: name) {
print("Hello again, \(name)!")
} else {
acquaintances.insert(name)
print("Hello, \(name). Nice to meet you!")
}
}
}
let greeter = Greeter()
greeter.greet(personNamed: "Anton")
greeter.greet(personNamed: "Mei")
greeter.greet(personNamed: "Anton")
此时执行Greeter
会得到如下输出:
Hello, Anton. Nice to meet you!
Hello, Mei. Nice to meet you!
Hello again, Anton!
通过lldb Greeter
指令, 运行Greeter
程序
$ lldb Greeter
(lldb) target create "Greeter"
Current executable set to 'Greeter' (x86_64).
设置断点, 可以通过 --line
参数设置断点行数, 也可以通过--name
根据方法名称设置断点
(lldb) breakpoint set --line 18
Breakpoint 1: where = Greeter`main + 70 at Greeter.swift:18, address = 0x0000000100001996
breakpoint set --name greet
Breakpoint 2: where = Greeter`Greeter.Greeter.greet (personNamed : Swift.String) -> () + 27 at Greeter.swift:9, address = 0x0000000100001bab
此时通过process launch
运行程序, 程序会在line 18断点处暂停运行.
(lldb) process launch
Process 97209 launched: 'Greeter' (x86_64)
Process 97209 stopped
* thread #1: tid = 0x1288be3, 0x0000000100001996 Greeter`main + 70 at Greeter.swift:18, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100001996 Greeter`main + 70 at Greeter.swift:18
15 }
16 }
18
-> 18 let greeter = Greeter()
19
20 greeter.greet(personNamed: "Anton")
21 greeter.greet(personNamed: "Mei")
输入thread step-over
跳转至程序在第20行的下个函数调用.
(lldb) thread step-over
Process 97209 stopped
* thread #1: tid = 0x1288be3, 0x00000001000019bd Greeter`main + 109 at Greeter.swift:20, queue = 'com.apple.main-thread', stop reason = step over
frame #0: 0x00000001000019bd Greeter`main + 109 at Greeter.swift:20
17
18 let greeter = Greeter()
19
-> 20 greeter.greet(personNamed: "Anton")
21 greeter.greet(personNamed: "Mei")
22 greeter.greet(personNamed: "Anton")
使用thread step-in
跳转到greet(personNamed:)
方法内部:
(lldb) thread step-in
Process 97209 stopped
* thread #1: tid = 0x1288be3, 0x0000000100001bab Greeter`Greeter.greet(name="Anton", self=0x0000000100606b10) -> () + 27 at Greeter.swift:9, queue = 'com.apple.main-thread', stop reason = step in
frame #0: 0x0000000100001bab Greeter`Greeter.greet(name="Anton", self=0x0000000100606b10) -> () + 27 at Greeter.swift:9
6 }
7
8 func greet(personNamed name: String) {
-> 9 if hasMet(personNamed: name) {
10 print("Hello again, \(name)!")
11 } else {
12 acquaintances.insert(name)
再次执行thread step-over
, 通过传入--count
参数, 指定step-over
执行次数
(lldb) thread step-over --count 4
Process 97209 stopped
* thread #1: tid = 0x1288be3, 0x0000000100001e0c Greeter`Greeter.greet(name="Mei", self=0x0000000100606b10) -> () + 636 at Greeter.swift:13, queue = 'com.apple.main-thread', stop reason = step over
frame #0: 0x0000000100001e0c Greeter`Greeter.greet(name="Mei", self=0x0000000100606b10) -> () + 636 at Greeter.swift:13
10 print("Hello again, \(name)!")
11 } else {
12 acquaintances.insert(name)
-> 13 print("Hello, \(name). Nice to meet you!")
14 }
15 }
16 }
使用thread backtrace
显示线程调用栈
(lldb) thread backtrace
* thread #1: tid = 0x1288be3, 0x0000000100001a98 Greeter`Greeter.hasMet(name="Anton", self=0x0000000101200190) -> Bool + 24 at Greeter.swift:5, queue = 'com.apple.main-thread', stop reason = step in
frame #0: 0x0000000100001a98 Greeter`Greeter.hasMet(name="Anton", self=0x0000000101200190) -> Bool + 24 at Greeter.swift:5
* frame #1: 0x0000000100001be4 Greeter`Greeter.greet(name="Anton", self=0x0000000101200190) -> () + 84 at Greeter.swift:9
frame #2: 0x00000001000019eb Greeter`main + 155 at Greeter.swift:20
frame #3: 0x00007fff949d05ad libdyld.dylib`start + 1
frame #4: 0x00007fff949d05ad libdyld.dylib`start + 1
通过frame variable
可以查看当前栈帧的变量信息
(lldb) frame variable
(String) name = "Anton"
(Greeter.Greeter) self = 0x0000000100502920 {
acquaintances = ([0] = "Anton")
}
通过expression
命令可以修改帧栈的变量信息从而改变程序的最终输出结果
(lldb) expression -- acquaintances.insert("Mei")
(lldb) expression -- acquaintances.remove("Anton")
(String?) $R1 = "Anton"
breakpoint disable
传入breakpoint ID 可以使对应的breakpoint 变为不可用状态
(lldb) breakpoint disable 2
1 breakpoints disabled.
之后执行process continue
, 程序就会继续执行
(lldb) process continue
Resuming thread 0x12f1d19 in process 97209
Process 97209 resuming
Hello again, Mei!
Hello, Anton. Nice to meet you!
Process 97209 exited with status = 0 (0x00000000)
文章中使用到的命令如下:
lldb <executable>
breakpoint set --line <linenum>
breakpoint set --name <name>
process launch
thread step-over
thread step-in
thread step-over --count 4
thread backtrace
frame variable
expression
breakpoint disable
process continue