diff options
Diffstat (limited to 'vendor/addr2line/examples/addr2line.rs')
-rw-r--r-- | vendor/addr2line/examples/addr2line.rs | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/vendor/addr2line/examples/addr2line.rs b/vendor/addr2line/examples/addr2line.rs new file mode 100644 index 0000000..97fc802 --- /dev/null +++ b/vendor/addr2line/examples/addr2line.rs @@ -0,0 +1,317 @@ +use std::borrow::Cow; +use std::fs::File; +use std::io::{BufRead, Lines, StdinLock, Write}; +use std::path::{Path, PathBuf}; + +use clap::{Arg, ArgAction, Command}; +use fallible_iterator::FallibleIterator; +use object::{Object, ObjectSection, SymbolMap, SymbolMapName}; +use typed_arena::Arena; + +use addr2line::{Context, Location}; + +fn parse_uint_from_hex_string(string: &str) -> Option<u64> { + if string.len() > 2 && string.starts_with("0x") { + u64::from_str_radix(&string[2..], 16).ok() + } else { + u64::from_str_radix(string, 16).ok() + } +} + +enum Addrs<'a> { + Args(clap::parser::ValuesRef<'a, String>), + Stdin(Lines<StdinLock<'a>>), +} + +impl<'a> Iterator for Addrs<'a> { + type Item = Option<u64>; + + fn next(&mut self) -> Option<Option<u64>> { + let text = match *self { + Addrs::Args(ref mut vals) => vals.next().map(Cow::from), + Addrs::Stdin(ref mut lines) => lines.next().map(Result::unwrap).map(Cow::from), + }; + text.as_ref() + .map(Cow::as_ref) + .map(parse_uint_from_hex_string) + } +} + +fn print_loc(loc: Option<&Location<'_>>, basenames: bool, llvm: bool) { + if let Some(loc) = loc { + if let Some(ref file) = loc.file.as_ref() { + let path = if basenames { + Path::new(Path::new(file).file_name().unwrap()) + } else { + Path::new(file) + }; + print!("{}:", path.display()); + } else { + print!("??:"); + } + if llvm { + print!("{}:{}", loc.line.unwrap_or(0), loc.column.unwrap_or(0)); + } else if let Some(line) = loc.line { + print!("{}", line); + } else { + print!("?"); + } + println!(); + } else if llvm { + println!("??:0:0"); + } else { + println!("??:0"); + } +} + +fn print_function(name: Option<&str>, language: Option<gimli::DwLang>, demangle: bool) { + if let Some(name) = name { + if demangle { + print!("{}", addr2line::demangle_auto(Cow::from(name), language)); + } else { + print!("{}", name); + } + } else { + print!("??"); + } +} + +fn load_file_section<'input, 'arena, Endian: gimli::Endianity>( + id: gimli::SectionId, + file: &object::File<'input>, + endian: Endian, + arena_data: &'arena Arena<Cow<'input, [u8]>>, +) -> Result<gimli::EndianSlice<'arena, Endian>, ()> { + // TODO: Unify with dwarfdump.rs in gimli. + let name = id.name(); + match file.section_by_name(name) { + Some(section) => match section.uncompressed_data().unwrap() { + Cow::Borrowed(b) => Ok(gimli::EndianSlice::new(b, endian)), + Cow::Owned(b) => Ok(gimli::EndianSlice::new(arena_data.alloc(b.into()), endian)), + }, + None => Ok(gimli::EndianSlice::new(&[][..], endian)), + } +} + +fn find_name_from_symbols<'a>( + symbols: &'a SymbolMap<SymbolMapName<'_>>, + probe: u64, +) -> Option<&'a str> { + symbols.get(probe).map(|x| x.name()) +} + +struct Options<'a> { + do_functions: bool, + do_inlines: bool, + pretty: bool, + print_addrs: bool, + basenames: bool, + demangle: bool, + llvm: bool, + exe: &'a PathBuf, + sup: Option<&'a PathBuf>, +} + +fn main() { + let matches = Command::new("addr2line") + .version(env!("CARGO_PKG_VERSION")) + .about("A fast addr2line Rust port") + .max_term_width(100) + .args(&[ + Arg::new("exe") + .short('e') + .long("exe") + .value_name("filename") + .value_parser(clap::value_parser!(PathBuf)) + .help( + "Specify the name of the executable for which addresses should be translated.", + ) + .required(true), + Arg::new("sup") + .long("sup") + .value_name("filename") + .value_parser(clap::value_parser!(PathBuf)) + .help("Path to supplementary object file."), + Arg::new("functions") + .short('f') + .long("functions") + .action(ArgAction::SetTrue) + .help("Display function names as well as file and line number information."), + Arg::new("pretty").short('p').long("pretty-print") + .action(ArgAction::SetTrue) + .help( + "Make the output more human friendly: each location are printed on one line.", + ), + Arg::new("inlines").short('i').long("inlines") + .action(ArgAction::SetTrue) + .help( + "If the address belongs to a function that was inlined, the source information for \ + all enclosing scopes back to the first non-inlined function will also be printed.", + ), + Arg::new("addresses").short('a').long("addresses") + .action(ArgAction::SetTrue) + .help( + "Display the address before the function name, file and line number information.", + ), + Arg::new("basenames") + .short('s') + .long("basenames") + .action(ArgAction::SetTrue) + .help("Display only the base of each file name."), + Arg::new("demangle").short('C').long("demangle") + .action(ArgAction::SetTrue) + .help( + "Demangle function names. \ + Specifying a specific demangling style (like GNU addr2line) is not supported. \ + (TODO)" + ), + Arg::new("llvm") + .long("llvm") + .action(ArgAction::SetTrue) + .help("Display output in the same format as llvm-symbolizer."), + Arg::new("addrs") + .action(ArgAction::Append) + .help("Addresses to use instead of reading from stdin."), + ]) + .get_matches(); + + let arena_data = Arena::new(); + + let opts = Options { + do_functions: matches.get_flag("functions"), + do_inlines: matches.get_flag("inlines"), + pretty: matches.get_flag("pretty"), + print_addrs: matches.get_flag("addresses"), + basenames: matches.get_flag("basenames"), + demangle: matches.get_flag("demangle"), + llvm: matches.get_flag("llvm"), + exe: matches.get_one::<PathBuf>("exe").unwrap(), + sup: matches.get_one::<PathBuf>("sup"), + }; + + let file = File::open(opts.exe).unwrap(); + let map = unsafe { memmap2::Mmap::map(&file).unwrap() }; + let object = &object::File::parse(&*map).unwrap(); + + let endian = if object.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + + let mut load_section = |id: gimli::SectionId| -> Result<_, _> { + load_file_section(id, object, endian, &arena_data) + }; + + let sup_map; + let sup_object = if let Some(sup_path) = opts.sup { + let sup_file = File::open(sup_path).unwrap(); + sup_map = unsafe { memmap2::Mmap::map(&sup_file).unwrap() }; + Some(object::File::parse(&*sup_map).unwrap()) + } else { + None + }; + + let symbols = object.symbol_map(); + let mut dwarf = gimli::Dwarf::load(&mut load_section).unwrap(); + if let Some(ref sup_object) = sup_object { + let mut load_sup_section = |id: gimli::SectionId| -> Result<_, _> { + load_file_section(id, sup_object, endian, &arena_data) + }; + dwarf.load_sup(&mut load_sup_section).unwrap(); + } + + let mut split_dwarf_loader = addr2line::builtin_split_dwarf_loader::SplitDwarfLoader::new( + |data, endian| { + gimli::EndianSlice::new(arena_data.alloc(Cow::Owned(data.into_owned())), endian) + }, + Some(opts.exe.clone()), + ); + let ctx = Context::from_dwarf(dwarf).unwrap(); + + let stdin = std::io::stdin(); + let addrs = matches + .get_many::<String>("addrs") + .map(Addrs::Args) + .unwrap_or_else(|| Addrs::Stdin(stdin.lock().lines())); + + for probe in addrs { + if opts.print_addrs { + let addr = probe.unwrap_or(0); + if opts.llvm { + print!("0x{:x}", addr); + } else { + print!("0x{:016x}", addr); + } + if opts.pretty { + print!(": "); + } else { + println!(); + } + } + + if opts.do_functions || opts.do_inlines { + let mut printed_anything = false; + if let Some(probe) = probe { + let frames = ctx.find_frames(probe); + let frames = split_dwarf_loader.run(frames).unwrap(); + let mut frames = frames.enumerate(); + while let Some((i, frame)) = frames.next().unwrap() { + if opts.pretty && i != 0 { + print!(" (inlined by) "); + } + + if opts.do_functions { + if let Some(func) = frame.function { + print_function( + func.raw_name().ok().as_ref().map(AsRef::as_ref), + func.language, + opts.demangle, + ); + } else { + let name = find_name_from_symbols(&symbols, probe); + print_function(name, None, opts.demangle); + } + + if opts.pretty { + print!(" at "); + } else { + println!(); + } + } + + print_loc(frame.location.as_ref(), opts.basenames, opts.llvm); + + printed_anything = true; + + if !opts.do_inlines { + break; + } + } + } + + if !printed_anything { + if opts.do_functions { + let name = probe.and_then(|probe| find_name_from_symbols(&symbols, probe)); + print_function(name, None, opts.demangle); + + if opts.pretty { + print!(" at "); + } else { + println!(); + } + } + + print_loc(None, opts.basenames, opts.llvm); + } + } else { + let loc = probe.and_then(|probe| ctx.find_location(probe).unwrap()); + print_loc(loc.as_ref(), opts.basenames, opts.llvm); + } + + if opts.llvm { + println!(); + } + std::io::stdout().flush().unwrap(); + } +} |