1use alloc::collections::btree_map::BTreeMap;
2use core::{
3 alloc::Layout,
4 ops::Range,
5 sync::atomic::{AtomicBool, Ordering},
6};
7
8use ax_sync::SpinLock;
9use axbacktrace::Backtrace;
10
11pub(crate) static TRACKING_ENABLED: AtomicBool = AtomicBool::new(false);
12
13#[ax_percpu::def_percpu]
14pub(crate) static IN_GLOBAL_ALLOCATOR: bool = false;
15#[derive(Debug)]
22pub struct AllocationInfo {
23 pub layout: Layout,
25 pub backtrace: Backtrace,
27 pub generation: u64,
29}
30
31pub(crate) struct GlobalState {
32 pub map: BTreeMap<usize, AllocationInfo>,
34 pub generation: u64,
35}
36
37static STATE: SpinLock<GlobalState> = SpinLock::new(GlobalState {
38 map: BTreeMap::new(),
39 generation: 0,
40});
41
42pub fn enable_tracking() {
44 TRACKING_ENABLED.store(true, Ordering::SeqCst);
45}
46
47pub fn disable_tracking() {
49 TRACKING_ENABLED.store(false, Ordering::SeqCst);
50}
51
52pub fn tracking_enabled() -> bool {
54 TRACKING_ENABLED.load(Ordering::SeqCst)
55}
56
57pub(crate) fn with_state<R>(f: impl FnOnce(Option<&mut GlobalState>) -> R) -> R {
58 let _guard = ax_sync::PreemptGuard::new();
59 unsafe {
61 ax_percpu::with_cpu_pin(|pin| {
62 if IN_GLOBAL_ALLOCATOR.read_current(pin) || !tracking_enabled() {
63 return f(None);
64 }
65
66 IN_GLOBAL_ALLOCATOR.write_current(pin, true);
67 let mut state = STATE.lock_irqsave();
68 let result = f(Some(&mut state));
69 drop(state);
70 IN_GLOBAL_ALLOCATOR.write_current(pin, false);
71 result
72 })
73 }
74 .expect("allocator tracking requires an installed CPU area")
75}
76
77pub fn current_generation() -> u64 {
84 STATE.lock_irqsave().generation
85}
86
87pub fn allocations_in(range: Range<u64>, visitor: impl FnMut(&AllocationInfo)) {
90 with_state(|state| {
91 state
92 .unwrap()
93 .map
94 .values()
95 .filter(move |info| range.contains(&info.generation))
96 .for_each(visitor)
97 });
98}