memscope_rs/quality/
validator.rs

1use std::collections::HashMap;
2use std::time::{Duration, Instant};
3
4/// Code quality validator for memory analysis operations
5pub struct QualityValidator {
6    /// Active validation rules
7    rules: Vec<ValidationRule>,
8    /// Rule execution statistics
9    rule_stats: HashMap<String, RuleStats>,
10    /// Validation configuration
11    config: ValidationConfig,
12}
13
14/// Individual validation rule
15#[derive(Debug, Clone)]
16pub struct ValidationRule {
17    /// Unique rule identifier
18    pub id: String,
19    /// Human-readable name
20    pub name: String,
21    /// Rule description
22    pub description: String,
23    /// Rule category
24    pub category: RuleCategory,
25    /// Severity level if rule fails
26    pub severity: ValidationSeverity,
27    /// Whether rule is enabled
28    pub enabled: bool,
29    /// Validation function
30    pub validator: ValidationFunction,
31}
32
33/// Categories of validation rules
34#[derive(Debug, Clone, PartialEq)]
35pub enum RuleCategory {
36    /// Memory safety and correctness
37    MemorySafety,
38    /// Performance and efficiency
39    Performance,
40    /// Code style and maintainability
41    CodeStyle,
42    /// Error handling patterns
43    ErrorHandling,
44    /// Thread safety
45    ThreadSafety,
46    /// Resource management
47    ResourceManagement,
48}
49
50/// Validation severity levels
51#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
52pub enum ValidationSeverity {
53    /// Informational note
54    Info,
55    /// Style or convention issue
56    Style,
57    /// Potential problem
58    Warning,
59    /// Definite problem
60    Error,
61    /// Critical issue that must be fixed
62    Critical,
63}
64
65/// Validation function type
66pub type ValidationFunction = fn(&ValidationContext) -> Result<(), ValidationError>;
67
68/// Context provided to validation rules
69#[derive(Debug)]
70pub struct ValidationContext {
71    /// Operation being validated
72    pub operation_name: String,
73    /// Performance metrics for the operation
74    pub metrics: OperationMetrics,
75    /// Memory usage information
76    pub memory_info: MemoryInfo,
77    /// Error handling status
78    pub error_handling: ErrorHandlingInfo,
79    /// Thread safety information
80    pub thread_safety: ThreadSafetyInfo,
81}
82
83/// Metrics for a specific operation
84#[derive(Debug, Clone)]
85pub struct OperationMetrics {
86    /// Average execution time
87    pub avg_duration: Duration,
88    /// Peak memory usage during operation
89    pub peak_memory: usize,
90    /// Success rate (0.0 to 1.0)
91    pub success_rate: f64,
92    /// Number of allocations performed
93    pub allocation_count: usize,
94    /// CPU usage percentage
95    pub cpu_usage: f64,
96}
97
98/// Memory usage information
99#[derive(Debug, Clone)]
100pub struct MemoryInfo {
101    /// Current memory usage in bytes
102    pub current_usage: usize,
103    /// Peak memory usage in bytes
104    pub peak_usage: usize,
105    /// Number of active allocations
106    pub active_allocations: usize,
107    /// Memory fragmentation ratio
108    pub fragmentation_ratio: f64,
109    /// Memory growth rate (bytes per second)
110    pub growth_rate: f64,
111}
112
113/// Error handling information
114#[derive(Debug, Clone)]
115pub struct ErrorHandlingInfo {
116    /// Whether errors are properly handled
117    pub has_error_handling: bool,
118    /// Number of potential error points
119    pub error_points: usize,
120    /// Number of handled error points
121    pub handled_error_points: usize,
122    /// Whether recovery mechanisms exist
123    pub has_recovery: bool,
124}
125
126/// Thread safety information
127#[derive(Debug, Clone)]
128pub struct ThreadSafetyInfo {
129    /// Whether operation is thread-safe
130    pub is_thread_safe: bool,
131    /// Number of shared resources accessed
132    pub shared_resources: usize,
133    /// Whether proper synchronization is used
134    pub has_synchronization: bool,
135    /// Lock contention level (0.0 to 1.0)
136    pub contention_level: f64,
137}
138
139/// Validation error details
140#[derive(Debug, Clone)]
141pub struct ValidationError {
142    /// Error message
143    pub message: String,
144    /// Suggested fix
145    pub suggestion: Option<String>,
146    /// Code location if applicable
147    pub location: Option<String>,
148}
149
150/// Result of running validation rules
151#[derive(Debug, Clone)]
152pub struct ValidationResult {
153    /// Overall validation status
154    pub status: ValidationStatus,
155    /// Individual rule results
156    pub rule_results: Vec<RuleResult>,
157    /// Summary statistics
158    pub summary: ValidationSummary,
159    /// Performance impact of validation
160    pub validation_overhead: Duration,
161}
162
163/// Overall validation status
164#[derive(Debug, Clone, PartialEq)]
165pub enum ValidationStatus {
166    /// All rules passed
167    Passed,
168    /// Some warnings found
169    WarningsFound,
170    /// Errors found that should be addressed
171    ErrorsFound,
172    /// Critical issues found
173    CriticalIssuesFound,
174}
175
176/// Result of a single validation rule
177#[derive(Debug, Clone)]
178pub struct RuleResult {
179    /// Rule that was executed
180    pub rule_id: String,
181    /// Whether rule passed
182    pub passed: bool,
183    /// Error details if rule failed
184    pub error: Option<ValidationError>,
185    /// Execution time for this rule
186    pub execution_time: Duration,
187}
188
189/// Summary of validation results
190#[derive(Debug, Clone)]
191pub struct ValidationSummary {
192    /// Total rules executed
193    pub total_rules: usize,
194    /// Number of rules that passed
195    pub passed_rules: usize,
196    /// Number of rules that failed
197    pub failed_rules: usize,
198    /// Number of critical issues
199    pub critical_issues: usize,
200    /// Number of errors
201    pub errors: usize,
202    /// Number of warnings
203    pub warnings: usize,
204    /// Overall quality score (0.0 to 1.0)
205    pub quality_score: f64,
206}
207
208/// Configuration for validation behavior
209#[derive(Debug, Clone)]
210pub struct ValidationConfig {
211    /// Whether to stop on first critical error
212    pub fail_fast: bool,
213    /// Maximum time to spend on validation
214    pub max_validation_time: Duration,
215    /// Whether to enable performance-intensive checks
216    pub enable_deep_checks: bool,
217    /// Minimum severity level to report
218    pub min_severity: ValidationSeverity,
219}
220
221/// Statistics for rule execution
222#[derive(Debug, Clone)]
223pub struct RuleStats {
224    /// Number of times rule was executed
225    execution_count: usize,
226    /// Total execution time
227    total_time: Duration,
228    /// Number of times rule failed
229    failure_count: usize,
230    /// Average execution time
231    avg_time: Duration,
232}
233
234impl QualityValidator {
235    /// Create new validator with default rules
236    pub fn new() -> Self {
237        let mut validator = Self {
238            rules: Vec::new(),
239            rule_stats: HashMap::new(),
240            config: ValidationConfig::default(),
241        };
242
243        validator.add_default_rules();
244        validator
245    }
246
247    /// Create validator with custom configuration
248    pub fn with_config(config: ValidationConfig) -> Self {
249        let mut validator = Self {
250            rules: Vec::new(),
251            rule_stats: HashMap::new(),
252            config,
253        };
254
255        validator.add_default_rules();
256        validator
257    }
258
259    /// Add custom validation rule
260    pub fn add_rule(&mut self, rule: ValidationRule) {
261        self.rules.push(rule);
262    }
263
264    /// Remove validation rule by ID
265    pub fn remove_rule(&mut self, rule_id: &str) -> bool {
266        if let Some(pos) = self.rules.iter().position(|r| r.id == rule_id) {
267            self.rules.remove(pos);
268            self.rule_stats.remove(rule_id);
269            true
270        } else {
271            false
272        }
273    }
274
275    /// Enable or disable a specific rule
276    pub fn set_rule_enabled(&mut self, rule_id: &str, enabled: bool) -> bool {
277        if let Some(rule) = self.rules.iter_mut().find(|r| r.id == rule_id) {
278            rule.enabled = enabled;
279            true
280        } else {
281            false
282        }
283    }
284
285    /// Validate operation with all enabled rules
286    pub fn validate(&mut self, context: &ValidationContext) -> ValidationResult {
287        let start_time = Instant::now();
288        let mut rule_results = Vec::new();
289        let mut should_stop = false;
290
291        // Collect rule information to avoid borrowing issues
292        let rules_info: Vec<_> = self
293            .rules
294            .iter()
295            .filter(|rule| rule.enabled)
296            .map(|rule| (rule.id.clone(), rule.severity.clone(), rule.validator))
297            .collect();
298
299        for (rule_id, severity, validator) in rules_info {
300            if should_stop && self.config.fail_fast {
301                break;
302            }
303
304            let rule_start = Instant::now();
305            let result = validator(context);
306            let rule_duration = rule_start.elapsed();
307
308            // Update statistics
309            self.update_rule_stats(&rule_id, rule_duration, result.is_err());
310
311            let rule_result = RuleResult {
312                rule_id,
313                passed: result.is_ok(),
314                error: result.err(),
315                execution_time: rule_duration,
316            };
317
318            if !rule_result.passed && severity >= ValidationSeverity::Critical {
319                should_stop = true;
320            }
321
322            rule_results.push(rule_result);
323
324            // Check timeout
325            if start_time.elapsed() > self.config.max_validation_time {
326                break;
327            }
328        }
329
330        let validation_overhead = start_time.elapsed();
331        let summary = self.calculate_summary(&rule_results);
332        let status = self.determine_status(&summary);
333
334        ValidationResult {
335            status,
336            rule_results,
337            summary,
338            validation_overhead,
339        }
340    }
341
342    /// Get statistics for all rules
343    pub fn get_rule_statistics(&self) -> &HashMap<String, RuleStats> {
344        &self.rule_stats
345    }
346
347    /// Reset all statistics
348    pub fn reset_statistics(&mut self) {
349        self.rule_stats.clear();
350    }
351
352    fn add_default_rules(&mut self) {
353        // Memory safety rules
354        self.add_rule(ValidationRule {
355            id: "memory_leak_check".to_string(),
356            name: "Memory Leak Detection".to_string(),
357            description: "Check for potential memory leaks in tracking operations".to_string(),
358            category: RuleCategory::MemorySafety,
359            severity: ValidationSeverity::Critical,
360            enabled: true,
361            validator: validate_memory_leaks,
362        });
363
364        self.add_rule(ValidationRule {
365            id: "allocation_overhead_check".to_string(),
366            name: "Allocation Overhead Check".to_string(),
367            description: "Ensure allocation tracking overhead is within acceptable limits"
368                .to_string(),
369            category: RuleCategory::Performance,
370            severity: ValidationSeverity::Warning,
371            enabled: true,
372            validator: validate_allocation_overhead,
373        });
374
375        // Performance rules
376        self.add_rule(ValidationRule {
377            id: "tracking_latency_check".to_string(),
378            name: "Tracking Latency Check".to_string(),
379            description: "Verify allocation tracking latency is acceptable".to_string(),
380            category: RuleCategory::Performance,
381            severity: ValidationSeverity::Error,
382            enabled: true,
383            validator: validate_tracking_latency,
384        });
385
386        self.add_rule(ValidationRule {
387            id: "symbol_resolution_performance".to_string(),
388            name: "Symbol Resolution Performance".to_string(),
389            description: "Check symbol resolution performance metrics".to_string(),
390            category: RuleCategory::Performance,
391            severity: ValidationSeverity::Warning,
392            enabled: true,
393            validator: validate_symbol_performance,
394        });
395
396        // Error handling rules
397        self.add_rule(ValidationRule {
398            id: "error_handling_coverage".to_string(),
399            name: "Error Handling Coverage".to_string(),
400            description: "Ensure proper error handling in critical paths".to_string(),
401            category: RuleCategory::ErrorHandling,
402            severity: ValidationSeverity::Error,
403            enabled: true,
404            validator: validate_error_handling,
405        });
406
407        // Thread safety rules
408        self.add_rule(ValidationRule {
409            id: "thread_safety_check".to_string(),
410            name: "Thread Safety Check".to_string(),
411            description: "Verify thread safety of concurrent operations".to_string(),
412            category: RuleCategory::ThreadSafety,
413            severity: ValidationSeverity::Critical,
414            enabled: true,
415            validator: validate_thread_safety,
416        });
417    }
418
419    #[allow(dead_code)]
420    fn execute_rule(
421        &self,
422        rule: &ValidationRule,
423        context: &ValidationContext,
424    ) -> Result<(), ValidationError> {
425        (rule.validator)(context)
426    }
427
428    fn update_rule_stats(&mut self, rule_id: &str, duration: Duration, failed: bool) {
429        let stats = self
430            .rule_stats
431            .entry(rule_id.to_string())
432            .or_insert(RuleStats {
433                execution_count: 0,
434                total_time: Duration::ZERO,
435                failure_count: 0,
436                avg_time: Duration::ZERO,
437            });
438
439        stats.execution_count += 1;
440        stats.total_time += duration;
441        if failed {
442            stats.failure_count += 1;
443        }
444        stats.avg_time = stats.total_time / stats.execution_count as u32;
445    }
446
447    fn calculate_summary(&self, results: &[RuleResult]) -> ValidationSummary {
448        let total_rules = results.len();
449        let passed_rules = results.iter().filter(|r| r.passed).count();
450        let failed_rules = total_rules - passed_rules;
451
452        let mut critical_issues = 0;
453        let mut errors = 0;
454        let mut warnings = 0;
455
456        for result in results {
457            if !result.passed {
458                if let Some(rule) = self.rules.iter().find(|r| r.id == result.rule_id) {
459                    match rule.severity {
460                        ValidationSeverity::Critical => critical_issues += 1,
461                        ValidationSeverity::Error => errors += 1,
462                        ValidationSeverity::Warning => warnings += 1,
463                        _ => {}
464                    }
465                }
466            }
467        }
468
469        let quality_score = if total_rules > 0 {
470            passed_rules as f64 / total_rules as f64
471        } else {
472            1.0
473        };
474
475        ValidationSummary {
476            total_rules,
477            passed_rules,
478            failed_rules,
479            critical_issues,
480            errors,
481            warnings,
482            quality_score,
483        }
484    }
485
486    fn determine_status(&self, summary: &ValidationSummary) -> ValidationStatus {
487        if summary.critical_issues > 0 {
488            ValidationStatus::CriticalIssuesFound
489        } else if summary.errors > 0 {
490            ValidationStatus::ErrorsFound
491        } else if summary.warnings > 0 {
492            ValidationStatus::WarningsFound
493        } else {
494            ValidationStatus::Passed
495        }
496    }
497}
498
499// Validation rule implementations
500fn validate_memory_leaks(context: &ValidationContext) -> Result<(), ValidationError> {
501    if context.memory_info.growth_rate > 10.0 * 1024.0 * 1024.0 {
502        // 10MB/sec
503        return Err(ValidationError {
504            message: format!(
505                "High memory growth rate detected: {:.2}MB/sec",
506                context.memory_info.growth_rate / (1024.0 * 1024.0)
507            ),
508            suggestion: Some("Check for memory leaks in allocation tracking".to_string()),
509            location: Some(context.operation_name.clone()),
510        });
511    }
512
513    if context.memory_info.fragmentation_ratio > 0.5 {
514        return Err(ValidationError {
515            message: format!(
516                "High memory fragmentation: {:.1}%",
517                context.memory_info.fragmentation_ratio * 100.0
518            ),
519            suggestion: Some("Consider implementing memory compaction".to_string()),
520            location: Some(context.operation_name.clone()),
521        });
522    }
523
524    Ok(())
525}
526
527fn validate_allocation_overhead(context: &ValidationContext) -> Result<(), ValidationError> {
528    let overhead_ratio =
529        context.metrics.peak_memory as f64 / (context.memory_info.current_usage as f64).max(1.0);
530
531    if overhead_ratio > 0.1 {
532        // 10% overhead threshold
533        return Err(ValidationError {
534            message: format!("High tracking overhead: {:.1}%", overhead_ratio * 100.0),
535            suggestion: Some(
536                "Optimize tracking data structures to reduce memory overhead".to_string(),
537            ),
538            location: Some(context.operation_name.clone()),
539        });
540    }
541
542    Ok(())
543}
544
545fn validate_tracking_latency(context: &ValidationContext) -> Result<(), ValidationError> {
546    if context.metrics.avg_duration > Duration::from_micros(100) {
547        return Err(ValidationError {
548            message: format!(
549                "High tracking latency: {:.2}µs",
550                context.metrics.avg_duration.as_micros()
551            ),
552            suggestion: Some("Optimize allocation tracking path for lower latency".to_string()),
553            location: Some(context.operation_name.clone()),
554        });
555    }
556
557    Ok(())
558}
559
560fn validate_symbol_performance(context: &ValidationContext) -> Result<(), ValidationError> {
561    if context.operation_name.contains("symbol")
562        && context.metrics.avg_duration > Duration::from_millis(10)
563    {
564        return Err(ValidationError {
565            message: format!(
566                "Slow symbol resolution: {:.2}ms",
567                context.metrics.avg_duration.as_millis()
568            ),
569            suggestion: Some("Consider symbol caching or preloading".to_string()),
570            location: Some(context.operation_name.clone()),
571        });
572    }
573
574    Ok(())
575}
576
577fn validate_error_handling(context: &ValidationContext) -> Result<(), ValidationError> {
578    let coverage_ratio = if context.error_handling.error_points > 0 {
579        context.error_handling.handled_error_points as f64
580            / context.error_handling.error_points as f64
581    } else {
582        1.0
583    };
584
585    if coverage_ratio < 0.9 {
586        return Err(ValidationError {
587            message: format!(
588                "Low error handling coverage: {:.1}%",
589                coverage_ratio * 100.0
590            ),
591            suggestion: Some("Add error handling for unhandled error points".to_string()),
592            location: Some(context.operation_name.clone()),
593        });
594    }
595
596    if !context.error_handling.has_recovery && context.operation_name.contains("critical") {
597        return Err(ValidationError {
598            message: "Critical operation lacks recovery mechanism".to_string(),
599            suggestion: Some("Implement recovery strategies for critical operations".to_string()),
600            location: Some(context.operation_name.clone()),
601        });
602    }
603
604    Ok(())
605}
606
607fn validate_thread_safety(context: &ValidationContext) -> Result<(), ValidationError> {
608    if context.thread_safety.shared_resources > 0 && !context.thread_safety.is_thread_safe {
609        return Err(ValidationError {
610            message: "Operation accesses shared resources without thread safety".to_string(),
611            suggestion: Some("Add proper synchronization for shared resource access".to_string()),
612            location: Some(context.operation_name.clone()),
613        });
614    }
615
616    if context.thread_safety.contention_level > 0.3 {
617        return Err(ValidationError {
618            message: format!(
619                "High lock contention: {:.1}%",
620                context.thread_safety.contention_level * 100.0
621            ),
622            suggestion: Some(
623                "Consider lock-free alternatives or finer-grained locking".to_string(),
624            ),
625            location: Some(context.operation_name.clone()),
626        });
627    }
628
629    Ok(())
630}
631
632impl Default for ValidationConfig {
633    fn default() -> Self {
634        Self {
635            fail_fast: false,
636            max_validation_time: Duration::from_secs(10),
637            enable_deep_checks: true,
638            min_severity: ValidationSeverity::Info,
639        }
640    }
641}
642
643impl Default for QualityValidator {
644    fn default() -> Self {
645        Self::new()
646    }
647}
648
649#[cfg(test)]
650mod tests {
651    use super::*;
652
653    #[test]
654    fn test_validator_creation() {
655        let validator = QualityValidator::new();
656        assert!(!validator.rules.is_empty());
657        assert!(validator.rule_stats.is_empty());
658    }
659
660    #[test]
661    fn test_rule_management() {
662        let mut validator = QualityValidator::new();
663        let initial_count = validator.rules.len();
664
665        let custom_rule = ValidationRule {
666            id: "test_rule".to_string(),
667            name: "Test Rule".to_string(),
668            description: "Test rule description".to_string(),
669            category: RuleCategory::CodeStyle,
670            severity: ValidationSeverity::Info,
671            enabled: true,
672            validator: |_| Ok(()),
673        };
674
675        validator.add_rule(custom_rule);
676        assert_eq!(validator.rules.len(), initial_count + 1);
677
678        assert!(validator.remove_rule("test_rule"));
679        assert_eq!(validator.rules.len(), initial_count);
680        assert!(!validator.remove_rule("nonexistent_rule"));
681    }
682
683    #[test]
684    fn test_validation_context() {
685        let context = ValidationContext {
686            operation_name: "test_operation".to_string(),
687            metrics: OperationMetrics {
688                avg_duration: Duration::from_micros(50),
689                peak_memory: 1024,
690                success_rate: 0.95,
691                allocation_count: 100,
692                cpu_usage: 5.0,
693            },
694            memory_info: MemoryInfo {
695                current_usage: 1024 * 1024,
696                peak_usage: 2 * 1024 * 1024,
697                active_allocations: 100,
698                fragmentation_ratio: 0.1,
699                growth_rate: 0.0,
700            },
701            error_handling: ErrorHandlingInfo {
702                has_error_handling: true,
703                error_points: 10,
704                handled_error_points: 9,
705                has_recovery: true,
706            },
707            thread_safety: ThreadSafetyInfo {
708                is_thread_safe: true,
709                shared_resources: 2,
710                has_synchronization: true,
711                contention_level: 0.1,
712            },
713        };
714
715        let mut validator = QualityValidator::new();
716        let result = validator.validate(&context);
717
718        assert!(matches!(
719            result.status,
720            ValidationStatus::Passed | ValidationStatus::WarningsFound
721        ));
722        assert!(result.summary.quality_score >= 0.0);
723        assert!(result.summary.quality_score <= 1.0);
724    }
725}