1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
use std::fmt;
use std::os::raw::c_void;

use debug_builders::DebugStruct;

/// A trait representing the resolution of a symbol in a file.
///
/// This trait is yielded as a trait object to the closure given to the
/// `backtrace::resolve` function, and it is virtually dispatched as it's
/// unknown which implementation is behind it.
///
/// A symbol can give contextual information about a funciton, for example the
/// name, filename, line number, precise address, etc. Not all information is
/// always available in a symbol, however, so all methods return an `Option`.
pub trait Symbol {
    /// Returns the name of this function as a byte array (may not always be
    /// valid UTF-8).
    fn name(&self) -> Option<&[u8]> { None }

    /// Returns the starting address of this function.
    fn addr(&self) -> Option<*mut c_void> { None }

    /// Returns the file name where this function was defined.
    ///
    /// This is currently only available when libbacktrace is being used (e.g.
    /// unix platforms other than OSX) and when a binary is compiled with
    /// debuginfo. If neither of these conditions is met then this will likely
    /// return `None`.
    fn filename(&self) -> Option<&[u8]> { None }

    /// Returns the line number for where this symbol is currently executing.
    ///
    /// This return value is typically `Some` if `filename` returns `Some`, and
    /// is consequently subject to similar caveats.
    fn lineno(&self) -> Option<u32> { None }
}

/// Resolve an address to a symbol, passing the symbol to the specified
/// closure.
///
/// This function will look up the given address in areas such as the local
/// symbol table, dynamic symbol table, or DWARF debug info (depending on the
/// activated implementation) to find symbols to yield.
///
/// The closure may not be called if resolution could not be performed, and it
/// also may be called more than once in the case of inlined functions.
///
/// Symbols yielded represent the execution at the specified `addr`, returning
/// file/line pairs for that addres (if available).
///
/// # Example
///
/// ```
/// extern crate backtrace;
///
/// fn main() {
///     backtrace::trace(&mut |frame| {
///         let ip = frame.ip();
///
///         backtrace::resolve(ip, &mut |symbol| {
///             // ...
///         });
///
///         false // only look at the top frame
///     });
/// }
/// ```
pub fn resolve(addr: *mut c_void, cb: &mut FnMut(&Symbol)) {
    resolve_imp(addr, cb)
}

impl fmt::Debug for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut d = DebugStruct::new(f, "Symbol");
        if let Some(name) = self.name() {
            d = d.field("name", &String::from_utf8_lossy(name));
        }
        if let Some(addr) = self.addr() {
            d = d.field("addr", &addr);
        }
        if let Some(filename) = self.filename() {
            d = d.field("filename", &String::from_utf8_lossy(filename));
        }
        if let Some(lineno) = self.lineno() {
            d = d.field("lineno", &lineno);
        }
        d.finish()
    }
}

cfg_if! {
    if #[cfg(all(windows, feature = "dbghelp"))] {
        mod dbghelp;
        use self::dbghelp::resolve as resolve_imp;
    } else if #[cfg(all(feature = "libbacktrace", unix,
                        not(target_os = "macos")))] {
        mod libbacktrace;
        use self::libbacktrace::resolve as resolve_imp;
    } else if #[cfg(all(unix, feature = "dladdr"))] {
        mod dladdr;
        use self::dladdr::resolve as resolve_imp;
    } else {
        mod noop;
        use self::noop::resolve as resolve_imp;
    }
}