1use 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
14static GLOBAL_BORROW_ANALYZER: OnceLock<Arc<BorrowAnalyzer>> = OnceLock::new();
16
17pub fn get_global_borrow_analyzer() -> Arc<BorrowAnalyzer> {
19 GLOBAL_BORROW_ANALYZER
20 .get_or_init(|| Arc::new(BorrowAnalyzer::new()))
21 .clone()
22}
23
24pub struct BorrowAnalyzer {
26 active_borrows: Mutex<HashMap<usize, Vec<BorrowInfo>>>,
28 borrow_history: Mutex<Vec<BorrowEvent>>,
30 conflicts: Mutex<Vec<BorrowConflict>>,
32}
33
34impl Default for BorrowAnalyzer {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl BorrowAnalyzer {
41 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 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 self.check_borrow_conflicts(ptr, &borrow_type, &borrow_info);
66
67 if let Ok(mut active) = self.active_borrows.lock() {
69 active.entry(ptr).or_default().push(borrow_info.clone());
70 }
71
72 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 pub fn end_borrow(&self, borrow_id: BorrowId) {
88 let end_time = current_timestamp();
89
90 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 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 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 fn is_conflicting_borrow(&self, existing: &BorrowType, new: &BorrowType) -> bool {
144 match (existing, new) {
145 (BorrowType::Mutable, _) | (_, BorrowType::Mutable) => true,
147 (BorrowType::Immutable, BorrowType::Immutable) => false,
149 (BorrowType::Shared, BorrowType::Immutable)
151 | (BorrowType::Immutable, BorrowType::Shared) => false,
152 _ => false,
154 }
155 }
156
157 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 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 pub fn get_borrow_statistics(&self) -> BorrowStatistics {
180 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 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 pub fn analyze_borrow_patterns(&self) -> BorrowPatternAnalysis {
244 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 let mut patterns = Vec::new();
264
265 let long_lived_threshold = 1_000_000; 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 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 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 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#[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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
343pub enum BorrowType {
344 Immutable,
346 Mutable,
348 Shared,
350 Weak,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct BorrowInfo {
357 pub id: BorrowId,
359 pub ptr: usize,
361 pub borrow_type: BorrowType,
363 pub var_name: String,
365 pub start_time: u64,
367 pub end_time: Option<u64>,
369 pub thread_id: String,
371 pub call_stack: Vec<String>,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
377pub struct BorrowEvent {
378 pub borrow_info: BorrowInfo,
380 pub event_type: BorrowEventType,
382 pub timestamp: u64,
384}
385
386#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
388pub enum BorrowEventType {
389 BorrowStart,
391 BorrowEnd,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct BorrowConflict {
398 pub ptr: usize,
400 pub existing_borrow: BorrowInfo,
402 pub conflicting_borrow: BorrowInfo,
404 pub conflict_type: ConflictType,
406 pub timestamp: u64,
408}
409
410#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
412pub enum ConflictType {
413 MultipleMutableBorrows,
415 MutableImmutableConflict,
417 Other,
419}
420
421#[derive(Debug, Clone, Default, Serialize, Deserialize)]
423pub struct BorrowStatistics {
424 pub total_borrows: usize,
426 pub active_borrows: usize,
428 pub total_conflicts: usize,
430 pub avg_borrow_duration: u64,
432 pub max_borrow_duration: u64,
434 pub by_type: HashMap<String, usize>,
436}
437
438#[derive(Debug, Clone, Default, Serialize, Deserialize)]
442pub struct BorrowPatternAnalysis {
443 pub patterns: Vec<BorrowPattern>,
445 pub total_events: usize,
447 pub analysis_timestamp: u64,
449}
450
451#[derive(Debug, Clone, Serialize, Deserialize)]
453pub struct BorrowPattern {
454 pub pattern_type: BorrowPatternType,
456 pub description: String,
458 pub severity: PatternSeverity,
460 pub suggestion: String,
462}
463
464#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
466pub enum BorrowPatternType {
467 LongLivedBorrows,
469 FrequentConflicts,
471 HighConcurrency,
473 NestedBorrows,
475}
476
477#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
479pub enum PatternSeverity {
480 Info,
482 Warning,
484 Error,
486}
487
488fn current_timestamp() -> u64 {
490 SystemTime::now()
491 .duration_since(UNIX_EPOCH)
492 .unwrap_or_default()
493 .as_nanos() as u64
494}
495
496fn capture_call_stack() -> Vec<String> {
498 #[cfg(feature = "backtrace")]
500 {
501 let bt = backtrace::Backtrace::new();
502 bt.frames()
503 .iter()
504 .skip(2) .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 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 let borrow_id = analyzer.track_borrow(0x1000, BorrowType::Immutable, "test_var");
532
533 let active = analyzer.get_active_borrows(0x1000);
535 assert_eq!(active.len(), 1);
536 assert_eq!(active[0].borrow_type, BorrowType::Immutable);
537
538 analyzer.end_borrow(borrow_id);
540
541 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 analyzer.track_borrow(0x1000, BorrowType::Mutable, "test_var1");
552
553 analyzer.track_borrow(0x1000, BorrowType::Mutable, "test_var2");
555
556 let conflicts = analyzer.get_conflicts();
558 assert!(!conflicts.is_empty());
559 assert_eq!(
560 conflicts[0].conflict_type,
561 ConflictType::MultipleMutableBorrows
562 );
563 }
564}