backtrace_on_stack_overflow/
lib.rs

1//! By default, Rust aborts on stackoverflow without printing a backtrace:
2//!
3//! ```console
4//! λ bat src/main.rs
5//! fn main() {
6//!     f(92)
7//! }
8//!
9//! fn f(x: u64) {
10//!     f(x)
11//! }
12//! λ cargo run
13//!     Finished dev [unoptimized + debuginfo] target(s) in 0.00s
14//!      Running `target/debug/so`
15//!
16//! thread 'main' has overflowed its stack
17//! fatal runtime error: stack overflow
18//! fish: Job 1, 'cargo run' terminated by signal SIGABRT (Abort)
19//! ```
20//!
21//! This crate fixes this:
22//!
23//! ```console
24//! λ bat src/main.rs
25//! fn main() {
26//!     unsafe { backtrace_on_stack_overflow::enable() };
27//!     f(92)
28//! }
29//!
30//! fn f(x: u64) {
31//!     f(x)
32//! }
33//! λ cargo run
34//!     Finished dev [unoptimized + debuginfo] target(s) in 0.01s
35//!      Running `target/debug/so`
36//! Stack Overflow:
37//!    0: backtrace_on_stack_overflow::handle_sigsegv
38//!              at /home/matklad/p/backtrace-on-stack-overflow/src/lib.rs:33:40
39//!    1: <unknown>
40//!    2: so::f
41//!              at src/main.rs:6
42//!    3: so::f
43//!              at src/main.rs:7:5
44//!    4: so::f
45//!              at src/main.rs:7:5
46//!    5: so::f
47//!              at src/main.rs:7:5
48//!    6: so::f
49//!              at src/main.rs:7:5
50//!    7: so::f
51//!              at src/main.rs:7:5
52//!    8: so::f
53//!              at src/main.rs:7:5
54//!    9: so::f
55//!              at src/main.rs:7:5
56//!   10: so::f
57//!              at src/main.rs:7:5
58//! ```
59//!
60//! This crate works for debugging, but is unsuited for being enabled in production.
61use nix::sys::signal;
62
63/// Best effort printing of backtrace on stack overflow.
64///
65/// Works on my machine, may summon laundry-eating nasal daemons.
66///
67/// PRs to make this more robust are welcome
68pub unsafe fn enable() {
69    static ONCE: std::sync::Once = std::sync::Once::new();
70
71    ONCE.call_once(|| {
72        // Use u128 for alignment.
73        let buf = Vec::leak(vec![0u128; 4096]);
74        let stack = libc::stack_t {
75            ss_sp: buf.as_ptr() as *mut libc::c_void,
76            ss_flags: 0,
77            ss_size: buf.len() * std::mem::size_of::<u128>(),
78        };
79        let mut old = libc::stack_t { ss_sp: std::ptr::null_mut(), ss_flags: 0, ss_size: 0 };
80        let ret = libc::sigaltstack(&stack, &mut old);
81        assert_eq!(ret, 0, "sigaltstack failed");
82
83        let sig_action = signal::SigAction::new(
84            signal::SigHandler::Handler(handle_sigsegv),
85            signal::SaFlags::SA_NODEFER | signal::SaFlags::SA_ONSTACK,
86            signal::SigSet::empty(),
87        );
88        signal::sigaction(signal::SIGSEGV, &sig_action).unwrap();
89        signal::sigaction(signal::SIGABRT, &sig_action).unwrap();
90    })
91}
92
93extern "C" fn handle_sigsegv(_: i32) {
94    eprintln!("Stack Overflow:\n{:?}", backtrace::Backtrace::new());
95    std::process::abort();
96}