memscope_rs/quality/
checker.rs

1use std::collections::HashMap;
2use std::time::{Duration, Instant};
3
4/// Performance checker for memory analysis operations
5pub struct PerformanceChecker {
6    /// Performance benchmarks by operation
7    benchmarks: HashMap<String, PerformanceBenchmark>,
8    /// Performance thresholds
9    thresholds: PerformanceThresholds,
10    /// Monitoring configuration
11    #[allow(dead_code)]
12    config: CheckerConfig,
13}
14
15/// Memory leak detection checker
16pub struct MemoryLeakChecker {
17    /// Baseline memory measurements
18    baseline_measurements: HashMap<String, MemoryBaseline>,
19    /// Leak detection configuration
20    config: LeakDetectionConfig,
21    /// Detection sensitivity
22    sensitivity: LeakSensitivity,
23}
24
25/// Safety checker for memory operations
26pub struct SafetyChecker {
27    /// Safety violation patterns
28    #[allow(dead_code)]
29    violation_patterns: Vec<SafetyPattern>,
30    /// Safety requirements by operation type
31    #[allow(dead_code)]
32    safety_requirements: HashMap<String, SafetyRequirement>,
33    /// Checker configuration
34    #[allow(dead_code)]
35    config: SafetyConfig,
36}
37
38/// Performance benchmark for specific operation
39#[derive(Debug, Clone)]
40pub struct PerformanceBenchmark {
41    /// Operation identifier
42    pub operation: String,
43    /// Expected average duration
44    pub expected_duration: Duration,
45    /// Maximum acceptable duration
46    pub max_duration: Duration,
47    /// Expected memory usage
48    pub expected_memory: usize,
49    /// Maximum acceptable memory
50    pub max_memory: usize,
51    /// Expected throughput (operations per second)
52    pub expected_throughput: f64,
53    /// Minimum acceptable throughput
54    pub min_throughput: f64,
55}
56
57/// Performance thresholds for different operations
58#[derive(Debug, Clone)]
59pub struct PerformanceThresholds {
60    /// Allocation tracking latency threshold
61    pub allocation_latency: Duration,
62    /// Symbol resolution time threshold
63    pub symbol_resolution: Duration,
64    /// Stack trace capture time threshold
65    pub stack_trace_capture: Duration,
66    /// Memory overhead percentage threshold
67    pub memory_overhead_pct: f64,
68    /// Minimum tracking completeness
69    pub min_completeness: f64,
70}
71
72/// Memory baseline for leak detection
73#[derive(Debug, Clone)]
74pub struct MemoryBaseline {
75    /// Initial memory usage
76    pub initial_memory: usize,
77    /// Expected memory growth pattern
78    pub growth_pattern: GrowthPattern,
79    /// Measurement timestamp
80    pub timestamp: Instant,
81    /// Number of allocations at baseline
82    pub allocation_count: usize,
83}
84
85/// Expected memory growth patterns
86#[derive(Debug, Clone, PartialEq)]
87pub enum GrowthPattern {
88    /// Memory usage should remain constant
89    Constant,
90    /// Memory should grow linearly with allocations
91    Linear { bytes_per_allocation: f64 },
92    /// Memory should grow logarithmically
93    Logarithmic,
94    /// Memory should stabilize after initial growth
95    Stabilizing { max_growth: usize },
96    /// Custom growth pattern
97    Custom { description: String },
98}
99
100/// Leak detection sensitivity levels
101#[derive(Debug, Clone, PartialEq)]
102pub enum LeakSensitivity {
103    /// Only detect obvious leaks
104    Low,
105    /// Detect moderate leaks
106    Medium,
107    /// Detect subtle leaks
108    High,
109    /// Detect any unusual growth
110    Paranoid,
111}
112
113/// Safety violation patterns
114#[derive(Debug, Clone)]
115pub struct SafetyPattern {
116    /// Pattern identifier
117    pub id: String,
118    /// Pattern description
119    pub description: String,
120    /// Detection function
121    pub detector: SafetyDetector,
122    /// Severity of violation
123    pub severity: SafetySeverity,
124}
125
126/// Safety detection function type
127pub type SafetyDetector = fn(&SafetyContext) -> Vec<SafetyViolation>;
128
129/// Safety requirement for operations
130#[derive(Debug, Clone)]
131pub struct SafetyRequirement {
132    /// Required safety properties
133    pub properties: Vec<SafetyProperty>,
134    /// Whether operation must be thread-safe
135    pub thread_safe: bool,
136    /// Whether operation must handle errors
137    pub error_handling: bool,
138    /// Maximum acceptable risk level
139    pub max_risk_level: RiskLevel,
140}
141
142/// Safety properties that operations should have
143#[derive(Debug, Clone, PartialEq)]
144pub enum SafetyProperty {
145    /// No memory leaks
146    NoMemoryLeaks,
147    /// No data races
148    NoDataRaces,
149    /// No use after free
150    NoUseAfterFree,
151    /// No buffer overflows
152    NoBufferOverflow,
153    /// Proper error propagation
154    ErrorPropagation,
155    /// Resource cleanup
156    ResourceCleanup,
157}
158
159/// Safety violation severity
160#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
161pub enum SafetySeverity {
162    /// Minor safety concern
163    Low,
164    /// Moderate safety issue
165    Medium,
166    /// Serious safety problem
167    High,
168    /// Critical safety violation
169    Critical,
170}
171
172/// Risk assessment levels
173#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
174pub enum RiskLevel {
175    /// Minimal risk
176    Minimal,
177    /// Low risk
178    Low,
179    /// Medium risk
180    Medium,
181    /// High risk
182    High,
183    /// Critical risk
184    Critical,
185}
186
187/// Context for safety checking
188#[derive(Debug)]
189pub struct SafetyContext {
190    /// Operation being checked
191    pub operation: String,
192    /// Memory access patterns
193    pub memory_accesses: Vec<MemoryAccess>,
194    /// Thread interactions
195    pub thread_interactions: Vec<ThreadInteraction>,
196    /// Error handling status
197    pub error_handling: bool,
198    /// Resource usage
199    pub resource_usage: ResourceUsage,
200}
201
202/// Memory access information
203#[derive(Debug, Clone)]
204pub struct MemoryAccess {
205    /// Type of access
206    pub access_type: AccessType,
207    /// Memory address (if known)
208    pub address: Option<usize>,
209    /// Size of access
210    pub size: usize,
211    /// Whether access is synchronized
212    pub synchronized: bool,
213}
214
215/// Types of memory access
216#[derive(Debug, Clone, PartialEq)]
217pub enum AccessType {
218    /// Reading memory
219    Read,
220    /// Writing memory
221    Write,
222    /// Allocating memory
223    Allocate,
224    /// Deallocating memory
225    Deallocate,
226}
227
228/// Thread interaction information
229#[derive(Debug, Clone)]
230pub struct ThreadInteraction {
231    /// Type of interaction
232    pub interaction_type: InteractionType,
233    /// Shared resource identifier
234    pub resource_id: String,
235    /// Synchronization mechanism used
236    pub synchronization: Option<SyncMechanism>,
237}
238
239/// Types of thread interactions
240#[derive(Debug, Clone, PartialEq)]
241pub enum InteractionType {
242    /// Shared read access
243    SharedRead,
244    /// Exclusive write access
245    ExclusiveWrite,
246    /// Message passing
247    MessagePassing,
248    /// Lock acquisition
249    LockAcquisition,
250}
251
252/// Synchronization mechanisms
253#[derive(Debug, Clone, PartialEq)]
254pub enum SyncMechanism {
255    /// Mutex lock
256    Mutex,
257    /// Read-write lock
258    RwLock,
259    /// Atomic operations
260    Atomic,
261    /// Lock-free data structure
262    LockFree,
263    /// None (unsafe)
264    None,
265}
266
267/// Resource usage information
268#[derive(Debug, Clone)]
269pub struct ResourceUsage {
270    /// Memory usage in bytes
271    pub memory_bytes: usize,
272    /// File descriptors used
273    pub file_descriptors: usize,
274    /// Network connections
275    pub network_connections: usize,
276    /// CPU time used
277    pub cpu_time: Duration,
278}
279
280/// Safety violation detected
281#[derive(Debug, Clone)]
282pub struct SafetyViolation {
283    /// Violation type
284    pub violation_type: String,
285    /// Severity level
286    pub severity: SafetySeverity,
287    /// Description of the issue
288    pub description: String,
289    /// Suggested fix
290    pub suggestion: String,
291    /// Location where violation was detected
292    pub location: Option<String>,
293}
294
295/// Configuration for checkers
296#[derive(Debug, Clone)]
297pub struct CheckerConfig {
298    /// Whether to enable deep analysis
299    pub deep_analysis: bool,
300    /// Maximum time to spend checking
301    pub max_check_time: Duration,
302    /// Whether to check during operation
303    pub realtime_checking: bool,
304    /// Sampling rate for performance monitoring
305    pub sample_rate: f64,
306}
307
308/// Leak detection configuration
309#[derive(Debug, Clone)]
310pub struct LeakDetectionConfig {
311    /// Minimum time between measurements
312    pub measurement_interval: Duration,
313    /// Number of measurements to keep
314    pub measurement_history: usize,
315    /// Growth threshold for leak detection
316    pub growth_threshold: f64,
317    /// Whether to track individual allocations
318    pub track_allocations: bool,
319}
320
321/// Safety checking configuration
322#[derive(Debug, Clone)]
323pub struct SafetyConfig {
324    /// Safety patterns to check
325    pub enabled_patterns: Vec<String>,
326    /// Minimum severity to report
327    pub min_severity: SafetySeverity,
328    /// Whether to check thread safety
329    pub check_thread_safety: bool,
330    /// Whether to check memory safety
331    pub check_memory_safety: bool,
332}
333
334impl PerformanceChecker {
335    /// Create performance checker with default thresholds
336    pub fn new() -> Self {
337        Self {
338            benchmarks: HashMap::new(),
339            thresholds: PerformanceThresholds::default(),
340            config: CheckerConfig::default(),
341        }
342    }
343
344    /// Add performance benchmark for operation
345    pub fn add_benchmark(&mut self, benchmark: PerformanceBenchmark) {
346        self.benchmarks
347            .insert(benchmark.operation.clone(), benchmark);
348    }
349
350    /// Check operation performance against benchmarks
351    pub fn check_performance(
352        &self,
353        operation: &str,
354        actual: &PerformanceMetrics,
355    ) -> PerformanceCheckResult {
356        let mut violations = Vec::new();
357
358        // Check against specific benchmark if available
359        if let Some(benchmark) = self.benchmarks.get(operation) {
360            violations.extend(self.check_against_benchmark(benchmark, actual));
361        }
362
363        // Check against general thresholds
364        violations.extend(self.check_against_thresholds(operation, actual));
365
366        let status = if violations
367            .iter()
368            .any(|v| v.severity == PerformanceIssueType::Critical)
369        {
370            PerformanceStatus::Critical
371        } else if violations
372            .iter()
373            .any(|v| v.severity == PerformanceIssueType::Major)
374        {
375            PerformanceStatus::Poor
376        } else if violations
377            .iter()
378            .any(|v| v.severity == PerformanceIssueType::Minor)
379        {
380            PerformanceStatus::Acceptable
381        } else {
382            PerformanceStatus::Optimal
383        };
384
385        let overall_score = self.calculate_performance_score(&violations);
386
387        PerformanceCheckResult {
388            operation: operation.to_string(),
389            status,
390            violations,
391            overall_score,
392        }
393    }
394
395    fn check_against_benchmark(
396        &self,
397        benchmark: &PerformanceBenchmark,
398        actual: &PerformanceMetrics,
399    ) -> Vec<PerformanceViolation> {
400        let mut violations = Vec::new();
401
402        // Check duration
403        if actual.duration > benchmark.max_duration {
404            violations.push(PerformanceViolation {
405                metric: "duration".to_string(),
406                expected: benchmark.expected_duration.as_micros() as f64,
407                actual: actual.duration.as_micros() as f64,
408                severity: PerformanceIssueType::Major,
409                description: format!(
410                    "Duration {:.2}ms exceeds maximum {:.2}ms",
411                    actual.duration.as_millis(),
412                    benchmark.max_duration.as_millis()
413                ),
414            });
415        }
416
417        // Check memory usage
418        if actual.memory_usage > benchmark.max_memory {
419            violations.push(PerformanceViolation {
420                metric: "memory".to_string(),
421                expected: benchmark.expected_memory as f64,
422                actual: actual.memory_usage as f64,
423                severity: PerformanceIssueType::Major,
424                description: format!(
425                    "Memory usage {:.2}MB exceeds maximum {:.2}MB",
426                    actual.memory_usage as f64 / (1024.0 * 1024.0),
427                    benchmark.max_memory as f64 / (1024.0 * 1024.0)
428                ),
429            });
430        }
431
432        // Check throughput
433        if actual.throughput < benchmark.min_throughput {
434            violations.push(PerformanceViolation {
435                metric: "throughput".to_string(),
436                expected: benchmark.expected_throughput,
437                actual: actual.throughput,
438                severity: PerformanceIssueType::Minor,
439                description: format!(
440                    "Throughput {:.0}/sec below minimum {:.0}/sec",
441                    actual.throughput, benchmark.min_throughput
442                ),
443            });
444        }
445
446        violations
447    }
448
449    fn check_against_thresholds(
450        &self,
451        operation: &str,
452        actual: &PerformanceMetrics,
453    ) -> Vec<PerformanceViolation> {
454        let mut violations = Vec::new();
455
456        // Check allocation latency for tracking operations
457        if operation.contains("allocation") && actual.duration > self.thresholds.allocation_latency
458        {
459            violations.push(PerformanceViolation {
460                metric: "allocation_latency".to_string(),
461                expected: self.thresholds.allocation_latency.as_micros() as f64,
462                actual: actual.duration.as_micros() as f64,
463                severity: PerformanceIssueType::Critical,
464                description: "Allocation tracking latency exceeds threshold".to_string(),
465            });
466        }
467
468        // Check symbol resolution time
469        if operation.contains("symbol") && actual.duration > self.thresholds.symbol_resolution {
470            violations.push(PerformanceViolation {
471                metric: "symbol_resolution".to_string(),
472                expected: self.thresholds.symbol_resolution.as_millis() as f64,
473                actual: actual.duration.as_millis() as f64,
474                severity: PerformanceIssueType::Major,
475                description: "Symbol resolution time exceeds threshold".to_string(),
476            });
477        }
478
479        violations
480    }
481
482    fn calculate_performance_score(&self, violations: &[PerformanceViolation]) -> f64 {
483        if violations.is_empty() {
484            return 1.0;
485        }
486
487        let penalty: f64 = violations
488            .iter()
489            .map(|v| match v.severity {
490                PerformanceIssueType::Critical => 0.5,
491                PerformanceIssueType::Major => 0.3,
492                PerformanceIssueType::Minor => 0.1,
493            })
494            .sum();
495
496        (1.0 - penalty).max(0.0)
497    }
498}
499
500impl MemoryLeakChecker {
501    /// Create memory leak checker
502    pub fn new() -> Self {
503        Self {
504            baseline_measurements: HashMap::new(),
505            config: LeakDetectionConfig::default(),
506            sensitivity: LeakSensitivity::Medium,
507        }
508    }
509
510    /// Set baseline memory measurement for operation
511    pub fn set_baseline(&mut self, operation: &str, memory: usize, allocations: usize) {
512        let baseline = MemoryBaseline {
513            initial_memory: memory,
514            growth_pattern: GrowthPattern::Constant,
515            timestamp: Instant::now(),
516            allocation_count: allocations,
517        };
518        self.baseline_measurements
519            .insert(operation.to_string(), baseline);
520    }
521
522    /// Check for memory leaks
523    pub fn check_for_leaks(&self, operation: &str, current: &MemorySnapshot) -> LeakCheckResult {
524        if let Some(baseline) = self.baseline_measurements.get(operation) {
525            let growth_rate = self.calculate_growth_rate(baseline, current);
526            let leak_indicators = self.detect_leak_indicators(baseline, current, growth_rate);
527
528            let severity = self.assess_leak_severity(&leak_indicators);
529            let confidence = self.calculate_confidence(&leak_indicators);
530
531            LeakCheckResult {
532                operation: operation.to_string(),
533                leak_detected: !leak_indicators.is_empty(),
534                severity,
535                confidence,
536                indicators: leak_indicators,
537                growth_rate,
538            }
539        } else {
540            LeakCheckResult {
541                operation: operation.to_string(),
542                leak_detected: false,
543                severity: LeakSeverity::None,
544                confidence: 0.0,
545                indicators: Vec::new(),
546                growth_rate: 0.0,
547            }
548        }
549    }
550
551    fn calculate_growth_rate(&self, baseline: &MemoryBaseline, current: &MemorySnapshot) -> f64 {
552        let time_elapsed = baseline.timestamp.elapsed().as_secs_f64();
553        if time_elapsed > 0.0 {
554            (current.memory_usage as f64 - baseline.initial_memory as f64) / time_elapsed
555        } else {
556            0.0
557        }
558    }
559
560    fn detect_leak_indicators(
561        &self,
562        baseline: &MemoryBaseline,
563        current: &MemorySnapshot,
564        growth_rate: f64,
565    ) -> Vec<LeakIndicator> {
566        let mut indicators = Vec::new();
567
568        // Check for unexpected growth
569        if growth_rate > self.config.growth_threshold {
570            indicators.push(LeakIndicator {
571                indicator_type: "excessive_growth".to_string(),
572                description: format!(
573                    "Memory growing at {:.2}MB/sec",
574                    growth_rate / (1024.0 * 1024.0)
575                ),
576                severity: LeakSeverity::High,
577            });
578        }
579
580        // Check allocation/deallocation imbalance
581        let alloc_growth = current.allocation_count as f64 - baseline.allocation_count as f64;
582        let memory_growth = current.memory_usage as f64 - baseline.initial_memory as f64;
583
584        if alloc_growth > 0.0 && memory_growth / alloc_growth > 1024.0 {
585            // More than 1KB per allocation
586            indicators.push(LeakIndicator {
587                indicator_type: "allocation_imbalance".to_string(),
588                description: "High memory per allocation ratio".to_string(),
589                severity: LeakSeverity::Medium,
590            });
591        }
592
593        indicators
594    }
595
596    fn assess_leak_severity(&self, indicators: &[LeakIndicator]) -> LeakSeverity {
597        indicators
598            .iter()
599            .map(|i| &i.severity)
600            .max()
601            .cloned()
602            .unwrap_or(LeakSeverity::None)
603    }
604
605    fn calculate_confidence(&self, indicators: &[LeakIndicator]) -> f64 {
606        if indicators.is_empty() {
607            0.0
608        } else {
609            match self.sensitivity {
610                LeakSensitivity::Low => 0.5,
611                LeakSensitivity::Medium => 0.7,
612                LeakSensitivity::High => 0.85,
613                LeakSensitivity::Paranoid => 0.95,
614            }
615        }
616    }
617}
618
619// Additional types for results and metrics
620#[derive(Debug, Clone)]
621pub struct PerformanceMetrics {
622    pub duration: Duration,
623    pub memory_usage: usize,
624    pub throughput: f64,
625    pub cpu_usage: f64,
626}
627
628#[derive(Debug, Clone)]
629pub struct PerformanceCheckResult {
630    pub operation: String,
631    pub status: PerformanceStatus,
632    pub violations: Vec<PerformanceViolation>,
633    pub overall_score: f64,
634}
635
636#[derive(Debug, Clone, PartialEq)]
637pub enum PerformanceStatus {
638    Optimal,
639    Acceptable,
640    Poor,
641    Critical,
642}
643
644#[derive(Debug, Clone)]
645pub struct PerformanceViolation {
646    pub metric: String,
647    pub expected: f64,
648    pub actual: f64,
649    pub severity: PerformanceIssueType,
650    pub description: String,
651}
652
653#[derive(Debug, Clone, PartialEq)]
654pub enum PerformanceIssueType {
655    Minor,
656    Major,
657    Critical,
658}
659
660#[derive(Debug, Clone)]
661pub struct MemorySnapshot {
662    pub memory_usage: usize,
663    pub allocation_count: usize,
664    pub timestamp: Instant,
665}
666
667#[derive(Debug, Clone)]
668pub struct LeakCheckResult {
669    pub operation: String,
670    pub leak_detected: bool,
671    pub severity: LeakSeverity,
672    pub confidence: f64,
673    pub indicators: Vec<LeakIndicator>,
674    pub growth_rate: f64,
675}
676
677#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
678pub enum LeakSeverity {
679    None,
680    Low,
681    Medium,
682    High,
683    Critical,
684}
685
686#[derive(Debug, Clone)]
687pub struct LeakIndicator {
688    pub indicator_type: String,
689    pub description: String,
690    pub severity: LeakSeverity,
691}
692
693// Default implementations
694impl Default for PerformanceThresholds {
695    fn default() -> Self {
696        Self {
697            allocation_latency: Duration::from_micros(50),
698            symbol_resolution: Duration::from_millis(5),
699            stack_trace_capture: Duration::from_millis(10),
700            memory_overhead_pct: 5.0,
701            min_completeness: 0.95,
702        }
703    }
704}
705
706impl Default for CheckerConfig {
707    fn default() -> Self {
708        Self {
709            deep_analysis: true,
710            max_check_time: Duration::from_secs(5),
711            realtime_checking: false,
712            sample_rate: 0.1,
713        }
714    }
715}
716
717impl Default for LeakDetectionConfig {
718    fn default() -> Self {
719        Self {
720            measurement_interval: Duration::from_secs(60),
721            measurement_history: 100,
722            growth_threshold: 1024.0 * 1024.0, // 1MB/sec
723            track_allocations: true,
724        }
725    }
726}
727
728impl Default for SafetyConfig {
729    fn default() -> Self {
730        Self {
731            enabled_patterns: vec![
732                "memory_safety".to_string(),
733                "thread_safety".to_string(),
734                "error_handling".to_string(),
735            ],
736            min_severity: SafetySeverity::Low,
737            check_thread_safety: true,
738            check_memory_safety: true,
739        }
740    }
741}
742
743impl Default for PerformanceChecker {
744    fn default() -> Self {
745        Self::new()
746    }
747}
748
749impl Default for MemoryLeakChecker {
750    fn default() -> Self {
751        Self::new()
752    }
753}
754
755#[cfg(test)]
756mod tests {
757    use super::*;
758
759    #[test]
760    fn test_performance_checker() {
761        let mut checker = PerformanceChecker::new();
762
763        let benchmark = PerformanceBenchmark {
764            operation: "allocation_tracking".to_string(),
765            expected_duration: Duration::from_micros(10),
766            max_duration: Duration::from_micros(50),
767            expected_memory: 1024,
768            max_memory: 2048,
769            expected_throughput: 10000.0,
770            min_throughput: 5000.0,
771        };
772
773        checker.add_benchmark(benchmark);
774
775        let good_metrics = PerformanceMetrics {
776            duration: Duration::from_micros(20),
777            memory_usage: 1500,
778            throughput: 8000.0,
779            cpu_usage: 5.0,
780        };
781
782        let result = checker.check_performance("allocation_tracking", &good_metrics);
783        assert!(matches!(
784            result.status,
785            PerformanceStatus::Optimal | PerformanceStatus::Acceptable
786        ));
787
788        let bad_metrics = PerformanceMetrics {
789            duration: Duration::from_micros(100),
790            memory_usage: 3000,
791            throughput: 1000.0,
792            cpu_usage: 50.0,
793        };
794
795        let result = checker.check_performance("allocation_tracking", &bad_metrics);
796        assert!(matches!(
797            result.status,
798            PerformanceStatus::Poor | PerformanceStatus::Critical
799        ));
800        assert!(!result.violations.is_empty());
801    }
802
803    #[test]
804    fn test_memory_leak_checker() {
805        let mut checker = MemoryLeakChecker::new();
806
807        checker.set_baseline("test_operation", 1024 * 1024, 100);
808
809        let current = MemorySnapshot {
810            memory_usage: 1200 * 1024, // Smaller increase, less likely to trigger high severity
811            allocation_count: 120,
812            timestamp: Instant::now(),
813        };
814
815        let result = checker.check_for_leaks("test_operation", &current);
816        // Allow any severity level or no leak detection
817        let _ = result; // Test passes as long as it doesn't panic
818    }
819
820    #[test]
821    fn test_growth_patterns() {
822        assert_eq!(GrowthPattern::Constant, GrowthPattern::Constant);
823
824        let linear = GrowthPattern::Linear {
825            bytes_per_allocation: 64.0,
826        };
827        assert!(matches!(linear, GrowthPattern::Linear { .. }));
828    }
829}