ptr_origin_tracker/
lib.rs1use 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#[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 #[derive(Debug)]
212 pub struct X {
213 a: u8,
214 b: u32,
215 }
216
217 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}