about summary refs log tree commit diff
path: root/src/libstd/sys/unix/backtrace/mod.rs
diff options
context:
space:
mode:
authorRichard Diamond <wichard@vitalitystudios.com>2015-08-19 23:55:50 -0500
committerRichard Diamond <wichard@vitalitystudios.com>2015-08-20 15:20:55 -0500
commit7925c7972ee6f3ea08b90dba32a8eafe5bd71792 (patch)
treeffd74ccc1dcd737e57955a3a341affdca558cecf /src/libstd/sys/unix/backtrace/mod.rs
parentaca2057ed5fb7af3f8905b2bc01f72fa001c35c8 (diff)
downloadrust-7925c7972ee6f3ea08b90dba32a8eafe5bd71792.tar.gz
rust-7925c7972ee6f3ea08b90dba32a8eafe5bd71792.zip
Refactor unix backtracing. NFC.
Diffstat (limited to 'src/libstd/sys/unix/backtrace/mod.rs')
-rw-r--r--src/libstd/sys/unix/backtrace/mod.rs119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/libstd/sys/unix/backtrace/mod.rs b/src/libstd/sys/unix/backtrace/mod.rs
new file mode 100644
index 00000000000..80dcffd0e63
--- /dev/null
+++ b/src/libstd/sys/unix/backtrace/mod.rs
@@ -0,0 +1,119 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/// 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 OSX, 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 OSX. It appears dladdr()
+///   uses magic to consult the local symbol table, or we're putting everything
+///   in the dynamic symbol table anyway. Regardless, for OSX, 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 OSX, 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::write;
+
+use io;
+use io::prelude::*;
+use libc;
+use str;
+
+use sys_common::backtrace::{demangle, HEX_WIDTH};
+
+// tracing impls:
+mod tracing;
+// symbol resolvers:
+mod printing;
+
+pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
+              s: Option<&[u8]>) -> io::Result<()> {
+    try!(write!(w, "  {:2}: {:2$?} - ", idx, addr, HEX_WIDTH));
+    match s.and_then(|s| str::from_utf8(s).ok()) {
+        Some(string) => try!(demangle(w, string)),
+        None => try!(write!(w, "<unknown>")),
+    }
+    w.write_all(&['\n' as u8])
+}
+
+pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
+                       more: bool) -> io::Result<()> {
+    let file = str::from_utf8(file).unwrap_or("<unknown>");
+    // prior line: "  ##: {:2$} - func"
+    try!(write!(w, "      {:3$}at {}:{}", "", file, line, HEX_WIDTH));
+    if more {
+        try!(write!(w, " <... and possibly more>"));
+    }
+    w.write_all(&['\n' as u8])
+}