1use crate::event::Event;
55use lazy_static::lazy_static;
56use parking_lot::Mutex;
57use std::sync::atomic::{AtomicU64, Ordering};
58
59lazy_static! {
60 static ref TRACKER: Mutex<Tracker> = Mutex::new(Tracker::new());
62}
63
64static TIMESTAMP: AtomicU64 = AtomicU64::new(0);
66
67pub struct Tracker {
69 events: Vec<Event>,
71
72 var_counter: u64,
74}
75
76impl Tracker {
77 pub fn new() -> Self {
79 Self {
80 events: Vec::new(),
81 var_counter: 0,
82 }
83 }
84
85 fn next_timestamp() -> u64 {
87 TIMESTAMP.fetch_add(1, Ordering::Relaxed)
88 }
89
90 fn next_var_id(&mut self, name: &str) -> String {
92 let id = format!("{}_{}", name, self.var_counter);
93 self.var_counter += 1;
94 id
95 }
96
97 #[cfg_attr(not(feature = "track"), allow(dead_code))]
99 pub fn record_new(&mut self, var_name: &str, type_name: &str) -> String {
100 let timestamp = Self::next_timestamp();
101 let var_id = self.next_var_id(var_name);
102
103 self.events.push(Event::New {
104 timestamp,
105 var_name: var_name.to_string(),
106 var_id: var_id.clone(),
107 type_name: type_name.to_string(),
108 });
109
110 var_id
111 }
112
113 #[cfg_attr(not(feature = "track"), allow(dead_code))]
115 pub fn record_borrow(&mut self, borrower_name: &str, owner_id: &str, mutable: bool) -> String {
116 let timestamp = Self::next_timestamp();
117 let borrower_id = self.next_var_id(borrower_name);
118
119 self.events.push(Event::Borrow {
120 timestamp,
121 borrower_name: borrower_name.to_string(),
122 borrower_id: borrower_id.clone(),
123 owner_id: owner_id.to_string(),
124 mutable,
125 });
126
127 borrower_id
128 }
129
130 #[allow(dead_code)]
132 pub fn record_move(&mut self, from_id: &str, to_name: &str) -> String {
133 let timestamp = Self::next_timestamp();
134 let to_id = self.next_var_id(to_name);
135
136 self.events.push(Event::Move {
137 timestamp,
138 from_id: from_id.to_string(),
139 to_name: to_name.to_string(),
140 to_id: to_id.clone(),
141 });
142
143 to_id
144 }
145
146 #[cfg_attr(not(feature = "track"), allow(dead_code))]
148 pub fn record_drop(&mut self, var_id: &str) {
149 let timestamp = Self::next_timestamp();
150
151 self.events.push(Event::Drop {
152 timestamp,
153 var_id: var_id.to_string(),
154 });
155 }
156
157 #[cfg_attr(not(feature = "track"), allow(dead_code))]
159 pub fn record_rc_new(
160 &mut self,
161 var_name: &str,
162 strong_count: usize,
163 weak_count: usize,
164 ) -> String {
165 let timestamp = Self::next_timestamp();
166 let var_id = self.next_var_id(var_name);
167
168 self.events.push(Event::RcNew {
169 timestamp,
170 var_name: var_name.to_string(),
171 var_id: var_id.clone(),
172 type_name: "Rc<T>".to_string(),
173 strong_count,
174 weak_count,
175 });
176
177 var_id
178 }
179
180 #[cfg_attr(not(feature = "track"), allow(dead_code))]
182 pub fn record_rc_clone(
183 &mut self,
184 var_name: &str,
185 source_name: &str,
186 strong_count: usize,
187 weak_count: usize,
188 ) -> String {
189 let timestamp = Self::next_timestamp();
190 let var_id = self.next_var_id(var_name);
191
192 self.events.push(Event::RcClone {
193 timestamp,
194 var_name: var_name.to_string(),
195 var_id: var_id.clone(),
196 source_id: source_name.to_string(),
197 strong_count,
198 weak_count,
199 });
200
201 var_id
202 }
203
204 #[cfg_attr(not(feature = "track"), allow(dead_code))]
206 pub fn record_arc_new(
207 &mut self,
208 var_name: &str,
209 strong_count: usize,
210 weak_count: usize,
211 ) -> String {
212 let timestamp = Self::next_timestamp();
213 let var_id = self.next_var_id(var_name);
214
215 self.events.push(Event::ArcNew {
216 timestamp,
217 var_name: var_name.to_string(),
218 var_id: var_id.clone(),
219 type_name: "Arc<T>".to_string(),
220 strong_count,
221 weak_count,
222 });
223
224 var_id
225 }
226
227 #[cfg_attr(not(feature = "track"), allow(dead_code))]
229 pub fn record_arc_clone(
230 &mut self,
231 var_name: &str,
232 source_name: &str,
233 strong_count: usize,
234 weak_count: usize,
235 ) -> String {
236 let timestamp = Self::next_timestamp();
237 let var_id = self.next_var_id(var_name);
238
239 self.events.push(Event::ArcClone {
240 timestamp,
241 var_name: var_name.to_string(),
242 var_id: var_id.clone(),
243 source_id: source_name.to_string(),
244 strong_count,
245 weak_count,
246 });
247
248 var_id
249 }
250
251 #[cfg_attr(not(feature = "track"), allow(dead_code))]
253 pub fn record_new_with_id(
254 &mut self,
255 id: usize,
256 var_name: &str,
257 type_name: &str,
258 location: &str,
259 ) -> String {
260 let timestamp = Self::next_timestamp();
261 let var_id = format!("{}_{}", var_name, id);
262
263 self.events.push(Event::New {
264 timestamp,
265 var_name: var_name.to_string(),
266 var_id: var_id.clone(),
267 type_name: format!("{} @ {}", type_name, location),
268 });
269
270 var_id
271 }
272
273 #[cfg_attr(not(feature = "track"), allow(dead_code))]
275 pub fn record_borrow_with_id(
276 &mut self,
277 borrower_id: usize,
278 owner_id: usize,
279 borrower_name: &str,
280 location: &str,
281 mutable: bool,
282 ) -> String {
283 let timestamp = Self::next_timestamp();
284 let borrower_var_id = format!("{}_{}", borrower_name, borrower_id);
285 let owner_var_id = format!("owner_{}", owner_id);
286
287 self.events.push(Event::Borrow {
288 timestamp,
289 borrower_name: format!("{} @ {}", borrower_name, location),
290 borrower_id: borrower_var_id.clone(),
291 owner_id: owner_var_id,
292 mutable,
293 });
294
295 borrower_var_id
296 }
297
298 #[allow(dead_code)]
300 pub fn record_move_with_id(
301 &mut self,
302 from_id: usize,
303 to_id: usize,
304 to_name: &str,
305 location: &str,
306 ) -> String {
307 let timestamp = Self::next_timestamp();
308 let from_var_id = format!("var_{}", from_id);
309 let to_var_id = format!("{}_{}", to_name, to_id);
310
311 self.events.push(Event::Move {
312 timestamp,
313 from_id: from_var_id,
314 to_name: format!("{} @ {}", to_name, location),
315 to_id: to_var_id.clone(),
316 });
317
318 to_var_id
319 }
320
321 #[cfg_attr(not(feature = "track"), allow(dead_code))]
323 pub fn record_drop_with_id(&mut self, id: usize, location: &str) {
324 let timestamp = Self::next_timestamp();
325 let var_id = format!("var_{} @ {}", id, location);
326
327 self.events.push(Event::Drop { timestamp, var_id });
328 }
329
330 #[cfg_attr(not(feature = "track"), allow(dead_code))]
332 pub fn record_rc_new_with_id(
333 &mut self,
334 id: usize,
335 var_name: &str,
336 type_name: &str,
337 location: &str,
338 strong_count: usize,
339 weak_count: usize,
340 ) -> String {
341 let timestamp = Self::next_timestamp();
342 let var_id = format!("{}_{}", var_name, id);
343
344 self.events.push(Event::RcNew {
345 timestamp,
346 var_name: var_name.to_string(),
347 var_id: var_id.clone(),
348 type_name: format!("{} @ {}", type_name, location),
349 strong_count,
350 weak_count,
351 });
352
353 var_id
354 }
355
356 #[cfg_attr(not(feature = "track"), allow(dead_code))]
358 pub fn record_rc_clone_with_id(
359 &mut self,
360 new_id: usize,
361 source_id: usize,
362 var_name: &str,
363 location: &str,
364 strong_count: usize,
365 weak_count: usize,
366 ) -> String {
367 let timestamp = Self::next_timestamp();
368 let var_id = format!("{}_{}", var_name, new_id);
369 let source_var_id = format!("var_{}", source_id);
370
371 self.events.push(Event::RcClone {
372 timestamp,
373 var_name: format!("{} @ {}", var_name, location),
374 var_id: var_id.clone(),
375 source_id: source_var_id,
376 strong_count,
377 weak_count,
378 });
379
380 var_id
381 }
382
383 #[cfg_attr(not(feature = "track"), allow(dead_code))]
385 pub fn record_arc_new_with_id(
386 &mut self,
387 id: usize,
388 var_name: &str,
389 type_name: &str,
390 location: &str,
391 strong_count: usize,
392 weak_count: usize,
393 ) -> String {
394 let timestamp = Self::next_timestamp();
395 let var_id = format!("{}_{}", var_name, id);
396
397 self.events.push(Event::ArcNew {
398 timestamp,
399 var_name: var_name.to_string(),
400 var_id: var_id.clone(),
401 type_name: format!("{} @ {}", type_name, location),
402 strong_count,
403 weak_count,
404 });
405
406 var_id
407 }
408
409 #[cfg_attr(not(feature = "track"), allow(dead_code))]
411 pub fn record_arc_clone_with_id(
412 &mut self,
413 new_id: usize,
414 source_id: usize,
415 var_name: &str,
416 location: &str,
417 strong_count: usize,
418 weak_count: usize,
419 ) -> String {
420 let timestamp = Self::next_timestamp();
421 let var_id = format!("{}_{}", var_name, new_id);
422 let source_var_id = format!("var_{}", source_id);
423
424 self.events.push(Event::ArcClone {
425 timestamp,
426 var_name: format!("{} @ {}", var_name, location),
427 var_id: var_id.clone(),
428 source_id: source_var_id,
429 strong_count,
430 weak_count,
431 });
432
433 var_id
434 }
435
436 pub fn record_refcell_new(&mut self, var_name: &str) -> String {
438 let timestamp = Self::next_timestamp();
439 let var_id = format!("refcell_{}", var_name);
440
441 self.events.push(Event::RefCellNew {
442 timestamp,
443 var_name: var_name.to_string(),
444 var_id: var_id.clone(),
445 type_name: "RefCell<T>".to_string(),
446 });
447
448 var_id
449 }
450
451 pub fn record_refcell_borrow(
453 &mut self,
454 borrow_id: &str,
455 refcell_id: &str,
456 is_mutable: bool,
457 location: &str,
458 ) {
459 let timestamp = Self::next_timestamp();
460
461 self.events.push(Event::RefCellBorrow {
462 timestamp,
463 borrow_id: borrow_id.to_string(),
464 refcell_id: refcell_id.to_string(),
465 is_mutable,
466 location: location.to_string(),
467 });
468 }
469
470 pub fn record_refcell_drop(&mut self, borrow_id: &str, location: &str) {
472 let timestamp = Self::next_timestamp();
473
474 self.events.push(Event::RefCellDrop {
475 timestamp,
476 borrow_id: borrow_id.to_string(),
477 location: location.to_string(),
478 });
479 }
480
481 pub fn record_cell_new(&mut self, var_name: &str) -> String {
483 let timestamp = Self::next_timestamp();
484 let var_id = format!("cell_{}", var_name);
485
486 self.events.push(Event::CellNew {
487 timestamp,
488 var_name: var_name.to_string(),
489 var_id: var_id.clone(),
490 type_name: "Cell<T>".to_string(),
491 });
492
493 var_id
494 }
495
496 pub fn record_cell_get(&mut self, cell_id: &str, location: &str) {
498 let timestamp = Self::next_timestamp();
499
500 self.events.push(Event::CellGet {
501 timestamp,
502 cell_id: cell_id.to_string(),
503 location: location.to_string(),
504 });
505 }
506
507 pub fn record_cell_set(&mut self, cell_id: &str, location: &str) {
509 let timestamp = Self::next_timestamp();
510
511 self.events.push(Event::CellSet {
512 timestamp,
513 cell_id: cell_id.to_string(),
514 location: location.to_string(),
515 });
516 }
517
518 pub fn record_static_init(
520 &mut self,
521 var_name: &str,
522 var_id: usize,
523 type_name: &str,
524 is_mutable: bool,
525 ) {
526 let timestamp = Self::next_timestamp();
527
528 self.events.push(Event::StaticInit {
529 timestamp,
530 var_name: var_name.to_string(),
531 var_id: var_id.to_string(),
532 type_name: type_name.to_string(),
533 is_mutable,
534 });
535 }
536
537 pub fn record_static_access(
539 &mut self,
540 var_id: usize,
541 var_name: &str,
542 is_write: bool,
543 location: &str,
544 ) {
545 let timestamp = Self::next_timestamp();
546
547 self.events.push(Event::StaticAccess {
548 timestamp,
549 var_id: var_id.to_string(),
550 var_name: var_name.to_string(),
551 is_write,
552 location: location.to_string(),
553 });
554 }
555
556 pub fn record_const_eval(
558 &mut self,
559 const_name: &str,
560 const_id: usize,
561 type_name: &str,
562 location: &str,
563 ) {
564 let timestamp = Self::next_timestamp();
565
566 self.events.push(Event::ConstEval {
567 timestamp,
568 const_name: const_name.to_string(),
569 const_id: const_id.to_string(),
570 type_name: type_name.to_string(),
571 location: location.to_string(),
572 });
573 }
574
575 pub fn record_raw_ptr_created(
577 &mut self,
578 var_name: &str,
579 var_id: usize,
580 ptr_type: &str,
581 address: usize,
582 location: &str,
583 ) {
584 let timestamp = Self::next_timestamp();
585
586 self.events.push(Event::RawPtrCreated {
587 timestamp,
588 var_name: var_name.to_string(),
589 var_id: var_id.to_string(),
590 ptr_type: ptr_type.to_string(),
591 address,
592 location: location.to_string(),
593 });
594 }
595
596 pub fn record_raw_ptr_deref(&mut self, ptr_id: usize, location: &str, is_write: bool) {
598 let timestamp = Self::next_timestamp();
599
600 self.events.push(Event::RawPtrDeref {
601 timestamp,
602 ptr_id: ptr_id.to_string(),
603 location: location.to_string(),
604 is_write,
605 });
606 }
607
608 pub fn record_unsafe_block_enter(&mut self, block_id: usize, location: &str) {
610 let timestamp = Self::next_timestamp();
611
612 self.events.push(Event::UnsafeBlockEnter {
613 timestamp,
614 block_id: block_id.to_string(),
615 location: location.to_string(),
616 });
617 }
618
619 pub fn record_unsafe_block_exit(&mut self, block_id: usize, location: &str) {
621 let timestamp = Self::next_timestamp();
622
623 self.events.push(Event::UnsafeBlockExit {
624 timestamp,
625 block_id: block_id.to_string(),
626 location: location.to_string(),
627 });
628 }
629
630 pub fn record_unsafe_fn_call(&mut self, fn_name: &str, location: &str) {
632 let timestamp = Self::next_timestamp();
633
634 self.events.push(Event::UnsafeFnCall {
635 timestamp,
636 fn_name: fn_name.to_string(),
637 location: location.to_string(),
638 });
639 }
640
641 pub fn record_ffi_call(&mut self, fn_name: &str, location: &str) {
643 let timestamp = Self::next_timestamp();
644
645 self.events.push(Event::FfiCall {
646 timestamp,
647 fn_name: fn_name.to_string(),
648 location: location.to_string(),
649 });
650 }
651
652 pub fn record_transmute(&mut self, from_type: &str, to_type: &str, location: &str) {
654 let timestamp = Self::next_timestamp();
655
656 self.events.push(Event::Transmute {
657 timestamp,
658 from_type: from_type.to_string(),
659 to_type: to_type.to_string(),
660 location: location.to_string(),
661 });
662 }
663
664 pub fn record_union_field_access(
666 &mut self,
667 union_name: &str,
668 field_name: &str,
669 location: &str,
670 ) {
671 let timestamp = Self::next_timestamp();
672
673 self.events.push(Event::UnionFieldAccess {
674 timestamp,
675 union_name: union_name.to_string(),
676 field_name: field_name.to_string(),
677 location: location.to_string(),
678 });
679 }
680
681 pub fn events(&self) -> &[Event] {
683 &self.events
684 }
685
686 pub fn clear(&mut self) {
688 self.events.clear();
689 self.var_counter = 0;
690 TIMESTAMP.store(0, Ordering::Relaxed);
691 }
692}
693
694impl Default for Tracker {
695 fn default() -> Self {
696 Self::new()
697 }
698}
699
700#[inline(always)]
739pub fn track_new<T>(
740 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
741 value: T,
742) -> T {
743 #[cfg(feature = "track")]
744 {
745 let type_name = std::any::type_name::<T>();
746 let mut tracker = TRACKER.lock();
747 tracker.record_new(name, type_name);
748 }
749 value
750}
751
752#[inline(always)]
781pub fn track_borrow<'a, T: ?Sized>(
782 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
783 value: &'a T,
784) -> &'a T {
785 #[cfg(feature = "track")]
786 {
787 let mut tracker = TRACKER.lock();
788 tracker.record_borrow(name, "unknown", false);
789 }
790 value
791}
792
793#[inline(always)]
822pub fn track_borrow_mut<'a, T: ?Sized>(
823 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
824 value: &'a mut T,
825) -> &'a mut T {
826 #[cfg(feature = "track")]
827 {
828 let mut tracker = TRACKER.lock();
829 tracker.record_borrow(name, "unknown", true);
830 }
831 value
832}
833
834#[inline(always)]
862pub fn track_move<T>(
863 #[cfg_attr(not(feature = "track"), allow(unused_variables))] from_name: &str,
864 #[cfg_attr(not(feature = "track"), allow(unused_variables))] to_name: &str,
865 value: T,
866) -> T {
867 #[cfg(feature = "track")]
868 {
869 let mut tracker = TRACKER.lock();
870 tracker.record_move(from_name, to_name);
871 }
872 value
873}
874
875#[inline(always)]
905pub fn track_drop(#[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str) {
906 #[cfg(feature = "track")]
907 {
908 let mut tracker = TRACKER.lock();
909 tracker.record_drop(name);
910 }
911}
912
913#[inline(always)]
937pub fn track_drop_batch(
938 #[cfg_attr(not(feature = "track"), allow(unused_variables))] names: &[&str],
939) {
940 #[cfg(feature = "track")]
941 {
942 let mut tracker = TRACKER.lock();
943 for &name in names {
944 tracker.record_drop(name);
945 }
946 }
947}
948
949pub fn reset() {
971 let mut tracker = TRACKER.lock();
972 tracker.clear();
973}
974
975pub fn get_events() -> Vec<Event> {
1009 TRACKER.lock().events().to_vec()
1010}
1011
1012pub fn get_events_filtered<F>(predicate: F) -> Vec<Event>
1025where
1026 F: Fn(&Event) -> bool,
1027{
1028 TRACKER.lock().events().iter().filter(|e| predicate(e)).cloned().collect()
1029}
1030
1031pub fn get_new_events() -> Vec<Event> {
1033 get_events_filtered(|e| e.is_new())
1034}
1035
1036pub fn get_borrow_events() -> Vec<Event> {
1038 get_events_filtered(|e| e.is_borrow())
1039}
1040
1041pub fn get_drop_events() -> Vec<Event> {
1043 get_events_filtered(|e| e.is_drop())
1044}
1045
1046pub fn get_move_events() -> Vec<Event> {
1048 get_events_filtered(|e| e.is_move())
1049}
1050
1051pub fn get_events_for_var(name: &str) -> Vec<Event> {
1064 get_events_filtered(|e| e.var_name().map(|n| n == name).unwrap_or(false))
1065}
1066
1067pub fn get_event_counts() -> (usize, usize, usize, usize) {
1084 let events = TRACKER.lock();
1085 let events = events.events();
1086 (
1087 events.iter().filter(|e| e.is_new()).count(),
1088 events.iter().filter(|e| e.is_borrow()).count(),
1089 events.iter().filter(|e| e.is_move()).count(),
1090 events.iter().filter(|e| e.is_drop()).count(),
1091 )
1092}
1093
1094#[derive(Debug, Clone, Default)]
1096pub struct TrackingSummary {
1097 pub variables_created: usize,
1099 pub variables_dropped: usize,
1101 pub immutable_borrows: usize,
1103 pub mutable_borrows: usize,
1105 pub moves: usize,
1107 pub rc_operations: usize,
1109 pub arc_operations: usize,
1111 pub refcell_operations: usize,
1113 pub cell_operations: usize,
1115 pub unsafe_operations: usize,
1117}
1118
1119impl std::fmt::Display for TrackingSummary {
1120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1121 writeln!(f, "=== BorrowScope Summary ===")?;
1122 writeln!(
1123 f,
1124 "Variables: {} created, {} dropped",
1125 self.variables_created, self.variables_dropped
1126 )?;
1127 writeln!(
1128 f,
1129 "Borrows: {} immutable, {} mutable",
1130 self.immutable_borrows, self.mutable_borrows
1131 )?;
1132 if self.moves > 0 {
1133 writeln!(f, "Moves: {}", self.moves)?;
1134 }
1135 if self.rc_operations > 0 || self.arc_operations > 0 {
1136 writeln!(
1137 f,
1138 "Smart pointers: {} Rc, {} Arc",
1139 self.rc_operations, self.arc_operations
1140 )?;
1141 }
1142 if self.refcell_operations > 0 || self.cell_operations > 0 {
1143 writeln!(
1144 f,
1145 "Interior mutability: {} RefCell, {} Cell",
1146 self.refcell_operations, self.cell_operations
1147 )?;
1148 }
1149 if self.unsafe_operations > 0 {
1150 writeln!(f, "Unsafe operations: {}", self.unsafe_operations)?;
1151 }
1152 Ok(())
1153 }
1154}
1155
1156pub fn get_summary() -> TrackingSummary {
1174 let events = TRACKER.lock();
1175 let events = events.events();
1176 let mut summary = TrackingSummary::default();
1177
1178 for event in events {
1179 match event {
1180 Event::New { .. } => summary.variables_created += 1,
1181 Event::Drop { .. } => summary.variables_dropped += 1,
1182 Event::Borrow { mutable, .. } => {
1183 if *mutable {
1184 summary.mutable_borrows += 1;
1185 } else {
1186 summary.immutable_borrows += 1;
1187 }
1188 }
1189 Event::Move { .. } => summary.moves += 1,
1190 Event::RcNew { .. } | Event::RcClone { .. } => summary.rc_operations += 1,
1191 Event::ArcNew { .. } | Event::ArcClone { .. } => summary.arc_operations += 1,
1192 Event::RefCellNew { .. } | Event::RefCellBorrow { .. } | Event::RefCellDrop { .. } => {
1193 summary.refcell_operations += 1
1194 }
1195 Event::CellNew { .. } | Event::CellGet { .. } | Event::CellSet { .. } => {
1196 summary.cell_operations += 1
1197 }
1198 Event::RawPtrCreated { .. }
1199 | Event::RawPtrDeref { .. }
1200 | Event::UnsafeBlockEnter { .. }
1201 | Event::UnsafeBlockExit { .. }
1202 | Event::UnsafeFnCall { .. }
1203 | Event::FfiCall { .. }
1204 | Event::Transmute { .. }
1205 | Event::UnionFieldAccess { .. } => summary.unsafe_operations += 1,
1206 _ => {}
1207 }
1208 }
1209 summary
1210}
1211
1212pub fn print_summary() {
1231 println!("{}", get_summary());
1232}
1233
1234#[inline(always)]
1236#[doc(hidden)]
1237pub fn __track_new_with_id_helper<T>(
1238 #[cfg_attr(not(feature = "track"), allow(unused_variables))] id: usize,
1239 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1240 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1241 value: T,
1242) -> T {
1243 #[cfg(feature = "track")]
1244 {
1245 let type_name = std::any::type_name::<T>();
1246 let mut tracker = TRACKER.lock();
1247 tracker.record_new_with_id(id, name, type_name, location);
1248 }
1249 value
1250}
1251
1252#[inline(always)]
1254pub fn track_new_with_id<T>(
1255 #[cfg_attr(not(feature = "track"), allow(unused_variables))] id: usize,
1256 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1257 #[cfg_attr(not(feature = "track"), allow(unused_variables))] type_name: &str,
1258 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1259 value: T,
1260) -> T {
1261 #[cfg(feature = "track")]
1262 {
1263 let mut tracker = TRACKER.lock();
1264 tracker.record_new_with_id(id, name, type_name, location);
1265 }
1266 value
1267}
1268
1269#[inline(always)]
1271pub fn track_borrow_with_id<'a, T: ?Sized>(
1272 #[cfg_attr(not(feature = "track"), allow(unused_variables))] borrower_id: usize,
1273 #[cfg_attr(not(feature = "track"), allow(unused_variables))] owner_id: usize,
1274 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1275 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1276 #[cfg_attr(not(feature = "track"), allow(unused_variables))] mutable: bool,
1277 value: &'a T,
1278) -> &'a T {
1279 #[cfg(feature = "track")]
1280 {
1281 let mut tracker = TRACKER.lock();
1282 tracker.record_borrow_with_id(borrower_id, owner_id, name, location, mutable);
1283 }
1284 value
1285}
1286
1287#[inline(always)]
1289pub fn track_borrow_mut_with_id<'a, T: ?Sized>(
1290 #[cfg_attr(not(feature = "track"), allow(unused_variables))] borrower_id: usize,
1291 #[cfg_attr(not(feature = "track"), allow(unused_variables))] owner_id: usize,
1292 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1293 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1294 value: &'a mut T,
1295) -> &'a mut T {
1296 #[cfg(feature = "track")]
1297 {
1298 let mut tracker = TRACKER.lock();
1299 tracker.record_borrow_with_id(borrower_id, owner_id, name, location, true);
1300 }
1301 value
1302}
1303
1304#[inline(always)]
1306pub fn track_move_with_id<T>(
1307 #[cfg_attr(not(feature = "track"), allow(unused_variables))] from_id: usize,
1308 #[cfg_attr(not(feature = "track"), allow(unused_variables))] to_id: usize,
1309 #[cfg_attr(not(feature = "track"), allow(unused_variables))] to_name: &str,
1310 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1311 value: T,
1312) -> T {
1313 #[cfg(feature = "track")]
1314 {
1315 let mut tracker = TRACKER.lock();
1316 tracker.record_move_with_id(from_id, to_id, to_name, location);
1317 }
1318 value
1319}
1320
1321#[inline(always)]
1323pub fn track_drop_with_id(
1324 #[cfg_attr(not(feature = "track"), allow(unused_variables))] id: usize,
1325 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1326) {
1327 #[cfg(feature = "track")]
1328 {
1329 let mut tracker = TRACKER.lock();
1330 tracker.record_drop_with_id(id, location);
1331 }
1332}
1333
1334#[inline(always)]
1336pub fn track_rc_new_with_id<T: ?Sized>(
1337 #[cfg_attr(not(feature = "track"), allow(unused_variables))] id: usize,
1338 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1339 #[cfg_attr(not(feature = "track"), allow(unused_variables))] type_name: &str,
1340 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1341 value: std::rc::Rc<T>,
1342) -> std::rc::Rc<T> {
1343 #[cfg(feature = "track")]
1344 {
1345 let strong_count = std::rc::Rc::strong_count(&value);
1346 let weak_count = std::rc::Rc::weak_count(&value);
1347 let mut tracker = TRACKER.lock();
1348 tracker.record_rc_new_with_id(id, name, type_name, location, strong_count, weak_count);
1349 }
1350 value
1351}
1352
1353#[inline(always)]
1355pub fn track_rc_clone_with_id<T: ?Sized>(
1356 #[cfg_attr(not(feature = "track"), allow(unused_variables))] new_id: usize,
1357 #[cfg_attr(not(feature = "track"), allow(unused_variables))] source_id: usize,
1358 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1359 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1360 value: std::rc::Rc<T>,
1361) -> std::rc::Rc<T> {
1362 #[cfg(feature = "track")]
1363 {
1364 let strong_count = std::rc::Rc::strong_count(&value);
1365 let weak_count = std::rc::Rc::weak_count(&value);
1366 let mut tracker = TRACKER.lock();
1367 tracker.record_rc_clone_with_id(
1368 new_id,
1369 source_id,
1370 name,
1371 location,
1372 strong_count,
1373 weak_count,
1374 );
1375 }
1376 value
1377}
1378
1379#[inline(always)]
1381pub fn track_arc_new_with_id<T: ?Sized>(
1382 #[cfg_attr(not(feature = "track"), allow(unused_variables))] id: usize,
1383 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1384 #[cfg_attr(not(feature = "track"), allow(unused_variables))] type_name: &str,
1385 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1386 value: std::sync::Arc<T>,
1387) -> std::sync::Arc<T> {
1388 #[cfg(feature = "track")]
1389 {
1390 let strong_count = std::sync::Arc::strong_count(&value);
1391 let weak_count = std::sync::Arc::weak_count(&value);
1392 let mut tracker = TRACKER.lock();
1393 tracker.record_arc_new_with_id(id, name, type_name, location, strong_count, weak_count);
1394 }
1395 value
1396}
1397
1398#[inline(always)]
1400pub fn track_arc_clone_with_id<T: ?Sized>(
1401 #[cfg_attr(not(feature = "track"), allow(unused_variables))] new_id: usize,
1402 #[cfg_attr(not(feature = "track"), allow(unused_variables))] source_id: usize,
1403 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1404 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1405 value: std::sync::Arc<T>,
1406) -> std::sync::Arc<T> {
1407 #[cfg(feature = "track")]
1408 {
1409 let strong_count = std::sync::Arc::strong_count(&value);
1410 let weak_count = std::sync::Arc::weak_count(&value);
1411 let mut tracker = TRACKER.lock();
1412 tracker.record_arc_clone_with_id(
1413 new_id,
1414 source_id,
1415 name,
1416 location,
1417 strong_count,
1418 weak_count,
1419 );
1420 }
1421 value
1422}
1423
1424#[inline(always)]
1453pub fn track_rc_new<T: ?Sized>(
1454 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1455 value: std::rc::Rc<T>,
1456) -> std::rc::Rc<T> {
1457 #[cfg(feature = "track")]
1458 {
1459 let strong_count = std::rc::Rc::strong_count(&value);
1460 let weak_count = std::rc::Rc::weak_count(&value);
1461 let mut tracker = TRACKER.lock();
1462 tracker.record_rc_new(name, strong_count, weak_count);
1463 }
1464 value
1465}
1466
1467#[inline(always)]
1500pub fn track_rc_clone<T: ?Sized>(
1501 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1502 #[cfg_attr(not(feature = "track"), allow(unused_variables))] source_name: &str,
1503 value: std::rc::Rc<T>,
1504) -> std::rc::Rc<T> {
1505 #[cfg(feature = "track")]
1506 {
1507 let strong_count = std::rc::Rc::strong_count(&value);
1508 let weak_count = std::rc::Rc::weak_count(&value);
1509 let mut tracker = TRACKER.lock();
1510 tracker.record_rc_clone(name, source_name, strong_count, weak_count);
1511 }
1512 value
1513}
1514
1515#[inline(always)]
1543pub fn track_arc_new<T: ?Sized>(
1544 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1545 value: std::sync::Arc<T>,
1546) -> std::sync::Arc<T> {
1547 #[cfg(feature = "track")]
1548 {
1549 let strong_count = std::sync::Arc::strong_count(&value);
1550 let weak_count = std::sync::Arc::weak_count(&value);
1551 let mut tracker = TRACKER.lock();
1552 tracker.record_arc_new(name, strong_count, weak_count);
1553 }
1554 value
1555}
1556
1557#[inline(always)]
1589pub fn track_arc_clone<T: ?Sized>(
1590 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1591 #[cfg_attr(not(feature = "track"), allow(unused_variables))] source_name: &str,
1592 value: std::sync::Arc<T>,
1593) -> std::sync::Arc<T> {
1594 #[cfg(feature = "track")]
1595 {
1596 let strong_count = std::sync::Arc::strong_count(&value);
1597 let weak_count = std::sync::Arc::weak_count(&value);
1598 let mut tracker = TRACKER.lock();
1599 tracker.record_arc_clone(name, source_name, strong_count, weak_count);
1600 }
1601 value
1602}
1603
1604#[inline(always)]
1631pub fn track_refcell_new<T>(
1632 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1633 value: std::cell::RefCell<T>,
1634) -> std::cell::RefCell<T> {
1635 #[cfg(feature = "track")]
1636 {
1637 let mut tracker = TRACKER.lock();
1638 tracker.record_refcell_new(name);
1639 }
1640 value
1641}
1642
1643#[inline(always)]
1673pub fn track_refcell_borrow<'a, T>(
1674 #[cfg_attr(not(feature = "track"), allow(unused_variables))] borrow_id: &str,
1675 #[cfg_attr(not(feature = "track"), allow(unused_variables))] refcell_id: &str,
1676 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1677 value: std::cell::Ref<'a, T>,
1678) -> std::cell::Ref<'a, T> {
1679 #[cfg(feature = "track")]
1680 {
1681 let mut tracker = TRACKER.lock();
1682 tracker.record_refcell_borrow(borrow_id, refcell_id, false, location);
1683 }
1684 value
1685}
1686
1687#[inline(always)]
1718pub fn track_refcell_borrow_mut<'a, T>(
1719 #[cfg_attr(not(feature = "track"), allow(unused_variables))] borrow_id: &str,
1720 #[cfg_attr(not(feature = "track"), allow(unused_variables))] refcell_id: &str,
1721 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1722 value: std::cell::RefMut<'a, T>,
1723) -> std::cell::RefMut<'a, T> {
1724 #[cfg(feature = "track")]
1725 {
1726 let mut tracker = TRACKER.lock();
1727 tracker.record_refcell_borrow(borrow_id, refcell_id, true, location);
1728 }
1729 value
1730}
1731
1732#[inline(always)]
1756pub fn track_refcell_drop(
1757 #[cfg_attr(not(feature = "track"), allow(unused_variables))] borrow_id: &str,
1758 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1759) {
1760 #[cfg(feature = "track")]
1761 {
1762 let mut tracker = TRACKER.lock();
1763 tracker.record_refcell_drop(borrow_id, location);
1764 }
1765}
1766
1767#[inline(always)]
1794pub fn track_cell_new<T>(
1795 #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
1796 value: std::cell::Cell<T>,
1797) -> std::cell::Cell<T> {
1798 #[cfg(feature = "track")]
1799 {
1800 let mut tracker = TRACKER.lock();
1801 tracker.record_cell_new(name);
1802 }
1803 value
1804}
1805
1806#[inline(always)]
1832pub fn track_cell_get<T: Copy>(
1833 #[cfg_attr(not(feature = "track"), allow(unused_variables))] cell_id: &str,
1834 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1835 value: T,
1836) -> T {
1837 #[cfg(feature = "track")]
1838 {
1839 let mut tracker = TRACKER.lock();
1840 tracker.record_cell_get(cell_id, location);
1841 }
1842 value
1843}
1844
1845#[inline(always)]
1871pub fn track_cell_set(
1872 #[cfg_attr(not(feature = "track"), allow(unused_variables))] cell_id: &str,
1873 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1874) {
1875 #[cfg(feature = "track")]
1876 {
1877 let mut tracker = TRACKER.lock();
1878 tracker.record_cell_set(cell_id, location);
1879 }
1880}
1881
1882#[inline(always)]
1898pub fn track_static_init<T>(
1899 #[cfg_attr(not(feature = "track"), allow(unused_variables))] var_name: &str,
1900 #[cfg_attr(not(feature = "track"), allow(unused_variables))] var_id: usize,
1901 #[cfg_attr(not(feature = "track"), allow(unused_variables))] type_name: &str,
1902 #[cfg_attr(not(feature = "track"), allow(unused_variables))] is_mutable: bool,
1903 value: T,
1904) -> T {
1905 #[cfg(feature = "track")]
1906 {
1907 let mut tracker = TRACKER.lock();
1908 tracker.record_static_init(var_name, var_id, type_name, is_mutable);
1909 }
1910 value
1911}
1912
1913#[inline(always)]
1924pub fn track_static_access(
1925 #[cfg_attr(not(feature = "track"), allow(unused_variables))] var_id: usize,
1926 #[cfg_attr(not(feature = "track"), allow(unused_variables))] var_name: &str,
1927 #[cfg_attr(not(feature = "track"), allow(unused_variables))] is_write: bool,
1928 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1929) {
1930 #[cfg(feature = "track")]
1931 {
1932 let mut tracker = TRACKER.lock();
1933 tracker.record_static_access(var_id, var_name, is_write, location);
1934 }
1935}
1936
1937#[inline(always)]
1953pub fn track_const_eval<T>(
1954 #[cfg_attr(not(feature = "track"), allow(unused_variables))] const_name: &str,
1955 #[cfg_attr(not(feature = "track"), allow(unused_variables))] const_id: usize,
1956 #[cfg_attr(not(feature = "track"), allow(unused_variables))] type_name: &str,
1957 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1958 value: T,
1959) -> T {
1960 #[cfg(feature = "track")]
1961 {
1962 let mut tracker = TRACKER.lock();
1963 tracker.record_const_eval(const_name, const_id, type_name, location);
1964 }
1965 value
1966}
1967
1968#[inline(always)]
1988pub fn track_raw_ptr<T: ?Sized>(
1989 #[cfg_attr(not(feature = "track"), allow(unused_variables))] var_name: &str,
1990 #[cfg_attr(not(feature = "track"), allow(unused_variables))] var_id: usize,
1991 #[cfg_attr(not(feature = "track"), allow(unused_variables))] ptr_type: &str,
1992 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
1993 ptr: *const T,
1994) -> *const T {
1995 #[cfg(feature = "track")]
1996 {
1997 let mut tracker = TRACKER.lock();
1998 tracker.record_raw_ptr_created(
1999 var_name,
2000 var_id,
2001 ptr_type,
2002 ptr as *const () as usize,
2003 location,
2004 );
2005 }
2006 ptr
2007}
2008
2009#[inline(always)]
2029pub fn track_raw_ptr_mut<T: ?Sized>(
2030 #[cfg_attr(not(feature = "track"), allow(unused_variables))] var_name: &str,
2031 #[cfg_attr(not(feature = "track"), allow(unused_variables))] var_id: usize,
2032 #[cfg_attr(not(feature = "track"), allow(unused_variables))] ptr_type: &str,
2033 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
2034 ptr: *mut T,
2035) -> *mut T {
2036 #[cfg(feature = "track")]
2037 {
2038 let mut tracker = TRACKER.lock();
2039 tracker.record_raw_ptr_created(
2040 var_name,
2041 var_id,
2042 ptr_type,
2043 ptr as *const () as usize,
2044 location,
2045 );
2046 }
2047 ptr
2048}
2049
2050#[inline(always)]
2060pub fn track_raw_ptr_deref(
2061 #[cfg_attr(not(feature = "track"), allow(unused_variables))] ptr_id: usize,
2062 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
2063 #[cfg_attr(not(feature = "track"), allow(unused_variables))] is_write: bool,
2064) {
2065 #[cfg(feature = "track")]
2066 {
2067 let mut tracker = TRACKER.lock();
2068 tracker.record_raw_ptr_deref(ptr_id, location, is_write);
2069 }
2070}
2071
2072#[inline(always)]
2081pub fn track_unsafe_block_enter(
2082 #[cfg_attr(not(feature = "track"), allow(unused_variables))] block_id: usize,
2083 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
2084) {
2085 #[cfg(feature = "track")]
2086 {
2087 let mut tracker = TRACKER.lock();
2088 tracker.record_unsafe_block_enter(block_id, location);
2089 }
2090}
2091
2092#[inline(always)]
2101pub fn track_unsafe_block_exit(
2102 #[cfg_attr(not(feature = "track"), allow(unused_variables))] block_id: usize,
2103 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
2104) {
2105 #[cfg(feature = "track")]
2106 {
2107 let mut tracker = TRACKER.lock();
2108 tracker.record_unsafe_block_exit(block_id, location);
2109 }
2110}
2111
2112#[inline(always)]
2121pub fn track_unsafe_fn_call(
2122 #[cfg_attr(not(feature = "track"), allow(unused_variables))] fn_name: &str,
2123 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
2124) {
2125 #[cfg(feature = "track")]
2126 {
2127 let mut tracker = TRACKER.lock();
2128 tracker.record_unsafe_fn_call(fn_name, location);
2129 }
2130}
2131
2132#[inline(always)]
2141pub fn track_ffi_call(
2142 #[cfg_attr(not(feature = "track"), allow(unused_variables))] fn_name: &str,
2143 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
2144) {
2145 #[cfg(feature = "track")]
2146 {
2147 let mut tracker = TRACKER.lock();
2148 tracker.record_ffi_call(fn_name, location);
2149 }
2150}
2151
2152#[inline(always)]
2162pub fn track_transmute(
2163 #[cfg_attr(not(feature = "track"), allow(unused_variables))] from_type: &str,
2164 #[cfg_attr(not(feature = "track"), allow(unused_variables))] to_type: &str,
2165 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
2166) {
2167 #[cfg(feature = "track")]
2168 {
2169 let mut tracker = TRACKER.lock();
2170 tracker.record_transmute(from_type, to_type, location);
2171 }
2172}
2173
2174#[inline(always)]
2184pub fn track_union_field_access(
2185 #[cfg_attr(not(feature = "track"), allow(unused_variables))] union_name: &str,
2186 #[cfg_attr(not(feature = "track"), allow(unused_variables))] field_name: &str,
2187 #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
2188) {
2189 #[cfg(feature = "track")]
2190 {
2191 let mut tracker = TRACKER.lock();
2192 tracker.record_union_field_access(union_name, field_name, location);
2193 }
2194}
2195
2196#[cfg(test)]
2197mod tests {
2198 use super::*;
2199 use crate::test_utils::TEST_LOCK;
2200
2201 #[test]
2202 fn test_tracker_new() {
2203 let mut tracker = Tracker::new();
2204 let id = tracker.record_new("x", "i32");
2205
2206 assert_eq!(tracker.events().len(), 1);
2207 assert!(id.starts_with("x_"));
2208 }
2209
2210 #[test]
2211 fn test_tracker_borrow() {
2212 let mut tracker = Tracker::new();
2213 let owner_id = tracker.record_new("s", "String");
2214 let borrower_id = tracker.record_borrow("r", &owner_id, false);
2215
2216 assert_eq!(tracker.events().len(), 2);
2217 assert!(borrower_id.starts_with("r_"));
2218 }
2219
2220 #[test]
2221 fn test_tracker_move() {
2222 let mut tracker = Tracker::new();
2223 let from_id = tracker.record_new("x", "String");
2224 let to_id = tracker.record_move(&from_id, "y");
2225
2226 assert_eq!(tracker.events().len(), 2);
2227 assert!(to_id.starts_with("y_"));
2228 }
2229
2230 #[test]
2231 fn test_tracker_drop() {
2232 let mut tracker = Tracker::new();
2233 let id = tracker.record_new("x", "i32");
2234 tracker.record_drop(&id);
2235
2236 assert_eq!(tracker.events().len(), 2);
2237 assert!(tracker.events()[1].is_drop());
2238 }
2239
2240 #[test]
2241 fn test_timestamp_ordering() {
2242 let mut tracker = Tracker::new();
2243 tracker.record_new("x", "i32");
2244 tracker.record_new("y", "i32");
2245 tracker.record_new("z", "i32");
2246
2247 let events = tracker.events();
2248 assert!(events[0].timestamp() < events[1].timestamp());
2249 assert!(events[1].timestamp() < events[2].timestamp());
2250 }
2251
2252 #[test]
2253 fn test_track_new_returns_value() {
2254 let _lock = TEST_LOCK.lock();
2255 reset();
2256
2257 let handles: Vec<_> = (0..4)
2258 .map(|i| {
2259 std::thread::spawn(move || {
2260 let value = track_new(&format!("x_{}", i), 42 + i);
2261 assert_eq!(value, 42 + i);
2262 })
2263 })
2264 .collect();
2265
2266 for handle in handles {
2267 handle.join().unwrap();
2268 }
2269 }
2270
2271 #[test]
2272 fn test_track_borrow_returns_reference() {
2273 let _lock = TEST_LOCK.lock();
2274 reset();
2275
2276 let handles: Vec<_> = (0..4)
2278 .map(|i| {
2279 std::thread::spawn(move || {
2280 let s = String::from("hello");
2281 let r = track_borrow(&format!("r_{}", i), &s);
2282 assert_eq!(r, "hello");
2283 })
2284 })
2285 .collect();
2286
2287 for handle in handles {
2288 handle.join().unwrap();
2289 }
2290
2291 let events = get_events();
2292 assert_eq!(events.iter().filter(|e| e.is_borrow()).count(), 4);
2293 }
2294
2295 #[test]
2296 fn test_track_borrow_mut_returns_reference() {
2297 let _lock = TEST_LOCK.lock();
2298 reset();
2299
2300 let mut s = String::from("hello");
2302 track_borrow_mut("r", &mut s);
2303 s.push_str(" world");
2304 assert_eq!(s, "hello world");
2305
2306 let events = get_events();
2307 assert_eq!(events.iter().filter(|e| e.is_borrow()).count(), 1);
2308 }
2309
2310 #[test]
2311 fn test_complete_workflow() {
2312 let _lock = TEST_LOCK.lock();
2313 reset();
2314
2315 let handles: Vec<_> = (0..4)
2316 .map(|i| {
2317 std::thread::spawn(move || {
2318 let x = track_new(&format!("x_{}", i), 5);
2319 let _r = track_borrow(&format!("r_{}", i), &x);
2320 track_drop(&format!("r_{}", i));
2321 track_drop(&format!("x_{}", i));
2322 })
2323 })
2324 .collect();
2325
2326 for handle in handles {
2327 handle.join().unwrap();
2328 }
2329
2330 let events = get_events();
2331 assert_eq!(events.len(), 16); assert_eq!(events.iter().filter(|e| e.is_new()).count(), 4);
2333 assert_eq!(events.iter().filter(|e| e.is_borrow()).count(), 4);
2334 assert_eq!(events.iter().filter(|e| e.is_drop()).count(), 8);
2335 }
2336
2337 #[test]
2338 fn test_reset() {
2339 let _lock = TEST_LOCK.lock();
2340 reset();
2341
2342 let handles: Vec<_> = (0..4)
2343 .map(|i| {
2344 std::thread::spawn(move || {
2345 track_new(&format!("x_{}", i), 5);
2346 track_new(&format!("y_{}", i), 10);
2347 })
2348 })
2349 .collect();
2350
2351 for handle in handles {
2352 handle.join().unwrap();
2353 }
2354
2355 assert_eq!(get_events().len(), 8); reset();
2358
2359 assert_eq!(get_events().len(), 0);
2360 }
2361
2362 #[test]
2363 fn test_unique_ids() {
2364 let _lock = TEST_LOCK.lock();
2365 reset();
2366
2367 let handles: Vec<_> = (0..4)
2368 .map(|_| {
2369 std::thread::spawn(|| {
2370 track_new("x", 1);
2371 track_new("x", 2);
2372 track_new("x", 3);
2373 })
2374 })
2375 .collect();
2376
2377 for handle in handles {
2378 handle.join().unwrap();
2379 }
2380
2381 let events = get_events();
2382 let ids: Vec<_> = events
2383 .iter()
2384 .filter_map(|e| match e {
2385 Event::New { var_id, .. } => Some(var_id.as_str()),
2386 _ => None,
2387 })
2388 .collect();
2389
2390 assert_eq!(ids.len(), 12); let mut unique_ids = ids.clone();
2394 unique_ids.sort_unstable();
2395 unique_ids.dedup();
2396 assert_eq!(unique_ids.len(), 12, "All IDs should be unique");
2397 }
2398
2399 #[test]
2400 fn test_concurrent_tracking() {
2401 let _lock = TEST_LOCK.lock();
2402 reset();
2403
2404 let handles: Vec<_> = (0..4)
2405 .map(|i| {
2406 std::thread::spawn(move || {
2407 for j in 0..10 {
2408 track_new(&format!("var_{}_{}", i, j), i * 10 + j);
2409 }
2410 })
2411 })
2412 .collect();
2413
2414 for handle in handles {
2415 handle.join().unwrap();
2416 }
2417
2418 let events = get_events();
2419 assert_eq!(events.len(), 40); }
2421
2422 #[test]
2423 fn test_timestamp_monotonicity_concurrent() {
2424 let _lock = TEST_LOCK.lock();
2425 reset();
2426
2427 let handles: Vec<_> = (0..4)
2428 .map(|i| {
2429 std::thread::spawn(move || {
2430 for j in 0..10 {
2431 track_new(&format!("var_{}_{}", i, j), i * 10 + j);
2432 }
2433 })
2434 })
2435 .collect();
2436
2437 for handle in handles {
2438 handle.join().unwrap();
2439 }
2440
2441 let events = get_events();
2442 let mut timestamps: Vec<_> = events.iter().map(|e| e.timestamp()).collect();
2443 timestamps.sort_unstable();
2444
2445 for i in 1..timestamps.len() {
2447 assert!(
2448 timestamps[i] > timestamps[i - 1],
2449 "Timestamps should be unique and monotonic"
2450 );
2451 }
2452 }
2453
2454 #[test]
2455 fn test_concurrent_reset() {
2456 let _lock = TEST_LOCK.lock();
2457 reset();
2458
2459 for i in 0..10 {
2461 track_new(&format!("var_{}", i), i);
2462 }
2463
2464 assert_eq!(get_events().len(), 10);
2465 reset();
2466 assert_eq!(get_events().len(), 0);
2467 }
2468
2469 #[test]
2470 fn test_high_contention() {
2471 let _lock = TEST_LOCK.lock();
2472 reset();
2473
2474 let handles: Vec<_> = (0..8)
2475 .map(|i| {
2476 std::thread::spawn(move || {
2477 for j in 0..100 {
2478 track_new(&format!("var_{}_{}", i, j), i * 100 + j);
2479 }
2480 })
2481 })
2482 .collect();
2483
2484 for handle in handles {
2485 handle.join().unwrap();
2486 }
2487
2488 let events = get_events();
2489 assert!(
2491 events.len() >= 600,
2492 "Expected at least 600/800 events, got {}",
2493 events.len()
2494 );
2495 assert!(
2496 events.iter().all(|e| e.is_new()),
2497 "All events should be New events"
2498 );
2499 }
2500}