diff --git a/CHANGELOG.md b/CHANGELOG.md index a18721ef..c3cb1cce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ Unreleased ---------- +- Added support for iteration over DWARF symbols to `inspect::Inspector` - Adjusted normalization logic to use "symbolic path" for reading build IDs when normalizing with `NormalizeOpts::map_files` equal to `false` - Adjusted `inspect::Inspector::for_each` to accept callback returning diff --git a/src/dwarf/resolver.rs b/src/dwarf/resolver.rs index 204e2695..0d509872 100644 --- a/src/dwarf/resolver.rs +++ b/src/dwarf/resolver.rs @@ -7,6 +7,7 @@ use std::fmt::Formatter; use std::fmt::Result as FmtResult; use std::mem; use std::mem::swap; +use std::ops::ControlFlow; use std::ops::Deref as _; use std::path::Path; use std::path::PathBuf; @@ -329,11 +330,24 @@ impl Inspect for DwarfResolver { } } - fn for_each(&self, _opts: &FindAddrOpts, _f: &mut ForEachFn<'_>) -> Result<()> { - // TODO: Implement this functionality. - Err(Error::with_unsupported( - "DWARF logic does not currently support symbol iteration", - )) + fn for_each(&self, opts: &FindAddrOpts, f: &mut ForEachFn<'_>) -> Result<()> { + if let SymType::Variable = opts.sym_type { + return Err(Error::with_unsupported("not implemented")) + } + + let mut overall_result = Ok(()); + let () = self.units.for_each_function(|func| { + let result = self.function_to_sym_info(func, opts.offset_in_file); + match result { + Ok(Some(sym_info)) => f(&sym_info), + Ok(None) => ControlFlow::Continue(()), + Err(err) => { + overall_result = Err(err); + ControlFlow::Break(()) + } + } + })?; + overall_result } } @@ -349,7 +363,7 @@ impl Debug for DwarfResolver { // directly. impl<'dwarf> Units<'dwarf> { /// Fill in source code information for an address to the provided - /// `IntSym`. + /// `ResolvedSym`. /// /// `addr` is a normalized address. fn fill_code_info<'slf>( diff --git a/src/dwarf/unit.rs b/src/dwarf/unit.rs index 4f254a04..bdba7ed3 100644 --- a/src/dwarf/unit.rs +++ b/src/dwarf/unit.rs @@ -67,8 +67,6 @@ impl<'dwarf> Unit<'dwarf> { } } - #[cfg(test)] - #[cfg(feature = "nightly")] pub(super) fn parse_functions<'unit>( &'unit self, units: &Units<'dwarf>, diff --git a/src/dwarf/units.rs b/src/dwarf/units.rs index 344ac581..1b6be7bc 100644 --- a/src/dwarf/units.rs +++ b/src/dwarf/units.rs @@ -25,6 +25,8 @@ // > IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // > DEALINGS IN THE SOFTWARE. +use std::ops::ControlFlow; + use crate::log::warn; use crate::once::OnceCell; use crate::ErrorExt as _; @@ -408,6 +410,22 @@ impl<'dwarf> Units<'dwarf> { .filter_map(move |unit| unit.find_name(name, self).transpose()) } + pub(crate) fn for_each_function(&self, mut f: F) -> Result<(), gimli::Error> + where + F: FnMut(&Function<'dwarf>) -> ControlFlow<()>, + { + for unit in self.units.iter() { + let functions = unit.parse_functions(self)?; + + for function in functions.functions.iter() { + if let ControlFlow::Break(()) = f(function) { + return Ok(()) + } + } + } + Ok(()) + } + /// Retrieve a [`gimli::UnitRef`] for the provided `unit`. #[inline] pub(crate) fn unit_ref<'unit>( diff --git a/src/elf/resolver.rs b/src/elf/resolver.rs index 20a5657c..6ebc8cb6 100644 --- a/src/elf/resolver.rs +++ b/src/elf/resolver.rs @@ -1,7 +1,6 @@ use std::fmt::Debug; use std::fmt::Formatter; use std::fmt::Result as FmtResult; -use std::ops::Deref as _; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; @@ -203,8 +202,11 @@ impl Inspect for ElfResolver { } fn for_each(&self, opts: &FindAddrOpts, f: &mut ForEachFn<'_>) -> Result<()> { - let parser = self.parser(); - parser.deref().for_each(opts, f) + match &self.backend { + #[cfg(feature = "dwarf")] + ElfBackend::Dwarf(dwarf) => dwarf.for_each(opts, f), + ElfBackend::Elf(parser) => parser.for_each(opts, f), + } } } diff --git a/src/inspect/inspector.rs b/src/inspect/inspector.rs index 906a3983..e782daef 100644 --- a/src/inspect/inspector.rs +++ b/src/inspect/inspector.rs @@ -154,13 +154,13 @@ impl Inspector { /// callback function. /// /// # Notes - /// - no symbol name demangling is performed currently - /// - currently only function symbols (as opposed to variables) are reported + /// - no symbol name demangling is performed and data is reported as it + /// appears in the symbol source /// - undefined symbols (such as ones referencing a different shared object) /// are not reported - /// - for the [`Elf`](Source::Elf) source, at present DWARF symbols are - /// ignored (irrespective of the [`debug_syms`][Elf::debug_syms] - /// configuration) + /// - for the [`Elf`](Source::Elf) source: + /// - if `debug_syms` is set, *only* debug symbols are consulted and no + /// support for variables is present /// - for the [`Breakpad`](Source::Breakpad) source: /// - no variable support is present /// - file offsets won't be reported diff --git a/tests/blazesym.rs b/tests/blazesym.rs index 4f88100b..e7ac5876 100644 --- a/tests/blazesym.rs +++ b/tests/blazesym.rs @@ -1121,11 +1121,18 @@ fn inspect_elf_file_offset() { } -/// Check that we can iterate over all symbols in an ELF file. +/// Check that we can iterate over all symbols in a symbolization source. #[test] -fn inspect_elf_breakpad_all_symbols() { +fn inspect_elf_dwarf_breakpad_all_symbols() { fn test(src: &inspect::Source) { let breakpad = matches!(src, inspect::Source::Breakpad(..)); + let dwarf = matches!( + src, + inspect::Source::Elf(inspect::Elf { + debug_syms: true, + .. + }) + ); let inspector = Inspector::new(); let mut syms = HashMap::::new(); let () = inspector @@ -1135,8 +1142,8 @@ fn inspect_elf_breakpad_all_symbols() { }) .unwrap(); - // Breakpad doesn't contain any or any reasonable information for - // some symbols. + // Breakpad and DWARF don't contain any or any reasonable information + // for some symbols. if !breakpad { let sym = syms.get("main").unwrap(); assert_eq!(sym.sym_type, SymType::Function); @@ -1151,7 +1158,7 @@ fn inspect_elf_breakpad_all_symbols() { let sym = syms.get("factorial_inline_test").unwrap(); assert_eq!(sym.sym_type, SymType::Function); - if !breakpad { + if !breakpad && !dwarf { let sym = syms.get("indirect_func").unwrap(); assert_eq!(sym.sym_type, SymType::Function); } @@ -1162,16 +1169,24 @@ fn inspect_elf_breakpad_all_symbols() { let sym = syms.get("resolve_indirect_func").unwrap(); assert_eq!(sym.sym_type, SymType::Function); - if !breakpad { + if !breakpad && !dwarf { let sym = syms.get("a_variable").unwrap(); assert_eq!(sym.sym_type, SymType::Variable); } } - let test_elf = Path::new(&env!("CARGO_MANIFEST_DIR")) + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) .join("data") .join("test-stable-addrs-no-dwarf.bin"); - let elf = inspect::Elf::new(test_elf); + let mut elf = inspect::Elf::new(path); + elf.debug_syms = false; + let src = inspect::Source::Elf(elf); + test(&src); + + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) + .join("data") + .join("test-stable-addrs-stripped-elf-with-dwarf.bin"); + let elf = inspect::Elf::new(path); let src = inspect::Source::Elf(elf); test(&src); @@ -1186,7 +1201,7 @@ fn inspect_elf_breakpad_all_symbols() { /// Check that early stopping of symbol iteration works as expected. #[test] -fn inspect_elf_breakpad_early_iter_stop() { +fn inspect_elf_dwarf_breakpad_early_iter_stop() { fn test(src: &inspect::Source) { let mut i = 0; let inspector = Inspector::new(); @@ -1202,10 +1217,18 @@ fn inspect_elf_breakpad_early_iter_stop() { .unwrap(); } - let test_elf = Path::new(&env!("CARGO_MANIFEST_DIR")) + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) .join("data") .join("test-stable-addrs-no-dwarf.bin"); - let elf = inspect::Elf::new(test_elf); + let mut elf = inspect::Elf::new(path); + elf.debug_syms = false; + let src = inspect::Source::Elf(elf); + test(&src); + + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) + .join("data") + .join("test-stable-addrs-stripped-elf-with-dwarf.bin"); + let elf = inspect::Elf::new(path); let src = inspect::Source::Elf(elf); test(&src); @@ -1218,14 +1241,41 @@ fn inspect_elf_breakpad_early_iter_stop() { } +/// Make sure that the `debug_syms` flag is honored. +#[test] +fn inspect_debug_syms_flag() { + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) + .join("data") + .join("test-stable-addrs-no-dwarf.bin"); + let mut elf = inspect::Elf::new(path); + elf.debug_syms = true; + let src = inspect::Source::Elf(elf); + let inspector = Inspector::new(); + // There aren't any debug symbols in the source (although there are ELF + // symbols). + let () = inspector.for_each(&src, |_sym| panic!()).unwrap(); + + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) + .join("data") + .join("test-stable-addrs-stripped-elf-with-dwarf.bin"); + let mut elf = inspect::Elf::new(path); + elf.debug_syms = false; + let src = inspect::Source::Elf(elf); + // There aren't any ELF symbols in the source (although there are DWARF + // symbols). + let () = inspector.for_each(&src, |_sym| panic!()).unwrap(); +} + + /// Check that we can iterate over all symbols in an ELF file, without /// encountering duplicates caused by dynamic/static symbol overlap. #[test] fn inspect_elf_all_symbols_without_duplicates() { - let test_elf = Path::new(&env!("CARGO_MANIFEST_DIR")) + let path = Path::new(&env!("CARGO_MANIFEST_DIR")) .join("data") .join("libtest-so.so"); - let elf = inspect::Elf::new(test_elf); + let mut elf = inspect::Elf::new(path); + elf.debug_syms = false; let src = inspect::Source::Elf(elf); let inspector = Inspector::new();