diff --git a/Cargo.toml b/Cargo.toml index a3e6042..56e12ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,10 @@ keywords = [ [features] default = ["gimli-symbolize"] gimli-symbolize = ["backtrace/gimli-symbolize"] +resolve-modules = ["regex"] [dependencies] termcolor = "1.0" atty = "0.2" backtrace = "0.3" +regex = { version = "1.4", optional = true } diff --git a/src/lib.rs b/src/lib.rs index a49f026..54f4db0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,6 +156,7 @@ pub struct Frame { pub name: Option, pub lineno: Option, pub filename: Option, + pub ip: usize, _private_ctor: (), } @@ -293,12 +294,82 @@ impl Frame { Ok(()) } + /// Get the module's name by walking /proc/self/maps + #[cfg(all( + feature = "resolve-modules", + unix, + not(any(target_os = "macos", target_os = "ios")) + ))] + fn module_info(&self) -> Option<(String, usize)> { + use regex::Regex; + use std::path::Path; + let re = Regex::new( + r"(?x) + ^ + (?P[0-9a-f]{8,16}) + - + (?P[0-9a-f]{8,16}) + \s + (?P[-rwxp]{4}) + \s + (?P[0-9a-f]{8}) + \s + [0-9a-f]+:[0-9a-f]+ + \s + [0-9]+ + \s+ + (?P.*) + $ + ", + ) + .unwrap(); + + let mapsfile = File::open("/proc/self/maps").expect("Unable to open /proc/self/maps"); + + for line in BufReader::new(mapsfile).lines() { + let line = line.unwrap(); + if let Some(caps) = re.captures(&line) { + let (start, end, path) = ( + usize::from_str_radix(caps.name("start").unwrap().as_str(), 16).unwrap(), + usize::from_str_radix(caps.name("end").unwrap().as_str(), 16).unwrap(), + caps.name("path").unwrap().as_str().to_string(), + ); + if self.ip >= start && self.ip < end { + return if let Some(filename) = Path::new(&path).file_name() { + Some((filename.to_str().unwrap().to_string(), start)) + } else { + None + }; + } + } + } + + None + } + + #[cfg(not(all( + feature = "resolve-modules", + unix, + not(any(target_os = "macos", target_os = "ios")) + )))] + fn module_info(&self) -> Option<(String, usize)> { + None + } + fn print(&self, i: usize, out: &mut impl WriteColor, s: &BacktracePrinter) -> IOResult { let is_dependency_code = self.is_dependency_code(); // Print frame index. write!(out, "{:>2}: ", i)?; + if s.should_print_addresses() { + if let Some((module_name, module_base)) = self.module_info() { + write!(out, "{}:0x{:08x} - ", module_name, self.ip - module_base)?; + } else { + write!(out, "0x{:016x} - ", self.ip)?; + } + } + // Does the function have a hash suffix? // (dodging a dep on the regex crate here) let name = self @@ -441,6 +512,7 @@ pub struct BacktracePrinter { is_panic_handler: bool, colors: ColorScheme, filters: Vec>, + should_print_addresses: bool, } impl Default for BacktracePrinter { @@ -453,6 +525,7 @@ impl Default for BacktracePrinter { colors: ColorScheme::classic(), is_panic_handler: false, filters: vec![Arc::new(default_frame_filter)], + should_print_addresses: false, } } } @@ -465,6 +538,7 @@ impl std::fmt::Debug for BacktracePrinter { .field("lib_verbosity", &self.lib_verbosity) .field("strip_function_hash", &self.strip_function_hash) .field("is_panic_handler", &self.is_panic_handler) + .field("print_addresses", &self.should_print_addresses) .field("colors", &self.colors) .finish() } @@ -517,6 +591,14 @@ impl BacktracePrinter { self } + /// Controls whether addresses (or module offsets if available) should be printed. + /// + /// Defaults to `false`. + pub fn print_addresses(mut self, val: bool) -> Self { + self.should_print_addresses = val; + self + } + /// Add a custom filter to the set of frame filters /// /// Filters are run in the order they are added. @@ -582,13 +664,14 @@ impl BacktracePrinter { let frames: Vec<_> = trace .frames() .iter() - .flat_map(|frame| frame.symbols()) + .flat_map(|frame| frame.symbols().iter().map(move |sym| (frame.ip(), sym))) .zip(1usize..) - .map(|(sym, n)| Frame { + .map(|((ip, sym), n)| Frame { name: sym.name().map(|x| x.to_string()), lineno: sym.lineno(), filename: sym.filename().map(|x| x.into()), n, + ip: ip as usize, _private_ctor: (), }) .collect(); @@ -723,6 +806,10 @@ impl BacktracePrinter { self.lib_verbosity } } + + fn should_print_addresses(&self) -> bool { + self.should_print_addresses + } } // ============================================================================================== //