ptr_origin_tracker/
lib.rs

1use log::debug;
2use std::clone::Clone;
3use std::cmp::{Eq, PartialEq};
4use std::convert::TryFrom;
5use std::fmt;
6use std::marker::Send;
7
8use std::hash::Hash;
9
10// narsty but must both be public
11#[doc(hidden)]
12pub use paste;
13#[doc(hidden)]
14pub use lazy_static;
15
16#[doc(hidden)]
17pub mod prelude {
18    pub use super::{Tracker,Trackable,setup};
19}
20
21#[derive(Debug, PartialEq, Eq)]
22pub enum Error<T>
23where
24    T: fmt::Debug,
25{
26    NullPointer,
27    UnknownPointer(*mut T),
28    DuplicateTracking(*mut T),
29}
30
31impl<T> fmt::Display for Error<T>
32where
33    T: fmt::Debug,
34{
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        write!(f, "Nullptr detected")
37    }
38}
39
40impl<T> std::error::Error for Error<T> where T: fmt::Debug {}
41
42pub trait Trackable {
43    type Tracker;
44
45    fn tracker() -> Self::Tracker;
46}
47
48#[macro_export(local_inner_macros)]
49macro_rules! setup {
50    ($t:ident) => {
51        $crate::paste::item! {
52            $crate::lazy_static::lazy_static! {
53                static ref [<$t _TRACKER_SINGLETON>] : $crate::Tracker<$t> = $crate::Tracker::<$t>::new();
54            }
55
56            impl Trackable for $t
57            {
58                type Tracker = $crate::Tracker<$t>;
59                fn tracker() -> Self::Tracker {
60                    [<$t _TRACKER_SINGLETON>].clone()
61                }
62            }
63        }
64    };
65}
66
67#[derive(Debug)]
68pub struct Cookie<T>(*mut T)
69where
70    T: fmt::Debug,
71    *mut T: Hash + Eq;
72
73impl<T> Cookie<T>
74where
75    T: fmt::Debug,
76    *mut T: Hash + Eq,
77{
78    pub fn as_ptr(&self) -> *mut T {
79        self.0
80    }
81
82    pub fn try_from(ptr: *mut T) -> std::result::Result<Self, Error<T>> {
83        Ok(Cookie::<T>(ptr))
84    }
85}
86use core::hash::Hasher;
87
88impl<T> PartialEq for Cookie<T>  where T: std::fmt::Debug {
89    fn eq(&self, other: &Self) -> bool {
90        self.0 == other.0
91    }
92}
93
94impl<T> Eq for Cookie<T>  where T: std::fmt::Debug {}
95
96impl<T> Hash for Cookie<T> where T: std::fmt::Debug {
97    fn hash<H: Hasher>(&self, state: &mut H) {
98        self.0.hash(state);
99    }
100}
101
102unsafe impl<T> Send for Cookie<T> where T: fmt::Debug {}
103
104use std::collections::HashSet;
105use std::sync::Arc;
106use std::sync::Mutex;
107
108#[derive(Debug)]
109pub struct Tracker<T>
110where
111    T: fmt::Debug,
112    Cookie<T>: Hash + Eq,
113    T: Trackable<Tracker = Self>,
114{
115    inner: Arc<Mutex<HashSet<Cookie<T>>>>,
116}
117
118impl<T> Clone for Tracker<T>
119where
120    T: fmt::Debug,
121    Cookie<T>: Hash + Eq,
122    T: Trackable<Tracker = Self>,
123{
124    fn clone(&self) -> Self {
125        Self {
126            inner: self.inner.clone(),
127        }
128    }
129}
130
131impl<T> Tracker<T>
132where
133    Cookie<T>: Hash + Eq,
134    T: fmt::Debug,
135    T: Trackable<Tracker = Self>,
136{
137    #[allow(unused)]
138    pub fn new() -> Self {
139        Self {
140            inner: Arc::new(Mutex::new(HashSet::with_capacity(128))),
141        }
142    }
143
144    fn tracker() -> Option<Tracker<T>> {
145        Some(<T as Trackable>::tracker())
146    }
147
148    pub fn track(handle: *mut T) -> Result<(), Error<T>> {
149        if let Some(tracker) = Self::tracker() {
150            let mut guard = tracker.inner.lock().unwrap();
151            let cookie = Cookie::try_from(handle)?;
152            if guard.contains(&cookie) {
153                return Err(Error::DuplicateTracking(handle));
154            } else {
155                let _ = guard.insert(cookie);
156                debug!("Added handle {:?}, total of {}", handle, guard.len());
157                return Ok(());
158            }
159        }
160        unreachable!("Forgot to setup a registry");
161    }
162
163    #[deprecated]
164    pub fn contains(handle: *mut T) -> bool {
165        Self::exists(handle)
166    }
167
168    pub fn exists(handle: *mut T) -> bool {
169        if let Some(tracker) = Self::tracker() {
170            let guard = tracker.inner.lock().unwrap();
171            debug!("Removed handle {:?}, total of {}", handle, guard.len());
172            let k = Cookie::try_from(handle).unwrap();
173            return guard.contains(&k);
174        }
175        unreachable!("Forgot to setup a registry");
176    }
177
178    pub fn untrack(handle: *mut T) -> Result<(), Error<T>> {
179        if let Some(tracker) = Self::tracker() {
180            let mut guard = tracker.inner.lock().unwrap();
181            debug!("Removed handle {:?}, total of {}", handle, guard.len());
182            let k = Cookie::try_from(handle).unwrap();
183            let _ = guard.remove(&k);
184            return Ok(());
185        }
186        unreachable!("Forgot to setup a registry");
187    }
188
189    pub fn cleanup<F>(f: F)
190    where
191        F: Fn(*mut T) -> (),
192    {
193        if let Some(tracker) = Self::tracker() {
194            let guard = tracker.inner.lock().unwrap();
195            for handle in guard.iter() {
196                f(handle.as_ptr())
197            }
198        }
199        unreachable!("Forgot to setup a registry");
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206
207    fn setup() {}
208
209    // helper demo struct
210    // could be anything that is send
211    #[derive(Debug)]
212    pub struct X {
213        a: u8,
214        b: u32,
215    }
216
217    // commonly we track pointers
218    // which are send
219    type Y = *mut X;
220
221    #[test]
222    fn tracky() {
223        setup!(X);
224
225        let mut x = X::default();
226        let ptr = &mut x as *mut X;
227        assert_eq!(Tracker::<X>::track(ptr), Ok(()));
228        assert!(Tracker::<X>::contains(ptr));
229        assert_eq!(Tracker::<X>::untrack(ptr), Ok(()));
230    }
231}