|
| 1 | +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
| 2 | +// file at the top-level directory of this distribution and at |
| 3 | +// http://rust-lang.org/COPYRIGHT. |
| 4 | +// |
| 5 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 7 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 8 | +// option. This file may not be copied, modified, or distributed |
| 9 | +// except according to those terms. |
| 10 | + |
| 11 | +/// Backtrace support built on libgcc with some extra OS-specific support |
| 12 | +/// |
| 13 | +/// Some methods of getting a backtrace: |
| 14 | +/// |
| 15 | +/// * The backtrace() functions on unix. It turns out this doesn't work very |
| 16 | +/// well for green threads on macOS, and the address to symbol portion of it |
| 17 | +/// suffers problems that are described below. |
| 18 | +/// |
| 19 | +/// * Using libunwind. This is more difficult than it sounds because libunwind |
| 20 | +/// isn't installed everywhere by default. It's also a bit of a hefty library, |
| 21 | +/// so possibly not the best option. When testing, libunwind was excellent at |
| 22 | +/// getting both accurate backtraces and accurate symbols across platforms. |
| 23 | +/// This route was not chosen in favor of the next option, however. |
| 24 | +/// |
| 25 | +/// * We're already using libgcc_s for exceptions in rust (triggering thread |
| 26 | +/// unwinding and running destructors on the stack), and it turns out that it |
| 27 | +/// conveniently comes with a function that also gives us a backtrace. All of |
| 28 | +/// these functions look like _Unwind_*, but it's not quite the full |
| 29 | +/// repertoire of the libunwind API. Due to it already being in use, this was |
| 30 | +/// the chosen route of getting a backtrace. |
| 31 | +/// |
| 32 | +/// After choosing libgcc_s for backtraces, the sad part is that it will only |
| 33 | +/// give us a stack trace of instruction pointers. Thankfully these instruction |
| 34 | +/// pointers are accurate (they work for green and native threads), but it's |
| 35 | +/// then up to us again to figure out how to translate these addresses to |
| 36 | +/// symbols. As with before, we have a few options. Before, that, a little bit |
| 37 | +/// of an interlude about symbols. This is my very limited knowledge about |
| 38 | +/// symbol tables, and this information is likely slightly wrong, but the |
| 39 | +/// general idea should be correct. |
| 40 | +/// |
| 41 | +/// When talking about symbols, it's helpful to know a few things about where |
| 42 | +/// symbols are located. Some symbols are located in the dynamic symbol table |
| 43 | +/// of the executable which in theory means that they're available for dynamic |
| 44 | +/// linking and lookup. Other symbols end up only in the local symbol table of |
| 45 | +/// the file. This loosely corresponds to pub and priv functions in Rust. |
| 46 | +/// |
| 47 | +/// Armed with this knowledge, we know that our solution for address to symbol |
| 48 | +/// translation will need to consult both the local and dynamic symbol tables. |
| 49 | +/// With that in mind, here's our options of translating an address to |
| 50 | +/// a symbol. |
| 51 | +/// |
| 52 | +/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() |
| 53 | +/// behind the scenes to translate, and this is why backtrace() was not used. |
| 54 | +/// Conveniently, this method works fantastically on macOS. It appears dladdr() |
| 55 | +/// uses magic to consult the local symbol table, or we're putting everything |
| 56 | +/// in the dynamic symbol table anyway. Regardless, for macOS, this is the |
| 57 | +/// method used for translation. It's provided by the system and easy to do.o |
| 58 | +/// |
| 59 | +/// Sadly, all other systems have a dladdr() implementation that does not |
| 60 | +/// consult the local symbol table. This means that most functions are blank |
| 61 | +/// because they don't have symbols. This means that we need another solution. |
| 62 | +/// |
| 63 | +/// * Use unw_get_proc_name(). This is part of the libunwind api (not the |
| 64 | +/// libgcc_s version of the libunwind api), but involves taking a dependency |
| 65 | +/// to libunwind. We may pursue this route in the future if we bundle |
| 66 | +/// libunwind, but libunwind was unwieldy enough that it was not chosen at |
| 67 | +/// this time to provide this functionality. |
| 68 | +/// |
| 69 | +/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a |
| 70 | +/// semi-reasonable solution. The stdlib already knows how to spawn processes, |
| 71 | +/// so in theory it could invoke readelf, parse the output, and consult the |
| 72 | +/// local/dynamic symbol tables from there. This ended up not getting chosen |
| 73 | +/// due to the craziness of the idea plus the advent of the next option. |
| 74 | +/// |
| 75 | +/// * Use `libbacktrace`. It turns out that this is a small library bundled in |
| 76 | +/// the gcc repository which provides backtrace and symbol translation |
| 77 | +/// functionality. All we really need from it is the backtrace functionality, |
| 78 | +/// and we only really need this on everything that's not macOS, so this is the |
| 79 | +/// chosen route for now. |
| 80 | +/// |
| 81 | +/// In summary, the current situation uses libgcc_s to get a trace of stack |
| 82 | +/// pointers, and we use dladdr() or libbacktrace to translate these addresses |
| 83 | +/// to symbols. This is a bit of a hokey implementation as-is, but it works for |
| 84 | +/// all unix platforms we support right now, so it at least gets the job done. |
| 85 | +
|
| 86 | +pub use self::tracing::unwind_backtrace; |
| 87 | +pub use self::printing::{foreach_symbol_fileline, resolve_symname}; |
| 88 | + |
| 89 | +// tracing impls: |
| 90 | +mod tracing; |
| 91 | +// symbol resolvers: |
| 92 | +mod printing; |
| 93 | + |
| 94 | +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "emscripten")))] |
| 95 | +pub mod gnu { |
| 96 | + use io; |
| 97 | + use fs; |
| 98 | + use libc::c_char; |
| 99 | + use vec::Vec; |
| 100 | + use ffi::OsStr; |
| 101 | + use os::unix::ffi::OsStrExt; |
| 102 | + use io::Read; |
| 103 | + |
| 104 | + pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> { |
| 105 | + let mut exefile = fs::File::open("sys:exe")?; |
| 106 | + let mut exename = Vec::new(); |
| 107 | + exefile.read_to_end(&mut exename)?; |
| 108 | + if exename.last() == Some(&b'\n') { |
| 109 | + exename.pop(); |
| 110 | + } |
| 111 | + let file = fs::File::open(OsStr::from_bytes(&exename))?; |
| 112 | + Ok((exename.into_iter().map(|c| c as c_char).collect(), file)) |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +pub struct BacktraceContext; |
0 commit comments