I have a failing Cargo test:
$ cargo test [snip] Running target/gunzip-c62d8688496249d8 running 2 tests test test_extract_failure ... FAILED test test_extract_success ... ok failures: ---- test_extract_failure stdout ---- task 'test_extract_failure' panicked at 'assertion failed: result.is_err()', /home/dhardy/other/flate2-rs/tests/gunzip.rs:19 failures: test_extract_failure test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured task '<main>' panicked at 'Some tests failed', /home/rustbuild/src/rust-buildbot/slave/nightly-linux/build/src/libtest/lib.rs:250
How do I launch the failing test in a debugger like GDB?
This should be a general question, but for those wanting to retrace my steps, install a recent nightly Rust build and:
git clone https://github.com/dhardy/flate2-rs.git git checkout 24979640a880 cd flate2-rs cargo test
Use breakpoints in VSCode Add "Rust" or "rust-analyzer" extension in your VSCode. Add "CodeLLDB" extension in your VSCode. Click on the debug icon in your VSCode, then click on "add launch.
Documentation tests It extracts code samples from documentation comments of the library target, and then executes them. Different from normal test targets, each code block compiles to a doctest executable on the fly with rustc . These executables run in parallel in separate processes.
rust-gdb is a prebuilt binary that comes with the Rust installation (using Rustup, for example) and is installed automatically.
With Rust, you can use print statements [ println() ], logging at the debug level, or the standard built-in dbg tool to exterminate bugs in your code.
You can get a test binary to filter the tests it runs by passing additional arguments to it; Cargo exposes this directly, too. Thus, cargo test test_extract_failure
will just run that specific case. (This is convenient if you have other tests that panic and are expected to fail, so that they won’t call the rust_panic
function I am about to mention, leaving only the offending call there.)
In order to use gdb, you’ll need to run the test binary directly (if you use Cargo it runs in a subprocess and thus gdb won’t catch panics inside it). Cargo helpfully tells you the file name, target/gunzip-c62d8688496249d8
. You can run this directly with --test
to make it a test run:
$ target/gunzip-c62d8688496249d8 --test test_extract_failure running 1 test test test_extract_failure ... FAILED failures: ---- test_extract_failure stdout ---- task 'test_extract_failure' panicked at 'assertion failed: result.is_err()', /home/dhardy/other/flate2-rs/tests/gunzip.rs:19 failures: test_extract_failure test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured task '<main>' panicked at 'Some tests failed', /home/rustbuild/src/rust-buildbot/slave/nightly-linux/build/src/libtest/lib.rs:250
Now to hook it up with gdb. There is a convenient function for which you can insert a breakpoint, rust_panic
. Once in gdb, break rust_panic
means it will pause whenever something triggers a panic, before actually doing the unwinding.
Here’s what a session might end up looking like:
$ gdb target/demo-92d91e26f6ebc557 … Reading symbols from target/demo-92d91e26f6ebc557...done. (gdb) break rust_panic Breakpoint 1 at 0xccb60 (gdb) run --test test_extract_failure Starting program: /tmp/demo/target/demo-92d91e26f6ebc557 --test test_extract_failure warning: Could not load shared library symbols for linux-vdso.so.1. Do you need "set solib-search-path" or "set sysroot"? [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". running 1 test [New Thread 0x7ffff6ef4700 (LWP 14254)] [New Thread 0x7ffff5fff700 (LWP 14255)] [Switching to Thread 0x7ffff5fff700 (LWP 14255)] Breakpoint 1, 0x0000555555620b60 in rust_panic () (gdb) bt #0 0x0000555555620b60 in rust_panic () #1 0x0000555555621274 in unwind::begin_unwind_inner::hb821324209c8ed246Qc () #2 0x000055555556bb6d in unwind::begin_unwind::h7834652822578025936 () #3 0x000055555556b9fd in demo::do_something () at <std macros>:8 #4 0x000055555556b98e in demo::test_extract_failure () at src/lib.rs:3 #5 0x000055555559aa4b in task::TaskBuilder::try_future::closure.8077 () #6 0x000055555560fd03 in task::TaskBuilder::spawn_internal::closure.30919 () #7 0x000055555561f672 in task::Task::spawn::closure.5759 () #8 0x0000555555621cac in rust_try_inner () #9 0x0000555555621c96 in rust_try () #10 0x000055555561f713 in unwind::try::ha8078a6ae9b50ccepFc () #11 0x000055555561f51c in task::Task::run::hdb5fabf381084abafOb () #12 0x000055555561f168 in task::Task::spawn::closure.5735 () #13 0x0000555555620595 in thread::thread_start::h4d73784c295273b3i6b () #14 0x00007ffff79c2314 in start_thread () from /usr/lib/libpthread.so.0 #15 0x00007ffff72e25bd in clone () from /usr/lib/libc.so.6 (gdb)
In that particular case, #0–#2 and #5–#15 are noise, #3 and #4 are the signal we want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With