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