backtrace/
print.rs

1#[cfg(feature = "std")]
2use super::{BacktraceFrame, BacktraceSymbol};
3use super::{BytesOrWideString, Frame, SymbolName};
4use core::ffi::c_void;
5use core::fmt;
6
7const HEX_WIDTH: usize = 2 + 2 * core::mem::size_of::<usize>();
8
9#[cfg(target_os = "fuchsia")]
10mod fuchsia;
11
12/// A formatter for backtraces.
13///
14/// This type can be used to print a backtrace regardless of where the backtrace
15/// itself comes from. If you have a `Backtrace` type then its `Debug`
16/// implementation already uses this printing format.
17pub struct BacktraceFmt<'a, 'b> {
18    fmt: &'a mut fmt::Formatter<'b>,
19    frame_index: usize,
20    format: PrintFmt,
21    print_path:
22        &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result + 'b),
23}
24
25/// The styles of printing that we can print
26#[derive(Copy, Clone, Eq, PartialEq)]
27pub enum PrintFmt {
28    /// Prints a terser backtrace which ideally only contains relevant information
29    Short,
30    /// Prints a backtrace that contains all possible information
31    Full,
32    #[doc(hidden)]
33    __Nonexhaustive,
34}
35
36impl<'a, 'b> BacktraceFmt<'a, 'b> {
37    /// Create a new `BacktraceFmt` which will write output to the provided
38    /// `fmt`.
39    ///
40    /// The `format` argument will control the style in which the backtrace is
41    /// printed, and the `print_path` argument will be used to print the
42    /// `BytesOrWideString` instances of filenames. This type itself doesn't do
43    /// any printing of filenames, but this callback is required to do so.
44    pub fn new(
45        fmt: &'a mut fmt::Formatter<'b>,
46        format: PrintFmt,
47        print_path: &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result
48                     + 'b),
49    ) -> Self {
50        BacktraceFmt {
51            fmt,
52            frame_index: 0,
53            format,
54            print_path,
55        }
56    }
57
58    /// Prints a preamble for the backtrace about to be printed.
59    ///
60    /// This is required on some platforms for backtraces to be fully
61    /// symbolicated later, and otherwise this should just be the first method
62    /// you call after creating a `BacktraceFmt`.
63    pub fn add_context(&mut self) -> fmt::Result {
64        #[cfg(target_os = "fuchsia")]
65        fuchsia::print_dso_context(self.fmt)?;
66        Ok(())
67    }
68
69    /// Adds a frame to the backtrace output.
70    ///
71    /// This commit returns an RAII instance of a `BacktraceFrameFmt` which can be used
72    /// to actually print a frame, and on destruction it will increment the
73    /// frame counter.
74    pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> {
75        BacktraceFrameFmt {
76            fmt: self,
77            symbol_index: 0,
78        }
79    }
80
81    /// Completes the backtrace output.
82    ///
83    /// This is currently a no-op but is added for future compatibility with
84    /// backtrace formats.
85    pub fn finish(&mut self) -> fmt::Result {
86        // Currently a no-op-- including this hook to allow for future additions.
87        Ok(())
88    }
89}
90
91/// A formatter for just one frame of a backtrace.
92///
93/// This type is created by the `BacktraceFmt::frame` function.
94pub struct BacktraceFrameFmt<'fmt, 'a, 'b> {
95    fmt: &'fmt mut BacktraceFmt<'a, 'b>,
96    symbol_index: usize,
97}
98
99impl BacktraceFrameFmt<'_, '_, '_> {
100    /// Prints a `BacktraceFrame` with this frame formatter.
101    ///
102    /// This will recursively print all `BacktraceSymbol` instances within the
103    /// `BacktraceFrame`.
104    ///
105    /// # Required features
106    ///
107    /// This function requires the `std` feature of the `backtrace` crate to be
108    /// enabled, and the `std` feature is enabled by default.
109    #[cfg(feature = "std")]
110    pub fn backtrace_frame(&mut self, frame: &BacktraceFrame) -> fmt::Result {
111        let symbols = frame.symbols();
112        for symbol in symbols {
113            self.backtrace_symbol(frame, symbol)?;
114        }
115        if symbols.is_empty() {
116            self.print_raw(frame.ip(), None, None, None)?;
117        }
118        Ok(())
119    }
120
121    /// Prints a `BacktraceSymbol` within a `BacktraceFrame`.
122    ///
123    /// # Required features
124    ///
125    /// This function requires the `std` feature of the `backtrace` crate to be
126    /// enabled, and the `std` feature is enabled by default.
127    #[cfg(feature = "std")]
128    pub fn backtrace_symbol(
129        &mut self,
130        frame: &BacktraceFrame,
131        symbol: &BacktraceSymbol,
132    ) -> fmt::Result {
133        self.print_raw_with_column(
134            frame.ip(),
135            symbol.name(),
136            // TODO: this isn't great that we don't end up printing anything
137            // with non-utf8 filenames. Thankfully almost everything is utf8 so
138            // this shouldn't be too too bad.
139            symbol
140                .filename()
141                .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))),
142            symbol.lineno(),
143            symbol.colno(),
144        )?;
145        Ok(())
146    }
147
148    /// Prints a raw traced `Frame` and `Symbol`, typically from within the raw
149    /// callbacks of this crate.
150    pub fn symbol(&mut self, frame: &Frame, symbol: &super::Symbol) -> fmt::Result {
151        self.print_raw_with_column(
152            frame.ip(),
153            symbol.name(),
154            symbol.filename_raw(),
155            symbol.lineno(),
156            symbol.colno(),
157        )?;
158        Ok(())
159    }
160
161    /// Adds a raw frame to the backtrace output.
162    ///
163    /// This method, unlike the previous, takes the raw arguments in case
164    /// they're being source from different locations. Note that this may be
165    /// called multiple times for one frame.
166    pub fn print_raw(
167        &mut self,
168        frame_ip: *mut c_void,
169        symbol_name: Option<SymbolName<'_>>,
170        filename: Option<BytesOrWideString<'_>>,
171        lineno: Option<u32>,
172    ) -> fmt::Result {
173        self.print_raw_with_column(frame_ip, symbol_name, filename, lineno, None)
174    }
175
176    /// Adds a raw frame to the backtrace output, including column information.
177    ///
178    /// This method, like the previous, takes the raw arguments in case
179    /// they're being source from different locations. Note that this may be
180    /// called multiple times for one frame.
181    pub fn print_raw_with_column(
182        &mut self,
183        frame_ip: *mut c_void,
184        symbol_name: Option<SymbolName<'_>>,
185        filename: Option<BytesOrWideString<'_>>,
186        lineno: Option<u32>,
187        colno: Option<u32>,
188    ) -> fmt::Result {
189        // Fuchsia is unable to symbolize within a process so it has a special
190        // format which can be used to symbolize later. Print that instead of
191        // printing addresses in our own format here.
192        if cfg!(target_os = "fuchsia") {
193            self.print_raw_fuchsia(frame_ip)?;
194        } else {
195            self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?;
196        }
197        self.symbol_index += 1;
198        Ok(())
199    }
200
201    #[allow(unused_mut)]
202    fn print_raw_generic(
203        &mut self,
204        mut frame_ip: *mut c_void,
205        symbol_name: Option<SymbolName<'_>>,
206        filename: Option<BytesOrWideString<'_>>,
207        lineno: Option<u32>,
208        colno: Option<u32>,
209    ) -> fmt::Result {
210        // No need to print "null" frames, it basically just means that the
211        // system backtrace was a bit eager to trace back super far.
212        if let PrintFmt::Short = self.fmt.format {
213            if frame_ip.is_null() {
214                return Ok(());
215            }
216        }
217
218        // To reduce TCB size in Sgx enclave, we do not want to implement symbol
219        // resolution functionality.  Rather, we can print the offset of the
220        // address here, which could be later mapped to correct function.
221        #[cfg(all(feature = "std", target_env = "sgx", target_vendor = "fortanix"))]
222        {
223            let image_base = std::os::fortanix_sgx::mem::image_base();
224            frame_ip = usize::wrapping_sub(frame_ip as usize, image_base as _) as _;
225        }
226
227        // Print the index of the frame as well as the optional instruction
228        // pointer of the frame. If we're beyond the first symbol of this frame
229        // though we just print appropriate whitespace.
230        if self.symbol_index == 0 {
231            write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?;
232            if let PrintFmt::Full = self.fmt.format {
233                write!(self.fmt.fmt, "{:1$?} - ", frame_ip, HEX_WIDTH)?;
234            }
235        } else {
236            write!(self.fmt.fmt, "      ")?;
237            if let PrintFmt::Full = self.fmt.format {
238                write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?;
239            }
240        }
241
242        // Next up write out the symbol name, using the alternate formatting for
243        // more information if we're a full backtrace. Here we also handle
244        // symbols which don't have a name,
245        match (symbol_name, &self.fmt.format) {
246            (Some(name), PrintFmt::Short) => write!(self.fmt.fmt, "{:#}", name)?,
247            (Some(name), PrintFmt::Full) => write!(self.fmt.fmt, "{}", name)?,
248            (None, _) | (_, PrintFmt::__Nonexhaustive) => write!(self.fmt.fmt, "<unknown>")?,
249        }
250        self.fmt.fmt.write_str("\n")?;
251
252        // And last up, print out the filename/line number if they're available.
253        if let (Some(file), Some(line)) = (filename, lineno) {
254            self.print_fileline(file, line, colno)?;
255        }
256
257        Ok(())
258    }
259
260    fn print_fileline(
261        &mut self,
262        file: BytesOrWideString<'_>,
263        line: u32,
264        colno: Option<u32>,
265    ) -> fmt::Result {
266        // Filename/line are printed on lines under the symbol name, so print
267        // some appropriate whitespace to sort of right-align ourselves.
268        if let PrintFmt::Full = self.fmt.format {
269            write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?;
270        }
271        write!(self.fmt.fmt, "             at ")?;
272
273        // Delegate to our internal callback to print the filename and then
274        // print out the line number.
275        (self.fmt.print_path)(self.fmt.fmt, file)?;
276        write!(self.fmt.fmt, ":{}", line)?;
277
278        // Add column number, if available.
279        if let Some(colno) = colno {
280            write!(self.fmt.fmt, ":{}", colno)?;
281        }
282
283        write!(self.fmt.fmt, "\n")?;
284        Ok(())
285    }
286
287    fn print_raw_fuchsia(&mut self, frame_ip: *mut c_void) -> fmt::Result {
288        // We only care about the first symbol of a frame
289        if self.symbol_index == 0 {
290            self.fmt.fmt.write_str("{{{bt:")?;
291            write!(self.fmt.fmt, "{}:{:?}", self.fmt.frame_index, frame_ip)?;
292            self.fmt.fmt.write_str("}}}\n")?;
293        }
294        Ok(())
295    }
296}
297
298impl Drop for BacktraceFrameFmt<'_, '_, '_> {
299    fn drop(&mut self) {
300        self.fmt.frame_index += 1;
301    }
302}