borrowscope_runtime/tracker/
sampling.rs

1//! Sampling functions for probabilistic tracking
2
3use std::sync::atomic::Ordering;
4use super::TRACKER;
5
6pub fn should_sample(rate: f64) -> bool {
7    use std::sync::atomic::AtomicU64;
8    
9    // Use a simple but fast PRNG (xorshift64)
10    static SEED: AtomicU64 = AtomicU64::new(0x853c49e6748fea9b);
11    
12    // xorshift64 step
13    let mut x = SEED.load(Ordering::Relaxed);
14    x ^= x << 13;
15    x ^= x >> 7;
16    x ^= x << 17;
17    SEED.store(x, Ordering::Relaxed);
18    
19    // Convert to 0.0-1.0 range and compare
20    (x as f64 / u64::MAX as f64) < rate
21}
22
23/// Track variable creation with sampling.
24/// Only records the event if the sample check passes.
25/// Returns the value unchanged regardless of sampling.
26///
27/// # Arguments
28/// * `name` - Variable name
29/// * `value` - The value being tracked
30/// * `sample_rate` - Probability of tracking (0.0 to 1.0)
31///
32/// # Examples
33/// ```rust
34/// # use borrowscope_runtime::*;
35/// # reset();
36/// // Track only ~10% of calls
37/// let x = track_new_sampled("x", 42, 0.1);
38/// ```
39#[inline(always)]
40pub fn track_new_sampled<T>(
41    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
42    value: T,
43    #[cfg_attr(not(feature = "track"), allow(unused_variables))] sample_rate: f64,
44) -> T {
45    #[cfg(feature = "track")]
46    {
47        if should_sample(sample_rate) {
48            let type_name = std::any::type_name::<T>();
49            let mut tracker = TRACKER.lock();
50            tracker.record_new(name, type_name);
51        }
52    }
53    value
54}
55
56/// Track variable creation with ID and sampling.
57/// Only records the event if the sample check passes.
58///
59/// # Arguments
60/// * `id` - Unique variable ID
61/// * `name` - Variable name  
62/// * `location` - Source location string
63/// * `value` - The value being tracked
64/// * `sample_rate` - Probability of tracking (0.0 to 1.0)
65#[inline(always)]
66pub fn track_new_with_id_sampled<T>(
67    #[cfg_attr(not(feature = "track"), allow(unused_variables))] id: usize,
68    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
69    #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
70    value: T,
71    #[cfg_attr(not(feature = "track"), allow(unused_variables))] sample_rate: f64,
72) -> T {
73    #[cfg(feature = "track")]
74    {
75        if should_sample(sample_rate) {
76            let type_name = std::any::type_name::<T>();
77            let mut tracker = TRACKER.lock();
78            tracker.record_new_with_id(id, name, type_name, location);
79        }
80    }
81    value
82}
83
84/// Track immutable borrow with sampling.
85#[inline(always)]
86pub fn track_borrow_sampled<'a, T: ?Sized>(
87    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
88    value: &'a T,
89    #[cfg_attr(not(feature = "track"), allow(unused_variables))] sample_rate: f64,
90) -> &'a T {
91    #[cfg(feature = "track")]
92    {
93        if should_sample(sample_rate) {
94            let mut tracker = TRACKER.lock();
95            tracker.record_borrow(name, "unknown", false);
96        }
97    }
98    value
99}
100
101/// Track mutable borrow with sampling.
102#[inline(always)]
103pub fn track_borrow_mut_sampled<'a, T: ?Sized>(
104    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
105    value: &'a mut T,
106    #[cfg_attr(not(feature = "track"), allow(unused_variables))] sample_rate: f64,
107) -> &'a mut T {
108    #[cfg(feature = "track")]
109    {
110        if should_sample(sample_rate) {
111            let mut tracker = TRACKER.lock();
112            tracker.record_borrow(name, "unknown", true);
113        }
114    }
115    value
116}
117
118/// Track drop with sampling.
119#[inline(always)]
120pub fn track_drop_sampled(
121    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
122    #[cfg_attr(not(feature = "track"), allow(unused_variables))] sample_rate: f64,
123) {
124    #[cfg(feature = "track")]
125    {
126        if should_sample(sample_rate) {
127            let mut tracker = TRACKER.lock();
128            tracker.record_drop(name);
129        }
130    }
131}
132
133/// Track move with sampling.
134#[inline(always)]
135pub fn track_move_sampled<T>(
136    #[cfg_attr(not(feature = "track"), allow(unused_variables))] from: &str,
137    #[cfg_attr(not(feature = "track"), allow(unused_variables))] to: &str,
138    value: T,
139    #[cfg_attr(not(feature = "track"), allow(unused_variables))] sample_rate: f64,
140) -> T {
141    #[cfg(feature = "track")]
142    {
143        if should_sample(sample_rate) {
144            let mut tracker = TRACKER.lock();
145            tracker.record_move(from, to);
146        }
147    }
148    value
149}
150