1use super::memory_leak_detector::{
8 AllocationEvent, AllocationType, GrowthPattern, GrowthTrend, LeakDetector, LeakSource,
9 MemoryGrowthAnalysis, MemoryLeakResult, MemoryUsageSnapshot,
10};
11use crate::error::{OptimError, Result};
12use scirs2_core::numeric::Float;
13use serde::{Deserialize, Serialize};
14use std::collections::{HashMap, HashSet, VecDeque};
15use std::fmt::Debug;
16use std::sync::{Arc, Mutex, RwLock};
17use std::thread;
18use std::time::{Duration, Instant};
19
20#[derive(Debug)]
25#[allow(dead_code)]
26pub struct ReferenceCountingDetector {
27 config: ReferenceCountingConfig,
29 reference_tracker: Arc<RwLock<ReferenceTracker>>,
31 cycle_detector: CycleDetector,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct ReferenceCountingConfig {
38 pub min_suspicious_refcount: usize,
40 pub max_normal_refcount: usize,
42 pub cycle_detection_depth: usize,
44 pub reference_age_threshold: u64,
46 pub enable_strong_ref_analysis: bool,
48 pub enable_weak_ref_analysis: bool,
50}
51
52impl Default for ReferenceCountingConfig {
53 fn default() -> Self {
54 Self {
55 min_suspicious_refcount: 5,
56 max_normal_refcount: 100,
57 cycle_detection_depth: 10,
58 reference_age_threshold: 300, enable_strong_ref_analysis: true,
60 enable_weak_ref_analysis: true,
61 }
62 }
63}
64
65#[derive(Debug)]
67pub struct ReferenceTracker {
68 active_references: HashMap<usize, ReferenceInfo>,
70 reference_graph: HashMap<usize, HashSet<usize>>,
72 reference_history: VecDeque<ReferenceEvent>,
74 suspectedleaks: Vec<SuspectedLeak>,
76}
77
78#[derive(Debug, Clone)]
80pub struct ReferenceInfo {
81 pub allocation_id: usize,
83 pub reference_count: usize,
85 pub created_at: Instant,
87 pub last_accessed: Instant,
89 pub reference_type: ReferenceType,
91 pub source_location: Option<String>,
93 pub references_to: HashSet<usize>,
95 pub referenced_by: HashSet<usize>,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub enum ReferenceType {
102 Strong,
104 Weak,
106 Shared,
108 Unique,
110}
111
112#[derive(Debug, Clone)]
114pub struct ReferenceEvent {
115 pub timestamp: Instant,
117 pub allocation_id: usize,
119 pub event_type: ReferenceEventType,
121 pub reference_count: usize,
123}
124
125#[derive(Debug, Clone)]
127pub enum ReferenceEventType {
128 Created,
130 Incremented,
132 Decremented,
134 Destroyed,
136 Accessed,
138}
139
140#[derive(Debug, Clone)]
142pub struct SuspectedLeak {
143 pub allocation_id: usize,
145 pub confidence: f64,
147 pub leak_type: LeakType,
149 pub evidence: Vec<String>,
151 pub detected_at: Instant,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub enum LeakType {
158 CircularReference,
160 DanglingReference,
162 UnreleasedReference,
164 ReferenceCountOverflow,
166 StaleReference,
168}
169
170impl ReferenceCountingDetector {
171 pub fn new(config: ReferenceCountingConfig) -> Self {
173 Self {
174 config,
175 reference_tracker: Arc::new(RwLock::new(ReferenceTracker::new())),
176 cycle_detector: CycleDetector::new(),
177 }
178 }
179
180 pub fn track_reference_operation(
182 &self,
183 allocation_id: usize,
184 event_type: ReferenceEventType,
185 reference_count: usize,
186 ) -> Result<()> {
187 let mut tracker = self.reference_tracker.write().map_err(|_| {
188 OptimError::InvalidState("Failed to acquire reference tracker lock".to_string())
189 })?;
190
191 let now = Instant::now();
192
193 let ref_info = tracker
195 .active_references
196 .entry(allocation_id)
197 .or_insert_with(|| ReferenceInfo {
198 allocation_id,
199 reference_count: 0,
200 created_at: now,
201 last_accessed: now,
202 reference_type: ReferenceType::Strong,
203 source_location: None,
204 references_to: HashSet::new(),
205 referenced_by: HashSet::new(),
206 });
207
208 ref_info.reference_count = reference_count;
209 ref_info.last_accessed = now;
210
211 let event = ReferenceEvent {
213 timestamp: now,
214 allocation_id,
215 event_type,
216 reference_count,
217 };
218
219 tracker.reference_history.push_back(event);
220
221 while tracker.reference_history.len() > 10000 {
223 tracker.reference_history.pop_front();
224 }
225
226 self.analyze_reference_patterns(&mut tracker)?;
228
229 Ok(())
230 }
231
232 fn analyze_reference_patterns(&self, tracker: &mut ReferenceTracker) -> Result<()> {
234 let now = Instant::now();
235
236 for (allocation_id, ref_info) in &tracker.active_references {
238 let mut evidence = Vec::new();
239 let mut confidence = 0.0;
240 let mut leak_type = LeakType::UnreleasedReference;
241
242 if ref_info.reference_count > self.config.max_normal_refcount {
244 evidence.push(format!(
245 "Reference count {} exceeds normal threshold {}",
246 ref_info.reference_count, self.config.max_normal_refcount
247 ));
248 confidence += 0.3;
249 leak_type = LeakType::ReferenceCountOverflow;
250 }
251
252 let age = now.duration_since(ref_info.created_at).as_secs();
254 if age > self.config.reference_age_threshold {
255 evidence.push(format!(
256 "Reference age {} seconds exceeds threshold {}",
257 age, self.config.reference_age_threshold
258 ));
259 confidence += 0.2;
260 }
261
262 let last_access_age = now.duration_since(ref_info.last_accessed).as_secs();
264 if last_access_age > self.config.reference_age_threshold / 2 {
265 evidence.push(format!(
266 "Reference not accessed for {} seconds",
267 last_access_age
268 ));
269 confidence += 0.15;
270 leak_type = LeakType::StaleReference;
271 }
272
273 if self.has_circular_reference(*allocation_id, &tracker.reference_graph) {
275 evidence.push("Circular reference detected".to_string());
276 confidence += 0.4;
277 leak_type = LeakType::CircularReference;
278 }
279
280 if confidence > 0.5 && !evidence.is_empty() {
282 let suspected_leak = SuspectedLeak {
283 allocation_id: *allocation_id,
284 confidence,
285 leak_type,
286 evidence,
287 detected_at: now,
288 };
289
290 tracker.suspectedleaks.push(suspected_leak);
291 }
292 }
293
294 tracker.suspectedleaks.retain(|leak| {
296 now.duration_since(leak.detected_at).as_secs() < 3600 });
298
299 Ok(())
300 }
301
302 fn has_circular_reference(
304 &self,
305 allocation_id: usize,
306 reference_graph: &HashMap<usize, HashSet<usize>>,
307 ) -> bool {
308 let mut visited = HashSet::new();
309 let mut recursion_stack = HashSet::new();
310
311 Self::dfs_cycle_detection(
312 allocation_id,
313 reference_graph,
314 &mut visited,
315 &mut recursion_stack,
316 )
317 }
318
319 fn dfs_cycle_detection(
321 node: usize,
322 graph: &HashMap<usize, HashSet<usize>>,
323 visited: &mut HashSet<usize>,
324 recursion_stack: &mut HashSet<usize>,
325 ) -> bool {
326 visited.insert(node);
327 recursion_stack.insert(node);
328
329 if let Some(neighbors) = graph.get(&node) {
330 for &neighbor in neighbors {
331 if !visited.contains(&neighbor) {
332 if Self::dfs_cycle_detection(neighbor, graph, visited, recursion_stack) {
333 return true;
334 }
335 } else if recursion_stack.contains(&neighbor) {
336 return true;
338 }
339 }
340 }
341
342 recursion_stack.remove(&node);
343 false
344 }
345}
346
347impl LeakDetector for ReferenceCountingDetector {
348 fn detect_leaks(
349 &self,
350 allocation_history: &VecDeque<AllocationEvent>,
351 usage_snapshots: &VecDeque<MemoryUsageSnapshot>,
352 ) -> Result<MemoryLeakResult> {
353 let tracker = self.reference_tracker.read().map_err(|_| {
354 OptimError::InvalidState("Failed to acquire reference tracker lock".to_string())
355 })?;
356
357 let mut leak_sources = Vec::new();
358 let mut total_leaked_bytes = 0;
359 let mut max_confidence = 0.0;
360
361 for suspected_leak in &tracker.suspectedleaks {
363 if let Some(ref_info) = tracker.active_references.get(&suspected_leak.allocation_id) {
364 let leak_size = allocation_history
366 .iter()
367 .find(|event| event.allocation_id == suspected_leak.allocation_id)
368 .map(|event| event.size)
369 .unwrap_or(0);
370
371 let leak_source = LeakSource {
372 source_type: AllocationType::OptimizerState, location: ref_info.source_location.clone(),
374 leak_size,
375 probability: suspected_leak.confidence,
376 stack_trace: None,
377 };
378
379 leak_sources.push(leak_source);
380 total_leaked_bytes += leak_size;
381 max_confidence = max_confidence.max(suspected_leak.confidence);
382 }
383 }
384
385 let growth_analysis = self.analyze_memory_growth(usage_snapshots)?;
387
388 let recommendations = self.generate_recommendations(&tracker.suspectedleaks);
390
391 let detailed_analysis = format!(
393 "Reference Counting Analysis:\n\
394 - Active References: {}\n\
395 - Suspected Leaks: {}\n\
396 - Circular References Detected: {}\n\
397 - Average Reference Count: {:.2}\n\
398 - Maximum Reference Count: {}",
399 tracker.active_references.len(),
400 tracker.suspectedleaks.len(),
401 tracker
402 .suspectedleaks
403 .iter()
404 .filter(|l| matches!(l.leak_type, LeakType::CircularReference))
405 .count(),
406 tracker
407 .active_references
408 .values()
409 .map(|r| r.reference_count)
410 .sum::<usize>() as f64
411 / tracker.active_references.len().max(1) as f64,
412 tracker
413 .active_references
414 .values()
415 .map(|r| r.reference_count)
416 .max()
417 .unwrap_or(0)
418 );
419
420 Ok(MemoryLeakResult {
421 leak_detected: !leak_sources.is_empty(),
422 severity: max_confidence,
423 confidence: max_confidence,
424 leaked_memory_bytes: total_leaked_bytes,
425 leak_sources,
426 growth_analysis,
427 recommendations,
428 detailed_analysis,
429 })
430 }
431
432 fn name(&self) -> &str {
433 "ReferenceCountingDetector"
434 }
435
436 fn config(&self) -> HashMap<String, String> {
437 let mut config = HashMap::new();
438 config.insert(
439 "min_suspicious_refcount".to_string(),
440 self.config.min_suspicious_refcount.to_string(),
441 );
442 config.insert(
443 "max_normal_refcount".to_string(),
444 self.config.max_normal_refcount.to_string(),
445 );
446 config.insert(
447 "cycle_detection_depth".to_string(),
448 self.config.cycle_detection_depth.to_string(),
449 );
450 config.insert(
451 "reference_age_threshold".to_string(),
452 self.config.reference_age_threshold.to_string(),
453 );
454 config
455 }
456}
457
458impl ReferenceCountingDetector {
459 fn analyze_memory_growth(
461 &self,
462 snapshots: &VecDeque<MemoryUsageSnapshot>,
463 ) -> Result<MemoryGrowthAnalysis> {
464 if snapshots.len() < 2 {
465 return Ok(MemoryGrowthAnalysis {
466 growth_trend: GrowthTrend::Stable,
467 growth_rate: 0.0,
468 projected_usage: Vec::new(),
469 pattern_type: GrowthPattern::Normal,
470 });
471 }
472
473 let memory_values: Vec<f64> = snapshots.iter().map(|s| s.total_memory as f64).collect();
474
475 let n = memory_values.len() as f64;
477 let sum_x = (0..memory_values.len()).sum::<usize>() as f64;
478 let sum_y = memory_values.iter().sum::<f64>();
479 let sum_xy = memory_values
480 .iter()
481 .enumerate()
482 .map(|(i, &y)| i as f64 * y)
483 .sum::<f64>();
484 let sum_x2 = (0..memory_values.len())
485 .map(|i| (i * i) as f64)
486 .sum::<f64>();
487
488 let growth_rate = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x);
489
490 let growth_trend = if growth_rate.abs() < 1000.0 {
492 GrowthTrend::Stable
493 } else if growth_rate > 0.0 {
494 if growth_rate > 100000.0 {
495 GrowthTrend::Exponential
496 } else {
497 GrowthTrend::Linear
498 }
499 } else {
500 GrowthTrend::Irregular
501 };
502
503 let pattern_type = if growth_rate > 50000.0 {
505 GrowthPattern::Leak
506 } else if memory_values
507 .windows(2)
508 .any(|w| (w[1] - w[0]).abs() > 10000000.0)
509 {
510 GrowthPattern::Burst
511 } else {
512 GrowthPattern::Normal
513 };
514
515 let last_timestamp = snapshots.back().expect("unwrap failed").timestamp;
517 let projected_usage = (1..=10)
518 .map(|i| {
519 let future_timestamp = last_timestamp + (i * 60); let future_memory =
521 memory_values.last().expect("unwrap failed") + (growth_rate * i as f64);
522 (future_timestamp, future_memory.max(0.0) as usize)
523 })
524 .collect();
525
526 Ok(MemoryGrowthAnalysis {
527 growth_trend,
528 growth_rate,
529 projected_usage,
530 pattern_type,
531 })
532 }
533
534 fn generate_recommendations(&self, suspectedleaks: &[SuspectedLeak]) -> Vec<String> {
536 let mut recommendations = Vec::new();
537
538 let circular_ref_count = suspectedleaks
539 .iter()
540 .filter(|l| matches!(l.leak_type, LeakType::CircularReference))
541 .count();
542
543 let stale_ref_count = suspectedleaks
544 .iter()
545 .filter(|l| matches!(l.leak_type, LeakType::StaleReference))
546 .count();
547
548 let overflow_count = suspectedleaks
549 .iter()
550 .filter(|l| matches!(l.leak_type, LeakType::ReferenceCountOverflow))
551 .count();
552
553 if circular_ref_count > 0 {
554 recommendations.push(format!(
555 "Detected {} circular references. Consider using weak references to break cycles.",
556 circular_ref_count
557 ));
558 }
559
560 if stale_ref_count > 0 {
561 recommendations.push(format!(
562 "Detected {} stale references. Implement automatic cleanup for unused references.",
563 stale_ref_count
564 ));
565 }
566
567 if overflow_count > 0 {
568 recommendations.push(format!(
569 "Detected {} reference count overflows. Review reference sharing patterns.",
570 overflow_count
571 ));
572 }
573
574 if suspectedleaks.len() > 10 {
575 recommendations.push(
576 "High number of suspected _leaks detected. Consider implementing reference pooling."
577 .to_string(),
578 );
579 }
580
581 if recommendations.is_empty() {
582 recommendations.push("Reference counting patterns appear healthy.".to_string());
583 }
584
585 recommendations
586 }
587}
588
589#[derive(Debug)]
591#[allow(dead_code)]
592pub struct CycleDetector {
593 max_depth: usize,
595}
596
597struct TarjanState {
599 index: usize,
600 stack: Vec<usize>,
601 indices: HashMap<usize, usize>,
602 lowlinks: HashMap<usize, usize>,
603 on_stack: HashSet<usize>,
604 strongly_connected_components: Vec<Vec<usize>>,
605}
606
607impl TarjanState {
608 fn new() -> Self {
609 Self {
610 index: 0,
611 stack: Vec::new(),
612 indices: HashMap::new(),
613 lowlinks: HashMap::new(),
614 on_stack: HashSet::new(),
615 strongly_connected_components: Vec::new(),
616 }
617 }
618}
619
620impl CycleDetector {
621 pub fn new() -> Self {
623 Self { max_depth: 20 }
624 }
625
626 pub fn detect_cycles(&self, graph: &HashMap<usize, HashSet<usize>>) -> Vec<Vec<usize>> {
628 let mut state = TarjanState::new();
629
630 for &node in graph.keys() {
631 if !state.indices.contains_key(&node) {
632 Self::strongconnect(node, &mut state, graph);
633 }
634 }
635
636 state
638 .strongly_connected_components
639 .into_iter()
640 .filter(|scc| {
641 scc.len() > 1
642 || (scc.len() == 1
643 && graph
644 .get(&scc[0])
645 .is_some_and(|neighbors| neighbors.contains(&scc[0])))
646 })
647 .collect()
648 }
649
650 fn strongconnect(v: usize, state: &mut TarjanState, graph: &HashMap<usize, HashSet<usize>>) {
652 state.indices.insert(v, state.index);
653 state.lowlinks.insert(v, state.index);
654 state.index += 1;
655 state.stack.push(v);
656 state.on_stack.insert(v);
657
658 if let Some(neighbors) = graph.get(&v) {
659 for &w in neighbors {
660 if !state.indices.contains_key(&w) {
661 Self::strongconnect(w, state, graph);
662 let v_lowlink = *state.lowlinks.get(&v).expect("unwrap failed");
663 let w_lowlink = *state.lowlinks.get(&w).expect("unwrap failed");
664 state.lowlinks.insert(v, v_lowlink.min(w_lowlink));
665 } else if state.on_stack.contains(&w) {
666 let v_lowlink = *state.lowlinks.get(&v).expect("unwrap failed");
667 let w_index = *state.indices.get(&w).expect("unwrap failed");
668 state.lowlinks.insert(v, v_lowlink.min(w_index));
669 }
670 }
671 }
672
673 if state.lowlinks.get(&v) == state.indices.get(&v) {
674 let mut component = Vec::new();
675 loop {
676 let w = state.stack.pop().expect("unwrap failed");
677 state.on_stack.remove(&w);
678 component.push(w);
679 if w == v {
680 break;
681 }
682 }
683 state.strongly_connected_components.push(component);
684 }
685 }
686}
687
688impl Default for ReferenceTracker {
689 fn default() -> Self {
690 Self::new()
691 }
692}
693
694impl ReferenceTracker {
695 pub fn new() -> Self {
697 Self {
698 active_references: HashMap::new(),
699 reference_graph: HashMap::new(),
700 reference_history: VecDeque::new(),
701 suspectedleaks: Vec::new(),
702 }
703 }
704}
705
706impl Default for CycleDetector {
707 fn default() -> Self {
708 Self::new()
709 }
710}
711
712#[allow(dead_code)]
717pub struct RealTimeMemoryMonitor {
718 config: RealTimeMonitorConfig,
720 state: Arc<Mutex<MonitorState>>,
722 alert_system: AlertSystem,
724 is_active: Arc<Mutex<bool>>,
726}
727
728#[derive(Debug, Clone)]
730pub struct RealTimeMonitorConfig {
731 pub sampling_interval_ms: u64,
733 pub memory_threshold_bytes: usize,
735 pub growth_rate_threshold: f64,
737 pub trend_window_size: usize,
739 pub enable_gc_hints: bool,
741}
742
743impl Default for RealTimeMonitorConfig {
744 fn default() -> Self {
745 Self {
746 sampling_interval_ms: 1000, memory_threshold_bytes: 100 * 1024 * 1024, growth_rate_threshold: 1024.0 * 1024.0, trend_window_size: 60, enable_gc_hints: true,
751 }
752 }
753}
754
755#[derive(Debug)]
757pub struct MonitorState {
758 memory_samples: VecDeque<MemorySample>,
760 current_memory_usage: usize,
762 peak_memory_usage: usize,
764 current_growth_rate: f64,
766 alert_history: VecDeque<MemoryAlert>,
768}
769
770#[derive(Debug, Clone)]
772pub struct MemorySample {
773 pub timestamp: Instant,
775 pub memory_usage: usize,
777 pub allocation_rate: f64,
779 pub deallocation_rate: f64,
781}
782
783#[derive(Debug, Clone)]
785pub struct MemoryAlert {
786 pub timestamp: Instant,
788 pub alert_type: AlertType,
790 pub message: String,
792 pub severity: AlertSeverity,
794}
795
796#[derive(Debug, Clone)]
798pub enum AlertType {
799 MemoryThreshold,
801 GrowthRate,
803 PotentialLeak,
805 Fragmentation,
807 SystemPressure,
809}
810
811#[derive(Debug, Clone)]
813pub enum AlertSeverity {
814 Low,
815 Medium,
816 High,
817 Critical,
818}
819
820type AlertCallback = Box<dyn Fn(&MemoryAlert) + Send + Sync>;
822
823pub struct AlertSystem {
825 alert_callbacks: Vec<AlertCallback>,
827}
828
829impl RealTimeMemoryMonitor {
830 pub fn new(config: RealTimeMonitorConfig) -> Self {
832 Self {
833 config,
834 state: Arc::new(Mutex::new(MonitorState::new())),
835 alert_system: AlertSystem::new(),
836 is_active: Arc::new(Mutex::new(false)),
837 }
838 }
839
840 pub fn start_monitoring(&self) -> Result<()> {
842 let mut is_active = self
843 .is_active
844 .lock()
845 .map_err(|_| OptimError::InvalidState("Failed to acquire monitor lock".to_string()))?;
846
847 if *is_active {
848 return Ok(()); }
850
851 *is_active = true;
852
853 let state = Arc::clone(&self.state);
854 let config = self.config.clone();
855 let is_active_flag = Arc::clone(&self.is_active);
856
857 thread::spawn(move || {
858 let mut _last_sample_time = Instant::now();
859
860 loop {
861 {
863 let active = is_active_flag.lock().expect("lock poisoned");
864 if !*active {
865 break;
866 }
867 }
868
869 thread::sleep(Duration::from_millis(config.sampling_interval_ms));
871
872 let now = Instant::now();
874 let sample = Self::take_memory_sample(now);
875
876 {
878 let mut monitor_state = state.lock().expect("lock poisoned");
879 monitor_state.add_sample(sample);
880 monitor_state.update_metrics(&config);
881 }
882
883 _last_sample_time = now;
884 }
885 });
886
887 Ok(())
888 }
889
890 pub fn stop_monitoring(&self) -> Result<()> {
892 let mut is_active = self
893 .is_active
894 .lock()
895 .map_err(|_| OptimError::InvalidState("Failed to acquire monitor lock".to_string()))?;
896
897 *is_active = false;
898 Ok(())
899 }
900
901 fn take_memory_sample(timestamp: Instant) -> MemorySample {
903 MemorySample {
906 timestamp,
907 memory_usage: Self::get_current_memory_usage(),
908 allocation_rate: 0.0, deallocation_rate: 0.0, }
911 }
912
913 fn get_current_memory_usage() -> usize {
915 64 * 1024 * 1024 }
923
924 pub fn get_statistics(&self) -> Result<MonitoringStatistics> {
926 let state = self.state.lock().map_err(|_| {
927 OptimError::InvalidState("Failed to acquire monitor state lock".to_string())
928 })?;
929
930 Ok(MonitoringStatistics {
931 current_memory_usage: state.current_memory_usage,
932 peak_memory_usage: state.peak_memory_usage,
933 current_growth_rate: state.current_growth_rate,
934 sample_count: state.memory_samples.len(),
935 recent_alerts: state.alert_history.iter().cloned().collect(),
936 })
937 }
938}
939
940#[derive(Debug, Clone)]
942pub struct MonitoringStatistics {
943 pub current_memory_usage: usize,
945 pub peak_memory_usage: usize,
947 pub current_growth_rate: f64,
949 pub sample_count: usize,
951 pub recent_alerts: Vec<MemoryAlert>,
953}
954
955impl MonitorState {
956 pub fn new() -> Self {
958 Self {
959 memory_samples: VecDeque::new(),
960 current_memory_usage: 0,
961 peak_memory_usage: 0,
962 current_growth_rate: 0.0,
963 alert_history: VecDeque::new(),
964 }
965 }
966
967 pub fn add_sample(&mut self, sample: MemorySample) {
969 self.current_memory_usage = sample.memory_usage;
970 self.peak_memory_usage = self.peak_memory_usage.max(sample.memory_usage);
971
972 self.memory_samples.push_back(sample);
973
974 while self.memory_samples.len() > 3600 {
976 self.memory_samples.pop_front();
978 }
979 }
980
981 pub fn update_metrics(&mut self, config: &RealTimeMonitorConfig) {
983 if self.memory_samples.len() >= 2 {
985 let window_size = config.trend_window_size.min(self.memory_samples.len());
986 let recent_samples: Vec<_> =
987 self.memory_samples.iter().rev().take(window_size).collect();
988
989 if recent_samples.len() >= 2 {
990 let first = recent_samples.last().expect("unwrap failed");
991 let last = recent_samples.first().expect("unwrap failed");
992 let time_diff = last.timestamp.duration_since(first.timestamp).as_secs_f64();
993
994 if time_diff > 0.0 {
995 let memory_diff = last.memory_usage as f64 - first.memory_usage as f64;
996 self.current_growth_rate = memory_diff / time_diff;
997 }
998 }
999 }
1000
1001 self.check_for_alerts(config);
1003 }
1004
1005 fn check_for_alerts(&mut self, config: &RealTimeMonitorConfig) {
1007 let now = Instant::now();
1008
1009 if self.current_memory_usage > config.memory_threshold_bytes {
1011 let alert = MemoryAlert {
1012 timestamp: now,
1013 alert_type: AlertType::MemoryThreshold,
1014 message: format!(
1015 "Memory usage {} exceeds threshold {}",
1016 self.current_memory_usage, config.memory_threshold_bytes
1017 ),
1018 severity: AlertSeverity::High,
1019 };
1020 self.alert_history.push_back(alert);
1021 }
1022
1023 if self.current_growth_rate > config.growth_rate_threshold {
1025 let alert = MemoryAlert {
1026 timestamp: now,
1027 alert_type: AlertType::GrowthRate,
1028 message: format!(
1029 "Memory growth rate {:.2} bytes/s exceeds threshold {:.2}",
1030 self.current_growth_rate, config.growth_rate_threshold
1031 ),
1032 severity: AlertSeverity::Medium,
1033 };
1034 self.alert_history.push_back(alert);
1035 }
1036
1037 while self.alert_history.len() > 1000 {
1039 self.alert_history.pop_front();
1040 }
1041 }
1042}
1043
1044impl AlertSystem {
1045 pub fn new() -> Self {
1047 Self {
1048 alert_callbacks: Vec::new(),
1049 }
1050 }
1051
1052 pub fn add_callback<F>(&mut self, callback: F)
1054 where
1055 F: Fn(&MemoryAlert) + Send + Sync + 'static,
1056 {
1057 self.alert_callbacks.push(Box::new(callback));
1058 }
1059
1060 pub fn trigger_alert(&self, alert: &MemoryAlert) {
1062 for callback in &self.alert_callbacks {
1063 callback(alert);
1064 }
1065 }
1066}
1067
1068impl Default for MonitorState {
1069 fn default() -> Self {
1070 Self::new()
1071 }
1072}
1073
1074impl Default for AlertSystem {
1075 fn default() -> Self {
1076 Self::new()
1077 }
1078}