Skip to main content

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