diff options
Diffstat (limited to 'vendor/addr2line/tests')
-rw-r--r-- | vendor/addr2line/tests/correctness.rs | 91 | ||||
-rw-r--r-- | vendor/addr2line/tests/output_equivalence.rs | 145 | ||||
-rw-r--r-- | vendor/addr2line/tests/parse.rs | 118 |
3 files changed, 354 insertions, 0 deletions
diff --git a/vendor/addr2line/tests/correctness.rs b/vendor/addr2line/tests/correctness.rs new file mode 100644 index 000000000..3f7b43373 --- /dev/null +++ b/vendor/addr2line/tests/correctness.rs @@ -0,0 +1,91 @@ +extern crate addr2line; +extern crate fallible_iterator; +extern crate findshlibs; +extern crate gimli; +extern crate memmap; +extern crate object; + +use addr2line::Context; +use fallible_iterator::FallibleIterator; +use findshlibs::{IterationControl, SharedLibrary, TargetSharedLibrary}; +use object::Object; +use std::fs::File; + +fn find_debuginfo() -> memmap::Mmap { + let path = std::env::current_exe().unwrap(); + let file = File::open(&path).unwrap(); + let map = unsafe { memmap::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 { memmap::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 ctx = Context::new(file).unwrap(); + + let mut bias = None; + TargetSharedLibrary::each(|lib| { + bias = Some(lib.virtual_memory_bias().0 as u64); + IterationControl::Break + }); + + let test = |sym: u64, expected_prefix: &str| { + let ip = sym.wrapping_sub(bias.unwrap()); + + let frames = ctx.find_frames(ip).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).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 000000000..9dc366672 --- /dev/null +++ b/vendor/addr2line/tests/output_equivalence.rs @@ -0,0 +1,145 @@ +extern crate backtrace; +extern crate findshlibs; +extern crate rustc_test as test; + +use std::env; +use std::ffi::OsStr; +use std::path::Path; +use std::process::Command; + +use backtrace::Backtrace; +use findshlibs::{IterationControl, SharedLibrary, TargetSharedLibrary}; +use test::{ShouldPanic, TestDesc, TestDescAndFn, TestFn, TestName}; + +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>) { + 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 + ); + } +} + +static FLAGS: &'static str = "aipsf"; + +fn make_tests() -> Vec<TestDescAndFn> { + (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| TestDescAndFn { + desc: TestDesc { + name: TestName::DynTestName(format!( + "addr2line {}", + param.as_ref().map_or("", String::as_str) + )), + ignore: false, + should_panic: ShouldPanic::No, + allow_fail: false, + }, + testfn: TestFn::DynTestFn(Box::new(move || { + run_test(param.as_ref().map(String::as_str)) + })), + }) + .collect() +} + +fn main() { + if !cfg!(target_os = "linux") { + return; + } + let args: Vec<_> = env::args().collect(); + test::test_main(&args, make_tests()); +} diff --git a/vendor/addr2line/tests/parse.rs b/vendor/addr2line/tests/parse.rs new file mode 100644 index 000000000..91d66e382 --- /dev/null +++ b/vendor/addr2line/tests/parse.rs @@ -0,0 +1,118 @@ +extern crate addr2line; +extern crate memmap; +extern crate object; + +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 { memmap::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(); + }); +} |