Coverage Report

Created: 2025-10-29 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/backtrace-0.3.74/src/print.rs
Line
Count
Source
1
#[cfg(feature = "std")]
2
use super::{BacktraceFrame, BacktraceSymbol};
3
use super::{BytesOrWideString, Frame, SymbolName};
4
use core::ffi::c_void;
5
use core::fmt;
6
7
const HEX_WIDTH: usize = 2 + 2 * core::mem::size_of::<usize>();
8
9
#[cfg(target_os = "fuchsia")]
10
mod 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.
17
pub 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]
28
pub 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
35
impl<'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
0
    pub fn new(
44
0
        fmt: &'a mut fmt::Formatter<'b>,
45
0
        format: PrintFmt,
46
0
        print_path: &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result
47
0
                     + 'b),
48
0
    ) -> Self {
49
0
        BacktraceFmt {
50
0
            fmt,
51
0
            frame_index: 0,
52
0
            format,
53
0
            print_path,
54
0
        }
55
0
    }
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
0
    pub fn add_context(&mut self) -> fmt::Result {
63
        #[cfg(target_os = "fuchsia")]
64
        fuchsia::print_dso_context(self.fmt)?;
65
0
        Ok(())
66
0
    }
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
0
    pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> {
74
0
        BacktraceFrameFmt {
75
0
            fmt: self,
76
0
            symbol_index: 0,
77
0
        }
78
0
    }
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
0
    pub fn finish(&mut self) -> fmt::Result {
85
        #[cfg(target_os = "fuchsia")]
86
        fuchsia::finish_context(self.fmt)?;
87
0
        Ok(())
88
0
    }
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
0
    pub fn message(&mut self, msg: &str) -> fmt::Result {
96
0
        self.fmt.write_str(msg)
97
0
    }
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
0
    pub fn formatter(&mut self) -> &mut fmt::Formatter<'b> {
104
0
        self.fmt
105
0
    }
106
}
107
108
/// A formatter for just one frame of a backtrace.
109
///
110
/// This type is created by the `BacktraceFmt::frame` function.
111
pub struct BacktraceFrameFmt<'fmt, 'a, 'b> {
112
    fmt: &'fmt mut BacktraceFmt<'a, 'b>,
113
    symbol_index: usize,
114
}
115
116
impl 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
0
    pub fn backtrace_frame(&mut self, frame: &BacktraceFrame) -> fmt::Result {
128
0
        let symbols = frame.symbols();
129
0
        for symbol in symbols {
130
0
            self.backtrace_symbol(frame, symbol)?;
131
        }
132
0
        if symbols.is_empty() {
133
0
            self.print_raw(frame.ip(), None, None, None)?;
134
0
        }
135
0
        Ok(())
136
0
    }
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
0
    pub fn backtrace_symbol(
146
0
        &mut self,
147
0
        frame: &BacktraceFrame,
148
0
        symbol: &BacktraceSymbol,
149
0
    ) -> fmt::Result {
150
0
        self.print_raw_with_column(
151
0
            frame.ip(),
152
0
            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
0
            symbol
157
0
                .filename()
158
0
                .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))),
159
0
            symbol.lineno(),
160
0
            symbol.colno(),
161
0
        )?;
162
0
        Ok(())
163
0
    }
164
165
    /// Prints a raw traced `Frame` and `Symbol`, typically from within the raw
166
    /// callbacks of this crate.
167
0
    pub fn symbol(&mut self, frame: &Frame, symbol: &super::Symbol) -> fmt::Result {
168
0
        self.print_raw_with_column(
169
0
            frame.ip(),
170
0
            symbol.name(),
171
0
            symbol.filename_raw(),
172
0
            symbol.lineno(),
173
0
            symbol.colno(),
174
0
        )?;
175
0
        Ok(())
176
0
    }
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
0
    pub fn print_raw(
184
0
        &mut self,
185
0
        frame_ip: *mut c_void,
186
0
        symbol_name: Option<SymbolName<'_>>,
187
0
        filename: Option<BytesOrWideString<'_>>,
188
0
        lineno: Option<u32>,
189
0
    ) -> fmt::Result {
190
0
        self.print_raw_with_column(frame_ip, symbol_name, filename, lineno, None)
191
0
    }
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
0
    pub fn print_raw_with_column(
199
0
        &mut self,
200
0
        frame_ip: *mut c_void,
201
0
        symbol_name: Option<SymbolName<'_>>,
202
0
        filename: Option<BytesOrWideString<'_>>,
203
0
        lineno: Option<u32>,
204
0
        colno: Option<u32>,
205
0
    ) -> 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
0
        if cfg!(target_os = "fuchsia") {
210
0
            self.print_raw_fuchsia(frame_ip)?;
211
        } else {
212
0
            self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?;
213
        }
214
0
        self.symbol_index += 1;
215
0
        Ok(())
216
0
    }
217
218
    #[allow(unused_mut)]
219
0
    fn print_raw_generic(
220
0
        &mut self,
221
0
        frame_ip: *mut c_void,
222
0
        symbol_name: Option<SymbolName<'_>>,
223
0
        filename: Option<BytesOrWideString<'_>>,
224
0
        lineno: Option<u32>,
225
0
        colno: Option<u32>,
226
0
    ) -> 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
0
        if let PrintFmt::Short = self.fmt.format {
230
0
            if frame_ip.is_null() {
231
0
                return Ok(());
232
0
            }
233
0
        }
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
0
        if self.symbol_index == 0 {
239
0
            write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?;
240
0
            if let PrintFmt::Full = self.fmt.format {
241
0
                write!(self.fmt.fmt, "{frame_ip:HEX_WIDTH$?} - ")?;
242
0
            }
243
        } else {
244
0
            write!(self.fmt.fmt, "      ")?;
245
0
            if let PrintFmt::Full = self.fmt.format {
246
0
                write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?;
247
0
            }
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
0
        match (symbol_name, &self.fmt.format) {
254
0
            (Some(name), PrintFmt::Short) => write!(self.fmt.fmt, "{name:#}")?,
255
0
            (Some(name), PrintFmt::Full) => write!(self.fmt.fmt, "{name}")?,
256
0
            (None, _) => write!(self.fmt.fmt, "<unknown>")?,
257
        }
258
0
        self.fmt.fmt.write_str("\n")?;
259
260
        // And last up, print out the filename/line number if they're available.
261
0
        if let (Some(file), Some(line)) = (filename, lineno) {
262
0
            self.print_fileline(file, line, colno)?;
263
0
        }
264
265
0
        Ok(())
266
0
    }
267
268
0
    fn print_fileline(
269
0
        &mut self,
270
0
        file: BytesOrWideString<'_>,
271
0
        line: u32,
272
0
        colno: Option<u32>,
273
0
    ) -> 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
0
        if let PrintFmt::Full = self.fmt.format {
277
0
            write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?;
278
0
        }
279
0
        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
0
        (self.fmt.print_path)(self.fmt.fmt, file)?;
284
0
        write!(self.fmt.fmt, ":{line}")?;
285
286
        // Add column number, if available.
287
0
        if let Some(colno) = colno {
288
0
            write!(self.fmt.fmt, ":{colno}")?;
289
0
        }
290
291
0
        write!(self.fmt.fmt, "\n")?;
292
0
        Ok(())
293
0
    }
294
295
0
    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
0
        if self.symbol_index == 0 {
298
0
            self.fmt.fmt.write_str("{{{bt:")?;
299
0
            write!(self.fmt.fmt, "{}:{:?}", self.fmt.frame_index, frame_ip)?;
300
0
            self.fmt.fmt.write_str("}}}\n")?;
301
0
        }
302
0
        Ok(())
303
0
    }
304
}
305
306
impl Drop for BacktraceFrameFmt<'_, '_, '_> {
307
0
    fn drop(&mut self) {
308
0
        self.fmt.frame_index += 1;
309
0
    }
310
}