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/symbolize/mod.rs
Line
Count
Source
1
use core::{fmt, str};
2
3
cfg_if::cfg_if! {
4
    if #[cfg(feature = "std")] {
5
        use std::path::Path;
6
        use std::prelude::v1::*;
7
    }
8
}
9
10
use super::backtrace::Frame;
11
use super::types::BytesOrWideString;
12
use core::ffi::c_void;
13
use rustc_demangle::{try_demangle, Demangle};
14
15
/// Resolve an address to a symbol, passing the symbol to the specified
16
/// closure.
17
///
18
/// This function will look up the given address in areas such as the local
19
/// symbol table, dynamic symbol table, or DWARF debug info (depending on the
20
/// activated implementation) to find symbols to yield.
21
///
22
/// The closure may not be called if resolution could not be performed, and it
23
/// also may be called more than once in the case of inlined functions.
24
///
25
/// Symbols yielded represent the execution at the specified `addr`, returning
26
/// file/line pairs for that address (if available).
27
///
28
/// Note that if you have a `Frame` then it's recommended to use the
29
/// `resolve_frame` function instead of this one.
30
///
31
/// # Required features
32
///
33
/// This function requires the `std` feature of the `backtrace` crate to be
34
/// enabled, and the `std` feature is enabled by default.
35
///
36
/// # Panics
37
///
38
/// This function strives to never panic, but if the `cb` provided panics then
39
/// some platforms will force a double panic to abort the process. Some
40
/// platforms use a C library which internally uses callbacks which cannot be
41
/// unwound through, so panicking from `cb` may trigger a process abort.
42
///
43
/// # Example
44
///
45
/// ```
46
/// extern crate backtrace;
47
///
48
/// fn main() {
49
///     backtrace::trace(|frame| {
50
///         let ip = frame.ip();
51
///
52
///         backtrace::resolve(ip, |symbol| {
53
///             // ...
54
///         });
55
///
56
///         false // only look at the top frame
57
///     });
58
/// }
59
/// ```
60
#[cfg(feature = "std")]
61
0
pub fn resolve<F: FnMut(&Symbol)>(addr: *mut c_void, cb: F) {
62
0
    let _guard = crate::lock::lock();
63
0
    unsafe { resolve_unsynchronized(addr, cb) }
64
0
}
65
66
/// Resolve a previously capture frame to a symbol, passing the symbol to the
67
/// specified closure.
68
///
69
/// This function performs the same function as `resolve` except that it takes a
70
/// `Frame` as an argument instead of an address. This can allow some platform
71
/// implementations of backtracing to provide more accurate symbol information
72
/// or information about inline frames for example. It's recommended to use this
73
/// if you can.
74
///
75
/// # Required features
76
///
77
/// This function requires the `std` feature of the `backtrace` crate to be
78
/// enabled, and the `std` feature is enabled by default.
79
///
80
/// # Panics
81
///
82
/// This function strives to never panic, but if the `cb` provided panics then
83
/// some platforms will force a double panic to abort the process. Some
84
/// platforms use a C library which internally uses callbacks which cannot be
85
/// unwound through, so panicking from `cb` may trigger a process abort.
86
///
87
/// # Example
88
///
89
/// ```
90
/// extern crate backtrace;
91
///
92
/// fn main() {
93
///     backtrace::trace(|frame| {
94
///         backtrace::resolve_frame(frame, |symbol| {
95
///             // ...
96
///         });
97
///
98
///         false // only look at the top frame
99
///     });
100
/// }
101
/// ```
102
#[cfg(feature = "std")]
103
0
pub fn resolve_frame<F: FnMut(&Symbol)>(frame: &Frame, cb: F) {
104
0
    let _guard = crate::lock::lock();
105
0
    unsafe { resolve_frame_unsynchronized(frame, cb) }
106
0
}
Unexecuted instantiation: backtrace::symbolize::resolve_frame::<<pprof::frames::Frames as core::convert::From<pprof::frames::UnresolvedFrames>>::from::{closure#0}>
Unexecuted instantiation: backtrace::symbolize::resolve_frame::<<backtrace::capture::Frame>::resolve_symbols::{closure#0}>
107
108
pub enum ResolveWhat<'a> {
109
    Address(*mut c_void),
110
    Frame(&'a Frame),
111
}
112
113
impl<'a> ResolveWhat<'a> {
114
    #[allow(dead_code)]
115
0
    fn address_or_ip(&self) -> *mut c_void {
116
0
        match self {
117
0
            ResolveWhat::Address(a) => adjust_ip(*a),
118
0
            ResolveWhat::Frame(f) => adjust_ip(f.ip()),
119
        }
120
0
    }
121
}
122
123
// IP values from stack frames are typically (always?) the instruction
124
// *after* the call that's the actual stack trace. Symbolizing this on
125
// causes the filename/line number to be one ahead and perhaps into
126
// the void if it's near the end of the function.
127
//
128
// This appears to basically always be the case on all platforms, so we always
129
// subtract one from a resolved ip to resolve it to the previous call
130
// instruction instead of the instruction being returned to.
131
//
132
// Ideally we would not do this. Ideally we would require callers of the
133
// `resolve` APIs here to manually do the -1 and account that they want location
134
// information for the *previous* instruction, not the current. Ideally we'd
135
// also expose on `Frame` if we are indeed the address of the next instruction
136
// or the current.
137
//
138
// For now though this is a pretty niche concern so we just internally always
139
// subtract one. Consumers should keep working and getting pretty good results,
140
// so we should be good enough.
141
0
fn adjust_ip(a: *mut c_void) -> *mut c_void {
142
0
    if a.is_null() {
143
0
        a
144
    } else {
145
0
        (a as usize - 1) as *mut c_void
146
    }
147
0
}
148
149
/// Same as `resolve`, only unsafe as it's unsynchronized.
150
///
151
/// This function does not have synchronization guarantees but is available when
152
/// the `std` feature of this crate isn't compiled in. See the `resolve`
153
/// function for more documentation and examples.
154
///
155
/// # Panics
156
///
157
/// See information on `resolve` for caveats on `cb` panicking.
158
0
pub unsafe fn resolve_unsynchronized<F>(addr: *mut c_void, mut cb: F)
159
0
where
160
0
    F: FnMut(&Symbol),
161
{
162
0
    imp::resolve(ResolveWhat::Address(addr), &mut cb)
163
0
}
164
165
/// Same as `resolve_frame`, only unsafe as it's unsynchronized.
166
///
167
/// This function does not have synchronization guarantees but is available
168
/// when the `std` feature of this crate isn't compiled in. See the
169
/// `resolve_frame` function for more documentation and examples.
170
///
171
/// # Panics
172
///
173
/// See information on `resolve_frame` for caveats on `cb` panicking.
174
0
pub unsafe fn resolve_frame_unsynchronized<F>(frame: &Frame, mut cb: F)
175
0
where
176
0
    F: FnMut(&Symbol),
177
{
178
0
    imp::resolve(ResolveWhat::Frame(frame), &mut cb)
179
0
}
Unexecuted instantiation: backtrace::symbolize::resolve_frame_unsynchronized::<<pprof::frames::Frames as core::convert::From<pprof::frames::UnresolvedFrames>>::from::{closure#0}>
Unexecuted instantiation: backtrace::symbolize::resolve_frame_unsynchronized::<<backtrace::capture::Frame>::resolve_symbols::{closure#0}>
180
181
/// A trait representing the resolution of a symbol in a file.
182
///
183
/// This trait is yielded as a trait object to the closure given to the
184
/// `backtrace::resolve` function, and it is virtually dispatched as it's
185
/// unknown which implementation is behind it.
186
///
187
/// A symbol can give contextual information about a function, for example the
188
/// name, filename, line number, precise address, etc. Not all information is
189
/// always available in a symbol, however, so all methods return an `Option`.
190
pub struct Symbol {
191
    // TODO: this lifetime bound needs to be persisted eventually to `Symbol`,
192
    // but that's currently a breaking change. For now this is safe since
193
    // `Symbol` is only ever handed out by reference and can't be cloned.
194
    inner: imp::Symbol<'static>,
195
}
196
197
impl Symbol {
198
    /// Returns the name of this function.
199
    ///
200
    /// The returned structure can be used to query various properties about the
201
    /// symbol name:
202
    ///
203
    /// * The `Display` implementation will print out the demangled symbol.
204
    /// * The raw `str` value of the symbol can be accessed (if it's valid
205
    ///   utf-8).
206
    /// * The raw bytes for the symbol name can be accessed.
207
0
    pub fn name(&self) -> Option<SymbolName<'_>> {
208
0
        self.inner.name()
209
0
    }
210
211
    /// Returns the starting address of this function.
212
0
    pub fn addr(&self) -> Option<*mut c_void> {
213
0
        self.inner.addr()
214
0
    }
215
216
    /// Returns the raw filename as a slice. This is mainly useful for `no_std`
217
    /// environments.
218
0
    pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
219
0
        self.inner.filename_raw()
220
0
    }
221
222
    /// Returns the column number for where this symbol is currently executing.
223
    ///
224
    /// Only gimli currently provides a value here and even then only if `filename`
225
    /// returns `Some`, and so it is then consequently subject to similar caveats.
226
0
    pub fn colno(&self) -> Option<u32> {
227
0
        self.inner.colno()
228
0
    }
229
230
    /// Returns the line number for where this symbol is currently executing.
231
    ///
232
    /// This return value is typically `Some` if `filename` returns `Some`, and
233
    /// is consequently subject to similar caveats.
234
0
    pub fn lineno(&self) -> Option<u32> {
235
0
        self.inner.lineno()
236
0
    }
237
238
    /// Returns the file name where this function was defined.
239
    ///
240
    /// This is currently only available when libbacktrace or gimli is being
241
    /// used (e.g. unix platforms other) and when a binary is compiled with
242
    /// debuginfo. If neither of these conditions is met then this will likely
243
    /// return `None`.
244
    ///
245
    /// # Required features
246
    ///
247
    /// This function requires the `std` feature of the `backtrace` crate to be
248
    /// enabled, and the `std` feature is enabled by default.
249
    #[cfg(feature = "std")]
250
    #[allow(unreachable_code)]
251
0
    pub fn filename(&self) -> Option<&Path> {
252
0
        self.inner.filename()
253
0
    }
254
}
255
256
impl fmt::Debug for Symbol {
257
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258
0
        let mut d = f.debug_struct("Symbol");
259
0
        if let Some(name) = self.name() {
260
0
            d.field("name", &name);
261
0
        }
262
0
        if let Some(addr) = self.addr() {
263
0
            d.field("addr", &addr);
264
0
        }
265
266
        #[cfg(feature = "std")]
267
        {
268
0
            if let Some(filename) = self.filename() {
269
0
                d.field("filename", &filename);
270
0
            }
271
        }
272
273
0
        if let Some(lineno) = self.lineno() {
274
0
            d.field("lineno", &lineno);
275
0
        }
276
0
        d.finish()
277
0
    }
278
}
279
280
cfg_if::cfg_if! {
281
    if #[cfg(feature = "cpp_demangle")] {
282
        // Maybe a parsed C++ symbol, if parsing the mangled symbol as Rust
283
        // failed.
284
        struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>);
285
286
        impl<'a> OptionCppSymbol<'a> {
287
            fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> {
288
                OptionCppSymbol(::cpp_demangle::BorrowedSymbol::new(input).ok())
289
            }
290
291
            fn none() -> OptionCppSymbol<'a> {
292
                OptionCppSymbol(None)
293
            }
294
        }
295
    }
296
}
297
298
/// A wrapper around a symbol name to provide ergonomic accessors to the
299
/// demangled name, the raw bytes, the raw string, etc.
300
pub struct SymbolName<'a> {
301
    bytes: &'a [u8],
302
    demangled: Option<Demangle<'a>>,
303
    #[cfg(feature = "cpp_demangle")]
304
    cpp_demangled: OptionCppSymbol<'a>,
305
}
306
307
impl<'a> SymbolName<'a> {
308
    /// Creates a new symbol name from the raw underlying bytes.
309
0
    pub fn new(bytes: &'a [u8]) -> SymbolName<'a> {
310
0
        let str_bytes = str::from_utf8(bytes).ok();
311
0
        let demangled = str_bytes.and_then(|s| try_demangle(s).ok());
312
313
        #[cfg(feature = "cpp_demangle")]
314
        let cpp = if demangled.is_none() {
315
            OptionCppSymbol::parse(bytes)
316
        } else {
317
            OptionCppSymbol::none()
318
        };
319
320
0
        SymbolName {
321
0
            bytes,
322
0
            demangled,
323
0
            #[cfg(feature = "cpp_demangle")]
324
0
            cpp_demangled: cpp,
325
0
        }
326
0
    }
327
328
    /// Returns the raw (mangled) symbol name as a `str` if the symbol is valid utf-8.
329
    ///
330
    /// Use the `Display` implementation if you want the demangled version.
331
0
    pub fn as_str(&self) -> Option<&'a str> {
332
0
        self.demangled
333
0
            .as_ref()
334
0
            .map(|s| s.as_str())
335
0
            .or_else(|| str::from_utf8(self.bytes).ok())
336
0
    }
337
338
    /// Returns the raw symbol name as a list of bytes
339
0
    pub fn as_bytes(&self) -> &'a [u8] {
340
0
        self.bytes
341
0
    }
342
}
343
344
0
fn format_symbol_name(
345
0
    fmt: fn(&str, &mut fmt::Formatter<'_>) -> fmt::Result,
346
0
    mut bytes: &[u8],
347
0
    f: &mut fmt::Formatter<'_>,
348
0
) -> fmt::Result {
349
0
    while bytes.len() > 0 {
350
0
        match str::from_utf8(bytes) {
351
0
            Ok(name) => {
352
0
                fmt(name, f)?;
353
0
                break;
354
            }
355
0
            Err(err) => {
356
0
                fmt("\u{FFFD}", f)?;
357
358
0
                match err.error_len() {
359
0
                    Some(len) => bytes = &bytes[err.valid_up_to() + len..],
360
0
                    None => break,
361
                }
362
            }
363
        }
364
    }
365
0
    Ok(())
366
0
}
367
368
impl<'a> fmt::Display for SymbolName<'a> {
369
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370
0
        if let Some(ref s) = self.demangled {
371
0
            return s.fmt(f);
372
0
        }
373
374
        #[cfg(feature = "cpp_demangle")]
375
        {
376
            if let Some(ref cpp) = self.cpp_demangled.0 {
377
                return cpp.fmt(f);
378
            }
379
        }
380
381
0
        format_symbol_name(fmt::Display::fmt, self.bytes, f)
382
0
    }
383
}
384
385
impl<'a> fmt::Debug for SymbolName<'a> {
386
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387
0
        if let Some(ref s) = self.demangled {
388
0
            return s.fmt(f);
389
0
        }
390
391
        #[cfg(all(feature = "std", feature = "cpp_demangle"))]
392
        {
393
            use std::fmt::Write;
394
395
            // This may to print if the demangled symbol isn't actually
396
            // valid, so handle the error here gracefully by not propagating
397
            // it outwards.
398
            if let Some(ref cpp) = self.cpp_demangled.0 {
399
                let mut s = String::new();
400
                if write!(s, "{cpp}").is_ok() {
401
                    return s.fmt(f);
402
                }
403
            }
404
        }
405
406
0
        format_symbol_name(fmt::Debug::fmt, self.bytes, f)
407
0
    }
408
}
409
410
/// Attempt to reclaim that cached memory used to symbolicate addresses.
411
///
412
/// This method will attempt to release any global data structures that have
413
/// otherwise been cached globally or in the thread which typically represent
414
/// parsed DWARF information or similar.
415
///
416
/// # Caveats
417
///
418
/// While this function is always available it doesn't actually do anything on
419
/// most implementations. Libraries like dbghelp or libbacktrace do not provide
420
/// facilities to deallocate state and manage the allocated memory. For now the
421
/// `std` feature of this crate is the only feature where this
422
/// function has any effect.
423
#[cfg(feature = "std")]
424
0
pub fn clear_symbol_cache() {
425
0
    let _guard = crate::lock::lock();
426
0
    unsafe {
427
0
        imp::clear_symbol_cache();
428
0
    }
429
0
}
430
431
cfg_if::cfg_if! {
432
    if #[cfg(miri)] {
433
        mod miri;
434
        use miri as imp;
435
    } else if #[cfg(all(windows, target_env = "msvc", not(target_vendor = "uwp")))] {
436
        mod dbghelp;
437
        use dbghelp as imp;
438
    } else if #[cfg(all(
439
        any(unix, all(windows, target_env = "gnu")),
440
        not(target_vendor = "uwp"),
441
        not(target_os = "emscripten"),
442
        any(not(backtrace_in_libstd), feature = "backtrace"),
443
    ))] {
444
        mod gimli;
445
        use gimli as imp;
446
    } else {
447
        mod noop;
448
        use noop as imp;
449
    }
450
}