从零介绍使用 LLDB debug RUST 程序的基本方法。
Rust 会使用 DWARF 格式在 binary 中嵌入调试信息,所以可以使用一些通用的调试工具,比如 GDB 和 LLDB。
DWARF 维基百科 地址:https://en.wikipedia.org/wiki/DWARF
Rust 提供了 rust-gdb 和 rust-lldb 两个命令用于调试,其相比原生的 gdb 和 lldb 添加了一些方便调试的脚本。
rustup 会安装 rust-lldb,但不会安装 lldb,需要自行安装。
例子程序
演示 debug 使用的是二分查找法:
1/// binary search 2/// 3/// # introduction 4/// 5/// * params: 6/// * nums: input search array 7/// * target: search target 8/// * return: 9/// * None: not found 10/// * Some: some index of target in array 11/// 12pub fn binary_search<T>(nums: Vec<T>, target: T) -> Option<usize> where T: Ord{ 13 let mut l = 0; 14 let mut r = nums.len(); 15 if r <= 0 { 16 return None; 17 } 18 while r > 0 && l < r { 19 let mid = (l + r) / 2; 20 if target == nums[mid] { 21 return Some(mid); 22 } else if target > nums[mid] { 23 l = mid + 1; 24 } else { 25 r = mid; 26 } 27 } 28 None 29} 30 31use std::env; 32 33fn main() { 34 println!("args: {:?}", env::args()); 35 assert_eq!( 36 Some(4), 37 search::binary_search(vec![2, 3, 5, 8, 10, 18, 100], 10) 38 ); 39}
rust-lldb 命令行使用
使用 rust-lldb debug 二进制文件,那么二进制文件中必须包含调试相关的信息。cargo build 默认会以 debug 模式进行构建,所以含有用于调试的 symbol,需要注意的是 cargo install 会以 release 模式构建,需要 cargo install --debug。
进入 LLDB REPL
通过 rust-lldb 命令加载可执行文件(或者在 REPL 中通过 file 载入),进入 LLDB 的 REPL。比如:
rust-lldb target/debug/<bin-name>
二进制文件的启动参数
使用
settings set — target.run-args "myarg"
给二进制文件添加一个启动参数 myarg 。
通过:
settings show target.run-args
可查看二进制文件的启动参数
例子代码会首先打印启动参数,所以设置完启动参数后,运行时,会将该参数打印出来:
⚠️ lldb repl 中直接输入 settings 命令可查看相关的子命令,包括清理调试参数,从文件读取参数,替换参数等。
断点
通过 b命令设置断点, 对应 GDB 中break 命令的模拟, 并不是 lldb 中的 breakpoint 的别名(alias)。
常用场景:
- 方法开始设置断点:
breakpoint set -n <func name>设置支持补全 - 文件某一行设置断点:
breakpoint set -f <文件路径> --line 15在指定的行设置断点 - 查看已有的 breakpoint:
b - 删除断点:
breakpoint delete [<断点编号>]如果没有设置断点编号,那么默认删除所有断点 - 使断点失效但是不删除断点记录:
breakpoint disable [<断点编号>]如果没有设置断点编号,那么默认 disable 所有断点 - 激活断点:
breakpoint enable [<断点编号>]如果没有设置断点编号,那么默认 enable 所有断点
断点列表,例子如下:
1(lldb) breakpoint list 2Current breakpoints: 31: name = 'binary_search', locations = 0 (pending) 4 52: file = 'search.rs', line = 19, exact_match = 0, locations = 1 6 2.1: where = algorithms`algorithms::search::binary_search::ha09558f76f86b6d5 + 78 at search.rs:19:4, address = algorithms[0x00000001000023ce], unresolved, hit count = 0 7 84: name = 'binary_search', locations = 1 Options: disabled 9 4.1: where = algorithms`algorithms::search::binary_search::ha09558f76f86b6d5 + 14 at search.rs:14:16, address = algorithms[0x000000010000238e], unresolved, hit count = 0
例子中
- 断点 1 使用了-M 参数设置断点,断点找不到,处于 pending 状态;
- 断点 2 设置了
search.rs文件的 19 行为断点(line = 19),且设置成功了; - 断点 3 被删除了,所以不在断点列表;
- 断点 4 被 disable 了(
Options: disabled),是基于函数名设置的断点(name = 'binary_search')
逻辑断点的 ID 为整数,并且其所在位置的父断点为 ID(两个之间以
.连接,例如上例中的2.1)。
例如:
断点命令
使用如下命令设置断点触发时执行的命令:
breakpoint command add <cmd-options> <breakpt-id>
例如:breakpoint command add 2.1。
在<cmd-options>中 可以添加--command 用来执行命令,或者--script 执行 python 脚本。
通过help breakpoint command add查看更多帮助信息。
在断点被触发时,会自动运行设置的命令, 如果没有指定断点 id, 那么默认将 lldb 的命令加到最后一个断点。
例如:
条件断点
在设置断点时,配置-c 即-condition ,配置条件断点。
例如在 例子代码中,r >3 时,自动打印堆栈信息:
1(lldb) breakpoint set -c "r > 3" -f search.rs -l 19 -C bt 2(lldb) breakpoint list 3Current breakpoints: 41: file = 'search.rs', line = 16, exact_match = 0, locations = 1, resolved = 1, hit count = 1 5 1.1: where = algorithms`algorithms::search::binary_search::ha09558f76f86b6d5 + 56 at search.rs:16:7, address = 0x00000001000023b8, resolved, hit count = 1 6 72: file = 'search.rs', line = 19, exact_match = 0, locations = 1 8 Breakpoint commands: 9 bt 10 11Condition: r > 3 12 13 2.1: where = algorithms`algorithms::search::binary_search::ha09558f76f86b6d5 + 78 at search.rs:19:4, address = 0x00000001000023ce, unresolved, hit count = 0 14 15//运行二进制文件 16 (lldb) r 17Process 59956 launched: '/Users/guangfuhe/Projects/rust/algorithms/target/debug/algorithms' (x86_64) 18args: Args { inner: ["/Users/guangfuhe/Projects/rust/algorithms/target/debug/algorithms"] } 19Process 59956 stopped 20* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 21 frame #0: 0x00000001000023b8 algorithms`algorithms::search::binary_search::ha09558f76f86b6d5(nums=vec![2, 3, 5, 8, 10, 18, 100], target=10) at search.rs:16:7 22 13 pub fn binary_search<T>(nums: Vec<T>, target: T) -> Option<usize> where T: Ord{ 23 14 let mut l = 0; 24 15 let mut r = nums.len(); 25-> 16 if r <= 0 { 26 17 return None; 27 18 } 28 19 while r > 0 && l < r { 29Target 0: (algorithms) stopped. 30(lldb) c 31Process 59956 resuming 32//到了断点自动触发的命令 33(lldb) bt 34* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 35 * frame #0: 0x00000001000023ce algorithms`algorithms::search::binary_search::ha09558f76f86b6d5(nums=vec![2, 3, 5, 8, 10, 18, 100], target=10) at search.rs:19:4 36 frame #1: 0x0000000100002928 algorithms`algorithms::main::h451514f7ba18c029 at main.rs:9:8 37 frame #2: 0x00000001000037d2 algorithms`std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::ha58704225b4f5f6e at rt.rs:67:33 38 frame #3: 0x000000010000b108 algorithms`std::panicking::try::do_call::hb5f9c52a170af65b [inlined] std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::h7021adb3f4199cb2 at rt.rs:52:12 [opt] 39 frame #4: 0x000000010000b0fc algorithms`std::panicking::try::do_call::hb5f9c52a170af65b at panicking.rs:292 [opt] 40 frame #5: 0x000000010000c6ff algorithms`__rust_maybe_catch_panic at lib.rs:78:7 [opt] 41 frame #6: 0x000000010000b9be algorithms`std::rt::lang_start_internal::h032b1be013493933 [inlined] std::panicking::try::h9686b6ceb1ebe04f at panicking.rs:270:12 [opt] 42 frame #7: 0x000000010000b98b algorithms`std::rt::lang_start_internal::h032b1be013493933 [inlined] std::panic::catch_unwind::hf68219c633474123 at panic.rs:394 [opt] 43 frame #8: 0x000000010000b98b algorithms`std::rt::lang_start_internal::h032b1be013493933 at rt.rs:51 [opt] 44 frame #9: 0x00000001000037b2 algorithms`std::rt::lang_start::h9af99707a1bf11eb(main=&0x1000027f0, argc=1, argv=&0x7ffeefbff968) at rt.rs:67:4 45 frame #10: 0x0000000100002ab2 algorithms`main + 34 46 frame #11: 0x00007fff6668f7fd libdyld.dylib`start + 1 47 frame #12: 0x00007fff6668f7fd libdyld.dylib`start + 1 48 49Process 59956 stopped 50* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 51 frame #0: 0x00000001000023ce algorithms`algorithms::search::binary_search::ha09558f76f86b6d5(nums=vec![2, 3, 5, 8, 10, 18, 100], target=10) at search.rs:19:4 52 16 if r <= 0 { 53 17 return None; 54 18 } 55-> 19 while r > 0 && l < r { 56 20 let mid = (l + r) / 2; 57 21 if target == nums[mid] { 58 22 return Some(mid); 59Target 0: (algorithms) stopped. 60
查看变量值
使用 **frame variable** 命令查看栈变量和对应的值
例如:
1(lldb) breakpoint set -n binary_search 2Breakpoint 1: where = algorithms`algorithms::search::binary_search::ha09558f76f86b6d5 + 14 at search.rs:14:16, address = 0x000000010000238e 3(lldb) r 4Process 47767 launched: '/Users/guangfuhe/Projects/rust/algorithms/target/debug/algorithms' (x86_64) 5args: Args { inner: ["/Users/guangfuhe/Projects/rust/algorithms/target/debug/algorithms"] } 6Process 47767 stopped 7* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 8 frame #0: 0x000000010000238e algorithms`algorithms::search::binary_search::ha09558f76f86b6d5(nums=vec![2, 3, 5, 8, 10, 18, 100], target=10) at search.rs:14:16 9 11 /// * Some: some index of target in array 10 12 /// 11 13 pub fn binary_search<T>(nums: Vec<T>, target: T) -> Option<usize> where T: Ord{ 12-> 14 let mut l = 0; 13 15 let mut r = nums.len(); 14 16 if r <= 0 { 15 17 return None; 16Target 0: (algorithms) stopped. 17(lldb) frame variable 18(alloc::vec::Vec<int>) nums = vec![2, 3, 5, 8, 10, 18, 100] 19(int) target = 10 20(lldb)
**watch**** 命令观察变量 变化: **watch set var r** (其中 r 是源代码中的变量)。此处需要在调试时运行二进制文件,结合断点完成变量观察设置。**
例如:
单步调试
r开始运行二进制程序
c continue,继续执行程序,直到结束或下一个断点
- 选择线程:
thread select <id>,在执行 r 命令的时候,会输出每个线程的线程 id。 - step in:
thread step-in单步执行,进入子函数,对应 gdb 的step命令或者s命令。 - step over:
thread step-over越过子函数,但子函数会执行。对应 gdb 的next或者n - step out :
thread step-out当单步执行到子函数内时,用 step out 就可以执行完子函数余下部分,并返回到上一层函数。 对应 gdb 的finish或者f。
表达式求值
使用expr 对栈变量
例如:
1(lldb) frame variable 2(alloc::vec::Vec<int>) nums = vec![2, 3, 5, 8, 10, 18, 100] 3(int) target = 10 4(unsigned long) l = 0 5(unsigned long) r = 7 6(lldb) expr r>1 7(bool) $0 = true 8(lldb) expr r+1 9(unsigned long) $1 = 8
自定义 lldb alias
设置断点:
- 全命令:
breakpoint set --file main.rs --line 18, 其中--file可以简写为-f,--line简写为-l。 - 自定义 alias:
command alias bfl breakpoint set -f %1 -l %2 - 使用 alias 同样设置上面的断点:
bfl main.rs 18
取消 alias:unalias <alias-name>, 例如unalias bfl
rust-lldb GUI 使用
进入 GUI: 在 lldb repl 中输入 gui 。
全局快捷键
说明
tab
选择 view
h
每个 view 不同的帮助信息
,
上一页
.
下一页
⬆️
选择上一个
⬇️
选择下一个
⬅️
unexpand / 选择父组件
➡️
expand
💡在进入 GUI 前,可以先二进制程序运行起来,进入到 GUI 就能看到源代码。

参考