print.rs - source
Description: Source of the Rust file `library/std/src/../../backtrace/src/print.rs`.

#
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 (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        writeln!(self.fmt.fmt)?;
    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}