1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
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() {
    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 {
        // 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(())
}