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