Debug RUST

Stella981
• 阅读 2160

从零介绍使用 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 使用的是二分查找法:

/// binary search
///
/// # introduction
///
/// * params:
///     * nums: input search array
///     * target: search target
/// * return:
///     * None: not found
///     * Some: some index of target in array
///
pub fn binary_search<T>(nums: Vec<T>, target: T) -> Option<usize> where T: Ord{
    let mut l = 0;
    let mut r = nums.len();
    if r <= 0 {
        return None;
    }
    while r > 0 && l < r {
        let mid = (l + r) / 2;
        if target == nums[mid] {
            return Some(mid);
        } else if target > nums[mid] {
            l = mid + 1;
        } else {
            r = mid;
        }
    }
    None
}

use std::env;

fn main() {
    println!("args: {:?}", env::args());
    assert_eq!(
        Some(4),
        search::binary_search(vec![2, 3, 5, 8, 10, 18, 100], 10)
    );
}

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

可查看二进制文件的启动参数

例子代码会首先打印启动参数,所以设置完启动参数后,运行时,会将该参数打印出来:

Debug RUST

⚠️ 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 所有断点

断点列表,例子如下:

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

2: file = 'search.rs', line = 19, exact_match = 0, locations = 1
  2.1: where = algorithms`algorithms::search::binary_search::ha09558f76f86b6d5 + 78 at search.rs:19:4, address = algorithms[0x00000001000023ce], unresolved, hit count = 0

4: name = 'binary_search', locations = 1 Options: disabled
  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)。

例如:

Debug RUST

断点命令

使用如下命令设置断点触发时执行的命令:

breakpoint command add <cmd-options> <breakpt-id>

例如:breakpoint command add 2.1

<cmd-options>中 可以添加--command 用来执行命令,或者--script 执行 python 脚本。

通过help breakpoint command add查看更多帮助信息。

在断点被触发时,会自动运行设置的命令, 如果没有指定断点 id, 那么默认将 lldb 的命令加到最后一个断点。

例如:

Debug RUST

条件断点

在设置断点时,配置-c-condition ,配置条件断点。

例如在 例子代码中,r >3 时,自动打印堆栈信息:

Debug RUST

(lldb) breakpoint set -c "r > 3" -f search.rs -l 19 -C bt
(lldb) breakpoint list
Current breakpoints:
1: file = 'search.rs', line = 16, exact_match = 0, locations = 1, resolved = 1, hit count = 1
  1.1: where = algorithms`algorithms::search::binary_search::ha09558f76f86b6d5 + 56 at search.rs:16:7, address = 0x00000001000023b8, resolved, hit count = 1

2: file = 'search.rs', line = 19, exact_match = 0, locations = 1
    Breakpoint commands:
      bt

Condition: r > 3

  2.1: where = algorithms`algorithms::search::binary_search::ha09558f76f86b6d5 + 78 at search.rs:19:4, address = 0x00000001000023ce, unresolved, hit count = 0

//运行二进制文件
 (lldb) r
Process 59956 launched: '/Users/guangfuhe/Projects/rust/algorithms/target/debug/algorithms' (x86_64)
args: Args { inner: ["/Users/guangfuhe/Projects/rust/algorithms/target/debug/algorithms"] }
Process 59956 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    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
   13   pub fn binary_search<T>(nums: Vec<T>, target: T) -> Option<usize> where T: Ord{
   14       let mut l = 0;
   15       let mut r = nums.len();
-> 16       if r <= 0 {
   17           return None;
   18       }
   19       while r > 0 && l < r {
Target 0: (algorithms) stopped.
(lldb) c
Process 59956 resuming
//到了断点自动触发的命令
(lldb)  bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * 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
    frame #1: 0x0000000100002928 algorithms`algorithms::main::h451514f7ba18c029 at main.rs:9:8
    frame #2: 0x00000001000037d2 algorithms`std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::ha58704225b4f5f6e at rt.rs:67:33
    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]
    frame #4: 0x000000010000b0fc algorithms`std::panicking::try::do_call::hb5f9c52a170af65b at panicking.rs:292 [opt]
    frame #5: 0x000000010000c6ff algorithms`__rust_maybe_catch_panic at lib.rs:78:7 [opt]
    frame #6: 0x000000010000b9be algorithms`std::rt::lang_start_internal::h032b1be013493933 [inlined] std::panicking::try::h9686b6ceb1ebe04f at panicking.rs:270:12 [opt]
    frame #7: 0x000000010000b98b algorithms`std::rt::lang_start_internal::h032b1be013493933 [inlined] std::panic::catch_unwind::hf68219c633474123 at panic.rs:394 [opt]
    frame #8: 0x000000010000b98b algorithms`std::rt::lang_start_internal::h032b1be013493933 at rt.rs:51 [opt]
    frame #9: 0x00000001000037b2 algorithms`std::rt::lang_start::h9af99707a1bf11eb(main=&0x1000027f0, argc=1, argv=&0x7ffeefbff968) at rt.rs:67:4
    frame #10: 0x0000000100002ab2 algorithms`main + 34
    frame #11: 0x00007fff6668f7fd libdyld.dylib`start + 1
    frame #12: 0x00007fff6668f7fd libdyld.dylib`start + 1

Process 59956 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    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
   16       if r <= 0 {
   17           return None;
   18       }
-> 19       while r > 0 && l < r {
   20           let mid = (l + r) / 2;
   21           if target == nums[mid] {
   22               return Some(mid);
Target 0: (algorithms) stopped.

查看变量值

使用 **frame variable** 命令查看栈变量和对应的值

例如:

(lldb) breakpoint set -n binary_search
Breakpoint 1: where = algorithms`algorithms::search::binary_search::ha09558f76f86b6d5 + 14 at search.rs:14:16, address = 0x000000010000238e
(lldb) r
Process 47767 launched: '/Users/guangfuhe/Projects/rust/algorithms/target/debug/algorithms' (x86_64)
args: Args { inner: ["/Users/guangfuhe/Projects/rust/algorithms/target/debug/algorithms"] }
Process 47767 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    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
   11   ///     * Some: some index of target in array
   12   ///
   13   pub fn binary_search<T>(nums: Vec<T>, target: T) -> Option<usize> where T: Ord{
-> 14       let mut l = 0;
   15       let mut r = nums.len();
   16       if r <= 0 {
   17           return None;
Target 0: (algorithms) stopped.
(lldb) frame variable
(alloc::vec::Vec<int>) nums = vec![2, 3, 5, 8, 10, 18, 100]
(int) target = 10
(lldb)

**watch**** 命令观察变量 变化: **watch set var r**其中 r 是源代码中的变量)此处需要在调试时运行二进制文件,结合断点完成变量观察设置。**

例如:

Debug RUST

单步调试

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 对栈变量

例如:

(lldb) frame variable
(alloc::vec::Vec<int>) nums = vec![2, 3, 5, 8, 10, 18, 100]
(int) target = 10
(unsigned long) l = 0
(unsigned long) r = 7
(lldb) expr r>1
(bool) $0 = true
(lldb) expr r+1
(unsigned long) $1 = 8

Debug RUST

自定义 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 就能看到源代码。

Debug RUST

参考

  1. lldb 官方tutorial
  2. lldb infoq: https://www.infoq.cn/article/2017/10/LLDB-debug-Swift
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这