Coverage Report

Created: 2021-03-22 08:29

/rust/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.56/src/print.rs
Line
Count
Source (jump to first uncovered line)
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
pub 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
36
impl<'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.
94
pub struct BacktraceFrameFmt<'fmt, 'a, 'b> {
95
    fmt: &'fmt mut BacktraceFmt<'a, 'b>,
96
    symbol_index: usize,
97
}
98
99
impl 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
0
    pub fn backtrace_frame(&mut self, frame: &BacktraceFrame) -> fmt::Result {
111
0
        let symbols = frame.symbols();
112
0
        for symbol in symbols {
113
0
            self.backtrace_symbol(frame, symbol)?;
114
        }
115
0
        if symbols.is_empty() {
116
0
            self.print_raw(frame.ip(), None, None, None)?;
117
0
        }
118
0
        Ok(())
119
0
    }
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
0
        self.print_raw_with_column(
134
0
            frame.ip(),
135
0
            symbol.name(),
136
0
            // TODO: this isn't great that we don't end up printing anything
137
0
            // with non-utf8 filenames. Thankfully almost everything is utf8 so
138
0
            // this shouldn't be too too bad.
139
0
            symbol
140
0
                .filename()
141
0
                .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))),
142
0
            symbol.lineno(),
143
0
            symbol.colno(),
144
0
        )?;
145
0
        Ok(())
146
0
    }
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
0
        self.print_raw_with_column(
152
0
            frame.ip(),
153
0
            symbol.name(),
154
0
            symbol.filename_raw(),
155
0
            symbol.lineno(),
156
0
            symbol.colno(),
157
0
        )?;
158
0
        Ok(())
159
0
    }
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
0
    fn print_raw_generic(
203
0
        &mut self,
204
0
        mut frame_ip: *mut c_void,
205
0
        symbol_name: Option<SymbolName<'_>>,
206
0
        filename: Option<BytesOrWideString<'_>>,
207
0
        lineno: Option<u32>,
208
0
        colno: Option<u32>,
209
0
    ) -> fmt::Result {
210
0
        // No need to print "null" frames, it basically just means that the
211
0
        // system backtrace was a bit eager to trace back super far.
212
0
        if let PrintFmt::Short = self.fmt.format {
213
0
            if frame_ip.is_null() {
214
0
                return Ok(());
215
0
            }
216
0
        }
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
0
        if self.symbol_index == 0 {
231
0
            write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?;
232
0
            if let PrintFmt::Full = self.fmt.format {
233
0
                write!(self.fmt.fmt, "{:1$?} - ", frame_ip, HEX_WIDTH)?;
234
0
            }
235
        } else {
236
0
            write!(self.fmt.fmt, "      ")?;
237
0
            if let PrintFmt::Full = self.fmt.format {
238
0
                write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?;
239
0
            }
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
0
        match (symbol_name, &self.fmt.format) {
246
0
            (Some(name), PrintFmt::Short) => write!(self.fmt.fmt, "{:#}", name)?,
247
0
            (Some(name), PrintFmt::Full) => write!(self.fmt.fmt, "{}", name)?,
248
0
            (None, _) | (_, PrintFmt::__Nonexhaustive) => write!(self.fmt.fmt, "<unknown>")?,
249
        }
250
0
        self.fmt.fmt.write_str("\n")?;
251
252
        // And last up, print out the filename/line number if they're available.
253
0
        if let (Some(file), Some(line)) = (filename, lineno) {
254
0
            self.print_fileline(file, line, colno)?;
255
0
        }
256
257
0
        Ok(())
258
0
    }
259
260
0
    fn print_fileline(
261
0
        &mut self,
262
0
        file: BytesOrWideString<'_>,
263
0
        line: u32,
264
0
        colno: Option<u32>,
265
0
    ) -> fmt::Result {
266
0
        // Filename/line are printed on lines under the symbol name, so print
267
0
        // some appropriate whitespace to sort of right-align ourselves.
268
0
        if let PrintFmt::Full = self.fmt.format {
269
0
            write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?;
270
0
        }
271
0
        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
0
        (self.fmt.print_path)(self.fmt.fmt, file)?;
276
0
        write!(self.fmt.fmt, ":{}", line)?;
277
278
        // Add column number, if available.
279
0
        if let Some(colno) = colno {
280
0
            write!(self.fmt.fmt, ":{}", colno)?;
281
0
        }
282
283
0
        write!(self.fmt.fmt, "\n")?;
284
0
        Ok(())
285
0
    }
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
298
impl Drop for BacktraceFrameFmt<'_, '_, '_> {
299
    fn drop(&mut self) {
300
        self.fmt.frame_index += 1;
301
    }
302
}