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}