1 extern crate addr2line
;
3 extern crate fallible_iterator
;
7 extern crate typed_arena
;
11 use std
::io
::{BufRead, Lines, StdinLock, Write}
;
14 use clap
::{App, Arg, Values}
;
15 use fallible_iterator
::FallibleIterator
;
16 use object
::{Object, ObjectSection}
;
17 use typed_arena
::Arena
;
19 use addr2line
::{Context, Location}
;
21 fn parse_uint_from_hex_string(string
: &str) -> u64 {
22 if string
.len() > 2 && string
.starts_with("0x") {
23 u64::from_str_radix(&string
[2..], 16).expect("Failed to parse address")
25 u64::from_str_radix(string
, 16).expect("Failed to parse address")
31 Stdin(Lines
<StdinLock
<'a
>>),
34 impl<'a
> Iterator
for Addrs
<'a
> {
37 fn next(&mut self) -> Option
<u64> {
38 let text
= match *self {
39 Addrs
::Args(ref mut vals
) => vals
.next().map(Cow
::from
),
40 Addrs
::Stdin(ref mut lines
) => lines
.next().map(Result
::unwrap
).map(Cow
::from
),
44 .map(parse_uint_from_hex_string
)
48 fn print_loc(loc
: &Option
<Location
>, basenames
: bool
, llvm
: bool
) {
49 if let Some(ref loc
) = *loc
{
50 let file
= loc
.file
.as_ref().unwrap();
51 let path
= if basenames
{
52 Path
::new(Path
::new(file
).file_name().unwrap())
56 print
!("{}:", path
.display());
58 print
!("{}:{}", loc
.line
.unwrap_or(0), loc
.column
.unwrap_or(0));
59 } else if let Some(line
) = loc
.line
{
72 fn print_function(name
: &str, language
: Option
<gimli
::DwLang
>, demangle
: bool
) {
74 print
!("{}", addr2line
::demangle_auto(Cow
::from(name
), language
));
80 fn load_file_section
<'input
, 'arena
, Endian
: gimli
::Endianity
>(
82 file
: &object
::File
<'input
>,
84 arena_data
: &'arena Arena
<Cow
<'input
, [u8]>>,
85 ) -> Result
<gimli
::EndianSlice
<'arena
, Endian
>, ()> {
86 // TODO: Unify with dwarfdump.rs in gimli.
88 match file
.section_by_name(name
) {
89 Some(section
) => match section
.uncompressed_data().unwrap() {
90 Cow
::Borrowed(b
) => Ok(gimli
::EndianSlice
::new(b
, endian
)),
91 Cow
::Owned(b
) => Ok(gimli
::EndianSlice
::new(arena_data
.alloc(b
.into()), endian
)),
93 None
=> Ok(gimli
::EndianSlice
::new(&[][..], endian
)),
98 let matches
= App
::new("hardliner")
100 .about("A fast addr2line clone")
102 Arg
::with_name("exe")
105 .value_name("filename")
107 "Specify the name of the executable for which addresses should be translated.",
112 Arg
::with_name("sup")
114 .value_name("filename")
115 .help("Path to supplementary object file."),
118 Arg
::with_name("functions")
121 .help("Display function names as well as file and line number information."),
124 Arg
::with_name("pretty")
126 .long("pretty-print")
128 "Make the output more human friendly: each location are printed on \
132 .arg(Arg
::with_name("inlines").short("i").long("inlines").help(
133 "If the address belongs to a function that was inlined, the source \
134 information for all enclosing scopes back to the first non-inlined \
135 function will also be printed.",
138 Arg
::with_name("addresses")
142 "Display the address before the function name, file and line \
143 number information.",
147 Arg
::with_name("basenames")
150 .help("Display only the base of each file name."),
152 .arg(Arg
::with_name("demangle").short("C").long("demangle").help(
153 "Demangle function names. \
154 Specifying a specific demangling style (like GNU addr2line) \
155 is not supported. (TODO)",
158 Arg
::with_name("llvm")
160 .help("Display output in the same format as llvm-symbolizer."),
163 Arg
::with_name("addrs")
166 .help("Addresses to use instead of reading from stdin."),
170 let arena_data
= Arena
::new();
172 let do_functions
= matches
.is_present("functions");
173 let do_inlines
= matches
.is_present("inlines");
174 let pretty
= matches
.is_present("pretty");
175 let print_addrs
= matches
.is_present("addresses");
176 let basenames
= matches
.is_present("basenames");
177 let demangle
= matches
.is_present("demangle");
178 let llvm
= matches
.is_present("llvm");
179 let path
= matches
.value_of("exe").unwrap();
181 let file
= File
::open(path
).unwrap();
182 let map
= unsafe { memmap::Mmap::map(&file).unwrap() }
;
183 let object
= &object
::File
::parse(&*map
).unwrap();
185 let endian
= if object
.is_little_endian() {
186 gimli
::RunTimeEndian
::Little
188 gimli
::RunTimeEndian
::Big
191 let mut load_section
= |id
: gimli
::SectionId
| -> Result
<_
, _
> {
192 load_file_section(id
, object
, endian
, &arena_data
)
196 let sup_object
= if let Some(sup_path
) = matches
.value_of("sup") {
197 let sup_file
= File
::open(sup_path
).unwrap();
198 sup_map
= unsafe { memmap::Mmap::map(&sup_file).unwrap() }
;
199 Some(object
::File
::parse(&*sup_map
).unwrap())
204 let symbols
= object
.symbol_map();
205 let mut dwarf
= gimli
::Dwarf
::load(&mut load_section
).unwrap();
206 if let Some(ref sup_object
) = sup_object
{
207 let mut load_sup_section
= |id
: gimli
::SectionId
| -> Result
<_
, _
> {
208 load_file_section(id
, sup_object
, endian
, &arena_data
)
210 dwarf
.load_sup(&mut load_sup_section
).unwrap();
213 let ctx
= Context
::from_dwarf(dwarf
).unwrap();
215 let stdin
= std
::io
::stdin();
219 .unwrap_or_else(|| Addrs
::Stdin(stdin
.lock().lines()));
224 print
!("0x{:x}", probe
);
226 print
!("0x{:016x}", probe
);
235 if do_functions
|| do_inlines
{
236 let mut printed_anything
= false;
237 let mut frames
= ctx
.find_frames(probe
).unwrap().enumerate();
238 while let Some((i
, frame
)) = frames
.next().unwrap() {
239 if pretty
&& i
!= 0 {
240 print
!(" (inlined by) ");
244 if let Some(func
) = frame
.function
{
245 print_function(&func
.raw_name().unwrap(), func
.language
, demangle
);
246 } else if let Some(name
) = symbols
.get(probe
).map(|x
| x
.name()) {
247 print_function(name
, None
, demangle
);
259 print_loc(&frame
.location
, basenames
, llvm
);
261 printed_anything
= true;
268 if !printed_anything
{
270 if let Some(name
) = symbols
.get(probe
).map(|x
| x
.name()) {
271 print_function(name
, None
, demangle
);
290 let loc
= ctx
.find_location(probe
).unwrap();
291 print_loc(&loc
, basenames
, llvm
);
297 std
::io
::stdout().flush().unwrap();