LLDB - 快速体验

265 阅读1分钟

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

参考链接 - LLDB Debugging Guide