Avoid allocation in the signal handler to fix a deadlock

Fixes https://github.com/servo/servo/issues/24881
CC https://github.com/rust-lang/backtrace-rs/pull/265
This commit is contained in:
Simon Sapin 2019-11-27 20:32:52 +01:00
parent 83bb96740d
commit 413f499da8
2 changed files with 107 additions and 8 deletions

98
ports/glutin/backtrace.rs Normal file
View file

@ -0,0 +1,98 @@
/* 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 doesnt 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() {
println!("{:?}", 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 {
let mut print_fn_frame = 0;
let mut frame_count = 0;
backtrace::trace(|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(|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(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(())
}

View file

@ -11,6 +11,7 @@ extern crate log;
extern crate sig;
mod app;
mod backtrace;
mod browser;
mod context;
mod embedder;
@ -23,7 +24,6 @@ mod skia_symbols;
mod window_trait;
use app::App;
use backtrace::Backtrace;
use getopts::Options;
use servo::config::opts::{self, ArgumentParsingResult};
use servo::config::servo_version;
@ -31,6 +31,7 @@ use servo::servo_config::pref;
use std::env;
use std::panic;
use std::process;
use std::sync::atomic;
use std::thread;
pub mod platform {
@ -49,17 +50,17 @@ fn install_crash_handler() {}
#[cfg(any(target_os = "macos", target_os = "linux"))]
fn install_crash_handler() {
use backtrace::Backtrace;
use libc::_exit;
use sig::ffi::Sig;
use std::thread;
extern "C" fn handler(sig: i32) {
let name = thread::current()
.name()
.map(|n| format!(" for thread \"{}\"", n))
.unwrap_or("".to_owned());
println!("Stack trace{}\n{:?}", name, Backtrace::new());
print!("Stack trace");
if let Some(name) = thread::current().name() {
print!(" for thread \"{}\"", name);
}
println!();
backtrace::print();
unsafe {
_exit(sig);
}
@ -139,7 +140,7 @@ pub fn main() {
println!("{} (thread {})", msg, name);
}
if env::var("RUST_BACKTRACE").is_ok() {
println!("{:?}", Backtrace::new());
backtrace::print();
}
error!("{}", msg);