Skip to content

Commit fa816d2

Browse files
authored
Merge pull request #22 from posit-dev/feature/sigsegv-backtrace
Log backtrace on sigsegv and sigbus signals
2 parents bbcfc57 + 65b72a9 commit fa816d2

File tree

5 files changed

+68
-7
lines changed

5 files changed

+68
-7
lines changed

Cargo.lock

+9-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ark/src/main.rs

+5
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ fn main() {
258258
// Initialize the logger.
259259
logger::initialize(log_file.as_deref());
260260

261+
// Register segfault handler to get a backtrace. Should be after
262+
// initialising `log!`. Note that R will not override this handler
263+
// because we set `R_SignalHandlers` to 0 before startup.
264+
stdext::traps::register_trap_handlers();
265+
261266
// Initialize harp.
262267
harp::initialize();
263268

crates/stdext/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@ description = """
77
Useful extensions to the Rust standard library.
88
"""
99

10+
[dependencies]
11+
backtrace = "0.3.67"
12+
libc = "0.2.146"
13+
log = "0.4.18"
14+
1015
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

crates/stdext/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub mod local;
1414
pub mod ok;
1515
pub mod push;
1616
pub mod spawn;
17+
pub mod traps;
1718
pub mod unwrap;
1819

1920
pub use crate::join::Joined;

crates/stdext/src/traps.rs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// traps.rs
3+
//
4+
// Copyright (C) 2023 Posit Software, PBC. All rights reserved.
5+
//
6+
//
7+
8+
// Call this after initialising the `log` package. Instruments SIGBUS,
9+
// SIGSEGV, and SIGILL to generate a backtrace with `info` verbosity
10+
// (lowest level so it's always reported).
11+
//
12+
// This uses `signal()` instead of `sigaction()` for Windows support
13+
// (SIGSEGV is one of the rare supported signals)
14+
//
15+
// Note that Rust also has a SIGSEGV handler to catch stack overflows. In
16+
// this case it displays an informative message and aborts the program (no
17+
// segfaults in Rust!). Ideally we'd save the Rust handler and notify
18+
// it. However the only safe way to notify an old handler on Unixes is to
19+
// use `sigaction()` so that we get the information needed to determine the
20+
// type of handler (old or new school). So we'd need to make a different
21+
// implementation for Windows (which only supports old style) and for Unix,
22+
// and this doesn't seem worth it.
23+
pub fn register_trap_handlers() {
24+
unsafe {
25+
libc::signal(libc::SIGBUS, backtrace_handler as libc::sighandler_t);
26+
libc::signal(libc::SIGSEGV, backtrace_handler as libc::sighandler_t);
27+
libc::signal(libc::SIGILL, backtrace_handler as libc::sighandler_t);
28+
}
29+
}
30+
31+
extern "C" fn backtrace_handler(signum: libc::c_int) {
32+
// Prevent infloop into the handler
33+
unsafe {
34+
libc::signal(signum, libc::SIG_DFL);
35+
}
36+
37+
let mut header = format!("\n>>> Backtrace for signal {}", signum);
38+
39+
if let Some(name) = std::thread::current().name() {
40+
header = format!("{}\n>>> In thread {}", header, name);
41+
}
42+
43+
// Unlike asynchronous signals, SIGSEGV and SIGBUS are synchronous and
44+
// always delivered to the thread that caused it, so we can just
45+
// capture the current thread's backtrace
46+
let bt = backtrace::Backtrace::new();
47+
log::info!("{}\n{:?}", header, bt);
48+
}

0 commit comments

Comments
 (0)