Skip to content

Add API to support mapping cache capacity configurable #503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
6 changes: 5 additions & 1 deletion src/symbolize/dbghelp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ impl Symbol<'_> {
#[repr(C, align(8))]
struct Aligned8<T>(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,
Expand Down
32 changes: 20 additions & 12 deletions src/symbolize/gimli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -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
Expand All @@ -265,7 +265,7 @@ impl Cache {
// never happen, and symbolicating backtraces would be ssssllllooooowwww.
static mut MAPPINGS_CACHE: Option<Cache> = 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)> {
Expand Down Expand Up @@ -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
Expand All @@ -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();
}

Expand All @@ -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
Expand All @@ -345,15 +353,15 @@ 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,
};

// 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,
};
Expand Down
6 changes: 5 additions & 1 deletion src/symbolize/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
56 changes: 54 additions & 2 deletions src/symbolize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ pub fn resolve<F: FnMut(&Symbol)>(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<F: FnMut(&Symbol)>(
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.
Expand Down Expand Up @@ -105,6 +115,17 @@ pub fn resolve_frame<F: FnMut(&Symbol)>(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<F: FnMut(&Symbol)>(
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),
Expand Down Expand Up @@ -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
Expand All @@ -159,7 +182,21 @@ pub unsafe fn resolve_unsynchronized<F>(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<F>(
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.
Expand All @@ -175,7 +212,22 @@ pub unsafe fn resolve_frame_unsynchronized<F>(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<F>(
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.
Expand Down
7 changes: 6 additions & 1 deletion src/symbolize/noop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down