Skip to main content

memscope_rs/analysis/detectors/
mod.rs

1//! Memory Analysis Detectors Module
2//!
3//! This module provides a unified interface for memory issue detection.
4//! All detectors implement the `Detector` trait and can be registered
5//! with the `AnalysisManager` for comprehensive memory analysis.
6//!
7//! # Architecture
8//!
9//! The detector system is built around the following core concepts:
10//!
11//! - **Detector Trait**: Base trait that all detectors must implement
12//! - **DetectionResult**: Standardized output format for all detectors
13//! - **Issue**: Unified issue representation with severity and category
14//! - **DetectorConfig**: Configuration interface for detector customization
15//!
16//! # Example
17//!
18//! ```rust,ignore
19//! use memscope_rs::analysis::detectors::{Detector, LeakDetector, LeakDetectorConfig};
20//!
21//! fn main() {
22//!     let config = LeakDetectorConfig::default();
23//!     let detector = LeakDetector::new(config);
24//!
25//!     let allocations = vec![];
26//!     let result = detector.detect(&allocations);
27//!
28//!     println!("Found {} issues", result.issues.len());
29//! }
30//! ```
31//!
32//! # Available Detectors
33//!
34//! - [`LeakDetector`](leak_detector::LeakDetector) - Memory leak detection
35//! - [`UafDetector`](uaf_detector::UafDetector) - Use-after-free detection
36//! - [`OverflowDetector`](overflow_detector::OverflowDetector) - Buffer overflow detection
37//! - [`SafetyDetector`](safety_detector::SafetyDetector) - Unified safety violations
38//! - [`LifecycleDetector`](lifecycle_detector::LifecycleDetector) - Lifecycle pattern detection
39//! - [`DoubleFreeDetector`](double_free_detector::DoubleFreeDetector) - Double-free detection
40//! - [`DataRaceDetector`](data_race_detector::DataRaceDetector) - Data race detection
41
42pub mod types;
43
44// Detector implementations
45pub mod data_race_detector;
46pub mod double_free_detector;
47pub mod leak_detector;
48pub mod lifecycle_detector;
49pub mod overflow_detector;
50pub mod safety_detector;
51pub mod uaf_detector;
52
53// Re-export core types
54pub use types::{
55    DetectionResult, DetectionStatistics, DetectorConfig, DetectorError, Issue, IssueCategory,
56    IssueSeverity, Location,
57};
58
59// Re-export detectors
60pub use data_race_detector::{DataRaceConfig, DataRaceDetector, DataRaceDetectorWithEvents};
61pub use double_free_detector::{
62    DoubleFreeConfig, DoubleFreeDetector, DoubleFreeDetectorWithEvents,
63};
64pub use leak_detector::{LeakDetector, LeakDetectorConfig};
65pub use lifecycle_detector::{LifecycleDetector, LifecycleDetectorConfig};
66pub use overflow_detector::{OverflowDetector, OverflowDetectorConfig};
67pub use safety_detector::{SafetyDetector, SafetyDetectorConfig};
68pub use uaf_detector::{UafDetector, UafDetectorConfig};
69
70use crate::capture::types::AllocationInfo;
71use std::fmt;
72
73/// Base trait for all memory detectors
74///
75/// All detectors must implement this trait to provide a unified interface
76/// for memory issue detection.
77///
78/// # Required Methods
79///
80/// - [`name()`](Detector::name) - Returns the detector name
81/// - [`version()`](Detector::version) - Returns the detector version
82/// - [`detect()`](Detector::detect) - Performs detection on allocations
83/// - [`config()`](Detector::config) - Returns the current configuration
84/// - [`update_config()`](Detector::update_config) - Updates the configuration
85///
86/// # Example
87///
88/// ```rust,ignore
89/// use memscope_rs::analysis::detectors::{Detector, DetectionResult, DetectorConfig};
90/// use memscope_rs::capture::types::AllocationInfo;
91///
92/// struct MyDetector {
93///     config: DetectorConfig,
94/// }
95///
96/// impl Detector for MyDetector {
97///     fn name(&self) -> &str {
98///         "MyDetector"
99///     }
100///
101///     fn version(&self) -> &str {
102///         "1.0.0"
103///     }
104///
105///     fn detect(&self, allocations: &[AllocationInfo]) -> DetectionResult {
106///         DetectionResult {
107///             detector_name: self.name().to_string(),
108///             issues: vec![],
109///             statistics: DetectionStatistics::default(),
110///             detection_time_ms: 0,
111///         }
112///     }
113///
114///     fn config(&self) -> &DetectorConfig {
115///         &self.config
116///     }
117///
118///     fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
119///         self.config = config;
120///         Ok(())
121///     }
122/// }
123/// ```
124pub trait Detector: Send + Sync + std::fmt::Debug {
125    /// Get detector name
126    ///
127    /// Returns a unique identifier for this detector.
128    fn name(&self) -> &str;
129
130    /// Get detector version
131    ///
132    /// Returns the version string following semantic versioning.
133    fn version(&self) -> &str;
134
135    /// Detect issues in allocations
136    ///
137    /// Analyzes the provided allocations and returns detected issues.
138    ///
139    /// # Arguments
140    ///
141    /// * `allocations` - Slice of allocation information to analyze
142    ///
143    /// # Returns
144    ///
145    /// A `DetectionResult` containing detected issues and statistics.
146    ///
147    /// # Example
148    ///
149    /// ```rust,ignore
150    /// use memscope_rs::analysis::detectors::Detector;
151    ///
152    /// fn analyze_leaks<D: Detector>(detector: &D, allocations: &[AllocationInfo]) {
153    ///     let result = detector.detect(allocations);
154    ///     for issue in result.issues {
155    ///         println!("Found issue: {}", issue.description);
156    ///     }
157    /// }
158    /// ```
159    fn detect(&self, allocations: &[AllocationInfo]) -> DetectionResult;
160
161    /// Get configuration
162    ///
163    /// Returns a reference to the current detector configuration.
164    fn config(&self) -> &DetectorConfig;
165
166    /// Update configuration
167    ///
168    /// Updates the detector configuration.
169    ///
170    /// # Arguments
171    ///
172    /// * `config` - New configuration to apply
173    ///
174    /// # Errors
175    ///
176    /// Returns a `DetectorError` if the configuration is invalid.
177    ///
178    /// # Example
179    ///
180    /// ```rust,ignore
181    /// use memscope_rs::analysis::detectors::{Detector, DetectorConfig};
182    ///
183    /// fn configure_detector<D: Detector>(detector: &mut D) -> Result<(), DetectorError> {
184    ///     let new_config = DetectorConfig {
185    ///         enabled: true,
186    ///         max_reported_issues: 100,
187    ///         ..Default::default()
188    ///     };
189    ///     detector.update_config(new_config)
190    /// }
191    /// ```
192    fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError>;
193}
194
195/// Detector registry for managing multiple detectors
196///
197/// Provides functionality to register, unregister, and run detectors.
198#[derive(Debug, Default)]
199pub struct DetectorRegistry {
200    detectors: Vec<Box<dyn Detector>>,
201}
202
203impl DetectorRegistry {
204    /// Create a new detector registry
205    pub fn new() -> Self {
206        Self::default()
207    }
208
209    /// Register a detector
210    ///
211    /// # Arguments
212    ///
213    /// * `detector` - Boxed detector to register
214    ///
215    /// # Example
216    ///
217    /// ```rust
218    /// use memscope_rs::analysis::detectors::{DetectorRegistry, LeakDetector, LeakDetectorConfig};
219    ///
220    /// let mut registry = DetectorRegistry::new();
221    /// let detector = LeakDetector::new(LeakDetectorConfig::default());
222    /// registry.register(Box::new(detector));
223    /// ```
224    pub fn register(&mut self, detector: Box<dyn Detector>) {
225        self.detectors.push(detector);
226    }
227
228    /// Unregister a detector by name
229    ///
230    /// # Arguments
231    ///
232    /// * `name` - Name of the detector to unregister
233    ///
234    /// # Returns
235    ///
236    /// `true` if a detector was found and removed, `false` otherwise.
237    pub fn unregister(&mut self, name: &str) -> bool {
238        let initial_len = self.detectors.len();
239        self.detectors.retain(|d| d.name() != name);
240        self.detectors.len() < initial_len
241    }
242
243    /// Get detector by name
244    ///
245    /// # Arguments
246    ///
247    /// * `name` - Name of the detector to retrieve
248    ///
249    /// # Returns
250    ///
251    /// Option reference to the detector if found.
252    pub fn get_detector(&self, name: &str) -> Option<&dyn Detector> {
253        self.detectors
254            .iter()
255            .find(|d| d.name() == name)
256            .map(|d| d.as_ref())
257    }
258
259    /// Run all registered detectors
260    ///
261    /// # Arguments
262    ///
263    /// * `allocations` - Allocations to analyze
264    ///
265    /// # Returns
266    ///
267    /// Vector of detection results from all detectors.
268    ///
269    /// # Example
270    ///
271    /// ```rust,ignore
272    /// use memscope_rs::analysis::detectors::DetectorRegistry;
273    ///
274    /// fn run_all_analysis(registry: &DetectorRegistry, allocations: &[AllocationInfo]) {
275    ///     let results = registry.run_all(allocations);
276    ///     for result in results {
277    ///         println!("{} found {} issues", result.detector_name, result.issues.len());
278    ///     }
279    /// }
280    /// ```
281    pub fn run_all(&self, allocations: &[AllocationInfo]) -> Vec<DetectionResult> {
282        self.detectors
283            .iter()
284            .map(|detector| detector.detect(allocations))
285            .collect()
286    }
287
288    /// Run a specific detector by name
289    ///
290    /// # Arguments
291    ///
292    /// * `name` - Name of the detector to run
293    /// * `allocations` - Allocations to analyze
294    ///
295    /// # Returns
296    ///
297    /// Option of detection result if the detector was found.
298    pub fn run_detector(
299        &self,
300        name: &str,
301        allocations: &[AllocationInfo],
302    ) -> Option<DetectionResult> {
303        self.get_detector(name)
304            .map(|detector| detector.detect(allocations))
305    }
306
307    /// Get all registered detector names
308    ///
309    /// # Returns
310    ///
311    /// Vector of detector names.
312    pub fn detector_names(&self) -> Vec<&str> {
313        self.detectors.iter().map(|d| d.name()).collect()
314    }
315
316    /// Get count of registered detectors
317    ///
318    /// # Returns
319    ///
320    /// Number of registered detectors.
321    pub fn len(&self) -> usize {
322        self.detectors.len()
323    }
324
325    /// Check if registry is empty
326    ///
327    /// # Returns
328    ///
329    /// `true` if no detectors are registered.
330    pub fn is_empty(&self) -> bool {
331        self.detectors.is_empty()
332    }
333}
334
335impl fmt::Display for DetectorRegistry {
336    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337        write!(
338            f,
339            "DetectorRegistry({} detectors: {})",
340            self.len(),
341            self.detector_names().join(", ")
342        )
343    }
344}
345
346#[cfg(test)]
347mod tests {
348    use super::*;
349    use crate::capture::types::AllocationInfo;
350
351    #[test]
352    fn test_detector_registry_new() {
353        let registry = DetectorRegistry::new();
354        assert!(registry.is_empty());
355        assert_eq!(registry.len(), 0);
356    }
357
358    #[test]
359    fn test_detector_registry_register() {
360        let mut registry = DetectorRegistry::new();
361        assert_eq!(registry.len(), 0);
362
363        // Create a simple test detector
364        #[derive(Debug)]
365        struct TestDetector {
366            config: DetectorConfig,
367        }
368
369        impl Detector for TestDetector {
370            fn name(&self) -> &str {
371                "TestDetector"
372            }
373
374            fn version(&self) -> &str {
375                "1.0.0"
376            }
377
378            fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
379                DetectionResult {
380                    detector_name: self.name().to_string(),
381                    issues: vec![],
382                    statistics: DetectionStatistics::default(),
383                    detection_time_ms: 0,
384                }
385            }
386
387            fn config(&self) -> &DetectorConfig {
388                &self.config
389            }
390
391            fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
392                self.config = config;
393                Ok(())
394            }
395        }
396
397        registry.register(Box::new(TestDetector {
398            config: DetectorConfig::default(),
399        }));
400        assert_eq!(registry.len(), 1);
401    }
402
403    #[test]
404    fn test_detector_registry_unregister() {
405        let mut registry = DetectorRegistry::new();
406
407        #[derive(Debug)]
408        struct TestDetector {
409            config: DetectorConfig,
410        }
411
412        impl Detector for TestDetector {
413            fn name(&self) -> &str {
414                "TestDetector"
415            }
416
417            fn version(&self) -> &str {
418                "1.0.0"
419            }
420
421            fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
422                DetectionResult {
423                    detector_name: self.name().to_string(),
424                    issues: vec![],
425                    statistics: DetectionStatistics::default(),
426                    detection_time_ms: 0,
427                }
428            }
429
430            fn config(&self) -> &DetectorConfig {
431                &self.config
432            }
433
434            fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
435                self.config = config;
436                Ok(())
437            }
438        }
439
440        registry.register(Box::new(TestDetector {
441            config: DetectorConfig::default(),
442        }));
443        assert_eq!(registry.len(), 1);
444
445        let removed = registry.unregister("TestDetector");
446        assert!(removed);
447        assert_eq!(registry.len(), 0);
448
449        let not_removed = registry.unregister("NonExistent");
450        assert!(!not_removed);
451    }
452
453    #[test]
454    fn test_detector_registry_get_detector() {
455        let mut registry = DetectorRegistry::new();
456
457        #[derive(Debug)]
458        struct TestDetector {
459            config: DetectorConfig,
460        }
461
462        impl Detector for TestDetector {
463            fn name(&self) -> &str {
464                "TestDetector"
465            }
466
467            fn version(&self) -> &str {
468                "1.0.0"
469            }
470
471            fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
472                DetectionResult {
473                    detector_name: self.name().to_string(),
474                    issues: vec![],
475                    statistics: DetectionStatistics::default(),
476                    detection_time_ms: 0,
477                }
478            }
479
480            fn config(&self) -> &DetectorConfig {
481                &self.config
482            }
483
484            fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
485                self.config = config;
486                Ok(())
487            }
488        }
489
490        registry.register(Box::new(TestDetector {
491            config: DetectorConfig::default(),
492        }));
493
494        let detector = registry.get_detector("TestDetector");
495        assert!(detector.is_some());
496        assert_eq!(detector.unwrap().name(), "TestDetector");
497
498        let not_found = registry.get_detector("NonExistent");
499        assert!(not_found.is_none());
500    }
501
502    #[test]
503    fn test_detector_registry_run_all() {
504        let mut registry = DetectorRegistry::new();
505
506        #[derive(Debug)]
507        struct TestDetector {
508            config: DetectorConfig,
509        }
510
511        impl Detector for TestDetector {
512            fn name(&self) -> &str {
513                "TestDetector"
514            }
515
516            fn version(&self) -> &str {
517                "1.0.0"
518            }
519
520            fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
521                DetectionResult {
522                    detector_name: self.name().to_string(),
523                    issues: vec![],
524                    statistics: DetectionStatistics::default(),
525                    detection_time_ms: 0,
526                }
527            }
528
529            fn config(&self) -> &DetectorConfig {
530                &self.config
531            }
532
533            fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
534                self.config = config;
535                Ok(())
536            }
537        }
538
539        registry.register(Box::new(TestDetector {
540            config: DetectorConfig::default(),
541        }));
542        let allocations = vec![
543            AllocationInfo::new(0x1000, 1024),
544            AllocationInfo::new(0x2000, 2048),
545        ];
546
547        let results = registry.run_all(&allocations);
548        assert_eq!(results.len(), 1);
549        assert_eq!(results[0].detector_name, "TestDetector");
550    }
551
552    #[test]
553    fn test_detector_registry_run_detector() {
554        let mut registry = DetectorRegistry::new();
555
556        #[derive(Debug)]
557        struct TestDetector {
558            config: DetectorConfig,
559        }
560
561        impl Detector for TestDetector {
562            fn name(&self) -> &str {
563                "TestDetector"
564            }
565
566            fn version(&self) -> &str {
567                "1.0.0"
568            }
569
570            fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
571                DetectionResult {
572                    detector_name: self.name().to_string(),
573                    issues: vec![],
574                    statistics: DetectionStatistics::default(),
575                    detection_time_ms: 0,
576                }
577            }
578
579            fn config(&self) -> &DetectorConfig {
580                &self.config
581            }
582
583            fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
584                self.config = config;
585                Ok(())
586            }
587        }
588
589        registry.register(Box::new(TestDetector {
590            config: DetectorConfig::default(),
591        }));
592
593        let allocations = vec![AllocationInfo::new(0x1000, 1024)];
594
595        let result = registry.run_detector("TestDetector", &allocations);
596        assert!(result.is_some());
597        assert_eq!(result.unwrap().detector_name, "TestDetector");
598
599        let not_found = registry.run_detector("NonExistent", &allocations);
600        assert!(not_found.is_none());
601    }
602
603    #[test]
604    fn test_detector_registry_detector_names() {
605        let mut registry = DetectorRegistry::new();
606
607        #[derive(Debug)]
608        struct TestDetector1 {
609            config: DetectorConfig,
610        }
611
612        impl Detector for TestDetector1 {
613            fn name(&self) -> &str {
614                "TestDetector1"
615            }
616
617            fn version(&self) -> &str {
618                "1.0.0"
619            }
620
621            fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
622                DetectionResult {
623                    detector_name: self.name().to_string(),
624                    issues: vec![],
625                    statistics: DetectionStatistics::default(),
626                    detection_time_ms: 0,
627                }
628            }
629
630            fn config(&self) -> &DetectorConfig {
631                &self.config
632            }
633
634            fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
635                self.config = config;
636                Ok(())
637            }
638        }
639
640        #[derive(Debug)]
641        struct TestDetector2 {
642            config: DetectorConfig,
643        }
644
645        impl Detector for TestDetector2 {
646            fn name(&self) -> &str {
647                "TestDetector2"
648            }
649
650            fn version(&self) -> &str {
651                "1.0.0"
652            }
653
654            fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
655                DetectionResult {
656                    detector_name: self.name().to_string(),
657                    issues: vec![],
658                    statistics: DetectionStatistics::default(),
659                    detection_time_ms: 0,
660                }
661            }
662
663            fn config(&self) -> &DetectorConfig {
664                &self.config
665            }
666
667            fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
668                self.config = config;
669                Ok(())
670            }
671        }
672
673        registry.register(Box::new(TestDetector1 {
674            config: DetectorConfig::default(),
675        }));
676        registry.register(Box::new(TestDetector2 {
677            config: DetectorConfig::default(),
678        }));
679
680        let names = registry.detector_names();
681        assert_eq!(names.len(), 2);
682        assert!(names.contains(&"TestDetector1"));
683        assert!(names.contains(&"TestDetector2"));
684    }
685
686    #[test]
687    fn test_detector_registry_display() {
688        let registry = DetectorRegistry::new();
689        let display = format!("{}", registry);
690        assert!(display.contains("DetectorRegistry"));
691        assert!(display.contains("0 detectors"));
692
693        let mut registry = DetectorRegistry::new();
694
695        #[derive(Debug)]
696        struct TestDetector {
697            config: DetectorConfig,
698        }
699
700        impl Detector for TestDetector {
701            fn name(&self) -> &str {
702                "TestDetector"
703            }
704
705            fn version(&self) -> &str {
706                "1.0.0"
707            }
708
709            fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
710                DetectionResult {
711                    detector_name: self.name().to_string(),
712                    issues: vec![],
713                    statistics: DetectionStatistics::default(),
714                    detection_time_ms: 0,
715                }
716            }
717
718            fn config(&self) -> &DetectorConfig {
719                &self.config
720            }
721
722            fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
723                self.config = config;
724                Ok(())
725            }
726        }
727
728        registry.register(Box::new(TestDetector {
729            config: DetectorConfig::default(),
730        }));
731
732        let display = format!("{}", registry);
733        assert!(display.contains("DetectorRegistry"));
734        assert!(display.contains("1 detectors"));
735        assert!(display.contains("TestDetector"));
736    }
737}