use alloc::collections::btree_map::BTreeMap;
use core::{
alloc::Layout,
ops::Range,
sync::atomic::{AtomicBool, Ordering},
};
use ax_kspin::SpinNoIrq;
use axbacktrace::Backtrace;
pub(crate) static TRACKING_ENABLED: AtomicBool = AtomicBool::new(false);
#[ax_percpu::def_percpu]
pub(crate) static IN_GLOBAL_ALLOCATOR: bool = false;
#[derive(Debug)]
pub struct AllocationInfo {
pub layout: Layout,
pub backtrace: Backtrace,
pub generation: u64,
}
pub(crate) struct GlobalState {
pub map: BTreeMap<usize, AllocationInfo>,
pub generation: u64,
}
static STATE: SpinNoIrq<GlobalState> = SpinNoIrq::new(GlobalState {
map: BTreeMap::new(),
generation: 0,
});
pub fn enable_tracking() {
TRACKING_ENABLED.store(true, Ordering::SeqCst);
}
pub fn disable_tracking() {
TRACKING_ENABLED.store(false, Ordering::SeqCst);
}
pub fn tracking_enabled() -> bool {
TRACKING_ENABLED.load(Ordering::SeqCst)
}
pub(crate) fn with_state<R>(f: impl FnOnce(Option<&mut GlobalState>) -> R) -> R {
IN_GLOBAL_ALLOCATOR.with_current(|in_global| {
if *in_global || !tracking_enabled() {
f(None)
} else {
*in_global = true;
let mut state = STATE.lock();
let result = f(Some(&mut state));
*in_global = false;
result
}
})
}
pub fn current_generation() -> u64 {
STATE.lock().generation
}
pub fn allocations_in(range: Range<u64>, visitor: impl FnMut(&AllocationInfo)) {
with_state(|state| {
state
.unwrap()
.map
.values()
.filter(move |info| range.contains(&info.generation))
.for_each(visitor)
});
}