Skip to content

Commit

Permalink
Add signal handler to catch segfault in build script.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdm committed Jul 6, 2020
1 parent 1175aca commit 34d968a
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webrender/Cargo.toml
Expand Up @@ -59,6 +59,11 @@ tracy-rs = { version = "0.1" }
mozangle = "0.3.1"
rand = "0.4"

[target.'cfg(any(target_os = "macos", target_os = "linux"))'.build-dependencies]
backtrace = "0.3"
sig = "1.0"
libc = "0.2"

[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
freetype = { version = "0.4", default-features = false }
libc = "0.2"
Expand Down
103 changes: 103 additions & 0 deletions webrender/backtrace.rs
@@ -0,0 +1,103 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Similar to `println!("{:?}", Backtrace::new())`, but doesn’t allocate.
//!
//! Seems to fix some deadlocks: https://github.com/servo/servo/issues/24881
//!
//! FIXME: if/when a future version of the `backtrace` crate has
//! https://github.com/rust-lang/backtrace-rs/pull/265, use that instead.

use std::fmt::{self, Write};
use backtrace::{BytesOrWideString, PrintFmt};

#[inline(never)]
pub(crate) fn print(w: &mut dyn std::io::Write) -> Result<(), std::io::Error> {
write!(w, "{:?}", Print {
print_fn_address: print as usize,
})
}

struct Print {
print_fn_address: usize,
}

impl fmt::Debug for Print {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
// Safety: we’re in a signal handler that is about to call `libc::_exit`.
// Potential data races from using `*_unsynchronized` functions are perhaps
// less bad than potential deadlocks?
unsafe {
let mut print_fn_frame = 0;
let mut frame_count = 0;
backtrace::trace_unsynchronized(|frame| {
let found = frame.symbol_address() as usize == self.print_fn_address;
if found {
print_fn_frame = frame_count;
}
frame_count += 1;
!found
});

let mode = PrintFmt::Short;
let mut p = print_path;
let mut f = backtrace::BacktraceFmt::new(fmt, mode, &mut p);
f.add_context()?;
let mut result = Ok(());
let mut frame_count = 0;
backtrace::trace_unsynchronized(|frame| {
let skip = frame_count < print_fn_frame;
frame_count += 1;
if skip {
return true
}

let mut frame_fmt = f.frame();
let mut any_symbol = false;
backtrace::resolve_frame_unsynchronized(frame, |symbol| {
any_symbol = true;
if let Err(e) = frame_fmt.symbol(frame, symbol) {
result = Err(e)
}
});
if !any_symbol {
if let Err(e) = frame_fmt.print_raw(frame.ip(), None, None, None) {
result = Err(e)
}
}
result.is_ok()
});
result?;
f.finish()
}
}
}

fn print_path(fmt: &mut fmt::Formatter, path: BytesOrWideString) -> fmt::Result {
match path {
BytesOrWideString::Bytes(mut bytes) => {
loop {
match std::str::from_utf8(bytes) {
Ok(s) => {
fmt.write_str(s)?;
break;
}
Err(err) => {
fmt.write_char(std::char::REPLACEMENT_CHARACTER)?;
match err.error_len() {
Some(len) => bytes = &bytes[err.valid_up_to() + len..],
None => break,
}
}
}
}
}
BytesOrWideString::Wide(wide) => {
for c in std::char::decode_utf16(wide.iter().cloned()) {
fmt.write_char(c.unwrap_or(std::char::REPLACEMENT_CHARACTER))?
}
}
}
Ok(())
}
30 changes: 30 additions & 0 deletions webrender/build.rs
Expand Up @@ -244,7 +244,37 @@ fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: &
Ok(())
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
mod backtrace;

#[cfg(any(target_os = "macos", target_os = "linux"))]
extern "C" fn handler(sig: i32) {
use std::sync::atomic;
static BEEN_HERE_BEFORE: atomic::AtomicBool = atomic::AtomicBool::new(false);
if !BEEN_HERE_BEFORE.swap(true, atomic::Ordering::SeqCst) {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
let _ = write!(&mut stdout, "Stack trace");
if let Some(name) = std::thread::current().name() {
let _ = write!(&mut stdout, " for thread \"{}\"", name);
}
let _ = write!(&mut stdout, "\n");
let _ = backtrace::print(&mut stdout);
}
unsafe {
libc::_exit(sig);
}
}

fn main() -> Result<(), std::io::Error> {
#[cfg(any(target_os = "macos", target_os = "linux"))]
{
sig::signal!(sig::ffi::Sig::SEGV, handler); // handle segfaults
sig::signal!(sig::ffi::Sig::ILL, handler); // handle stack overflow and unsupported CPUs
sig::signal!(sig::ffi::Sig::IOT, handler); // handle double panics
sig::signal!(sig::ffi::Sig::BUS, handler); // handle invalid memory access
}

let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());

let shaders_file_path = Path::new(&out_dir).join("shaders.rs");
Expand Down

0 comments on commit 34d968a

Please sign in to comment.