use std::collections::HashMap;
use std::time::{Duration, Instant};
pub struct PerformanceChecker {
benchmarks: HashMap<String, PerformanceBenchmark>,
thresholds: PerformanceThresholds,
config: CheckerConfig,
}
impl PerformanceChecker {
pub fn config(&self) -> &CheckerConfig {
&self.config
}
}
pub struct MemoryLeakChecker {
baseline_measurements: HashMap<String, MemoryBaseline>,
config: LeakDetectionConfig,
sensitivity: LeakSensitivity,
}
impl MemoryLeakChecker {
pub fn config(&self) -> &LeakDetectionConfig {
&self.config
}
}
pub struct SafetyChecker {
violation_patterns: Vec<SafetyPattern>,
safety_requirements: HashMap<String, SafetyRequirement>,
config: SafetyConfig,
}
impl SafetyChecker {
pub fn violation_patterns(&self) -> &[SafetyPattern] {
&self.violation_patterns
}
pub fn safety_requirements(&self) -> &HashMap<String, SafetyRequirement> {
&self.safety_requirements
}
pub fn config(&self) -> &SafetyConfig {
&self.config
}
}
#[derive(Debug, Clone)]
pub struct PerformanceBenchmark {
pub operation: String,
pub expected_duration: Duration,
pub max_duration: Duration,
pub expected_memory: usize,
pub max_memory: usize,
pub expected_throughput: f64,
pub min_throughput: f64,
}
#[derive(Debug, Clone)]
pub struct PerformanceThresholds {
pub allocation_latency: Duration,
pub symbol_resolution: Duration,
pub stack_trace_capture: Duration,
pub memory_overhead_pct: f64,
pub min_completeness: f64,
}
#[derive(Debug, Clone)]
pub struct MemoryBaseline {
pub initial_memory: usize,
pub growth_pattern: GrowthPattern,
pub timestamp: Instant,
pub allocation_count: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub enum GrowthPattern {
Constant,
Linear { bytes_per_allocation: f64 },
Logarithmic,
Stabilizing { max_growth: usize },
Custom { description: String },
}
#[derive(Debug, Clone, PartialEq)]
pub enum LeakSensitivity {
Low,
Medium,
High,
Paranoid,
}
#[derive(Debug, Clone)]
pub struct SafetyPattern {
pub id: String,
pub description: String,
pub detector: SafetyDetector,
pub severity: SafetySeverity,
}
pub type SafetyDetector = fn(&SafetyContext) -> Vec<SafetyViolation>;
#[derive(Debug, Clone)]
pub struct SafetyRequirement {
pub properties: Vec<SafetyProperty>,
pub thread_safe: bool,
pub error_handling: bool,
pub max_risk_level: RiskLevel,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SafetyProperty {
NoMemoryLeaks,
NoDataRaces,
NoUseAfterFree,
NoBufferOverflow,
ErrorPropagation,
ResourceCleanup,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum SafetySeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum RiskLevel {
Minimal,
Low,
Medium,
High,
Critical,
}
#[derive(Debug)]
pub struct SafetyContext {
pub operation: String,
pub memory_accesses: Vec<MemoryAccess>,
pub thread_interactions: Vec<ThreadInteraction>,
pub error_handling: bool,
pub resource_usage: ResourceUsage,
}
#[derive(Debug, Clone)]
pub struct MemoryAccess {
pub access_type: AccessType,
pub address: Option<usize>,
pub size: usize,
pub synchronized: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum AccessType {
Read,
Write,
Allocate,
Deallocate,
}
#[derive(Debug, Clone)]
pub struct ThreadInteraction {
pub interaction_type: InteractionType,
pub resource_id: String,
pub synchronization: Option<SyncMechanism>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum InteractionType {
SharedRead,
ExclusiveWrite,
MessagePassing,
LockAcquisition,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SyncMechanism {
Mutex,
RwLock,
Atomic,
LockFree,
None,
}
#[derive(Debug, Clone)]
pub struct ResourceUsage {
pub memory_bytes: usize,
pub file_descriptors: usize,
pub network_connections: usize,
pub cpu_time: Duration,
}
#[derive(Debug, Clone)]
pub struct SafetyViolation {
pub violation_type: String,
pub severity: SafetySeverity,
pub description: String,
pub suggestion: String,
pub location: Option<String>,
}
#[derive(Debug, Clone)]
pub struct CheckerConfig {
pub deep_analysis: bool,
pub max_check_time: Duration,
pub realtime_checking: bool,
pub sample_rate: f64,
}
#[derive(Debug, Clone)]
pub struct LeakDetectionConfig {
pub measurement_interval: Duration,
pub measurement_history: usize,
pub growth_threshold: f64,
pub track_allocations: bool,
}
#[derive(Debug, Clone)]
pub struct SafetyConfig {
pub enabled_patterns: Vec<String>,
pub min_severity: SafetySeverity,
pub check_thread_safety: bool,
pub check_memory_safety: bool,
}
impl PerformanceChecker {
pub fn new() -> Self {
Self {
benchmarks: HashMap::new(),
thresholds: PerformanceThresholds::default(),
config: CheckerConfig::default(),
}
}
pub fn add_benchmark(&mut self, benchmark: PerformanceBenchmark) {
self.benchmarks
.insert(benchmark.operation.clone(), benchmark);
}
pub fn check_performance(
&self,
operation: &str,
actual: &PerformanceMetrics,
) -> PerformanceCheckResult {
let mut violations = Vec::new();
if let Some(benchmark) = self.benchmarks.get(operation) {
violations.extend(self.check_against_benchmark(benchmark, actual));
}
violations.extend(self.check_against_thresholds(operation, actual));
let status = if violations
.iter()
.any(|v| v.severity == PerformanceIssueType::Critical)
{
PerformanceStatus::Critical
} else if violations
.iter()
.any(|v| v.severity == PerformanceIssueType::Major)
{
PerformanceStatus::Poor
} else if violations
.iter()
.any(|v| v.severity == PerformanceIssueType::Minor)
{
PerformanceStatus::Acceptable
} else {
PerformanceStatus::Optimal
};
let overall_score = self.calculate_performance_score(&violations);
PerformanceCheckResult {
operation: operation.to_string(),
status,
violations,
overall_score,
}
}
fn check_against_benchmark(
&self,
benchmark: &PerformanceBenchmark,
actual: &PerformanceMetrics,
) -> Vec<PerformanceViolation> {
let mut violations = Vec::new();
if actual.duration > benchmark.max_duration {
violations.push(PerformanceViolation {
metric: "duration".to_string(),
expected: benchmark.expected_duration.as_micros() as f64,
actual: actual.duration.as_micros() as f64,
severity: PerformanceIssueType::Major,
description: format!(
"Duration {:.2}ms exceeds maximum {:.2}ms",
actual.duration.as_millis(),
benchmark.max_duration.as_millis()
),
});
}
if actual.memory_usage > benchmark.max_memory {
violations.push(PerformanceViolation {
metric: "memory".to_string(),
expected: benchmark.expected_memory as f64,
actual: actual.memory_usage as f64,
severity: PerformanceIssueType::Major,
description: format!(
"Memory usage {:.2}MB exceeds maximum {:.2}MB",
actual.memory_usage as f64 / (1024.0 * 1024.0),
benchmark.max_memory as f64 / (1024.0 * 1024.0)
),
});
}
if actual.throughput < benchmark.min_throughput {
violations.push(PerformanceViolation {
metric: "throughput".to_string(),
expected: benchmark.expected_throughput,
actual: actual.throughput,
severity: PerformanceIssueType::Minor,
description: format!(
"Throughput {:.0}/sec below minimum {:.0}/sec",
actual.throughput, benchmark.min_throughput
),
});
}
violations
}
fn check_against_thresholds(
&self,
operation: &str,
actual: &PerformanceMetrics,
) -> Vec<PerformanceViolation> {
let mut violations = Vec::new();
if operation.contains("allocation") && actual.duration > self.thresholds.allocation_latency
{
violations.push(PerformanceViolation {
metric: "allocation_latency".to_string(),
expected: self.thresholds.allocation_latency.as_micros() as f64,
actual: actual.duration.as_micros() as f64,
severity: PerformanceIssueType::Critical,
description: "Allocation tracking latency exceeds threshold".to_string(),
});
}
if operation.contains("symbol") && actual.duration > self.thresholds.symbol_resolution {
violations.push(PerformanceViolation {
metric: "symbol_resolution".to_string(),
expected: self.thresholds.symbol_resolution.as_millis() as f64,
actual: actual.duration.as_millis() as f64,
severity: PerformanceIssueType::Major,
description: "Symbol resolution time exceeds threshold".to_string(),
});
}
violations
}
fn calculate_performance_score(&self, violations: &[PerformanceViolation]) -> f64 {
if violations.is_empty() {
return 1.0;
}
let penalty: f64 = violations
.iter()
.map(|v| match v.severity {
PerformanceIssueType::Critical => 0.5,
PerformanceIssueType::Major => 0.3,
PerformanceIssueType::Minor => 0.1,
})
.sum();
(1.0 - penalty).max(0.0)
}
}
impl MemoryLeakChecker {
pub fn new() -> Self {
Self {
baseline_measurements: HashMap::new(),
config: LeakDetectionConfig::default(),
sensitivity: LeakSensitivity::Medium,
}
}
pub fn set_baseline(&mut self, operation: &str, memory: usize, allocations: usize) {
let baseline = MemoryBaseline {
initial_memory: memory,
growth_pattern: GrowthPattern::Constant,
timestamp: Instant::now(),
allocation_count: allocations,
};
self.baseline_measurements
.insert(operation.to_string(), baseline);
}
pub fn check_for_leaks(&self, operation: &str, current: &MemorySnapshot) -> LeakCheckResult {
if let Some(baseline) = self.baseline_measurements.get(operation) {
let growth_rate = self.calculate_growth_rate(baseline, current);
let leak_indicators = self.detect_leak_indicators(baseline, current, growth_rate);
let severity = self.assess_leak_severity(&leak_indicators);
let confidence = self.calculate_confidence(&leak_indicators);
LeakCheckResult {
operation: operation.to_string(),
leak_detected: !leak_indicators.is_empty(),
severity,
confidence,
indicators: leak_indicators,
growth_rate,
}
} else {
LeakCheckResult {
operation: operation.to_string(),
leak_detected: false,
severity: LeakSeverity::None,
confidence: 0.0,
indicators: Vec::new(),
growth_rate: 0.0,
}
}
}
fn calculate_growth_rate(&self, baseline: &MemoryBaseline, current: &MemorySnapshot) -> f64 {
let time_elapsed = baseline.timestamp.elapsed().as_secs_f64();
if time_elapsed > 0.0 {
(current.memory_usage as f64 - baseline.initial_memory as f64) / time_elapsed
} else {
0.0
}
}
fn detect_leak_indicators(
&self,
baseline: &MemoryBaseline,
current: &MemorySnapshot,
growth_rate: f64,
) -> Vec<LeakIndicator> {
let mut indicators = Vec::new();
if growth_rate > self.config.growth_threshold {
indicators.push(LeakIndicator {
indicator_type: "excessive_growth".to_string(),
description: format!(
"Memory growing at {:.2}MB/sec",
growth_rate / (1024.0 * 1024.0)
),
severity: LeakSeverity::High,
});
}
let alloc_growth = current.allocation_count as f64 - baseline.allocation_count as f64;
let memory_growth = current.memory_usage as f64 - baseline.initial_memory as f64;
if alloc_growth > 0.0 && memory_growth / alloc_growth > 1024.0 {
indicators.push(LeakIndicator {
indicator_type: "allocation_imbalance".to_string(),
description: "High memory per allocation ratio".to_string(),
severity: LeakSeverity::Medium,
});
}
indicators
}
fn assess_leak_severity(&self, indicators: &[LeakIndicator]) -> LeakSeverity {
indicators
.iter()
.map(|i| &i.severity)
.max()
.cloned()
.unwrap_or(LeakSeverity::None)
}
fn calculate_confidence(&self, indicators: &[LeakIndicator]) -> f64 {
if indicators.is_empty() {
0.0
} else {
match self.sensitivity {
LeakSensitivity::Low => 0.5,
LeakSensitivity::Medium => 0.7,
LeakSensitivity::High => 0.85,
LeakSensitivity::Paranoid => 0.95,
}
}
}
}
#[derive(Debug, Clone)]
pub struct PerformanceMetrics {
pub duration: Duration,
pub memory_usage: usize,
pub throughput: f64,
pub cpu_usage: f64,
}
#[derive(Debug, Clone)]
pub struct PerformanceCheckResult {
pub operation: String,
pub status: PerformanceStatus,
pub violations: Vec<PerformanceViolation>,
pub overall_score: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PerformanceStatus {
Optimal,
Acceptable,
Poor,
Critical,
}
#[derive(Debug, Clone)]
pub struct PerformanceViolation {
pub metric: String,
pub expected: f64,
pub actual: f64,
pub severity: PerformanceIssueType,
pub description: String,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PerformanceIssueType {
Minor,
Major,
Critical,
}
#[derive(Debug, Clone)]
pub struct MemorySnapshot {
pub memory_usage: usize,
pub allocation_count: usize,
pub timestamp: Instant,
}
#[derive(Debug, Clone)]
pub struct LeakCheckResult {
pub operation: String,
pub leak_detected: bool,
pub severity: LeakSeverity,
pub confidence: f64,
pub indicators: Vec<LeakIndicator>,
pub growth_rate: f64,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum LeakSeverity {
None,
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct LeakIndicator {
pub indicator_type: String,
pub description: String,
pub severity: LeakSeverity,
}
impl Default for PerformanceThresholds {
fn default() -> Self {
Self {
allocation_latency: Duration::from_micros(50),
symbol_resolution: Duration::from_millis(5),
stack_trace_capture: Duration::from_millis(10),
memory_overhead_pct: 5.0,
min_completeness: 0.95,
}
}
}
impl Default for CheckerConfig {
fn default() -> Self {
Self {
deep_analysis: true,
max_check_time: Duration::from_secs(5),
realtime_checking: false,
sample_rate: 0.1,
}
}
}
impl Default for LeakDetectionConfig {
fn default() -> Self {
Self {
measurement_interval: Duration::from_secs(60),
measurement_history: 100,
growth_threshold: 1024.0 * 1024.0, track_allocations: true,
}
}
}
impl Default for SafetyConfig {
fn default() -> Self {
Self {
enabled_patterns: vec![
"memory_safety".to_string(),
"thread_safety".to_string(),
"error_handling".to_string(),
],
min_severity: SafetySeverity::Low,
check_thread_safety: true,
check_memory_safety: true,
}
}
}
impl Default for PerformanceChecker {
fn default() -> Self {
Self::new()
}
}
impl Default for MemoryLeakChecker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_performance_checker() {
let mut checker = PerformanceChecker::new();
let benchmark = PerformanceBenchmark {
operation: "allocation_tracking".to_string(),
expected_duration: Duration::from_micros(10),
max_duration: Duration::from_micros(50),
expected_memory: 1024,
max_memory: 2048,
expected_throughput: 10000.0,
min_throughput: 5000.0,
};
checker.add_benchmark(benchmark);
let good_metrics = PerformanceMetrics {
duration: Duration::from_micros(20),
memory_usage: 1500,
throughput: 8000.0,
cpu_usage: 5.0,
};
let result = checker.check_performance("allocation_tracking", &good_metrics);
assert!(matches!(
result.status,
PerformanceStatus::Optimal | PerformanceStatus::Acceptable
));
let bad_metrics = PerformanceMetrics {
duration: Duration::from_micros(100),
memory_usage: 3000,
throughput: 1000.0,
cpu_usage: 50.0,
};
let result = checker.check_performance("allocation_tracking", &bad_metrics);
assert!(matches!(
result.status,
PerformanceStatus::Poor | PerformanceStatus::Critical
));
assert!(!result.violations.is_empty());
}
#[test]
fn test_memory_leak_checker() {
let mut checker = MemoryLeakChecker::new();
checker.set_baseline("test_operation", 1024 * 1024, 100);
let current = MemorySnapshot {
memory_usage: 1200 * 1024, allocation_count: 120,
timestamp: Instant::now(),
};
let result = checker.check_for_leaks("test_operation", ¤t);
let _ = result; }
#[test]
fn test_growth_patterns() {
assert_eq!(GrowthPattern::Constant, GrowthPattern::Constant);
let linear = GrowthPattern::Linear {
bytes_per_allocation: 64.0,
};
assert!(matches!(linear, GrowthPattern::Linear { .. }));
}
}