mini-backtrace
=======
[](https://crates.io/crates/mini-backtrace)
[](https://docs.rs/mini-backtrace)
This crate provides backtrace support for `no_std` and embedded programs.
This is done through by compiling LLVM's libunwind with certain flags to remove
all OS dependencies, including libc and any memory allocations.
## Usage
### Setup
There are two prerequisites for using this crate:
1. Unwind tables must be built into the binary, even with unwinding is set to
abort. This can be done with the `-C force-unwind-tables` flag.
2. Several `__eh_frame_*` symbols need to be defined by the linker so that the
the unwinding tables can be located by libunwind. This can be done by
including the [`eh_frame.ld`] linker script fragment.
Both of these can be done by setting `RUSTFLAGS`:
```sh
export RUSTFLAGS="-Cforce-unwind-tables -Clink-arg=-Wl,eh_frame.ld"
```
Note that these flags also apply to build-dependencies and proc
macros by default. This can be worked around by explicitly
specifying a target when invoking cargo:
```sh
# Applies RUSTFLAGS to everything
cargo build
# Doesn't apply RUSTFLAGS to build dependencies and proc macros
cargo build --target x86_64-unknown-linux-gnu
```
[`eh_frame.ld`]: https://github.com/Amanieu/mini-backtrace/blob/master/eh_frame.ld
### Capturing backtraces
Add the `mini-backtrace` crate as a dependency to your program:
```toml
[dependencies]
mini-backtrace = "0.1"
```
You can capture a backtrace by using the `Backtrace` type which returns a list
of frames as an `ArrayVec` of instruction pointer addresses.
```rust
use mini_backtrace::Backtrace;
// Capture up to 16 frames. This is returned using an ArrayVec that doesn't
// perform any dynamic memory allocation.
let bt = Backtrace::<16>::capture();
println!("Backtrace:");
for frame in bt.frames {
println!(" {:#x}", frame);
}
if bt.frames_omitted {
println!(" ... <frames omitted>");
}
```
This will output:
```text
Backtrace:
0x5587058c3eb1
0x5587058c3cdb
0x5587058c491e
0x5587058c38b1
0x5587058daf1a
0x5587058c3890
0x5587058c414c
```
### Position-independent code
If your code is executing at a different address than the one it is linked at
then you will need to fix up the frame pointer addresses to be relative to the
module base address. This can be done with the following function:
```rust
fn adjust_for_pic(ip: usize) -> usize {
extern "C" {
// Symbol defined by the linker
static __executable_start: [u8; 0];
}
let base = unsafe { __executable_start.as_ptr() as usize };
ip - base
}
```
After post-processing, the output should look like this:
```text
Backtrace:
0x8eb1
0x8cdb
0x999e
0x88b1
0x1ffba
0x8890
0x91cc
```
Have a look at `examples/backtrace.rs` for a complete example.
Note that `adjust_for_pic` should *only* be called for position-independent
binaries. Statically-linked binaries should emit unadjusted addresses so that
the backtraces can be correctly resolved.
### Resolving backtraces
The addresses generated by `Backtrace` can be converted to human-readable
function names, filenames and line numbers by using the `addr2line` tool from
LLVM or binutils with [rustfilt] to demangle Rust symbol names.
Simply run `addr2line -fipe /path/to/binary | rustfilt` in a terminal and then
paste the addresses from the backtrace:
```text
$ llvm-addr2line -fipe target/x86_64-unknown-linux-gnu/debug/examples/backtrace | rustfilt
0x8ed1
0x8ea6
0x8e96
0x8cdb
0x99be
0x88b1
0x1ffda
0x8890
0x91ec
backtrace::bar at /home/amanieu/code/mini-backtrace/examples/backtrace.rs:15
backtrace::foo at /home/amanieu/code/mini-backtrace/examples/backtrace.rs:10
backtrace::main at /home/amanieu/code/mini-backtrace/examples/backtrace.rs:5
core::ops::function::FnOnce::call_once at /home/amanieu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227
std::sys_common::backtrace::__rust_begin_short_backtrace at /home/amanieu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:128
std::rt::lang_start::{{closure}} at /home/amanieu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:49
std::panicking::try at /rustc/676ee14729462585b969bbc52f32c307403f4126/library/std/src/panicking.rs:344
(inlined by) std::panic::catch_unwind at /rustc/676ee14729462585b969bbc52f32c307403f4126/library/std/src/panic.rs:431
(inlined by) std::rt::lang_start_internal at /rustc/676ee14729462585b969bbc52f32c307403f4126/library/std/src/rt.rs:34
std::rt::lang_start at /home/amanieu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:48
main at ??:0
```
[rustfilt]: https://github.com/luser/rustfilt
### Backtraces from signal/interrupt handlers
The libunwind unwinder used by this crate is usually unable to unwind past
signal handler or interrupt handler frames. Instead, you can use
`Backtrace::capture_from_context` and pass in the register state at the point
where the exception occurred. In a signal handler this can be obtained through
the `uc_mcontext` field of `ucontext_t`.
This is currently only implemented for:
- AArch64
- RISC-V (RV32 & RV64)
## [Change log](CHANGELOG.md)
## License
Licensed under either of:
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.