diff options
Diffstat (limited to 'vendor/addr2line/tests/output_equivalence.rs')
-rw-r--r-- | vendor/addr2line/tests/output_equivalence.rs | 135 |
1 files changed, 135 insertions, 0 deletions
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(); +} |