Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a debugger like GDB or LLDB to debug a crate in Rust?

I am writing a new crate. I wrote some tests for it and run the tests using cargo test. After that, some test_xxx executables are generated in the target folder. I have enabled the debug option in Cargo.toml. By running gdb targets/test_xxx, I can list and debug the code in the test_xxx executable. However, I could not step into the functions in the crate. There is no debug information. How can I build/link the crate to include its debug information?

like image 225
Minjie Zha Avatar asked Nov 20 '14 05:11

Minjie Zha


People also ask

Can you use GDB with Rust?

GDB has support for several languages, such as C/C++, but also modern languages such as Go and Rust.

What is Rust LLDB?

LLDB provides a modern, high performance debugger framework and is the default debugger for macOS and iOS.

Is LLDB same as GDB?

In brief, LLDB and GDB are two debuggers. The main difference between LLDB and GDB is that in LLDB, the programmer can debug programs written in C, Objective C and C++ while, in GDB, the programmer can debug programs written in Ada, C, C++, Objective C, Pascal, FORTRAN and Go.

Is LLDB compatible with GDB?

The standard LLDB installation provides you with an extensive set of commands designed to be compatible with familiar GDB commands. In addition to using the standard configuration, you can easily customize LLDB to suit your needs. Both GDB and LLDB are of course excellent debuggers without doubt.


1 Answers

Your question is a bit fuzzy, so I'll describe what I did:

Create a new crate:

cargo new --lib so
cd so/

Add a small bit of code:

src/lib.rs

fn thing1(a: i32) -> i32 {
    a + 2
}

fn thing2(a: i32) -> i32 {
    a * a
}

pub fn do_a_thing(a: i32, b: i32) -> i32 {
    thing2(b) - thing1(a)
}

Created an external test; one that lives in tests. This matches your comment about test_XXX, as best I can guess:

tests/alpha.rs

#[test]
fn it_works() {
    assert_eq!(1, so::do_a_thing(1, 2))
}

Run the tests (output trimmed):

% cargo test

     Running target/debug/deps/alpha-b839f50a40d747a9

running 1 test
test it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Open it up in my debugger:

% lldb target/debug/alpha-b839f50a40d747a9

Set a regular expression breakpoint on a method in the crate and run it:

(lldb) br set -r 'do_a_thing'
Breakpoint 1: where = alpha-b839f50a40d747a9`so::do_a_thing::hd55d34fb5a87e372 + 14 at lib.rs:10:11, address = 0x0000000100001f9e
(lldb) r
Process 53895 launched: '/tmp/so/target/debug/alpha-b839f50a40d747a9' (x86_64)

running 1 test
Process 53895 stopped
* thread #2, name = 'it_works', stop reason = breakpoint 1.1
    frame #0: 0x0000000100001f9e alpha-b839f50a40d747a9`so::do_a_thing::hd55d34fb5a87e372(a=1, b=2) at lib.rs:10:11
   7    }
   8
   9    pub fn do_a_thing(a: i32, b: i32) -> i32 {
-> 10       thing2(b) - thing1(a)
   11   }
Target 0: (alpha-b839f50a40d747a9) stopped.
(lldb) p b
(int) $0 = 2
(lldb) p a
(int) $1 = 1
(lldb) br set -r 'thing2'
Breakpoint 2: where = alpha-b839f50a40d747a9`so::thing2::hf3cb71248518a556 + 11 at lib.rs:6:4, address = 0x0000000100001f5b
(lldb) c
Process 53895 resuming
Process 53895 stopped
* thread #2, name = 'it_works', stop reason = breakpoint 2.1
    frame #0: 0x0000000100001f5b alpha-b839f50a40d747a9`so::thing2::hf3cb71248518a556(a=2) at lib.rs:6:4
   3    }
   4
   5    fn thing2(a: i32) -> i32 {
-> 6        a * a
   7    }
   8
   9    pub fn do_a_thing(a: i32, b: i32) -> i32 {
Target 0: (alpha-b839f50a40d747a9) stopped.
(lldb) p a
(int) $2 = 2

This indicates that I was able to set breakpoints and debug in my crate.

For dependent crates

I added this to my Cargo.toml:

[dependencies]
time = "0.1.0"

Along with this code:

src/lib.rs

pub fn do_another_thing() -> bool {
    time::precise_time_ns() % 2 == 0
}

And this test (still in the external test file):

tests/alpha.rs

#[test]
fn it_works2() {
    assert_eq!(true, so::do_another_thing())
}

Built and ran the tests as before, then opened it in the debugger as before:

(lldb) br set -r 'precise_time'
Breakpoint 1: where = alpha-25aace4e290c57ee`time::precise_time_ns::h21114d10b3e2c8e8 + 8 at lib.rs:161:4, address = 0x0000000100002278
(lldb) r
Process 54043 launched: '/tmp/so/target/debug/alpha-25aace4e290c57ee' (x86_64)

running 2 tests
test it_works ... Process 54043 stopped
* thread #2, name = 'it_works2', stop reason = breakpoint 1.1
    frame #0: 0x0000000100002278 alpha-25aace4e290c57ee`time::precise_time_ns::h21114d10b3e2c8e8 at lib.rs:161:4
   158   */
   159  #[inline]
   160  pub fn precise_time_ns() -> u64 {
-> 161      sys::get_precise_ns()
   162  }
   163
   164

Why a regex breakpoint?

Function names are mangled and namespaced. Our functions are actually called so::do_a_thing::hd55d34fb5a87e372 or time::precise_time_ns::h21114d10b3e2c8e8. Using a regex breakpoint selects those easily without thinking about the exact name.

You can also use LLDB's autocompletion

(lldb) b so::<TAB>
Available completions:
    so::do_a_thing::hfb57d28ba1650245
    so::do_another_thing::hace29914503d7a2f
    so::thing1::ha6f7818d54de28d4
    so::thing2::h2518577906df58fd

(lldb) b time::<TAB>
Available completions:
    time::precise_time_ns::h21114d10b3e2c8e8
    time::sys::inner::mac::get_precise_ns::h64c88ed88da4ac18
    time::sys::inner::mac::info::_$u7b$$u7b$closure$u7d$$u7d$::ha03be28d018f231b
    time::sys::inner::mac::info::h364c1a0ef2ef1f0c

Using GDB instead of LLDB

You can also use GDB, but it looks like it may be a bit harder. After setting a breakpoint in the primary crate's code, I stepped in and saw the function name seem to be the mangled version. You can use that as a breakpoint though.

Note my regex breakpoint doesn't work as intended, and also note the line number of the real breakpoint:

(gdb) rbreak precise_time_ns
u64 _ZN4time15precise_time_nsE()();
static u64 _ZN4time15precise_time_ns18os_precise_time_nsE()();
Breakpoint 1 ('_ZN4time15precise_time_ns18os_precise_time_ns13closure.24322E') pending.
<function, no debug info> time::precise_time_ns::os_precise_time_ns::closure.24322;

(gdb) b _ZN4time15precise_time_nsE
Breakpoint 2 at 0x1000052a0: file lib.rs, line 172.

(gdb) r
Starting program: /private/tmp/so/target/alpha-e0c6f11f426d14d2

running 2 tests
test it_works ... ok
[Switching to process 49131 thread 0xd03]

Breakpoint 1, _ZN4time15precise_time_nsE () at lib.rs:172
172 pub fn precise_time_ns() -> u64 {
(gdb) list
167
168 /**
169  * Returns the current value of a high-resolution performance counter
170  * in nanoseconds since an unspecified epoch.
171  */
172 pub fn precise_time_ns() -> u64 {
173     return os_precise_time_ns();
174
175     #[cfg(windows)]
176     fn os_precise_time_ns() -> u64 {

Debugger wrappers

There are two wrapper programs shipped with Rust: rust-lldb and rust-gdb. These are intended to increase the usefulness of each debugger. It's worth giving them a shot.

like image 155
Shepmaster Avatar answered Oct 24 '22 16:10

Shepmaster