diff options
Diffstat (limited to 'vendor/addr2line/tests')
-rw-r--r-- | vendor/addr2line/tests/correctness.rs | 126 | ||||
-rw-r--r-- | vendor/addr2line/tests/output_equivalence.rs | 135 | ||||
-rw-r--r-- | vendor/addr2line/tests/parse.rs | 114 |
3 files changed, 375 insertions, 0 deletions
diff --git a/vendor/addr2line/tests/correctness.rs b/vendor/addr2line/tests/correctness.rs new file mode 100644 index 0000000..73ee462 --- /dev/null +++ b/vendor/addr2line/tests/correctness.rs @@ -0,0 +1,126 @@ +use addr2line::Context; +use fallible_iterator::FallibleIterator; +use findshlibs::{IterationControl, SharedLibrary, TargetSharedLibrary}; +use object::Object; +use std::borrow::Cow; +use std::fs::File; +use std::sync::Arc; + +fn find_debuginfo() -> memmap2::Mmap { + let path = std::env::current_exe().unwrap(); + let file = File::open(&path).unwrap(); + let map = unsafe { memmap2::Mmap::map(&file).unwrap() }; + let file = &object::File::parse(&*map).unwrap(); + if let Ok(uuid) = file.mach_uuid() { + for candidate in path.parent().unwrap().read_dir().unwrap() { + let path = candidate.unwrap().path(); + if !path.to_str().unwrap().ends_with(".dSYM") { + continue; + } + for candidate in path.join("Contents/Resources/DWARF").read_dir().unwrap() { + let path = candidate.unwrap().path(); + let file = File::open(&path).unwrap(); + let map = unsafe { memmap2::Mmap::map(&file).unwrap() }; + let file = &object::File::parse(&*map).unwrap(); + if file.mach_uuid().unwrap() == uuid { + return map; + } + } + } + } + + return map; +} + +#[test] +fn correctness() { + let map = find_debuginfo(); + let file = &object::File::parse(&*map).unwrap(); + let module_base = file.relative_address_base(); + + let endian = if file.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + + fn load_section<'data: 'file, 'file, O, Endian>( + id: gimli::SectionId, + file: &'file O, + endian: Endian, + ) -> Result<gimli::EndianArcSlice<Endian>, gimli::Error> + where + O: object::Object<'data, 'file>, + Endian: gimli::Endianity, + { + use object::ObjectSection; + + let data = file + .section_by_name(id.name()) + .and_then(|section| section.uncompressed_data().ok()) + .unwrap_or(Cow::Borrowed(&[])); + Ok(gimli::EndianArcSlice::new(Arc::from(&*data), endian)) + } + + let dwarf = gimli::Dwarf::load(|id| load_section(id, file, endian)).unwrap(); + let ctx = Context::from_dwarf(dwarf).unwrap(); + let mut split_dwarf_loader = addr2line::builtin_split_dwarf_loader::SplitDwarfLoader::new( + |data, endian| gimli::EndianArcSlice::new(Arc::from(&*data), endian), + None, + ); + + let mut bias = None; + TargetSharedLibrary::each(|lib| { + bias = Some((lib.virtual_memory_bias().0 as u64).wrapping_sub(module_base)); + IterationControl::Break + }); + + #[allow(unused_mut)] + let mut test = |sym: u64, expected_prefix: &str| { + let ip = sym.wrapping_sub(bias.unwrap()); + + let frames = ctx.find_frames(ip); + let frames = split_dwarf_loader.run(frames).unwrap(); + let frame = frames.last().unwrap().unwrap(); + let name = frame.function.as_ref().unwrap().demangle().unwrap(); + // Old rust versions generate DWARF with wrong linkage name, + // so only check the start. + if !name.starts_with(expected_prefix) { + panic!("incorrect name '{}', expected {:?}", name, expected_prefix); + } + }; + + test(test_function as u64, "correctness::test_function"); + test( + small::test_function as u64, + "correctness::small::test_function", + ); + test(auxiliary::foo as u64, "auxiliary::foo"); +} + +mod small { + pub fn test_function() { + println!("y"); + } +} + +fn test_function() { + println!("x"); +} + +#[test] +fn zero_function() { + let map = find_debuginfo(); + let file = &object::File::parse(&*map).unwrap(); + let ctx = Context::new(file).unwrap(); + for probe in 0..10 { + assert!( + ctx.find_frames(probe) + .skip_all_loads() + .unwrap() + .count() + .unwrap() + < 10 + ); + } +} diff --git a/vendor/addr2line/tests/output_equivalence.rs b/vendor/addr2line/tests/output_equivalence.rs new file mode 100644 index 0000000..ef026e3 --- /dev/null +++ b/vendor/addr2line/tests/output_equivalence.rs @@ -0,0 +1,135 @@ +use std::env; +use std::ffi::OsStr; +use std::path::Path; +use std::process::Command; + +use backtrace::Backtrace; +use findshlibs::{IterationControl, SharedLibrary, TargetSharedLibrary}; +use libtest_mimic::{Arguments, Failed, Trial}; + +#[inline(never)] +fn make_trace() -> Vec<String> { + fn foo() -> Backtrace { + bar() + } + #[inline(never)] + fn bar() -> Backtrace { + baz() + } + #[inline(always)] + fn baz() -> Backtrace { + Backtrace::new_unresolved() + } + + let mut base_addr = None; + TargetSharedLibrary::each(|lib| { + base_addr = Some(lib.virtual_memory_bias().0 as isize); + IterationControl::Break + }); + let addrfix = -base_addr.unwrap(); + + let trace = foo(); + trace + .frames() + .iter() + .take(5) + .map(|x| format!("{:p}", (x.ip() as *const u8).wrapping_offset(addrfix))) + .collect() +} + +fn run_cmd<P: AsRef<OsStr>>(exe: P, me: &Path, flags: Option<&str>, trace: &str) -> String { + let mut cmd = Command::new(exe); + cmd.env("LC_ALL", "C"); // GNU addr2line is localized, we aren't + cmd.env("RUST_BACKTRACE", "1"); // if a child crashes, we want to know why + + if let Some(flags) = flags { + cmd.arg(flags); + } + cmd.arg("--exe").arg(me).arg(trace); + + let output = cmd.output().unwrap(); + + assert!(output.status.success()); + String::from_utf8(output.stdout).unwrap() +} + +fn run_test(flags: Option<&str>) -> Result<(), Failed> { + let me = env::current_exe().unwrap(); + let mut exe = me.clone(); + assert!(exe.pop()); + if exe.file_name().unwrap().to_str().unwrap() == "deps" { + assert!(exe.pop()); + } + exe.push("examples"); + exe.push("addr2line"); + + assert!(exe.is_file()); + + let trace = make_trace(); + + // HACK: GNU addr2line has a bug where looking up multiple addresses can cause the second + // lookup to fail. Workaround by doing one address at a time. + for addr in &trace { + let theirs = run_cmd("addr2line", &me, flags, addr); + let ours = run_cmd(&exe, &me, flags, addr); + + // HACK: GNU addr2line does not tidy up paths properly, causing double slashes to be printed. + // We consider our behavior to be correct, so we fix their output to match ours. + let theirs = theirs.replace("//", "/"); + + assert!( + theirs == ours, + "Output not equivalent: + +$ addr2line {0} --exe {1} {2} +{4} +$ {3} {0} --exe {1} {2} +{5} + + +", + flags.unwrap_or(""), + me.display(), + trace.join(" "), + exe.display(), + theirs, + ours + ); + } + Ok(()) +} + +static FLAGS: &str = "aipsf"; + +fn make_tests() -> Vec<Trial> { + (0..(1 << FLAGS.len())) + .map(|bits| { + if bits == 0 { + None + } else { + let mut param = String::new(); + param.push('-'); + for (i, flag) in FLAGS.chars().enumerate() { + if (bits & (1 << i)) != 0 { + param.push(flag); + } + } + Some(param) + } + }) + .map(|param| { + Trial::test( + format!("addr2line {}", param.as_ref().map_or("", String::as_str)), + move || run_test(param.as_ref().map(String::as_str)), + ) + }) + .collect() +} + +fn main() { + if !cfg!(target_os = "linux") { + return; + } + let args = Arguments::from_args(); + libtest_mimic::run(&args, make_tests()).exit(); +} diff --git a/vendor/addr2line/tests/parse.rs b/vendor/addr2line/tests/parse.rs new file mode 100644 index 0000000..4dafe38 --- /dev/null +++ b/vendor/addr2line/tests/parse.rs @@ -0,0 +1,114 @@ +use std::borrow::Cow; +use std::env; +use std::fs::File; +use std::path::{self, PathBuf}; + +use object::Object; + +fn release_fixture_path() -> PathBuf { + if let Ok(p) = env::var("ADDR2LINE_FIXTURE_PATH") { + return p.into(); + } + + let mut path = PathBuf::new(); + if let Ok(dir) = env::var("CARGO_MANIFEST_DIR") { + path.push(dir); + } + path.push("fixtures"); + path.push("addr2line-release"); + path +} + +fn with_file<F: FnOnce(&object::File<'_>)>(target: &path::Path, f: F) { + let file = File::open(target).unwrap(); + let map = unsafe { memmap2::Mmap::map(&file).unwrap() }; + let file = object::File::parse(&*map).unwrap(); + f(&file) +} + +fn dwarf_load<'a>(object: &object::File<'a>) -> gimli::Dwarf<Cow<'a, [u8]>> { + let load_section = |id: gimli::SectionId| -> Result<Cow<'a, [u8]>, gimli::Error> { + use object::ObjectSection; + + let data = object + .section_by_name(id.name()) + .and_then(|section| section.data().ok()) + .unwrap_or(&[][..]); + Ok(Cow::Borrowed(data)) + }; + gimli::Dwarf::load(&load_section).unwrap() +} + +fn dwarf_borrow<'a>( + dwarf: &'a gimli::Dwarf<Cow<'_, [u8]>>, +) -> gimli::Dwarf<gimli::EndianSlice<'a, gimli::LittleEndian>> { + let borrow_section: &dyn for<'b> Fn( + &'b Cow<'_, [u8]>, + ) -> gimli::EndianSlice<'b, gimli::LittleEndian> = + &|section| gimli::EndianSlice::new(section, gimli::LittleEndian); + dwarf.borrow(&borrow_section) +} + +#[test] +fn parse_base_rc() { + let target = release_fixture_path(); + + with_file(&target, |file| { + addr2line::ObjectContext::new(file).unwrap(); + }); +} + +#[test] +fn parse_base_slice() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let dwarf = dwarf_load(file); + let dwarf = dwarf_borrow(&dwarf); + addr2line::Context::from_dwarf(dwarf).unwrap(); + }); +} + +#[test] +fn parse_lines_rc() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let context = addr2line::ObjectContext::new(file).unwrap(); + context.parse_lines().unwrap(); + }); +} + +#[test] +fn parse_lines_slice() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let dwarf = dwarf_load(file); + let dwarf = dwarf_borrow(&dwarf); + let context = addr2line::Context::from_dwarf(dwarf).unwrap(); + context.parse_lines().unwrap(); + }); +} + +#[test] +fn parse_functions_rc() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let context = addr2line::ObjectContext::new(file).unwrap(); + context.parse_functions().unwrap(); + }); +} + +#[test] +fn parse_functions_slice() { + let target = release_fixture_path(); + + with_file(&target, |file| { + let dwarf = dwarf_load(file); + let dwarf = dwarf_borrow(&dwarf); + let context = addr2line::Context::from_dwarf(dwarf).unwrap(); + context.parse_functions().unwrap(); + }); +} |