memscope_rs/analysis/
borrow_analysis.rs

1//! Borrow checker integration and analysis
2//!
3//! This module implements borrow tracking features from ComplexTypeForRust.md:
4//! - Track borrow and mutable borrow lifetimes
5//! - Runtime borrow checking integration
6//! - Borrow conflict detection
7
8use crate::core::safe_operations::SafeLock;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::sync::{Arc, Mutex, OnceLock};
12use std::time::{SystemTime, UNIX_EPOCH};
13
14/// Global borrow analyzer instance
15static GLOBAL_BORROW_ANALYZER: OnceLock<Arc<BorrowAnalyzer>> = OnceLock::new();
16
17/// Get the global borrow analyzer instance
18pub fn get_global_borrow_analyzer() -> Arc<BorrowAnalyzer> {
19    GLOBAL_BORROW_ANALYZER
20        .get_or_init(|| Arc::new(BorrowAnalyzer::new()))
21        .clone()
22}
23
24/// Borrow analysis system
25pub struct BorrowAnalyzer {
26    /// Active borrows tracking
27    active_borrows: Mutex<HashMap<usize, Vec<BorrowInfo>>>,
28    /// Borrow history for analysis
29    borrow_history: Mutex<Vec<BorrowEvent>>,
30    /// Detected borrow conflicts
31    conflicts: Mutex<Vec<BorrowConflict>>,
32}
33
34impl Default for BorrowAnalyzer {
35    fn default() -> Self {
36        Self::new()
37    }
38}
39
40impl BorrowAnalyzer {
41    /// Create a new borrow analyzer
42    pub fn new() -> Self {
43        Self {
44            active_borrows: Mutex::new(HashMap::new()),
45            borrow_history: Mutex::new(Vec::new()),
46            conflicts: Mutex::new(Vec::new()),
47        }
48    }
49
50    /// Track a new borrow
51    pub fn track_borrow(&self, ptr: usize, borrow_type: BorrowType, var_name: &str) -> BorrowId {
52        let borrow_id = BorrowId::new();
53        let borrow_info = BorrowInfo {
54            id: borrow_id,
55            ptr,
56            borrow_type: borrow_type.clone(),
57            var_name: var_name.to_string(),
58            start_time: current_timestamp(),
59            end_time: None,
60            thread_id: format!("{:?}", std::thread::current().id()),
61            call_stack: capture_call_stack(),
62        };
63
64        // Check for conflicts before adding
65        self.check_borrow_conflicts(ptr, &borrow_type, &borrow_info);
66
67        // Add to active borrows
68        if let Ok(mut active) = self.active_borrows.lock() {
69            active.entry(ptr).or_default().push(borrow_info.clone());
70        }
71
72        // Record the borrow event
73        let event = BorrowEvent {
74            borrow_info: borrow_info.clone(),
75            event_type: BorrowEventType::BorrowStart,
76            timestamp: current_timestamp(),
77        };
78
79        if let Ok(mut history) = self.borrow_history.lock() {
80            history.push(event);
81        }
82
83        borrow_id
84    }
85
86    /// End a borrow
87    pub fn end_borrow(&self, borrow_id: BorrowId) {
88        let end_time = current_timestamp();
89
90        // Find and remove from active borrows
91        if let Ok(mut active) = self.active_borrows.lock() {
92            for (_, borrows) in active.iter_mut() {
93                if let Some(pos) = borrows.iter().position(|b| b.id == borrow_id) {
94                    let mut borrow_info = borrows.remove(pos);
95                    borrow_info.end_time = Some(end_time);
96
97                    // Record the end event
98                    let event = BorrowEvent {
99                        borrow_info: borrow_info.clone(),
100                        event_type: BorrowEventType::BorrowEnd,
101                        timestamp: end_time,
102                    };
103
104                    if let Ok(mut history) = self.borrow_history.lock() {
105                        history.push(event);
106                    }
107                    break;
108                }
109            }
110        }
111    }
112
113    /// Check for borrow conflicts
114    fn check_borrow_conflicts(
115        &self,
116        ptr: usize,
117        new_borrow_type: &BorrowType,
118        new_borrow: &BorrowInfo,
119    ) {
120        if let Ok(active) = self.active_borrows.lock() {
121            if let Some(existing_borrows) = active.get(&ptr) {
122                for existing in existing_borrows {
123                    if self.is_conflicting_borrow(&existing.borrow_type, new_borrow_type) {
124                        let conflict = BorrowConflict {
125                            ptr,
126                            existing_borrow: existing.clone(),
127                            conflicting_borrow: new_borrow.clone(),
128                            conflict_type: self
129                                .determine_conflict_type(&existing.borrow_type, new_borrow_type),
130                            timestamp: current_timestamp(),
131                        };
132
133                        if let Ok(mut conflicts) = self.conflicts.lock() {
134                            conflicts.push(conflict);
135                        }
136                    }
137                }
138            }
139        }
140    }
141
142    /// Check if two borrow types conflict
143    fn is_conflicting_borrow(&self, existing: &BorrowType, new: &BorrowType) -> bool {
144        match (existing, new) {
145            // Mutable borrow conflicts with any other borrow
146            (BorrowType::Mutable, _) | (_, BorrowType::Mutable) => true,
147            // Multiple immutable borrows are allowed
148            (BorrowType::Immutable, BorrowType::Immutable) => false,
149            // Shared borrows don't conflict with immutable
150            (BorrowType::Shared, BorrowType::Immutable)
151            | (BorrowType::Immutable, BorrowType::Shared) => false,
152            // Other combinations are safe
153            _ => false,
154        }
155    }
156
157    /// Determine the type of conflict
158    fn determine_conflict_type(&self, existing: &BorrowType, new: &BorrowType) -> ConflictType {
159        match (existing, new) {
160            (BorrowType::Mutable, BorrowType::Mutable) => ConflictType::MultipleMutableBorrows,
161            (BorrowType::Mutable, BorrowType::Immutable)
162            | (BorrowType::Immutable, BorrowType::Mutable) => {
163                ConflictType::MutableImmutableConflict
164            }
165            _ => ConflictType::Other,
166        }
167    }
168
169    /// Get current active borrows for a pointer
170    pub fn get_active_borrows(&self, ptr: usize) -> Vec<BorrowInfo> {
171        if let Ok(active) = self.active_borrows.lock() {
172            active.get(&ptr).cloned().unwrap_or_default()
173        } else {
174            Vec::new()
175        }
176    }
177
178    /// Get borrow statistics
179    pub fn get_borrow_statistics(&self) -> BorrowStatistics {
180        // Avoid holding multiple locks simultaneously to prevent deadlock
181        let (total_borrows, durations, by_type) = {
182            let history = match self.borrow_history.safe_lock() {
183                Ok(h) => h,
184                Err(_) => return BorrowStatistics::default(),
185            };
186            let total = history.len();
187            let mut durations = Vec::new();
188            let mut by_type = HashMap::new();
189
190            for event in history.iter() {
191                if let Some(end_time) = event.borrow_info.end_time {
192                    durations.push(end_time - event.borrow_info.start_time);
193                }
194                let type_name = format!("{:?}", event.borrow_info.borrow_type);
195                *by_type.entry(type_name).or_insert(0) += 1;
196            }
197            (total, durations, by_type)
198        };
199
200        let total_conflicts = {
201            let conflicts = match self.conflicts.safe_lock() {
202                Ok(c) => c,
203                Err(_) => return BorrowStatistics::default(),
204            };
205            conflicts.len()
206        };
207
208        let active_borrows: usize = {
209            let active = match self.active_borrows.safe_lock() {
210                Ok(a) => a,
211                Err(_) => return BorrowStatistics::default(),
212            };
213            active.values().map(|v| v.len()).sum()
214        };
215
216        let avg_borrow_duration = if !durations.is_empty() {
217            durations.iter().sum::<u64>() / durations.len() as u64
218        } else {
219            0
220        };
221
222        let max_borrow_duration = durations.iter().max().copied().unwrap_or(0);
223
224        BorrowStatistics {
225            total_borrows,
226            active_borrows,
227            total_conflicts,
228            avg_borrow_duration,
229            max_borrow_duration,
230            by_type,
231        }
232    }
233
234    /// Get all detected conflicts
235    pub fn get_conflicts(&self) -> Vec<BorrowConflict> {
236        self.conflicts
237            .safe_lock()
238            .map(|c| c.clone())
239            .unwrap_or_default()
240    }
241
242    /// Analyze borrow patterns
243    pub fn analyze_borrow_patterns(&self) -> BorrowPatternAnalysis {
244        let history = match self.borrow_history.safe_lock() {
245            Ok(h) => h,
246            Err(_) => return BorrowPatternAnalysis::default(),
247        };
248        let conflicts = match self.conflicts.safe_lock() {
249            Ok(c) => c,
250            Err(_) => return BorrowPatternAnalysis::default(),
251        };
252
253        // Analyze common patterns
254        let mut patterns = Vec::new();
255
256        // Pattern: Long-lived borrows
257        let long_lived_threshold = 1_000_000; // 1ms in nanoseconds
258        let long_lived_count = history
259            .iter()
260            .filter(|event| {
261                if let Some(end_time) = event.borrow_info.end_time {
262                    end_time - event.borrow_info.start_time > long_lived_threshold
263                } else {
264                    false
265                }
266            })
267            .count();
268
269        if long_lived_count > 0 {
270            patterns.push(BorrowPattern {
271                pattern_type: BorrowPatternType::LongLivedBorrows,
272                description: format!("{long_lived_count} borrows lasted longer than 1ms"),
273                severity: if long_lived_count > 10 {
274                    PatternSeverity::Warning
275                } else {
276                    PatternSeverity::Info
277                },
278                suggestion: "Consider reducing borrow scope or using RAII patterns".to_string(),
279            });
280        }
281
282        // Pattern: Frequent conflicts
283        if conflicts.len() > 5 {
284            patterns.push(BorrowPattern {
285                pattern_type: BorrowPatternType::FrequentConflicts,
286                description: format!("{} borrow conflicts detected", conflicts.len()),
287                severity: PatternSeverity::Warning,
288                suggestion: "Review borrow patterns and consider refactoring to reduce conflicts"
289                    .to_string(),
290            });
291        }
292
293        // Pattern: Many concurrent borrows
294        let max_concurrent = self.calculate_max_concurrent_borrows();
295        if max_concurrent > 10 {
296            patterns.push(BorrowPattern {
297                pattern_type: BorrowPatternType::HighConcurrency,
298                description: format!("Up to {max_concurrent} concurrent borrows detected"),
299                severity: PatternSeverity::Info,
300                suggestion: "High concurrency detected - ensure this is intentional".to_string(),
301            });
302        }
303
304        BorrowPatternAnalysis {
305            patterns,
306            total_events: history.len(),
307            analysis_timestamp: current_timestamp(),
308        }
309    }
310
311    /// Calculate maximum concurrent borrows
312    fn calculate_max_concurrent_borrows(&self) -> usize {
313        self.active_borrows
314            .safe_lock()
315            .map(|active| active.values().map(|v| v.len()).max().unwrap_or(0))
316            .unwrap_or(0)
317    }
318}
319
320/// Unique identifier for a borrow
321#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
322pub struct BorrowId(u64);
323
324impl BorrowId {
325    fn new() -> Self {
326        use std::sync::atomic::{AtomicU64, Ordering};
327        static COUNTER: AtomicU64 = AtomicU64::new(1);
328        Self(COUNTER.fetch_add(1, Ordering::Relaxed))
329    }
330}
331
332/// Types of borrows
333#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
334pub enum BorrowType {
335    /// Immutable borrow (&T)
336    Immutable,
337    /// Mutable borrow (&mut T)
338    Mutable,
339    /// Shared reference (Arc, Rc)
340    Shared,
341    /// Weak reference
342    Weak,
343}
344
345/// Information about a borrow
346#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct BorrowInfo {
348    /// Unique borrow identifier
349    pub id: BorrowId,
350    /// Pointer being borrowed
351    pub ptr: usize,
352    /// Type of borrow
353    pub borrow_type: BorrowType,
354    /// Variable name
355    pub var_name: String,
356    /// When the borrow started
357    pub start_time: u64,
358    /// When the borrow ended (if it has ended)
359    pub end_time: Option<u64>,
360    /// Thread where the borrow occurred
361    pub thread_id: String,
362    /// Call stack at borrow time
363    pub call_stack: Vec<String>,
364}
365
366/// Borrow event for tracking
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct BorrowEvent {
369    /// Borrow information
370    pub borrow_info: BorrowInfo,
371    /// Type of event
372    pub event_type: BorrowEventType,
373    /// Event timestamp
374    pub timestamp: u64,
375}
376
377/// Types of borrow events
378#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
379pub enum BorrowEventType {
380    /// Borrow started
381    BorrowStart,
382    /// Borrow ended
383    BorrowEnd,
384}
385
386/// Borrow conflict information
387#[derive(Debug, Clone, Serialize, Deserialize)]
388pub struct BorrowConflict {
389    /// Pointer where conflict occurred
390    pub ptr: usize,
391    /// Existing borrow
392    pub existing_borrow: BorrowInfo,
393    /// Conflicting borrow attempt
394    pub conflicting_borrow: BorrowInfo,
395    /// Type of conflict
396    pub conflict_type: ConflictType,
397    /// When the conflict was detected
398    pub timestamp: u64,
399}
400
401/// Types of borrow conflicts
402#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
403pub enum ConflictType {
404    /// Multiple mutable borrows
405    MultipleMutableBorrows,
406    /// Mutable and immutable borrow conflict
407    MutableImmutableConflict,
408    /// Other conflict type
409    Other,
410}
411
412/// Borrow statistics
413#[derive(Debug, Clone, Default, Serialize, Deserialize)]
414pub struct BorrowStatistics {
415    /// Total number of borrows tracked
416    pub total_borrows: usize,
417    /// Currently active borrows
418    pub active_borrows: usize,
419    /// Total conflicts detected
420    pub total_conflicts: usize,
421    /// Average borrow duration in nanoseconds
422    pub avg_borrow_duration: u64,
423    /// Maximum borrow duration in nanoseconds
424    pub max_borrow_duration: u64,
425    /// Count by borrow type
426    pub by_type: HashMap<String, usize>,
427}
428
429// BorrowAnalysis struct doesn't exist in this file, removing the Default impl
430
431/// Borrow pattern analysis
432#[derive(Debug, Clone, Default, Serialize, Deserialize)]
433pub struct BorrowPatternAnalysis {
434    /// Detected patterns
435    pub patterns: Vec<BorrowPattern>,
436    /// Total events analyzed
437    pub total_events: usize,
438    /// Analysis timestamp
439    pub analysis_timestamp: u64,
440}
441
442/// Detected borrow pattern
443#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct BorrowPattern {
445    /// Type of pattern
446    pub pattern_type: BorrowPatternType,
447    /// Description of the pattern
448    pub description: String,
449    /// Severity level
450    pub severity: PatternSeverity,
451    /// Suggested action
452    pub suggestion: String,
453}
454
455/// Types of borrow patterns
456#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
457pub enum BorrowPatternType {
458    /// Long-lived borrows
459    LongLivedBorrows,
460    /// Frequent conflicts
461    FrequentConflicts,
462    /// High concurrency
463    HighConcurrency,
464    /// Nested borrows
465    NestedBorrows,
466}
467
468/// Pattern severity levels
469#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
470pub enum PatternSeverity {
471    /// Informational
472    Info,
473    /// Warning
474    Warning,
475    /// Error
476    Error,
477}
478
479/// Get current timestamp
480fn current_timestamp() -> u64 {
481    SystemTime::now()
482        .duration_since(UNIX_EPOCH)
483        .unwrap_or_default()
484        .as_nanos() as u64
485}
486
487/// Capture call stack (simplified)
488fn capture_call_stack() -> Vec<String> {
489    // In a real implementation, this would capture the actual call stack
490    // For now, return a placeholder
491    vec!["<call_stack_placeholder>".to_string()]
492}
493
494#[cfg(test)]
495mod tests {
496    use super::*;
497
498    #[test]
499    fn test_borrow_tracking() {
500        let analyzer = BorrowAnalyzer::new();
501
502        // Track an immutable borrow
503        let borrow_id = analyzer.track_borrow(0x1000, BorrowType::Immutable, "test_var");
504
505        // Check active borrows
506        let active = analyzer.get_active_borrows(0x1000);
507        assert_eq!(active.len(), 1);
508        assert_eq!(active[0].borrow_type, BorrowType::Immutable);
509
510        // End the borrow
511        analyzer.end_borrow(borrow_id);
512
513        // Check that it's no longer active
514        let active = analyzer.get_active_borrows(0x1000);
515        assert_eq!(active.len(), 0);
516    }
517
518    #[test]
519    fn test_borrow_conflicts() {
520        let analyzer = BorrowAnalyzer::new();
521
522        // Track a mutable borrow
523        analyzer.track_borrow(0x1000, BorrowType::Mutable, "test_var1");
524
525        // Try to track another mutable borrow (should create conflict)
526        analyzer.track_borrow(0x1000, BorrowType::Mutable, "test_var2");
527
528        // Check conflicts
529        let conflicts = analyzer.get_conflicts();
530        assert!(!conflicts.is_empty());
531        assert_eq!(
532            conflicts[0].conflict_type,
533            ConflictType::MultipleMutableBorrows
534        );
535    }
536}