1use alloc::collections::btree_map::BTreeMap;
2use core::{
3 alloc::Layout,
4 ops::Range,
5 sync::atomic::{AtomicBool, Ordering},
6};
7
8use ax_kspin::SpinNoIrq;
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
16#[derive(Debug)]
18pub struct AllocationInfo {
19 pub layout: Layout,
21 pub backtrace: Backtrace,
23 pub generation: u64,
25}
26
27pub(crate) struct GlobalState {
28 pub map: BTreeMap<usize, AllocationInfo>,
30 pub generation: u64,
31}
32
33static STATE: SpinNoIrq<GlobalState> = SpinNoIrq::new(GlobalState {
34 map: BTreeMap::new(),
35 generation: 0,
36});
37
38pub fn enable_tracking() {
40 TRACKING_ENABLED.store(true, Ordering::SeqCst);
41}
42
43pub fn disable_tracking() {
45 TRACKING_ENABLED.store(false, Ordering::SeqCst);
46}
47
48pub fn tracking_enabled() -> bool {
50 TRACKING_ENABLED.load(Ordering::SeqCst)
51}
52
53pub(crate) fn with_state<R>(f: impl FnOnce(Option<&mut GlobalState>) -> R) -> R {
54 IN_GLOBAL_ALLOCATOR.with_current(|in_global| {
55 if *in_global || !tracking_enabled() {
56 f(None)
57 } else {
58 *in_global = true;
59 let mut state = STATE.lock();
60 let result = f(Some(&mut state));
61 *in_global = false;
62 result
63 }
64 })
65}
66
67pub fn current_generation() -> u64 {
74 STATE.lock().generation
75}
76
77pub fn allocations_in(range: Range<u64>, visitor: impl FnMut(&AllocationInfo)) {
80 with_state(|state| {
81 state
82 .unwrap()
83 .map
84 .values()
85 .filter(move |info| range.contains(&info.generation))
86 .for_each(visitor)
87 });
88}