Skip to content

Commit

Permalink
Replace clap with bpaf
Browse files Browse the repository at this point in the history
  • Loading branch information
pacak committed Aug 25, 2022
1 parent 313e592 commit 2c628f2
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 133 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ compiler_builtins = { version = '0.1.2', optional = true }

[dev-dependencies]
memmap2 = "0.5.5"
clap = "3.1.6"
bpaf = { version = "0.5.3", default-features = false }
backtrace = "0.3.13"
findshlibs = "0.10"
rustc-test = "0.3"
Expand Down
289 changes: 157 additions & 132 deletions examples/addr2line.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
extern crate addr2line;
extern crate clap;
extern crate bpaf;
extern crate fallible_iterator;
extern crate gimli;
extern crate memmap2;
Expand All @@ -8,45 +8,25 @@ extern crate typed_arena;

use std::borrow::Cow;
use std::fs::File;
use std::io::{BufRead, Lines, StdinLock, Write};
use std::path::Path;
use std::io::{BufRead, Write};
use std::path::{Path, PathBuf};

use clap::{Arg, Command, Values};
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> {
fn parse_uint_from_hex_string(string: &str) -> Result<u64, std::num::ParseIntError> {
if string.len() > 2 && string.starts_with("0x") {
u64::from_str_radix(&string[2..], 16).ok()
u64::from_str_radix(&string[2..], 16)
} else {
u64::from_str_radix(string, 16).ok()
}
}

enum Addrs<'a> {
Args(Values<'a>),
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)
u64::from_str_radix(string, 16)
}
}

fn print_loc(loc: Option<&Location>, basenames: bool, llvm: bool) {
if let Some(ref loc) = loc {
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())
Expand Down Expand Up @@ -108,68 +88,103 @@ fn find_name_from_symbols<'a>(
symbols.get(probe).map(|x| x.name())
}

struct Options {
exe: PathBuf,
sup: Option<PathBuf>,
do_functions: bool,
do_inlines: bool,
pretty: bool,
print_addrs: bool,
basenames: bool,
demangle: bool,
llvm: bool,
addrs: Vec<Option<u64>>,
}

fn options() -> Options {
use bpaf::*;

let print_addrs = short('a')
.long("addresses")
.help("Display the address before the function name, file and line number information.")
.switch();

let exe = short('e')
.long("exe")
.help("Specify the name of the executable for which addresses should be translated.")
.argument_os("filename")
.map(PathBuf::from);

let sup = long("sup")
.help("Path to supplementary object file.")
.argument_os("filename")
.map(PathBuf::from)
.optional();

let do_functions = short('f')
.long("functions")
.help("Display function names as well as file and line number information.")
.switch();

let pretty = short('p')
.long("pretty-print")
.help("Make the output more human friendly: each location are printed on one line.")
.switch();

let do_inlines = short('i')
.long("inlines")
.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.",
)
.switch();

let basenames = short('s')
.long("basenames")
.help("Display only the base of each file name.")
.switch();

let demangle = short('C')
.long("demangle")
.help(
"Demangle function names.
Specifying a specific demangling style (like GNU addr2line) is not supported. (TODO)",
)
.switch();

let llvm = short('L')
.long("llvm")
.help("Display output in the same format as llvm-symbolizer.")
.switch();

let addrs = positional("addrs")
.help("Addresses to use instead of reading from stdin.")
.map(|s| parse_uint_from_hex_string(&s).ok())
.many();

construct!(Options {
print_addrs,
do_functions,
exe,
sup,
pretty,
demangle,
basenames,
do_inlines,
llvm,
addrs,
})
.to_options()
.version(env!("CARGO_PKG_VERSION"))
.run()
}

fn main() {
let matches = Command::new("addr2line")
.version("0.1")
.about("A fast addr2line Rust port")
.args(&[
Arg::new("exe")
.short('e')
.long("exe")
.value_name("filename")
.help(
"Specify the name of the executable for which addresses should be translated.",
)
.required(true),
Arg::new("sup")
.long("sup")
.value_name("filename")
.help("Path to supplementary object file."),
Arg::new("functions")
.short('f')
.long("functions")
.help("Display function names as well as file and line number information."),
Arg::new("pretty").short('p').long("pretty-print").help(
"Make the output more human friendly: each location are printed on one line.",
),
Arg::new("inlines").short('i').long("inlines").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").help(
"Display the address before the function name, file and line number information.",
),
Arg::new("basenames")
.short('s')
.long("basenames")
.help("Display only the base of each file name."),
Arg::new("demangle").short('C').long("demangle").help(
"Demangle function names. \
Specifying a specific demangling style (like GNU addr2line) is not supported. \
(TODO)"
),
Arg::new("llvm")
.long("llvm")
.help("Display output in the same format as llvm-symbolizer."),
Arg::new("addrs")
.takes_value(true)
.multiple_occurrences(true)
.help("Addresses to use instead of reading from stdin."),
])
.get_matches();
let opts = options();

let arena_data = Arena::new();

let do_functions = matches.is_present("functions");
let do_inlines = matches.is_present("inlines");
let pretty = matches.is_present("pretty");
let print_addrs = matches.is_present("addresses");
let basenames = matches.is_present("basenames");
let demangle = matches.is_present("demangle");
let llvm = matches.is_present("llvm");
let path = matches.value_of("exe").unwrap();

let file = File::open(path).unwrap();
let file = File::open(opts.exe).unwrap();
let map = unsafe { memmap2::Mmap::map(&file).unwrap() };
let object = &object::File::parse(&*map).unwrap();

Expand All @@ -184,7 +199,7 @@ fn main() {
};

let sup_map;
let sup_object = if let Some(sup_path) = matches.value_of("sup") {
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())
Expand All @@ -203,85 +218,95 @@ fn main() {

let ctx = Context::from_dwarf(dwarf).unwrap();

let stdin = std::io::stdin();
let addrs = matches
.values_of("addrs")
.map(Addrs::Args)
.unwrap_or_else(|| Addrs::Stdin(stdin.lock().lines()));
let addrs = if opts.addrs.is_empty() {
std::io::stdin()
.lock()
.lines()
.map(|s| parse_uint_from_hex_string(&s.unwrap()).ok())
.collect::<Vec<_>>()
} else {
opts.addrs
};

for probe in addrs {
if print_addrs {
let addr = probe.unwrap_or(0);
if llvm {
for mprobe in addrs {
let probe = match mprobe {
Some(probe) => probe,
None => {
println!("??:0");
continue;
}
};
if opts.print_addrs {
let addr = probe;
if opts.llvm {
print!("0x{:x}", addr);
} else {
print!("0x{:016x}", addr);
}
if pretty {
if opts.pretty {
print!(": ");
} else {
println!();
}
}

if do_functions || do_inlines {
if opts.do_functions || opts.do_inlines {
let mut printed_anything = false;
if let Some(probe) = probe {
let mut frames = ctx.find_frames(probe).unwrap().enumerate();
while let Some((i, frame)) = frames.next().unwrap() {
if pretty && i != 0 {
print!(" (inlined by) ");

let mut frames = ctx.find_frames(probe).unwrap().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 do_functions {
if let Some(func) = frame.function {
print_function(
func.raw_name().ok().as_ref().map(AsRef::as_ref),
func.language,
demangle,
);
} else {
let name = find_name_from_symbols(&symbols, probe);
print_function(name, None, demangle);
}

if pretty {
print!(" at ");
} else {
println!();
}
if opts.pretty {
print!(" at ");
} else {
println!();
}
}

print_loc(frame.location.as_ref(), basenames, llvm);
print_loc(frame.location.as_ref(), opts.basenames, opts.llvm);

printed_anything = true;
printed_anything = true;

if !do_inlines {
break;
}
if !opts.do_inlines {
break;
}
}

if !printed_anything {
if do_functions {
let name = probe.and_then(|probe| find_name_from_symbols(&symbols, probe));
print_function(name, None, demangle);
if opts.do_functions {
let name = find_name_from_symbols(&symbols, probe);
print_function(name, None, opts.demangle);

if pretty {
if opts.pretty {
print!(" at ");
} else {
println!();
}
}

print_loc(None, basenames, llvm);
print_loc(None, opts.basenames, opts.llvm);
}
} else {
let loc = probe.and_then(|probe| ctx.find_location(probe).unwrap());
print_loc(loc.as_ref(), basenames, llvm);
let loc = ctx.find_location(probe).unwrap();
print_loc(loc.as_ref(), opts.basenames, opts.llvm);
}

if llvm {
if opts.llvm {
println!();
}
std::io::stdout().flush().unwrap();
Expand Down

0 comments on commit 2c628f2

Please sign in to comment.