use crate::core::safe_operations::SafeLock;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
use std::time::{SystemTime, UNIX_EPOCH};
static GLOBAL_BORROW_ANALYZER: OnceLock<Arc<BorrowAnalyzer>> = OnceLock::new();
pub fn get_global_borrow_analyzer() -> Arc<BorrowAnalyzer> {
GLOBAL_BORROW_ANALYZER
.get_or_init(|| Arc::new(BorrowAnalyzer::new()))
.clone()
}
pub struct BorrowAnalyzer {
active_borrows: Mutex<HashMap<usize, Vec<BorrowInfo>>>,
borrow_history: Mutex<Vec<BorrowEvent>>,
conflicts: Mutex<Vec<BorrowConflict>>,
}
impl Default for BorrowAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl BorrowAnalyzer {
pub fn new() -> Self {
Self {
active_borrows: Mutex::new(HashMap::new()),
borrow_history: Mutex::new(Vec::new()),
conflicts: Mutex::new(Vec::new()),
}
}
pub fn track_borrow(&self, ptr: usize, borrow_type: BorrowType, var_name: &str) -> BorrowId {
let borrow_id = BorrowId::new();
let borrow_info = BorrowInfo {
id: borrow_id,
ptr,
borrow_type: borrow_type.clone(),
var_name: var_name.to_string(),
start_time: current_timestamp(),
end_time: None,
thread_id: format!("{:?}", std::thread::current().id()),
call_stack: capture_call_stack(),
};
self.check_borrow_conflicts(ptr, &borrow_type, &borrow_info);
if let Ok(mut active) = self.active_borrows.lock() {
active.entry(ptr).or_default().push(borrow_info.clone());
}
let event = BorrowEvent {
borrow_info: borrow_info.clone(),
event_type: BorrowEventType::BorrowStart,
timestamp: current_timestamp(),
};
if let Ok(mut history) = self.borrow_history.lock() {
history.push(event);
}
borrow_id
}
pub fn end_borrow(&self, borrow_id: BorrowId) {
let end_time = current_timestamp();
if let Ok(mut active) = self.active_borrows.lock() {
for (_, borrows) in active.iter_mut() {
if let Some(pos) = borrows.iter().position(|b| b.id == borrow_id) {
let mut borrow_info = borrows.remove(pos);
borrow_info.end_time = Some(end_time);
let event = BorrowEvent {
borrow_info: borrow_info.clone(),
event_type: BorrowEventType::BorrowEnd,
timestamp: end_time,
};
if let Ok(mut history) = self.borrow_history.lock() {
history.push(event);
}
break;
}
}
}
}
fn check_borrow_conflicts(
&self,
ptr: usize,
new_borrow_type: &BorrowType,
new_borrow: &BorrowInfo,
) {
if let Ok(active) = self.active_borrows.lock() {
if let Some(existing_borrows) = active.get(&ptr) {
for existing in existing_borrows {
if self.is_conflicting_borrow(&existing.borrow_type, new_borrow_type) {
let conflict = BorrowConflict {
ptr,
existing_borrow: existing.clone(),
conflicting_borrow: new_borrow.clone(),
conflict_type: self
.determine_conflict_type(&existing.borrow_type, new_borrow_type),
timestamp: current_timestamp(),
};
if let Ok(mut conflicts) = self.conflicts.lock() {
conflicts.push(conflict);
}
}
}
}
}
}
fn is_conflicting_borrow(&self, existing: &BorrowType, new: &BorrowType) -> bool {
match (existing, new) {
(BorrowType::Mutable, _) | (_, BorrowType::Mutable) => true,
(BorrowType::Immutable, BorrowType::Immutable) => false,
(BorrowType::Shared, BorrowType::Immutable)
| (BorrowType::Immutable, BorrowType::Shared) => false,
_ => false,
}
}
fn determine_conflict_type(&self, existing: &BorrowType, new: &BorrowType) -> ConflictType {
match (existing, new) {
(BorrowType::Mutable, BorrowType::Mutable) => ConflictType::MultipleMutableBorrows,
(BorrowType::Mutable, BorrowType::Immutable)
| (BorrowType::Immutable, BorrowType::Mutable) => {
ConflictType::MutableImmutableConflict
}
_ => ConflictType::Other,
}
}
pub fn get_active_borrows(&self, ptr: usize) -> Vec<BorrowInfo> {
if let Ok(active) = self.active_borrows.lock() {
active.get(&ptr).cloned().unwrap_or_default()
} else {
Vec::new()
}
}
pub fn get_borrow_statistics(&self) -> BorrowStatistics {
let (total_borrows, durations, by_type) = {
let history = match self.borrow_history.safe_lock() {
Ok(h) => h,
Err(_) => return BorrowStatistics::default(),
};
let total = history.len();
let mut durations = Vec::new();
let mut by_type = HashMap::new();
for event in history.iter() {
if let Some(end_time) = event.borrow_info.end_time {
durations.push(end_time - event.borrow_info.start_time);
}
let type_name = format!("{:?}", event.borrow_info.borrow_type);
*by_type.entry(type_name).or_insert(0) += 1;
}
(total, durations, by_type)
};
let total_conflicts = {
let conflicts = match self.conflicts.safe_lock() {
Ok(c) => c,
Err(_) => return BorrowStatistics::default(),
};
conflicts.len()
};
let active_borrows: usize = {
let active = match self.active_borrows.safe_lock() {
Ok(a) => a,
Err(_) => return BorrowStatistics::default(),
};
active.values().map(|v| v.len()).sum()
};
let avg_borrow_duration = if !durations.is_empty() {
durations.iter().sum::<u64>() / durations.len() as u64
} else {
0
};
let max_borrow_duration = durations.iter().max().copied().unwrap_or(0);
BorrowStatistics {
total_borrows,
active_borrows,
total_conflicts,
avg_borrow_duration,
max_borrow_duration,
by_type,
}
}
pub fn get_conflicts(&self) -> Vec<BorrowConflict> {
self.conflicts
.safe_lock()
.map(|c| c.clone())
.unwrap_or_default()
}
pub fn analyze_borrow_patterns(&self) -> BorrowPatternAnalysis {
let history_data: Vec<_> = {
let history = match self.borrow_history.safe_lock() {
Ok(h) => h,
Err(_) => return BorrowPatternAnalysis::default(),
};
history.iter().cloned().collect()
};
let conflicts_data: Vec<_> = {
let conflicts = match self.conflicts.safe_lock() {
Ok(c) => c,
Err(_) => return BorrowPatternAnalysis::default(),
};
conflicts.iter().cloned().collect()
};
let mut patterns = Vec::new();
let long_lived_threshold = 1_000_000; let long_lived_count = history_data
.iter()
.filter(|event| {
if let Some(end_time) = event.borrow_info.end_time {
end_time - event.borrow_info.start_time > long_lived_threshold
} else {
false
}
})
.count();
if long_lived_count > 0 {
patterns.push(BorrowPattern {
pattern_type: BorrowPatternType::LongLivedBorrows,
description: format!("{long_lived_count} borrows lasted longer than 1ms"),
severity: if long_lived_count > 10 {
PatternSeverity::Warning
} else {
PatternSeverity::Info
},
suggestion: "Consider reducing borrow scope or using RAII patterns".to_string(),
});
}
if conflicts_data.len() > 5 {
patterns.push(BorrowPattern {
pattern_type: BorrowPatternType::FrequentConflicts,
description: format!("{} borrow conflicts detected", conflicts_data.len()),
severity: PatternSeverity::Warning,
suggestion: "Review borrow patterns and consider refactoring to reduce conflicts"
.to_string(),
});
}
let max_concurrent = self.calculate_max_concurrent_borrows();
if max_concurrent > 10 {
patterns.push(BorrowPattern {
pattern_type: BorrowPatternType::HighConcurrency,
description: format!("Up to {max_concurrent} concurrent borrows detected"),
severity: PatternSeverity::Info,
suggestion: "High concurrency detected - ensure this is intentional".to_string(),
});
}
BorrowPatternAnalysis {
patterns,
total_events: history_data.len(),
analysis_timestamp: current_timestamp(),
}
}
fn calculate_max_concurrent_borrows(&self) -> usize {
self.active_borrows
.safe_lock()
.map(|active| active.values().map(|v| v.len()).max().unwrap_or(0))
.unwrap_or(0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct BorrowId(u64);
impl BorrowId {
fn new() -> Self {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(1);
Self(COUNTER.fetch_add(1, Ordering::Relaxed))
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum BorrowType {
Immutable,
Mutable,
Shared,
Weak,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BorrowInfo {
pub id: BorrowId,
pub ptr: usize,
pub borrow_type: BorrowType,
pub var_name: String,
pub start_time: u64,
pub end_time: Option<u64>,
pub thread_id: String,
pub call_stack: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BorrowEvent {
pub borrow_info: BorrowInfo,
pub event_type: BorrowEventType,
pub timestamp: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum BorrowEventType {
BorrowStart,
BorrowEnd,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BorrowConflict {
pub ptr: usize,
pub existing_borrow: BorrowInfo,
pub conflicting_borrow: BorrowInfo,
pub conflict_type: ConflictType,
pub timestamp: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ConflictType {
MultipleMutableBorrows,
MutableImmutableConflict,
Other,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BorrowStatistics {
pub total_borrows: usize,
pub active_borrows: usize,
pub total_conflicts: usize,
pub avg_borrow_duration: u64,
pub max_borrow_duration: u64,
pub by_type: HashMap<String, usize>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BorrowPatternAnalysis {
pub patterns: Vec<BorrowPattern>,
pub total_events: usize,
pub analysis_timestamp: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BorrowPattern {
pub pattern_type: BorrowPatternType,
pub description: String,
pub severity: PatternSeverity,
pub suggestion: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum BorrowPatternType {
LongLivedBorrows,
FrequentConflicts,
HighConcurrency,
NestedBorrows,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PatternSeverity {
Info,
Warning,
Error,
}
fn current_timestamp() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64
}
fn capture_call_stack() -> Vec<String> {
#[cfg(feature = "backtrace")]
{
let bt = backtrace::Backtrace::new();
bt.frames()
.iter()
.skip(2) .filter_map(|frame| {
frame
.symbols()
.first()
.and_then(|sym| sym.name())
.map(|name| name.to_string())
})
.collect()
}
#[cfg(not(feature = "backtrace"))]
{
Vec::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_borrow_tracking() {
let analyzer = BorrowAnalyzer::new();
let borrow_id = analyzer.track_borrow(0x1000, BorrowType::Immutable, "test_var");
let active = analyzer.get_active_borrows(0x1000);
assert_eq!(active.len(), 1);
assert_eq!(active[0].borrow_type, BorrowType::Immutable);
analyzer.end_borrow(borrow_id);
let active = analyzer.get_active_borrows(0x1000);
assert_eq!(active.len(), 0);
}
#[test]
fn test_borrow_conflicts() {
let analyzer = BorrowAnalyzer::new();
analyzer.track_borrow(0x1000, BorrowType::Mutable, "test_var1");
analyzer.track_borrow(0x1000, BorrowType::Mutable, "test_var2");
let conflicts = analyzer.get_conflicts();
assert!(!conflicts.is_empty());
assert_eq!(
conflicts[0].conflict_type,
ConflictType::MultipleMutableBorrows
);
}
}