blob: 149203a1b6368535b1e24402eef65365f4352b49 [file] [log] [blame]
mod auxiliary;
macro_rules! pos {
() => {
(file!(), line!())
};
}
macro_rules! check {
($($pos:expr),*) => ({
verify(&[$($pos,)* pos!()]);
})
}
type Pos = (&'static str, u32);
#[test]
fn doit() {
if
// Skip musl which is by default statically linked and doesn't support
// dynamic libraries.
!cfg!(target_env = "musl")
// Skip Miri, since it doesn't support dynamic libraries.
&& !cfg!(miri)
{
// TODO(#238) this shouldn't have to happen first in this function, but
// currently it does.
let mut dir = std::env::current_exe().unwrap();
dir.pop();
if cfg!(windows) {
dir.push("dylib_dep.dll");
} else if cfg!(target_os = "macos") {
dir.push("libdylib_dep.dylib");
} else {
dir.push("libdylib_dep.so");
}
unsafe {
let lib = libloading::Library::new(&dir).unwrap();
let api = lib.get::<extern "C" fn(Pos, fn(Pos, Pos))>(b"foo").unwrap();
api(pos!(), |a, b| {
check!(a, b);
});
}
}
outer(pos!());
}
#[inline(never)]
fn outer(main_pos: Pos) {
inner(main_pos, pos!());
inner_inlined(main_pos, pos!());
}
#[inline(never)]
#[rustfmt::skip]
fn inner(main_pos: Pos, outer_pos: Pos) {
check!(main_pos, outer_pos);
check!(main_pos, outer_pos);
let inner_pos = pos!(); auxiliary::callback(|aux_pos| {
check!(main_pos, outer_pos, inner_pos, aux_pos);
});
let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| {
check!(main_pos, outer_pos, inner_pos, aux_pos);
});
}
#[inline(always)]
#[rustfmt::skip]
fn inner_inlined(main_pos: Pos, outer_pos: Pos) {
check!(main_pos, outer_pos);
check!(main_pos, outer_pos);
#[inline(always)]
fn inner_further_inlined(main_pos: Pos, outer_pos: Pos, inner_pos: Pos) {
check!(main_pos, outer_pos, inner_pos);
}
inner_further_inlined(main_pos, outer_pos, pos!());
let inner_pos = pos!(); auxiliary::callback(|aux_pos| {
check!(main_pos, outer_pos, inner_pos, aux_pos);
});
let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| {
check!(main_pos, outer_pos, inner_pos, aux_pos);
});
// this tests a distinction between two independent calls to the inlined function.
// (un)fortunately, LLVM somehow merges two consecutive such calls into one node.
inner_further_inlined(main_pos, outer_pos, pos!());
}
fn verify(filelines: &[Pos]) {
let trace = backtrace::Backtrace::new();
println!("-----------------------------------");
println!("looking for:");
for (file, line) in filelines.iter().rev() {
println!("\t{}:{}", file, line);
}
println!("found:\n{:?}", trace);
let mut symbols = trace.frames().iter().flat_map(|frame| frame.symbols());
let mut iter = filelines.iter().rev();
while let Some((file, line)) = iter.next() {
loop {
let sym = match symbols.next() {
Some(sym) => sym,
None => panic!("failed to find {}:{}", file, line),
};
if let Some(filename) = sym.filename() {
if let Some(lineno) = sym.lineno() {
if filename.ends_with(file) && lineno == *line {
break;
}
}
}
}
}
}