servoshell/
backtrace.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Similar to `println!("{:?}", Backtrace::new())`, but doesn’t allocate.
6//!
7//! Seems to fix some deadlocks: <https://github.com/servo/servo/issues/24881>
8//!
9//! FIXME: if/when a future version of the `backtrace` crate has
10//! <https://github.com/rust-lang/backtrace-rs/pull/265>, use that instead.
11
12use std::fmt::{self, Write};
13
14use backtrace::{BytesOrWideString, PrintFmt};
15
16#[inline(never)]
17pub(crate) fn print(w: &mut dyn std::io::Write) -> Result<(), std::io::Error> {
18    write!(
19        w,
20        "{:?}",
21        Print {
22            print_fn_address: print as usize,
23        }
24    )
25}
26
27#[cfg(target_env = "ohos")]
28pub(crate) fn print_ohos() {
29    // Print to `hilog`
30    log::error!(
31        "{:?}",
32        Print {
33            print_fn_address: print as usize,
34        }
35    )
36}
37
38struct Print {
39    print_fn_address: usize,
40}
41
42impl fmt::Debug for Print {
43    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
44        // Safety: we’re in a signal handler that is about to call `libc::_exit`.
45        // Potential data races from using `*_unsynchronized` functions are perhaps
46        // less bad than potential deadlocks?
47        unsafe {
48            let mut print_fn_frame = 0;
49            let mut frame_count = 0;
50            backtrace::trace_unsynchronized(|frame| {
51                let found = frame.symbol_address() as usize == self.print_fn_address;
52                if found {
53                    print_fn_frame = frame_count;
54                }
55                frame_count += 1;
56                !found
57            });
58
59            let mode = PrintFmt::Short;
60            let mut p = print_path;
61            let mut f = backtrace::BacktraceFmt::new(fmt, mode, &mut p);
62            f.add_context()?;
63            let mut result = Ok(());
64            let mut frame_count = 0;
65            backtrace::trace_unsynchronized(|frame| {
66                let skip = frame_count < print_fn_frame;
67                frame_count += 1;
68                if skip {
69                    return true;
70                }
71
72                let mut frame_fmt = f.frame();
73                let mut any_symbol = false;
74                backtrace::resolve_frame_unsynchronized(frame, |symbol| {
75                    any_symbol = true;
76                    if let Err(e) = frame_fmt.symbol(frame, symbol) {
77                        result = Err(e)
78                    }
79                });
80                if !any_symbol {
81                    if let Err(e) = frame_fmt.print_raw(frame.ip(), None, None, None) {
82                        result = Err(e)
83                    }
84                }
85                result.is_ok()
86            });
87            result?;
88            f.finish()
89        }
90    }
91}
92
93fn print_path(fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>) -> fmt::Result {
94    match path {
95        BytesOrWideString::Bytes(mut bytes) => loop {
96            match std::str::from_utf8(bytes) {
97                Ok(s) => {
98                    fmt.write_str(s)?;
99                    break;
100                },
101                Err(err) => {
102                    fmt.write_char(std::char::REPLACEMENT_CHARACTER)?;
103                    match err.error_len() {
104                        Some(len) => bytes = &bytes[err.valid_up_to() + len..],
105                        None => break,
106                    }
107                },
108            }
109        },
110        BytesOrWideString::Wide(wide) => {
111            for c in std::char::decode_utf16(wide.iter().cloned()) {
112                fmt.write_char(c.unwrap_or(std::char::REPLACEMENT_CHARACTER))?
113            }
114        },
115    }
116    Ok(())
117}