diff --git a/src/lib.rs b/src/lib.rs index e5dea3387..8bc9beda9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,8 +113,12 @@ extern crate alloc; pub use self::backtrace::{trace_unsynchronized, Frame}; mod backtrace; -pub use self::symbolize::resolve_frame_unsynchronized; -pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName}; +pub use self::symbolize::{ + resolve_frame_unsynchronized, resolve_frame_unsynchronized_customized_cache, +}; +pub use self::symbolize::{ + resolve_unsynchronized, resolve_unsynchronized_customized_cache, Symbol, SymbolName, +}; mod symbolize; pub use self::types::BytesOrWideString; @@ -129,7 +133,7 @@ pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt}; cfg_if::cfg_if! { if #[cfg(feature = "std")] { pub use self::backtrace::trace; - pub use self::symbolize::{resolve, resolve_frame}; + pub use self::symbolize::{resolve, resolve_customized_cache, resolve_frame, resolve_frame_customized_cache}; pub use self::capture::{Backtrace, BacktraceFrame, BacktraceSymbol}; mod capture; } diff --git a/src/symbolize/dbghelp.rs b/src/symbolize/dbghelp.rs index 181dba731..e5673f40d 100644 --- a/src/symbolize/dbghelp.rs +++ b/src/symbolize/dbghelp.rs @@ -71,7 +71,11 @@ impl Symbol<'_> { #[repr(C, align(8))] struct Aligned8(T); -pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { +pub unsafe fn resolve( + what: ResolveWhat<'_>, + _cache_capacity: usize, + cb: &mut dyn FnMut(&super::Symbol), +) { // Ensure this process's symbols are initialized let dbghelp = match dbghelp::init() { Ok(dbghelp) => dbghelp, diff --git a/src/symbolize/gimli.rs b/src/symbolize/gimli.rs index cd4cec58c..46efaff4a 100644 --- a/src/symbolize/gimli.rs +++ b/src/symbolize/gimli.rs @@ -52,8 +52,6 @@ cfg_if::cfg_if! { mod stash; -const MAPPINGS_CACHE_SIZE: usize = 4; - struct Mapping { // 'static lifetime is a lie to hack around lack of support for self-referential structs. cx: Context<'static>, @@ -240,19 +238,21 @@ struct LibrarySegment { // unsafe because this is required to be externally synchronized pub unsafe fn clear_symbol_cache() { - Cache::with_global(|cache| cache.mappings.clear()); + Cache::with_global(super::DEFAULT_MAPPINGS_CACHE_SIZE, |cache| { + cache.mappings.clear() + }); } impl Cache { - fn new() -> Cache { + fn new(cache_capacity: usize) -> Cache { Cache { - mappings: Vec::with_capacity(MAPPINGS_CACHE_SIZE), + mappings: Vec::with_capacity(cache_capacity), libraries: native_libraries(), } } // unsafe because this is required to be externally synchronized - unsafe fn with_global(f: impl FnOnce(&mut Self)) { + unsafe fn with_global(cache_capacity: usize, f: impl FnOnce(&mut Self)) { // A very small, very simple LRU cache for debug info mappings. // // The hit rate should be very high, since the typical stack doesn't cross @@ -265,7 +265,7 @@ impl Cache { // never happen, and symbolicating backtraces would be ssssllllooooowwww. static mut MAPPINGS_CACHE: Option = None; - f(MAPPINGS_CACHE.get_or_insert_with(|| Cache::new())) + f(MAPPINGS_CACHE.get_or_insert_with(|| Cache::new(cache_capacity))) } fn avma_to_svma(&self, addr: *const u8) -> Option<(usize, *const u8)> { @@ -302,7 +302,11 @@ impl Cache { .next() } - fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<&'a mut Context<'a>> { + fn mapping_for_lib<'a>( + &'a mut self, + lib: usize, + cache_capacity: usize, + ) -> Option<&'a mut Context<'a>> { let idx = self.mappings.iter().position(|(idx, _)| *idx == lib); // Invariant: after this conditional completes without early returning @@ -321,7 +325,7 @@ impl Cache { let name = &self.libraries[lib].name; let mapping = Mapping::new(name.as_ref())?; - if self.mappings.len() == MAPPINGS_CACHE_SIZE { + if self.mappings.len() == cache_capacity { self.mappings.pop(); } @@ -335,7 +339,11 @@ impl Cache { } } -pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { +pub unsafe fn resolve( + what: ResolveWhat<'_>, + cache_capacity: usize, + cb: &mut dyn FnMut(&super::Symbol), +) { let addr = what.address_or_ip(); let mut call = |sym: Symbol<'_>| { // Extend the lifetime of `sym` to `'static` since we are unfortunately @@ -345,7 +353,7 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) (cb)(&super::Symbol { inner: sym }); }; - Cache::with_global(|cache| { + Cache::with_global(cache_capacity, |cache| { let (lib, addr) = match cache.avma_to_svma(addr as *const u8) { Some(pair) => pair, None => return, @@ -353,7 +361,7 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) // Finally, get a cached mapping or create a new mapping for this file, and // evaluate the DWARF info to find the file/line/name for this address. - let cx = match cache.mapping_for_lib(lib) { + let cx = match cache.mapping_for_lib(lib, cache_capacity) { Some(cx) => cx, None => return, }; diff --git a/src/symbolize/miri.rs b/src/symbolize/miri.rs index 5b0dc3084..3e7d9a8b7 100644 --- a/src/symbolize/miri.rs +++ b/src/symbolize/miri.rs @@ -5,7 +5,11 @@ use super::super::backtrace::miri::{resolve_addr, Frame}; use super::BytesOrWideString; use super::{ResolveWhat, SymbolName}; -pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { +pub unsafe fn resolve( + what: ResolveWhat<'_>, + _cache_capacity: usize, + cb: &mut dyn FnMut(&super::Symbol), +) { let sym = match what { ResolveWhat::Address(addr) => Symbol { inner: resolve_addr(addr), diff --git a/src/symbolize/mod.rs b/src/symbolize/mod.rs index dbc346522..ceddf5c46 100644 --- a/src/symbolize/mod.rs +++ b/src/symbolize/mod.rs @@ -62,6 +62,16 @@ pub fn resolve(addr: *mut c_void, cb: F) { let _guard = crate::lock::lock(); unsafe { resolve_unsynchronized(addr, cb) } } +/// Same as `resolve`, but the user can specified the shared libraries mapping cache capacity. +#[cfg(feature = "std")] +pub fn resolve_customized_cache( + cache_capacity: usize, + addr: *mut c_void, + cb: F, +) { + let _guard = crate::lock::lock(); + unsafe { resolve_unsynchronized_customized_cache(addr, cache_capacity, cb) } +} /// Resolve a previously capture frame to a symbol, passing the symbol to the /// specified closure. @@ -105,6 +115,17 @@ pub fn resolve_frame(frame: &Frame, cb: F) { unsafe { resolve_frame_unsynchronized(frame, cb) } } +/// Same as `resolve_frame`, but the user can specified the shared libraries mapping cache capacity. +#[cfg(feature = "std")] +pub fn resolve_frame_customized_cache( + frame: &Frame, + cb: F, + cache_capacity: usize, +) { + let _guard = crate::lock::lock(); + unsafe { resolve_frame_unsynchronized_customized_cache(frame, cache_capacity, cb) } +} + pub enum ResolveWhat<'a> { Address(*mut c_void), Frame(&'a Frame), @@ -146,6 +167,8 @@ fn adjust_ip(a: *mut c_void) -> *mut c_void { } } +const DEFAULT_MAPPINGS_CACHE_SIZE: usize = 4; + /// Same as `resolve`, only unsafe as it's unsynchronized. /// /// This function does not have synchronization guarantees but is available when @@ -159,7 +182,21 @@ pub unsafe fn resolve_unsynchronized(addr: *mut c_void, mut cb: F) where F: FnMut(&Symbol), { - imp::resolve(ResolveWhat::Address(addr), &mut cb) + imp::resolve( + ResolveWhat::Address(addr), + DEFAULT_MAPPINGS_CACHE_SIZE, + &mut cb, + ) +} +/// Same as `resolve_unsynchronized`, but the user can specified the shared libraries mapping cache capacity. +pub unsafe fn resolve_unsynchronized_customized_cache( + addr: *mut c_void, + cache_capacity: usize, + mut cb: F, +) where + F: FnMut(&Symbol), +{ + imp::resolve(ResolveWhat::Address(addr), cache_capacity, &mut cb) } /// Same as `resolve_frame`, only unsafe as it's unsynchronized. @@ -175,7 +212,22 @@ pub unsafe fn resolve_frame_unsynchronized(frame: &Frame, mut cb: F) where F: FnMut(&Symbol), { - imp::resolve(ResolveWhat::Frame(frame), &mut cb) + imp::resolve( + ResolveWhat::Frame(frame), + DEFAULT_MAPPINGS_CACHE_SIZE, + &mut cb, + ) +} + +/// Same as `resolve_frame_unsynchronized`, but the user can specified the shared libraries mapping cache capacity. +pub unsafe fn resolve_frame_unsynchronized_customized_cache( + frame: &Frame, + cache_capacity: usize, + mut cb: F, +) where + F: FnMut(&Symbol), +{ + imp::resolve(ResolveWhat::Frame(frame), cache_capacity, &mut cb) } /// A trait representing the resolution of a symbol in a file. diff --git a/src/symbolize/noop.rs b/src/symbolize/noop.rs index c53336531..faf2b88e6 100644 --- a/src/symbolize/noop.rs +++ b/src/symbolize/noop.rs @@ -5,7 +5,12 @@ use super::{BytesOrWideString, ResolveWhat, SymbolName}; use core::ffi::c_void; use core::marker; -pub unsafe fn resolve(_addr: ResolveWhat<'_>, _cb: &mut dyn FnMut(&super::Symbol)) {} +pub unsafe fn resolve( + _addr: ResolveWhat<'_>, + _cache_capacity: usize, + _cb: &mut dyn FnMut(&super::Symbol), +) { +} pub struct Symbol<'a> { _marker: marker::PhantomData<&'a i32>,