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 108 109 110
/// Backtrace support built on libgcc with some extra OS-specific support /// /// Some methods of getting a backtrace: /// /// * The backtrace() functions on unix. It turns out this doesn't work very /// well for green threads on macOS, and the address to symbol portion of it /// suffers problems that are described below. /// /// * Using libunwind. This is more difficult than it sounds because libunwind /// isn't installed everywhere by default. It's also a bit of a hefty library, /// so possibly not the best option. When testing, libunwind was excellent at /// getting both accurate backtraces and accurate symbols across platforms. /// This route was not chosen in favor of the next option, however. /// /// * We're already using libgcc_s for exceptions in rust (triggering thread /// unwinding and running destructors on the stack), and it turns out that it /// conveniently comes with a function that also gives us a backtrace. All of /// these functions look like _Unwind_*, but it's not quite the full /// repertoire of the libunwind API. Due to it already being in use, this was /// the chosen route of getting a backtrace. /// /// After choosing libgcc_s for backtraces, the sad part is that it will only /// give us a stack trace of instruction pointers. Thankfully these instruction /// pointers are accurate (they work for green and native threads), but it's /// then up to us again to figure out how to translate these addresses to /// symbols. As with before, we have a few options. Before, that, a little bit /// of an interlude about symbols. This is my very limited knowledge about /// symbol tables, and this information is likely slightly wrong, but the /// general idea should be correct. /// /// When talking about symbols, it's helpful to know a few things about where /// symbols are located. Some symbols are located in the dynamic symbol table /// of the executable which in theory means that they're available for dynamic /// linking and lookup. Other symbols end up only in the local symbol table of /// the file. This loosely corresponds to pub and priv functions in Rust. /// /// Armed with this knowledge, we know that our solution for address to symbol /// translation will need to consult both the local and dynamic symbol tables. /// With that in mind, here's our options of translating an address to /// a symbol. /// /// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() /// behind the scenes to translate, and this is why backtrace() was not used. /// Conveniently, this method works fantastically on macOS. It appears dladdr() /// uses magic to consult the local symbol table, or we're putting everything /// in the dynamic symbol table anyway. Regardless, for macOS, this is the /// method used for translation. It's provided by the system and easy to do.o /// /// Sadly, all other systems have a dladdr() implementation that does not /// consult the local symbol table. This means that most functions are blank /// because they don't have symbols. This means that we need another solution. /// /// * Use unw_get_proc_name(). This is part of the libunwind api (not the /// libgcc_s version of the libunwind api), but involves taking a dependency /// to libunwind. We may pursue this route in the future if we bundle /// libunwind, but libunwind was unwieldy enough that it was not chosen at /// this time to provide this functionality. /// /// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a /// semi-reasonable solution. The stdlib already knows how to spawn processes, /// so in theory it could invoke readelf, parse the output, and consult the /// local/dynamic symbol tables from there. This ended up not getting chosen /// due to the craziness of the idea plus the advent of the next option. /// /// * Use `libbacktrace`. It turns out that this is a small library bundled in /// the gcc repository which provides backtrace and symbol translation /// functionality. All we really need from it is the backtrace functionality, /// and we only really need this on everything that's not macOS, so this is the /// chosen route for now. /// /// In summary, the current situation uses libgcc_s to get a trace of stack /// pointers, and we use dladdr() or libbacktrace to translate these addresses /// to symbols. This is a bit of a hokey implementation as-is, but it works for /// all unix platforms we support right now, so it at least gets the job done. pub use self::tracing::unwind_backtrace; pub use self::printing::{foreach_symbol_fileline, resolve_symname}; // tracing impls: mod tracing; // symbol resolvers: mod printing; #[cfg(not(target_os = "emscripten"))] pub mod gnu { use crate::io; use crate::fs; use libc::c_char; #[cfg(not(any(target_os = "macos", target_os = "ios")))] pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> { Err(io::Error::new(io::ErrorKind::Other, "Not implemented")) } #[cfg(any(target_os = "macos", target_os = "ios"))] pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> { use crate::env; use crate::os::unix::ffi::OsStrExt; let filename = env::current_exe()?; let file = fs::File::open(&filename)?; let mut filename_cstr: Vec<_> = filename.as_os_str().as_bytes().iter() .map(|&x| x as c_char).collect(); filename_cstr.push(0); // Null terminate Ok((filename_cstr, file)) } } pub struct BacktraceContext;