Skip to main content

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        // Collect data with individual locks to prevent deadlock
245        // Always acquire borrow_history first, then conflicts
246        let history_data: Vec<_> = {
247            let history = match self.borrow_history.safe_lock() {
248                Ok(h) => h,
249                Err(_) => return BorrowPatternAnalysis::default(),
250            };
251            history.iter().cloned().collect()
252        };
253
254        let conflicts_data: Vec<_> = {
255            let conflicts = match self.conflicts.safe_lock() {
256                Ok(c) => c,
257                Err(_) => return BorrowPatternAnalysis::default(),
258            };
259            conflicts.iter().cloned().collect()
260        };
261
262        // Analyze common patterns
263        let mut patterns = Vec::new();
264
265        // Pattern: Long-lived borrows
266        let long_lived_threshold = 1_000_000; // 1ms in nanoseconds
267        let long_lived_count = history_data
268            .iter()
269            .filter(|event| {
270                if let Some(end_time) = event.borrow_info.end_time {
271                    end_time - event.borrow_info.start_time > long_lived_threshold
272                } else {
273                    false
274                }
275            })
276            .count();
277
278        if long_lived_count > 0 {
279            patterns.push(BorrowPattern {
280                pattern_type: BorrowPatternType::LongLivedBorrows,
281                description: format!("{long_lived_count} borrows lasted longer than 1ms"),
282                severity: if long_lived_count > 10 {
283                    PatternSeverity::Warning
284                } else {
285                    PatternSeverity::Info
286                },
287                suggestion: "Consider reducing borrow scope or using RAII patterns".to_string(),
288            });
289        }
290
291        // Pattern: Frequent conflicts
292        if conflicts_data.len() > 5 {
293            patterns.push(BorrowPattern {
294                pattern_type: BorrowPatternType::FrequentConflicts,
295                description: format!("{} borrow conflicts detected", conflicts_data.len()),
296                severity: PatternSeverity::Warning,
297                suggestion: "Review borrow patterns and consider refactoring to reduce conflicts"
298                    .to_string(),
299            });
300        }
301
302        // Pattern: Many concurrent borrows
303        let max_concurrent = self.calculate_max_concurrent_borrows();
304        if max_concurrent > 10 {
305            patterns.push(BorrowPattern {
306                pattern_type: BorrowPatternType::HighConcurrency,
307                description: format!("Up to {max_concurrent} concurrent borrows detected"),
308                severity: PatternSeverity::Info,
309                suggestion: "High concurrency detected - ensure this is intentional".to_string(),
310            });
311        }
312
313        BorrowPatternAnalysis {
314            patterns,
315            total_events: history_data.len(),
316            analysis_timestamp: current_timestamp(),
317        }
318    }
319
320    /// Calculate maximum concurrent borrows
321    fn calculate_max_concurrent_borrows(&self) -> usize {
322        self.active_borrows
323            .safe_lock()
324            .map(|active| active.values().map(|v| v.len()).max().unwrap_or(0))
325            .unwrap_or(0)
326    }
327}
328
329/// Unique identifier for a borrow
330#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
331pub struct BorrowId(u64);
332
333impl BorrowId {
334    fn new() -> Self {
335        use std::sync::atomic::{AtomicU64, Ordering};
336        static COUNTER: AtomicU64 = AtomicU64::new(1);
337        Self(COUNTER.fetch_add(1, Ordering::Relaxed))
338    }
339}
340
341/// Types of borrows
342#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
343pub enum BorrowType {
344    /// Immutable borrow (&T)
345    Immutable,
346    /// Mutable borrow (&mut T)
347    Mutable,
348    /// Shared reference (Arc, Rc)
349    Shared,
350    /// Weak reference
351    Weak,
352}
353
354/// Information about a borrow
355#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct BorrowInfo {
357    /// Unique borrow identifier
358    pub id: BorrowId,
359    /// Pointer being borrowed
360    pub ptr: usize,
361    /// Type of borrow
362    pub borrow_type: BorrowType,
363    /// Variable name
364    pub var_name: String,
365    /// When the borrow started
366    pub start_time: u64,
367    /// When the borrow ended (if it has ended)
368    pub end_time: Option<u64>,
369    /// Thread where the borrow occurred
370    pub thread_id: String,
371    /// Call stack at borrow time
372    pub call_stack: Vec<String>,
373}
374
375/// Borrow event for tracking
376#[derive(Debug, Clone, Serialize, Deserialize)]
377pub struct BorrowEvent {
378    /// Borrow information
379    pub borrow_info: BorrowInfo,
380    /// Type of event
381    pub event_type: BorrowEventType,
382    /// Event timestamp
383    pub timestamp: u64,
384}
385
386/// Types of borrow events
387#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
388pub enum BorrowEventType {
389    /// Borrow started
390    BorrowStart,
391    /// Borrow ended
392    BorrowEnd,
393}
394
395/// Borrow conflict information
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct BorrowConflict {
398    /// Pointer where conflict occurred
399    pub ptr: usize,
400    /// Existing borrow
401    pub existing_borrow: BorrowInfo,
402    /// Conflicting borrow attempt
403    pub conflicting_borrow: BorrowInfo,
404    /// Type of conflict
405    pub conflict_type: ConflictType,
406    /// When the conflict was detected
407    pub timestamp: u64,
408}
409
410/// Types of borrow conflicts
411#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
412pub enum ConflictType {
413    /// Multiple mutable borrows
414    MultipleMutableBorrows,
415    /// Mutable and immutable borrow conflict
416    MutableImmutableConflict,
417    /// Other conflict type
418    Other,
419}
420
421/// Borrow statistics
422#[derive(Debug, Clone, Default, Serialize, Deserialize)]
423pub struct BorrowStatistics {
424    /// Total number of borrows tracked
425    pub total_borrows: usize,
426    /// Currently active borrows
427    pub active_borrows: usize,
428    /// Total conflicts detected
429    pub total_conflicts: usize,
430    /// Average borrow duration in nanoseconds
431    pub avg_borrow_duration: u64,
432    /// Maximum borrow duration in nanoseconds
433    pub max_borrow_duration: u64,
434    /// Count by borrow type
435    pub by_type: HashMap<String, usize>,
436}
437
438// BorrowAnalysis struct doesn't exist in this file, removing the Default impl
439
440/// Borrow pattern analysis
441#[derive(Debug, Clone, Default, Serialize, Deserialize)]
442pub struct BorrowPatternAnalysis {
443    /// Detected patterns
444    pub patterns: Vec<BorrowPattern>,
445    /// Total events analyzed
446    pub total_events: usize,
447    /// Analysis timestamp
448    pub analysis_timestamp: u64,
449}
450
451/// Detected borrow pattern
452#[derive(Debug, Clone, Serialize, Deserialize)]
453pub struct BorrowPattern {
454    /// Type of pattern
455    pub pattern_type: BorrowPatternType,
456    /// Description of the pattern
457    pub description: String,
458    /// Severity level
459    pub severity: PatternSeverity,
460    /// Suggested action
461    pub suggestion: String,
462}
463
464/// Types of borrow patterns
465#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
466pub enum BorrowPatternType {
467    /// Long-lived borrows
468    LongLivedBorrows,
469    /// Frequent conflicts
470    FrequentConflicts,
471    /// High concurrency
472    HighConcurrency,
473    /// Nested borrows
474    NestedBorrows,
475}
476
477/// Pattern severity levels
478#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
479pub enum PatternSeverity {
480    /// Informational
481    Info,
482    /// Warning
483    Warning,
484    /// Error
485    Error,
486}
487
488/// Get current timestamp
489fn current_timestamp() -> u64 {
490    SystemTime::now()
491        .duration_since(UNIX_EPOCH)
492        .unwrap_or_default()
493        .as_nanos() as u64
494}
495
496/// Capture call stack (simplified)
497fn capture_call_stack() -> Vec<String> {
498    // Use backtrace crate to capture real call stack
499    #[cfg(feature = "backtrace")]
500    {
501        let bt = backtrace::Backtrace::new();
502        bt.frames()
503            .iter()
504            .skip(2) // Skip capture_call_stack and caller
505            .filter_map(|frame| {
506                frame
507                    .symbols()
508                    .first()
509                    .and_then(|sym| sym.name())
510                    .map(|name| name.to_string())
511            })
512            .collect()
513    }
514
515    #[cfg(not(feature = "backtrace"))]
516    {
517        // Return empty vector when backtrace feature is not enabled
518        Vec::new()
519    }
520}
521
522#[cfg(test)]
523mod tests {
524    use super::*;
525
526    #[test]
527    fn test_borrow_tracking() {
528        let analyzer = BorrowAnalyzer::new();
529
530        // Track an immutable borrow
531        let borrow_id = analyzer.track_borrow(0x1000, BorrowType::Immutable, "test_var");
532
533        // Check active borrows
534        let active = analyzer.get_active_borrows(0x1000);
535        assert_eq!(active.len(), 1);
536        assert_eq!(active[0].borrow_type, BorrowType::Immutable);
537
538        // End the borrow
539        analyzer.end_borrow(borrow_id);
540
541        // Check that it's no longer active
542        let active = analyzer.get_active_borrows(0x1000);
543        assert_eq!(active.len(), 0);
544    }
545
546    #[test]
547    fn test_borrow_conflicts() {
548        let analyzer = BorrowAnalyzer::new();
549
550        // Track a mutable borrow
551        analyzer.track_borrow(0x1000, BorrowType::Mutable, "test_var1");
552
553        // Try to track another mutable borrow (should create conflict)
554        analyzer.track_borrow(0x1000, BorrowType::Mutable, "test_var2");
555
556        // Check conflicts
557        let conflicts = analyzer.get_conflicts();
558        assert!(!conflicts.is_empty());
559        assert_eq!(
560            conflicts[0].conflict_type,
561            ConflictType::MultipleMutableBorrows
562        );
563    }
564}