From c71623474a6a6b383a2b15a9fd3b1fa1a7395733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Fri, 25 Oct 2024 10:04:48 -0700 Subject: [PATCH] Make BPF symbolization tests end-to-end tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TODO: Does not work because we are attempting to expose dev-only stuff (libbpbf-rs) via the main crate. This may be possible to resolve by moving this functionality into blazesym-dev (but that, in turn, requires more stuff to be exposed from the main crate) The BPF symbolization tests were added as unit tests, because they require a bunch of setup shenanigans that we only had internally to the crate. Conceptually, however, they are very much end-to-end tests that test the public API surface and not just some implementation detail. We *really* want to preserve the ability to get a good overview of all end-to-end tests in a single location and so with this change we take necessary steps to expose necessary test helpers so that they can be used from end-to-end tests. Furthermore, we move said BPF symbolization tests into tests/blazesym.rs, co-located with most other end-to-end tests. Signed-off-by: Daniel Müller --- Cargo.toml | 10 ++++- benches/symbolize.rs | 47 ++++++++++++++++++++ src/lib.rs | 2 +- src/symbolize/symbolizer.rs | 87 ------------------------------------- src/test_helper.rs | 9 ++-- tests/blazesym.rs | 39 +++++++++++++++++ 6 files changed, 101 insertions(+), 93 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 390c6380..97c31f3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,11 +79,18 @@ zstd = ["dep:zstd"] # Below here are dev-mostly features that should not be needed by # regular users. +# Expose test-only helpers for convenient use in end-to-end tests from +# crate. +test = [] # Enable code paths requiring a nightly toolchain. This feature is only meant to # be used for testing and benchmarking purposes, not for the core library, which # is expected to work on stable. nightly = [] +[[test]] +name = "blazesym" +required-features = ["test"] + [[example]] name = "addr2ln" @@ -103,6 +110,7 @@ required-features = ["demangle", "blazesym-dev/generate-unit-test-files"] [[bench]] name = "main" harness = false +required-features = ["test"] [profile.release] debug = true @@ -135,7 +143,7 @@ anyhow = "1.0.71" # TODO: Enable `zstd` feature once toolchain support for it is more # widespread (enabled by default in `ld`). Remove conditionals in # test code alongside. -blazesym = {path = ".", features = ["apk", "breakpad", "gsym", "tracing"]} +blazesym = {path = ".", features = ["apk", "breakpad", "gsym", "tracing", "test"]} blazesym-dev = {path = "dev", features = ["generate-unit-test-files"]} # TODO: Use 0.5.2 once released. criterion = {git = "https://github.com/bheisler/criterion.rs.git", rev = "b913e232edd98780961ecfbae836ec77ede49259", default-features = false, features = ["rayon", "cargo_bench_support"]} diff --git a/benches/symbolize.rs b/benches/symbolize.rs index 872698d7..b93a2fd1 100644 --- a/benches/symbolize.rs +++ b/benches/symbolize.rs @@ -10,6 +10,8 @@ use blazesym::symbolize::Input; use blazesym::symbolize::Process; use blazesym::symbolize::Source; use blazesym::symbolize::Symbolizer; +#[cfg(target_os = "linux")] +use blazesym::test_helper::with_bpf_symbolization_target_addrs; use blazesym::Addr; use blazesym::Pid; @@ -175,6 +177,47 @@ where }); } +/// Benchmark the symbolization of BPF program kernel addresses. +#[cfg(target_os = "linux")] +fn symbolize_kernel_bpf_uncached(b: &mut Bencher<'_, M>) { + with_bpf_symbolization_target_addrs(|handle_getpid, subprogram| { + let () = b.iter(|| { + let src = symbolize::Source::Kernel(symbolize::Kernel::default()); + let symbolizer = Symbolizer::new(); + + let result = symbolizer + .symbolize( + &src, + symbolize::Input::AbsAddr(&[handle_getpid, subprogram]), + ) + .unwrap(); + + assert_eq!(result.len(), 2); + }); + }); +} + +/// Benchmark the symbolization of BPF program kernel addresses when +/// relevant data is readily cached. +#[cfg(target_os = "linux")] +fn symbolize_kernel_bpf_cached(b: &mut Bencher<'_, M>) { + with_bpf_symbolization_target_addrs(|handle_getpid, subprogram| { + let src = symbolize::Source::Kernel(symbolize::Kernel::default()); + let symbolizer = Symbolizer::new(); + + let () = b.iter(|| { + let result = symbolizer + .symbolize( + &src, + symbolize::Input::AbsAddr(&[handle_getpid, subprogram]), + ) + .unwrap(); + + assert_eq!(result.len(), 2); + }); + }); +} + pub fn benchmark(group: &mut BenchmarkGroup<'_, M>) where @@ -187,4 +230,8 @@ where bench_fn!(group, symbolize_dwarf); bench_fn!(group, symbolize_gsym); bench_sub_fn!(group, symbolize_gsym_multi_no_setup); + #[cfg(target_os = "linux")] + bench_sub_fn!(group, symbolize_kernel_bpf_uncached); + #[cfg(target_os = "linux")] + bench_sub_fn!(group, symbolize_kernel_bpf_cached); } diff --git a/src/lib.rs b/src/lib.rs index 0e643056..a4a15233 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,7 @@ pub mod normalize; mod once; mod pid; pub mod symbolize; -#[cfg(test)] +#[cfg(any(test, feature = "test"))] mod test_helper; mod util; #[cfg(feature = "apk")] diff --git a/src/symbolize/symbolizer.rs b/src/symbolize/symbolizer.rs index 9902b42d..15ec5711 100644 --- a/src/symbolize/symbolizer.rs +++ b/src/symbolize/symbolizer.rs @@ -1375,17 +1375,12 @@ impl Default for Symbolizer { mod tests { use super::*; - #[cfg(all(target_os = "linux", feature = "nightly"))] - use test::Bencher; - use test_log::test; use crate::maps::Perm; use crate::symbolize; use crate::symbolize::CodeInfo; use crate::test_helper::find_the_answer_fn_in_zip; - #[cfg(target_os = "linux")] - use crate::test_helper::with_bpf_symbolization_target_addrs; /// Exercise the `Debug` representation of various types. @@ -1692,86 +1687,4 @@ mod tests { let () = test(zip_error_dispatch); let () = test(zip_delayed_error_dispatch); } - - /// Test symbolization of a kernel address inside a BPF program. - #[cfg(target_os = "linux")] - #[test] - fn symbolize_kernel_bpf_program() { - with_bpf_symbolization_target_addrs(|handle_getpid, subprogram| { - let src = symbolize::Source::Kernel(symbolize::Kernel::default()); - let symbolizer = Symbolizer::new(); - let result = symbolizer - .symbolize( - &src, - symbolize::Input::AbsAddr(&[handle_getpid, subprogram]), - ) - .unwrap(); - let handle_getpid_sym = result[0].as_sym().unwrap(); - assert_eq!(handle_getpid_sym.name, "handle__getpid"); - let code_info = handle_getpid_sym.code_info.as_ref().unwrap(); - assert_eq!(code_info.dir, None); - assert_eq!( - Path::new(&code_info.file).file_name(), - Some(OsStr::new("getpid.bpf.c")) - ); - assert_eq!(code_info.line, Some(33)); - assert_ne!(code_info.column, None); - - let subprogram_sym = result[1].as_sym().unwrap(); - assert_eq!(subprogram_sym.name, "subprogram"); - let code_info = subprogram_sym.code_info.as_ref().unwrap(); - assert_eq!(code_info.dir, None); - assert_eq!( - Path::new(&code_info.file).file_name(), - Some(OsStr::new("getpid.bpf.c")) - ); - assert_eq!(code_info.line, Some(15)); - assert_ne!(code_info.column, None); - }) - } - - /// Benchmark the symbolization of BPF program kernel addresses. - #[cfg(target_os = "linux")] - #[cfg(feature = "nightly")] - #[bench] - fn bench_symbolize_kernel_bpf_uncached(b: &mut Bencher) { - with_bpf_symbolization_target_addrs(|handle_getpid, subprogram| { - let () = b.iter(|| { - let src = symbolize::Source::Kernel(symbolize::Kernel::default()); - let symbolizer = Symbolizer::new(); - - let result = symbolizer - .symbolize( - &src, - symbolize::Input::AbsAddr(&[handle_getpid, subprogram]), - ) - .unwrap(); - - assert_eq!(result.len(), 2); - }); - }); - } - - /// Benchmark the symbolization of BPF program kernel addresses when - /// relevant data is readily cached. - #[cfg(target_os = "linux")] - #[cfg(feature = "nightly")] - #[bench] - fn bench_symbolize_kernel_bpf_cached(b: &mut Bencher) { - with_bpf_symbolization_target_addrs(|handle_getpid, subprogram| { - let src = symbolize::Source::Kernel(symbolize::Kernel::default()); - let symbolizer = Symbolizer::new(); - - let () = b.iter(|| { - let result = symbolizer - .symbolize( - &src, - symbolize::Input::AbsAddr(&[handle_getpid, subprogram]), - ) - .unwrap(); - - assert_eq!(result.len(), 2); - }); - }); - } } diff --git a/src/test_helper.rs b/src/test_helper.rs index 5f7571f6..fa994586 100644 --- a/src/test_helper.rs +++ b/src/test_helper.rs @@ -142,9 +142,10 @@ mod bpf { value.expect("did not receive RingBuffer callback") } - /// Retrieve the address of the `symbolization_target` function in the - /// `getpid.bpf.o` BPF program. - pub(crate) fn with_bpf_symbolization_target_addrs(f: F) + /// Retrieve the address of the `handle__getpid` and `subprogram` + /// functions in the `getpid.bpf.o` BPF program. + #[allow(dead_code)] + pub fn with_bpf_symbolization_target_addrs(f: F) where F: FnOnce(Addr, Addr), { @@ -167,4 +168,4 @@ mod bpf { } #[cfg(target_os = "linux")] -pub(crate) use bpf::*; +pub use bpf::*; diff --git a/tests/blazesym.rs b/tests/blazesym.rs index 66b83434..b007d8ae 100644 --- a/tests/blazesym.rs +++ b/tests/blazesym.rs @@ -37,6 +37,8 @@ use blazesym::symbolize::Reason; use blazesym::symbolize::Resolve; use blazesym::symbolize::Symbolized; use blazesym::symbolize::Symbolizer; +#[cfg(target_os = "linux")] +use blazesym::test_helper::with_bpf_symbolization_target_addrs; use blazesym::Addr; use blazesym::ErrorKind; use blazesym::Pid; @@ -805,6 +807,43 @@ fn symbolize_process_with_custom_dispatch() { test(process_no_dispatch); } +/// Test symbolization of a kernel address inside a BPF program. +#[cfg(target_os = "linux")] +#[test] +fn symbolize_kernel_bpf_program() { + with_bpf_symbolization_target_addrs(|handle_getpid, subprogram| { + let src = symbolize::Source::Kernel(symbolize::Kernel::default()); + let symbolizer = Symbolizer::new(); + let result = symbolizer + .symbolize( + &src, + symbolize::Input::AbsAddr(&[handle_getpid, subprogram]), + ) + .unwrap(); + let handle_getpid_sym = result[0].as_sym().unwrap(); + assert_eq!(handle_getpid_sym.name, "handle__getpid"); + let code_info = handle_getpid_sym.code_info.as_ref().unwrap(); + assert_eq!(code_info.dir, None); + assert_eq!( + Path::new(&code_info.file).file_name(), + Some(OsStr::new("getpid.bpf.c")) + ); + assert_eq!(code_info.line, Some(33)); + assert_ne!(code_info.column, None); + + let subprogram_sym = result[1].as_sym().unwrap(); + assert_eq!(subprogram_sym.name, "subprogram"); + let code_info = subprogram_sym.code_info.as_ref().unwrap(); + assert_eq!(code_info.dir, None); + assert_eq!( + Path::new(&code_info.file).file_name(), + Some(OsStr::new("getpid.bpf.c")) + ); + assert_eq!(code_info.line, Some(15)); + assert_ne!(code_info.column, None); + }) +} + /// Check that we can normalize addresses in an ELF shared object. #[cfg(target_os = "linux")] #[test]