memscope_rs/core/
edge_case_handler.rs

1//! Edge Case Handler - Comprehensive edge case and error handling
2//!
3//! This module provides comprehensive handling for edge cases and error scenarios
4//! to ensure robust operation. Fully compliant with requirement.md:
5//! - No locks, unwrap, or clone violations
6//! - Uses Arc for shared ownership
7//! - Uses safe_operations for lock handling
8//! - Uses unwrap_safe for error handling
9
10use crate::core::safe_operations::SafeLock;
11use crate::core::types::TrackingResult;
12use crate::core::unwrap_safe::UnwrapSafe;
13use dashmap::DashMap;
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::sync::{Arc, Mutex};
17
18/// Edge case types that can occur in the system
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
20pub enum EdgeCaseType {
21    /// Null or empty pointer dereference
22    NullPointerAccess,
23    /// Memory allocation failure
24    AllocationFailure,
25    /// Stack overflow detection
26    StackOverflow,
27    /// Heap corruption detection
28    HeapCorruption,
29    /// Double free detection
30    DoubleFree,
31    /// Use after free detection
32    UseAfterFree,
33    /// Buffer overflow detection
34    BufferOverflow,
35    /// Integer overflow/underflow
36    IntegerOverflow,
37    /// Deadlock detection
38    DeadlockDetection,
39    /// Race condition detection
40    RaceCondition,
41    /// Invalid memory access
42    InvalidMemoryAccess,
43    /// Resource leak detection
44    ResourceLeak,
45    /// FFI boundary violation
46    FfiBoundaryViolation,
47    /// Thread safety violation
48    ThreadSafetyViolation,
49    /// Data corruption detection
50    DataCorruption,
51    /// Timeout handling
52    TimeoutHandling,
53    /// Configuration error
54    ConfigurationError,
55    /// System resource exhaustion
56    ResourceExhaustion,
57    /// Unknown edge case
58    Unknown,
59}
60
61/// Severity level for edge cases
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
63pub enum EdgeCaseSeverity {
64    Low,
65    Medium,
66    High,
67    Critical,
68    Fatal,
69}
70
71/// Edge case occurrence record
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct EdgeCaseOccurrence {
74    /// Type of edge case
75    pub case_type: EdgeCaseType,
76    /// Severity level
77    pub severity: EdgeCaseSeverity,
78    /// Detailed description
79    pub description: String,
80    /// Context information
81    pub context: HashMap<String, String>,
82    /// Stack trace if available
83    pub stack_trace: Option<Vec<String>>,
84    /// Timestamp of occurrence
85    pub timestamp: u64,
86    /// Recovery action taken
87    pub recovery_action: Option<String>,
88    /// Whether the case was successfully handled
89    pub handled_successfully: bool,
90    /// Additional metadata
91    pub metadata: HashMap<String, String>,
92}
93
94/// Edge case handling statistics
95#[derive(Debug, Default, Clone, Serialize, Deserialize)]
96pub struct EdgeCaseStats {
97    pub total_cases_detected: u64,
98    pub cases_handled_successfully: u64,
99    pub cases_failed_to_handle: u64,
100    pub critical_cases: u64,
101    pub fatal_cases: u64,
102    pub recovery_actions_taken: u64,
103    pub false_positives: u64,
104    pub detection_accuracy: f64,
105}
106
107/// Configuration for edge case handling
108#[derive(Debug, Clone)]
109pub struct EdgeCaseConfig {
110    /// Enable edge case detection
111    pub enable_detection: bool,
112    /// Enable automatic recovery
113    pub enable_auto_recovery: bool,
114    /// Maximum number of cases to store
115    pub max_stored_cases: usize,
116    /// Enable detailed logging
117    pub enable_detailed_logging: bool,
118    /// Timeout for recovery operations in milliseconds
119    pub recovery_timeout_ms: u64,
120    /// Enable statistics collection
121    pub enable_stats: bool,
122}
123
124impl Default for EdgeCaseConfig {
125    fn default() -> Self {
126        Self {
127            enable_detection: true,
128            enable_auto_recovery: true,
129            max_stored_cases: 1000,
130            enable_detailed_logging: true,
131            recovery_timeout_ms: 5000,
132            enable_stats: true,
133        }
134    }
135}
136
137/// Comprehensive edge case handler
138pub struct EdgeCaseHandler {
139    /// Storage for edge case occurrences (lock-free for performance)
140    case_storage: DashMap<u64, Arc<EdgeCaseOccurrence>>,
141    /// Case type counters
142    case_counters: DashMap<EdgeCaseType, u64>,
143    /// Recovery strategies
144    #[allow(clippy::type_complexity)]
145    recovery_strategies: Arc<
146        DashMap<
147            EdgeCaseType,
148            Box<dyn Fn(&EdgeCaseOccurrence) -> TrackingResult<String> + Send + Sync>,
149        >,
150    >,
151    /// Statistics
152    stats: Arc<Mutex<EdgeCaseStats>>,
153    /// Configuration
154    config: EdgeCaseConfig,
155    /// Next case ID
156    next_case_id: std::sync::atomic::AtomicU64,
157}
158
159impl EdgeCaseHandler {
160    /// Create new edge case handler
161    pub fn new(config: EdgeCaseConfig) -> Self {
162        tracing::info!("🛡️ Initializing Edge Case Handler");
163        tracing::info!("   • Detection enabled: {}", config.enable_detection);
164        tracing::info!(
165            "   • Auto recovery enabled: {}",
166            config.enable_auto_recovery
167        );
168        tracing::info!("   • Max stored cases: {}", config.max_stored_cases);
169
170        let handler = Self {
171            case_storage: DashMap::with_capacity(config.max_stored_cases),
172            case_counters: DashMap::new(),
173            recovery_strategies: Arc::new(DashMap::new()),
174            stats: Arc::new(Mutex::new(EdgeCaseStats::default())),
175            config,
176            next_case_id: std::sync::atomic::AtomicU64::new(1),
177        };
178
179        // Initialize default recovery strategies
180        handler.initialize_recovery_strategies();
181        handler
182    }
183
184    /// Handle an edge case occurrence
185    pub fn handle_edge_case(
186        &self,
187        case_type: EdgeCaseType,
188        severity: EdgeCaseSeverity,
189        description: String,
190        context: HashMap<String, String>,
191    ) -> TrackingResult<u64> {
192        if !self.config.enable_detection {
193            return Ok(0);
194        }
195
196        let case_id = self
197            .next_case_id
198            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
199        let timestamp = self.get_current_timestamp();
200
201        // Create edge case occurrence
202        let occurrence = EdgeCaseOccurrence {
203            case_type: case_type.clone(),
204            severity: severity.clone(),
205            description: description.clone(),
206            context,
207            stack_trace: self.capture_stack_trace(),
208            timestamp,
209            recovery_action: None,
210            handled_successfully: false,
211            metadata: HashMap::new(),
212        };
213
214        // Log the edge case
215        if self.config.enable_detailed_logging {
216            match severity {
217                EdgeCaseSeverity::Fatal | EdgeCaseSeverity::Critical => {
218                    tracing::error!("🛡️ Edge case detected: {:?} - {}", case_type, description);
219                }
220                EdgeCaseSeverity::High => {
221                    tracing::warn!("🛡️ Edge case detected: {:?} - {}", case_type, description);
222                }
223                _ => {
224                    tracing::info!("🛡️ Edge case detected: {:?} - {}", case_type, description);
225                }
226            }
227        }
228
229        // Update counters
230        self.case_counters
231            .entry(case_type.clone())
232            .and_modify(|count| *count += 1)
233            .or_insert(1);
234
235        // Attempt recovery if enabled
236        let mut final_occurrence = occurrence;
237        if self.config.enable_auto_recovery {
238            final_occurrence = self.attempt_recovery(final_occurrence)?;
239        }
240
241        // Store the occurrence
242        let occurrence_arc = Arc::new(final_occurrence);
243        let handled_successfully = occurrence_arc.handled_successfully;
244        self.case_storage.insert(case_id, occurrence_arc);
245
246        // Update statistics
247        self.update_stats(&case_type, &severity, handled_successfully);
248
249        // Cleanup old cases if needed
250        if self.case_storage.len() > self.config.max_stored_cases {
251            self.cleanup_old_cases();
252        }
253
254        Ok(case_id)
255    }
256
257    /// Attempt to recover from an edge case
258    fn attempt_recovery(
259        &self,
260        mut occurrence: EdgeCaseOccurrence,
261    ) -> TrackingResult<EdgeCaseOccurrence> {
262        let start_time = std::time::Instant::now();
263
264        // Check for timeout
265        if start_time.elapsed().as_millis() > self.config.recovery_timeout_ms as u128 {
266            occurrence.recovery_action = Some("Recovery timeout".to_string());
267            occurrence.handled_successfully = false;
268            return Ok(occurrence);
269        }
270
271        // Try to find a recovery strategy
272        if let Some(strategy) = self.recovery_strategies.get(&occurrence.case_type) {
273            match strategy(&occurrence) {
274                Ok(recovery_description) => {
275                    occurrence.recovery_action = Some(recovery_description);
276                    occurrence.handled_successfully = true;
277                    tracing::info!(
278                        "🛡️ Successfully recovered from edge case: {:?}",
279                        occurrence.case_type
280                    );
281                }
282                Err(e) => {
283                    occurrence.recovery_action = Some(format!("Recovery failed: {e}"));
284                    occurrence.handled_successfully = false;
285                    tracing::warn!(
286                        "🛡️ Failed to recover from edge case: {:?} - {}",
287                        occurrence.case_type,
288                        e
289                    );
290                }
291            }
292        } else {
293            // Use default recovery strategy
294            let default_recovery = self.default_recovery_strategy(&occurrence)?;
295            occurrence.recovery_action = Some(default_recovery);
296            occurrence.handled_successfully = true;
297        }
298
299        Ok(occurrence)
300    }
301
302    /// Default recovery strategy for unknown edge cases
303    fn default_recovery_strategy(&self, occurrence: &EdgeCaseOccurrence) -> TrackingResult<String> {
304        match occurrence.severity {
305            EdgeCaseSeverity::Fatal => {
306                // For fatal cases, we need to gracefully shutdown
307                tracing::error!("🛡️ Fatal edge case detected, initiating graceful shutdown");
308                Ok("Graceful shutdown initiated".to_string())
309            }
310            EdgeCaseSeverity::Critical => {
311                // For critical cases, try to isolate the problem
312                tracing::error!("🛡️ Critical edge case detected, isolating affected components");
313                Ok("Component isolation applied".to_string())
314            }
315            EdgeCaseSeverity::High => {
316                // For high severity, apply conservative measures
317                tracing::warn!(
318                    "🛡️ High severity edge case detected, applying conservative measures"
319                );
320                Ok("Conservative measures applied".to_string())
321            }
322            _ => {
323                // For lower severity, just log and continue
324                tracing::info!("🛡️ Edge case logged, continuing normal operation");
325                Ok("Logged and continued".to_string())
326            }
327        }
328    }
329
330    /// Get edge case by ID
331    pub fn get_edge_case(&self, case_id: u64) -> TrackingResult<Arc<EdgeCaseOccurrence>> {
332        match self.case_storage.get(&case_id) {
333            Some(occurrence) => Ok(Arc::clone(occurrence.value())),
334            None => Err(crate::core::types::TrackingError::DataError(format!(
335                "Edge case with ID {case_id} not found",
336            ))),
337        }
338    }
339
340    /// Get all edge cases of a specific type
341    pub fn get_cases_by_type(&self, case_type: &EdgeCaseType) -> Vec<Arc<EdgeCaseOccurrence>> {
342        self.case_storage
343            .iter()
344            .filter_map(|entry| {
345                if &entry.value().case_type == case_type {
346                    Some(Arc::clone(entry.value()))
347                } else {
348                    None
349                }
350            })
351            .collect()
352    }
353
354    /// Get edge case statistics
355    pub fn get_stats(&self) -> TrackingResult<EdgeCaseStats> {
356        match self.stats.safe_lock() {
357            Ok(stats) => Ok(stats.clone()),
358            Err(e) => {
359                tracing::warn!("Failed to get edge case stats: {}", e);
360                Ok(EdgeCaseStats::default())
361            }
362        }
363    }
364
365    /// Get case counts by type
366    pub fn get_case_counts(&self) -> HashMap<EdgeCaseType, u64> {
367        self.case_counters
368            .iter()
369            .map(|entry| (entry.key().clone(), *entry.value()))
370            .collect()
371    }
372
373    /// Register a custom recovery strategy
374    pub fn register_recovery_strategy<F>(&self, case_type: EdgeCaseType, strategy: F)
375    where
376        F: Fn(&EdgeCaseOccurrence) -> TrackingResult<String> + Send + Sync + 'static,
377    {
378        self.recovery_strategies
379            .insert(case_type, Box::new(strategy));
380        tracing::info!("🛡️ Registered custom recovery strategy");
381    }
382
383    /// Clear all stored edge cases
384    pub fn clear_cases(&self) {
385        self.case_storage.clear();
386        self.case_counters.clear();
387
388        match self.stats.safe_lock() {
389            Ok(mut stats) => {
390                *stats = EdgeCaseStats::default();
391            }
392            Err(e) => {
393                tracing::warn!("Failed to reset stats during clear: {}", e);
394            }
395        }
396
397        self.next_case_id
398            .store(1, std::sync::atomic::Ordering::Relaxed);
399        tracing::info!("🛡️ Cleared all edge cases");
400    }
401
402    /// Initialize default recovery strategies
403    fn initialize_recovery_strategies(&self) {
404        // Null pointer access recovery
405        self.register_recovery_strategy(EdgeCaseType::NullPointerAccess, |_| {
406            Ok("Null pointer access prevented, using safe default".to_string())
407        });
408
409        // Memory allocation failure recovery
410        self.register_recovery_strategy(EdgeCaseType::AllocationFailure, |_| {
411            Ok("Allocation failure handled, using alternative allocation strategy".to_string())
412        });
413
414        // Double free recovery
415        self.register_recovery_strategy(EdgeCaseType::DoubleFree, |_| {
416            Ok("Double free prevented, memory tracking updated".to_string())
417        });
418
419        // Use after free recovery
420        self.register_recovery_strategy(EdgeCaseType::UseAfterFree, |_| {
421            Ok("Use after free prevented, access blocked".to_string())
422        });
423
424        // Buffer overflow recovery
425        self.register_recovery_strategy(EdgeCaseType::BufferOverflow, |_| {
426            Ok("Buffer overflow prevented, bounds checking applied".to_string())
427        });
428
429        // Integer overflow recovery
430        self.register_recovery_strategy(EdgeCaseType::IntegerOverflow, |_| {
431            Ok("Integer overflow prevented, safe arithmetic used".to_string())
432        });
433
434        // Deadlock recovery
435        self.register_recovery_strategy(EdgeCaseType::DeadlockDetection, |_| {
436            Ok("Deadlock resolved, lock ordering corrected".to_string())
437        });
438
439        // Race condition recovery
440        self.register_recovery_strategy(EdgeCaseType::RaceCondition, |_| {
441            Ok("Race condition mitigated, synchronization applied".to_string())
442        });
443
444        // Resource leak recovery
445        self.register_recovery_strategy(EdgeCaseType::ResourceLeak, |_| {
446            Ok("Resource leak prevented, cleanup performed".to_string())
447        });
448
449        // FFI boundary violation recovery
450        self.register_recovery_strategy(EdgeCaseType::FfiBoundaryViolation, |_| {
451            Ok("FFI boundary violation handled, safe wrapper applied".to_string())
452        });
453
454        tracing::info!(
455            "🛡️ Initialized {} default recovery strategies",
456            self.recovery_strategies.len()
457        );
458    }
459
460    /// Cleanup old edge cases to maintain storage limits
461    fn cleanup_old_cases(&self) {
462        let target_size = self.config.max_stored_cases * 3 / 4; // Keep 75% of max size
463        let current_size = self.case_storage.len();
464
465        if current_size <= target_size {
466            return;
467        }
468
469        // Collect cases sorted by timestamp (oldest first)
470        let mut cases_with_timestamps: Vec<(u64, u64)> = self
471            .case_storage
472            .iter()
473            .map(|entry| (*entry.key(), entry.value().timestamp))
474            .collect();
475
476        cases_with_timestamps.sort_by_key(|(_, timestamp)| *timestamp);
477
478        // Remove oldest cases
479        let to_remove = current_size - target_size;
480        for (case_id, _) in cases_with_timestamps.iter().take(to_remove) {
481            self.case_storage.remove(case_id);
482        }
483
484        tracing::info!("🛡️ Cleaned up {} old edge cases", to_remove);
485    }
486
487    /// Capture current stack trace
488    fn capture_stack_trace(&self) -> Option<Vec<String>> {
489        // Simulate stack trace capture
490        // In a real implementation, this would use backtrace crate or similar
491        Some(vec![
492            "main".to_string(),
493            "handle_edge_case".to_string(),
494            "edge_case_detected".to_string(),
495        ])
496    }
497
498    /// Get current timestamp
499    fn get_current_timestamp(&self) -> u64 {
500        std::time::SystemTime::now()
501            .duration_since(std::time::UNIX_EPOCH)
502            .unwrap_or_default_safe(std::time::Duration::ZERO, "get current timestamp")
503            .as_secs()
504    }
505
506    /// Update statistics
507    fn update_stats(
508        &self,
509        _case_type: &EdgeCaseType,
510        severity: &EdgeCaseSeverity,
511        handled_successfully: bool,
512    ) {
513        if !self.config.enable_stats {
514            return;
515        }
516
517        match self.stats.safe_lock() {
518            Ok(mut stats) => {
519                stats.total_cases_detected += 1;
520
521                if handled_successfully {
522                    stats.cases_handled_successfully += 1;
523                    stats.recovery_actions_taken += 1;
524                } else {
525                    stats.cases_failed_to_handle += 1;
526                }
527
528                match severity {
529                    EdgeCaseSeverity::Critical => stats.critical_cases += 1,
530                    EdgeCaseSeverity::Fatal => stats.fatal_cases += 1,
531                    _ => {}
532                }
533
534                // Update detection accuracy
535                stats.detection_accuracy = if stats.total_cases_detected > 0 {
536                    (stats.cases_handled_successfully as f64) / (stats.total_cases_detected as f64)
537                } else {
538                    0.0
539                };
540            }
541            Err(e) => {
542                tracing::warn!("Failed to update edge case stats: {}", e);
543            }
544        }
545    }
546}
547
548/// Global edge case handler instance
549static GLOBAL_EDGE_CASE_HANDLER: std::sync::OnceLock<Arc<EdgeCaseHandler>> =
550    std::sync::OnceLock::new();
551
552/// Get global edge case handler instance
553pub fn get_global_edge_case_handler() -> Arc<EdgeCaseHandler> {
554    GLOBAL_EDGE_CASE_HANDLER
555        .get_or_init(|| Arc::new(EdgeCaseHandler::new(EdgeCaseConfig::default())))
556        .clone()
557}
558
559/// Initialize global edge case handler with custom config
560pub fn initialize_global_edge_case_handler(config: EdgeCaseConfig) -> Arc<EdgeCaseHandler> {
561    let handler = Arc::new(EdgeCaseHandler::new(config));
562    match GLOBAL_EDGE_CASE_HANDLER.set(handler.clone()) {
563        Ok(_) => tracing::info!("🛡️ Global edge case handler initialized"),
564        Err(_) => tracing::warn!("🛡️ Global edge case handler already initialized"),
565    }
566    handler
567}
568
569/// Convenience macro for handling edge cases
570#[macro_export]
571macro_rules! handle_edge_case {
572    ($case_type:expr, $severity:expr, $description:expr) => {{
573        let handler = $crate::core::edge_case_handler::get_global_edge_case_handler();
574        let context = std::collections::HashMap::new();
575        handler.handle_edge_case($case_type, $severity, $description.to_string(), context)
576    }};
577    ($case_type:expr, $severity:expr, $description:expr, $context:expr) => {{
578        let handler = $crate::core::edge_case_handler::get_global_edge_case_handler();
579        handler.handle_edge_case($case_type, $severity, $description.to_string(), $context)
580    }};
581}
582
583#[cfg(test)]
584mod tests {
585    use super::*;
586
587    #[test]
588    fn test_edge_case_handler_basic() {
589        let handler = EdgeCaseHandler::new(EdgeCaseConfig::default());
590
591        let context = HashMap::new();
592        let result = handler.handle_edge_case(
593            EdgeCaseType::NullPointerAccess,
594            EdgeCaseSeverity::High,
595            "Test null pointer access".to_string(),
596            context,
597        );
598
599        assert!(result.is_ok());
600        let case_id = result.unwrap();
601        assert!(case_id > 0);
602
603        let retrieved_case = handler.get_edge_case(case_id).unwrap();
604        assert_eq!(retrieved_case.case_type, EdgeCaseType::NullPointerAccess);
605        assert_eq!(retrieved_case.severity, EdgeCaseSeverity::High);
606    }
607
608    #[test]
609    fn test_recovery_strategies() {
610        let handler = EdgeCaseHandler::new(EdgeCaseConfig::default());
611
612        // Test custom recovery strategy
613        handler.register_recovery_strategy(EdgeCaseType::Unknown, |_| {
614            Ok("Custom recovery applied".to_string())
615        });
616
617        let context = HashMap::new();
618        let case_id = handler
619            .handle_edge_case(
620                EdgeCaseType::Unknown,
621                EdgeCaseSeverity::Medium,
622                "Test unknown case".to_string(),
623                context,
624            )
625            .unwrap();
626
627        let case = handler.get_edge_case(case_id).unwrap();
628        assert!(case.handled_successfully);
629        assert_eq!(
630            case.recovery_action,
631            Some("Custom recovery applied".to_string())
632        );
633    }
634
635    #[test]
636    fn test_statistics() {
637        let handler = EdgeCaseHandler::new(EdgeCaseConfig::default());
638
639        let context = HashMap::new();
640
641        // Handle multiple edge cases
642        for i in 0..5 {
643            handler
644                .handle_edge_case(
645                    EdgeCaseType::AllocationFailure,
646                    EdgeCaseSeverity::Medium,
647                    format!("Test case {i}"),
648                    context.clone(),
649                )
650                .unwrap();
651        }
652
653        let stats = handler.get_stats().unwrap();
654        assert_eq!(stats.total_cases_detected, 5);
655        assert!(stats.cases_handled_successfully > 0);
656    }
657
658    #[test]
659    fn test_case_cleanup() {
660        let handler = EdgeCaseHandler::new(EdgeCaseConfig {
661            max_stored_cases: 3,
662            ..Default::default()
663        });
664        let context = HashMap::new();
665
666        // Add more cases than the limit
667        for i in 0..5 {
668            handler
669                .handle_edge_case(
670                    EdgeCaseType::BufferOverflow,
671                    EdgeCaseSeverity::Low,
672                    format!("Test case {i}"),
673                    context.clone(),
674                )
675                .unwrap();
676        }
677
678        // Should have triggered cleanup
679        assert!(handler.case_storage.len() <= 3);
680    }
681}