diff options
Diffstat (limited to 'library/backtrace/crates')
22 files changed, 438 insertions, 0 deletions
diff --git a/library/backtrace/crates/as-if-std/Cargo.toml b/library/backtrace/crates/as-if-std/Cargo.toml new file mode 100644 index 000000000..c763227f2 --- /dev/null +++ b/library/backtrace/crates/as-if-std/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "as-if-std" +version = "0.1.0" +authors = ["Alex Crichton <alex@alexcrichton.com>"] +edition = "2018" +publish = false + +[lib] +test = false +doc = false +doctest = false +bench = false + +[dependencies] +cfg-if = "1.0" +rustc-demangle = "0.1.4" +libc = { version = "0.2.45", default-features = false } +addr2line = { version = "0.16.0", default-features = false, optional = true } +miniz_oxide = { version = "0.4.0", default-features = false } + +[dependencies.object] +version = "0.28" +default-features = false +optional = true +features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] + +[features] +default = ['backtrace'] +backtrace = ['addr2line', 'object'] diff --git a/library/backtrace/crates/as-if-std/build.rs b/library/backtrace/crates/as-if-std/build.rs new file mode 100644 index 000000000..7018b1017 --- /dev/null +++ b/library/backtrace/crates/as-if-std/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-cfg=backtrace_in_libstd"); +} diff --git a/library/backtrace/crates/as-if-std/src/lib.rs b/library/backtrace/crates/as-if-std/src/lib.rs new file mode 100644 index 000000000..c0f49b77d --- /dev/null +++ b/library/backtrace/crates/as-if-std/src/lib.rs @@ -0,0 +1,21 @@ +// A crate which builds the `backtrace` crate as-if it's included as a +// submodule into the standard library. We try to set this crate up similarly +// to the standard library itself to minimize the likelihood of issues when +// updating the `backtrace` crate. + +#![no_std] + +extern crate alloc; + +// We want to `pub use std::*` in the root but we don't want `std` available in +// the root namespace, so do this in a funky inner module. +mod __internal { + extern crate std; + pub use std::*; +} + +pub use __internal::*; + +// This is the magical part which we hope works. +#[path = "../../../src/lib.rs"] +mod the_backtrace_crate; diff --git a/library/backtrace/crates/cpp_smoke_test/Cargo.toml b/library/backtrace/crates/cpp_smoke_test/Cargo.toml new file mode 100644 index 000000000..1325d7c04 --- /dev/null +++ b/library/backtrace/crates/cpp_smoke_test/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "cpp_smoke_test" +version = "0.1.0" +authors = ["Nick Fitzgerald <fitzgen@gmail.com>"] +build = "build.rs" + +[build-dependencies] +cc = "1.0" + +[dependencies] +backtrace = { path = "../..", features = ["cpp_demangle"] } diff --git a/library/backtrace/crates/cpp_smoke_test/build.rs b/library/backtrace/crates/cpp_smoke_test/build.rs new file mode 100644 index 000000000..5ddc63d82 --- /dev/null +++ b/library/backtrace/crates/cpp_smoke_test/build.rs @@ -0,0 +1,14 @@ +fn main() { + compile_cpp(); +} + +fn compile_cpp() { + println!("cargo:rerun-if-changed=cpp/trampoline.cpp"); + + cc::Build::new() + .cpp(true) + .debug(true) + .opt_level(0) + .file("cpp/trampoline.cpp") + .compile("libcpptrampoline.a"); +} diff --git a/library/backtrace/crates/cpp_smoke_test/cpp/trampoline.cpp b/library/backtrace/crates/cpp_smoke_test/cpp/trampoline.cpp new file mode 100644 index 000000000..61e09604c --- /dev/null +++ b/library/backtrace/crates/cpp_smoke_test/cpp/trampoline.cpp @@ -0,0 +1,14 @@ +#include <stdio.h> + +namespace space { + template <typename FuncT> + void templated_trampoline(FuncT func) { + func(); + } +} + +typedef void (*FuncPtr)(); + +extern "C" void cpp_trampoline(FuncPtr func) { + space::templated_trampoline(func); +} diff --git a/library/backtrace/crates/cpp_smoke_test/src/lib.rs b/library/backtrace/crates/cpp_smoke_test/src/lib.rs new file mode 100644 index 000000000..2002ef17d --- /dev/null +++ b/library/backtrace/crates/cpp_smoke_test/src/lib.rs @@ -0,0 +1,2 @@ +#[test] +fn it_works() {} diff --git a/library/backtrace/crates/cpp_smoke_test/tests/smoke.rs b/library/backtrace/crates/cpp_smoke_test/tests/smoke.rs new file mode 100644 index 000000000..b9aef47f5 --- /dev/null +++ b/library/backtrace/crates/cpp_smoke_test/tests/smoke.rs @@ -0,0 +1,73 @@ +extern crate backtrace; +extern crate cpp_smoke_test; + +use std::sync::atomic::{AtomicBool, Ordering}; + +extern "C" { + fn cpp_trampoline(func: extern "C" fn()) -> (); +} + +#[test] +#[ignore] // fixme(fitzgen/cpp_demangle#73) +fn smoke_test_cpp() { + static RAN_ASSERTS: AtomicBool = AtomicBool::new(false); + + extern "C" fn assert_cpp_frames() { + let mut physical_frames = Vec::new(); + backtrace::trace(|cx| { + physical_frames.push(cx.ip()); + + // We only want to capture this closure's frame, assert_cpp_frames, + // space::templated_trampoline, and cpp_trampoline. Those are + // logical frames, which might be inlined into fewer physical + // frames, so we may end up with extra logical frames after + // resolving these. + physical_frames.len() < 4 + }); + + let names: Vec<_> = physical_frames + .into_iter() + .flat_map(|ip| { + let mut logical_frame_names = vec![]; + + backtrace::resolve(ip, |sym| { + let sym_name = sym.name().expect("Should have a symbol name"); + let demangled = sym_name.to_string(); + logical_frame_names.push(demangled); + }); + + assert!( + !logical_frame_names.is_empty(), + "Should have resolved at least one symbol for the physical frame" + ); + + logical_frame_names + }) + // Skip the backtrace::trace closure and assert_cpp_frames, and then + // take the two C++ frame names. + .skip_while(|name| !name.contains("trampoline")) + .take(2) + .collect(); + + println!("actual names = {:#?}", names); + + let expected = [ + "void space::templated_trampoline<void (*)()>(void (*)())", + "cpp_trampoline", + ]; + println!("expected names = {:#?}", expected); + + assert_eq!(names.len(), expected.len()); + for (actual, expected) in names.iter().zip(expected.iter()) { + assert_eq!(actual, expected); + } + + RAN_ASSERTS.store(true, Ordering::SeqCst); + } + + assert!(!RAN_ASSERTS.load(Ordering::SeqCst)); + unsafe { + cpp_trampoline(assert_cpp_frames); + } + assert!(RAN_ASSERTS.load(Ordering::SeqCst)); +} diff --git a/library/backtrace/crates/debuglink/Cargo.toml b/library/backtrace/crates/debuglink/Cargo.toml new file mode 100644 index 000000000..6b55b1394 --- /dev/null +++ b/library/backtrace/crates/debuglink/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "debuglink" +version = "0.1.0" +edition = "2018" + +[dependencies] +backtrace = { path = "../.." } diff --git a/library/backtrace/crates/debuglink/src/main.rs b/library/backtrace/crates/debuglink/src/main.rs new file mode 100644 index 000000000..99265ae9a --- /dev/null +++ b/library/backtrace/crates/debuglink/src/main.rs @@ -0,0 +1,34 @@ +// Test that the debuginfo is being found by checking that the +// backtrace contains `main` and that the source filename uses +// the path given in the command line arguments. +// +// For dwz tests, this assumes that the path string will be moved into +// the dwz file. +fn main() { + let crate_dir = std::env::args().skip(1).next().unwrap(); + let expect = std::path::Path::new(&crate_dir).join("src/main.rs"); + + let bt = backtrace::Backtrace::new(); + println!("{:?}", bt); + + let mut found_main = false; + + for frame in bt.frames() { + let symbols = frame.symbols(); + if symbols.is_empty() { + continue; + } + + if let Some(name) = symbols[0].name() { + let name = format!("{:#}", name); + if name == "debuglink::main" { + found_main = true; + let filename = symbols[0].filename().unwrap(); + assert_eq!(filename, expect); + break; + } + } + } + + assert!(found_main); +} diff --git a/library/backtrace/crates/dylib-dep/Cargo.toml b/library/backtrace/crates/dylib-dep/Cargo.toml new file mode 100644 index 000000000..c3d4a8c2f --- /dev/null +++ b/library/backtrace/crates/dylib-dep/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "dylib-dep" +version = "0.1.0" +edition = "2018" +authors = [] +publish = false + +[lib] +name = "dylib_dep" +crate-type = ["cdylib", "rlib"] diff --git a/library/backtrace/crates/dylib-dep/src/lib.rs b/library/backtrace/crates/dylib-dep/src/lib.rs new file mode 100644 index 000000000..201807797 --- /dev/null +++ b/library/backtrace/crates/dylib-dep/src/lib.rs @@ -0,0 +1,14 @@ +#![allow(improper_ctypes_definitions)] + +type Pos = (&'static str, u32); + +macro_rules! pos { + () => { + (file!(), line!()) + }; +} + +#[no_mangle] +pub extern "C" fn foo(outer: Pos, inner: fn(Pos, Pos)) { + inner(outer, pos!()); +} diff --git a/library/backtrace/crates/line-tables-only/Cargo.toml b/library/backtrace/crates/line-tables-only/Cargo.toml new file mode 100644 index 000000000..e2967d3d3 --- /dev/null +++ b/library/backtrace/crates/line-tables-only/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "line-tables-only" +version = "0.1.0" +edition = "2018" + +[build-dependencies] +cc = "1.0" + +[dependencies] +libc = { version = "0.2", default-features = false } + +[dependencies.backtrace] +path = "../.." +features = [ + 'libunwind', + 'std', +] + +[features] +libbacktrace = ['backtrace/libbacktrace'] +gimli-symbolize = ['backtrace/gimli-symbolize'] diff --git a/library/backtrace/crates/line-tables-only/build.rs b/library/backtrace/crates/line-tables-only/build.rs new file mode 100644 index 000000000..125fb7645 --- /dev/null +++ b/library/backtrace/crates/line-tables-only/build.rs @@ -0,0 +1,10 @@ +fn main() { + println!("cargo:rerun-if-changed=src/callback.c"); + + cc::Build::new() + .opt_level(0) + .debug(false) + .flag("-g1") + .file("src/callback.c") + .compile("libcallback.a"); +} diff --git a/library/backtrace/crates/line-tables-only/src/callback.c b/library/backtrace/crates/line-tables-only/src/callback.c new file mode 100644 index 000000000..c9d5d072a --- /dev/null +++ b/library/backtrace/crates/line-tables-only/src/callback.c @@ -0,0 +1,14 @@ + +typedef void (*callback) (void *data); + +void baz(callback cb, void *data) { + cb(data); +} + +void bar(callback cb, void *data) { + baz(cb, data); +} + +void foo(callback cb, void *data) { + bar(cb, data); +} diff --git a/library/backtrace/crates/line-tables-only/src/lib.rs b/library/backtrace/crates/line-tables-only/src/lib.rs new file mode 100644 index 000000000..bd5afcb3a --- /dev/null +++ b/library/backtrace/crates/line-tables-only/src/lib.rs @@ -0,0 +1,57 @@ +#[cfg(test)] +mod tests { + use std::path::Path; + use backtrace::Backtrace; + use libc::c_void; + + pub type Callback = extern "C" fn(data: *mut c_void); + + extern "C" { + fn foo(cb: Callback, data: *mut c_void); + } + + extern "C" fn store_backtrace(data: *mut c_void) { + let bt = backtrace::Backtrace::new(); + unsafe { *(data as *mut Option<Backtrace>) = Some(bt) }; + } + + fn assert_contains(backtrace: &Backtrace, + expected_name: &str, + expected_file: &str, + expected_line: u32) { + + let expected_file = Path::new(expected_file); + + for frame in backtrace.frames() { + for symbol in frame.symbols() { + if let Some(name) = symbol.name() { + if name.as_bytes() == expected_name.as_bytes() { + assert!(symbol.filename().unwrap().ends_with(expected_file)); + assert_eq!(symbol.lineno(), Some(expected_line)); + return; + } + } + } + } + + panic!("symbol {:?} not found in backtrace: {:?}", expected_name, backtrace); + } + + /// Verifies that when debug info includes only lines tables the generated + /// backtrace is still generated successfully. The test exercises behaviour + /// that failed previously when compiling with clang -g1. + /// + /// The test case uses C rather than rust, since at that time when it was + /// written the debug info generated at level 1 in rustc was essentially + /// the same as at level 2. + #[test] + #[cfg_attr(windows, ignore)] + fn backtrace_works_with_line_tables_only() { + let mut backtrace: Option<Backtrace> = None; + unsafe { foo(store_backtrace, &mut backtrace as *mut _ as *mut c_void) }; + let backtrace = backtrace.expect("backtrace"); + assert_contains(&backtrace, "foo", "src/callback.c", 13); + assert_contains(&backtrace, "bar", "src/callback.c", 9); + assert_contains(&backtrace, "baz", "src/callback.c", 5); + } +} diff --git a/library/backtrace/crates/macos_frames_test/Cargo.toml b/library/backtrace/crates/macos_frames_test/Cargo.toml new file mode 100644 index 000000000..278d51e79 --- /dev/null +++ b/library/backtrace/crates/macos_frames_test/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "macos_frames_test" +version = "0.1.0" +authors = ["Aaron Hill <aa1ronham@gmail.com>"] +edition = "2018" + +[dependencies.backtrace] +path = "../.." diff --git a/library/backtrace/crates/macos_frames_test/src/lib.rs b/library/backtrace/crates/macos_frames_test/src/lib.rs new file mode 100644 index 000000000..65e2cc340 --- /dev/null +++ b/library/backtrace/crates/macos_frames_test/src/lib.rs @@ -0,0 +1 @@ +// intentionally blank diff --git a/library/backtrace/crates/macos_frames_test/tests/main.rs b/library/backtrace/crates/macos_frames_test/tests/main.rs new file mode 100644 index 000000000..f0e905b24 --- /dev/null +++ b/library/backtrace/crates/macos_frames_test/tests/main.rs @@ -0,0 +1,30 @@ +// Based on from https://github.com/rust-lang/rust/blob/2cb0b8582ebbf9784db9cec06fff517badbf4553/src/test/ui/issues/issue-45731.rs +// This needs to go in a crate by itself, since it modifies the dSYM for the entire test +// output directory. +// +// Note that this crate is *not* part of the overall `backtrace-rs` workspace, +// so that it gets its own 'target' directory. We manually invoke this test +// in .github/workflows/main.yml by passing `--manifest-path` to Cargo +#[test] +#[cfg(target_os = "macos")] +fn backtrace_no_dsym() { + use std::{env, fs}; + + // Find our dSYM and replace the DWARF binary with an empty file + let mut dsym_path = env::current_exe().unwrap(); + let executable_name = dsym_path.file_name().unwrap().to_str().unwrap().to_string(); + assert!(dsym_path.pop()); // Pop executable + dsym_path.push(format!( + "{}.dSYM/Contents/Resources/DWARF/{0}", + executable_name + )); + let _ = fs::OpenOptions::new() + .read(false) + .write(true) + .truncate(true) + .create(false) + .open(&dsym_path) + .unwrap(); + + backtrace::Backtrace::new(); +} diff --git a/library/backtrace/crates/without_debuginfo/Cargo.toml b/library/backtrace/crates/without_debuginfo/Cargo.toml new file mode 100644 index 000000000..19d76cbec --- /dev/null +++ b/library/backtrace/crates/without_debuginfo/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "without_debuginfo" +version = "0.1.0" +authors = ["Alex Crichton <alex@alexcrichton.com>"] +edition = "2018" + +[dependencies.backtrace] +path = "../.." +default-features = false +features = ['std'] + +[profile.dev] +debug = false + +[profile.test] +debug = false + +[features] +libbacktrace = ['backtrace/libbacktrace'] +gimli-symbolize = ['backtrace/gimli-symbolize'] diff --git a/library/backtrace/crates/without_debuginfo/src/lib.rs b/library/backtrace/crates/without_debuginfo/src/lib.rs new file mode 100644 index 000000000..65e2cc340 --- /dev/null +++ b/library/backtrace/crates/without_debuginfo/src/lib.rs @@ -0,0 +1 @@ +// intentionally blank diff --git a/library/backtrace/crates/without_debuginfo/tests/smoke.rs b/library/backtrace/crates/without_debuginfo/tests/smoke.rs new file mode 100644 index 000000000..5a0dfea15 --- /dev/null +++ b/library/backtrace/crates/without_debuginfo/tests/smoke.rs @@ -0,0 +1,44 @@ +#[test] +fn all_frames_have_symbols() { + println!("{:?}", backtrace::Backtrace::new()); + + let mut missing_symbols = 0; + let mut has_symbols = 0; + backtrace::trace(|frame| { + let mut any = false; + backtrace::resolve_frame(frame, |sym| { + if sym.name().is_some() { + any = true; + } + }); + if any { + has_symbols += 1; + } else if !frame.ip().is_null() { + missing_symbols += 1; + } + true + }); + + // FIXME(#346) currently on MinGW we can't symbolize kernel32.dll and other + // system libraries, which means we miss the last few symbols. + if cfg!(windows) && cfg!(target_env = "gnu") { + assert!(missing_symbols < has_symbols && has_symbols > 4); + } else { + assert_eq!(missing_symbols, 0); + } +} + +#[test] +fn all_frames_have_module_base_address() { + let mut missing_base_addresses = 0; + backtrace::trace(|frame| { + if frame.module_base_address().is_none() { + missing_base_addresses += 1; + } + true + }); + + if cfg!(windows) { + assert_eq!(missing_base_addresses, 0); + } +} |