scirs2_core/memory/metrics/
collector.rs1use std::collections::{HashMap, VecDeque};
6use std::sync::{Mutex, RwLock};
7use std::time::{Duration, Instant};
8
9use crate::memory::metrics::event::{MemoryEvent, MemoryEventType};
10use rand::prelude::*;
11use rand::Rng;
12#[cfg(feature = "memory_metrics")]
13#[cfg(feature = "serialization")]
14use serde::{Deserialize, Serialize};
15
16struct Random {
18 rng: StdRng,
19}
20
21impl Default for Random {
22 fn default() -> Self {
23 Self {
24 rng: StdRng::seed_from_u64(0), }
26 }
27}
28
29impl Random {
30 fn gen_range(&mut self, range: std::ops::Range<f64>) -> f64 {
31 self.rng.gen_range(range)
32 }
33}
34
35#[derive(Debug, Clone)]
37pub struct MemoryMetricsConfig {
38 pub enabled: bool,
40 pub capture_call_stacks: bool,
42 pub max_events: usize,
44 pub real_time_aggregation: bool,
46 pub samplingrate: f64,
48}
49
50impl Default for MemoryMetricsConfig {
51 fn default() -> Self {
52 Self {
53 enabled: true,
54 capture_call_stacks: cfg!(feature = "memory_call_stack"),
55 max_events: 10000,
56 real_time_aggregation: true,
57 samplingrate: 1.0,
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
64pub struct AllocationStats {
65 pub count: usize,
67 pub total_bytes: usize,
69 pub average_size: f64,
71 pub peak_usage: usize,
73}
74
75#[derive(Debug, Clone)]
77#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
78pub struct ComponentMemoryStats {
79 pub current_usage: usize,
81 pub peak_usage: usize,
83 pub allocation_count: usize,
85 pub total_allocated: usize,
87 pub avg_allocation_size: f64,
89}
90
91#[derive(Debug, Clone)]
93#[cfg_attr(
94 feature = "memory_metrics",
95 derive(serde::Serialize, serde::Deserialize)
96)]
97pub struct MemoryReport {
98 pub total_current_usage: usize,
100 pub total_peak_usage: usize,
102 pub total_allocation_count: usize,
104 pub total_allocated_bytes: usize,
106 pub component_stats: HashMap<String, ComponentMemoryStats>,
108 pub duration: Duration,
110}
111
112pub struct MemoryMetricsCollector {
114 config: MemoryMetricsConfig,
116 events: RwLock<VecDeque<MemoryEvent>>,
118 current_usage: RwLock<HashMap<String, usize>>,
120 peak_usage: RwLock<HashMap<String, usize>>,
122 allocation_count: RwLock<HashMap<String, usize>>,
124 total_allocated: RwLock<HashMap<String, usize>>,
126 avg_allocation_size: RwLock<HashMap<String, f64>>,
128 start_time: Instant,
130 rng: Mutex<Random>,
132}
133
134impl MemoryMetricsCollector {
135 pub fn new(config: MemoryMetricsConfig) -> Self {
137 Self {
138 config,
139 events: RwLock::new(VecDeque::with_capacity(1000)),
140 current_usage: RwLock::new(HashMap::new()),
141 peak_usage: RwLock::new(HashMap::new()),
142 allocation_count: RwLock::new(HashMap::new()),
143 total_allocated: RwLock::new(HashMap::new()),
144 avg_allocation_size: RwLock::new(HashMap::new()),
145 start_time: Instant::now(),
146 rng: Mutex::new(Random::default()),
147 }
148 }
149
150 pub fn record_event(&self, event: MemoryEvent) {
152 if !self.config.enabled {
153 return;
154 }
155
156 if self.config.samplingrate < 1.0 {
158 let mut rng = self.rng.lock().expect("Operation failed");
159 if rng.gen_range(0.0..1.0) > self.config.samplingrate {
160 return;
161 }
162 }
163
164 if self.config.real_time_aggregation {
166 self.update_metrics(&event);
167 }
168
169 if self.config.max_events > 0 {
171 let mut events = self.events.write().expect("Operation failed");
172 events.push_back(event);
173
174 while events.len() > self.config.max_events {
176 events.pop_front();
177 }
178 }
179 }
180
181 fn update_metrics(&self, event: &MemoryEvent) {
183 match event.event_type {
184 MemoryEventType::Allocation => {
185 let mut current_usage = self.current_usage.write().expect("Operation failed");
187 let component_usage = current_usage.entry(event.component.clone()).or_insert(0);
188 *component_usage += event.size;
189
190 let mut peak_usage = self.peak_usage.write().expect("Operation failed");
192 let peak = peak_usage.entry(event.component.clone()).or_insert(0);
193 *peak = (*peak).max(*component_usage);
194
195 let mut allocation_count = self.allocation_count.write().expect("Operation failed");
197 let count = allocation_count.entry(event.component.clone()).or_insert(0);
198 *count += 1;
199
200 let mut total_allocated = self.total_allocated.write().expect("Operation failed");
202 let total = total_allocated.entry(event.component.clone()).or_insert(0);
203 *total += event.size;
204
205 let mut avg_allocation_size =
207 self.avg_allocation_size.write().expect("Operation failed");
208 let avg = avg_allocation_size
209 .entry(event.component.clone())
210 .or_insert(0.0);
211 *avg = (*avg * (*count as f64 - 1.0) + event.size as f64) / *count as f64;
212 }
213 MemoryEventType::Deallocation => {
214 let mut current_usage = self.current_usage.write().expect("Operation failed");
216 let component_usage = current_usage.entry(event.component.clone()).or_insert(0);
217 *component_usage = component_usage.saturating_sub(event.size);
218 }
219 MemoryEventType::Resize => {
220 if let Some(old_size) = event
222 .metadata
223 .get("old_size")
224 .and_then(|s| s.parse::<usize>().ok())
225 {
226 let size_diff = event.size as isize - old_size as isize;
227
228 let mut current_usage = self.current_usage.write().expect("Operation failed");
229 let component_usage = current_usage.entry(event.component.clone()).or_insert(0);
230
231 if size_diff > 0 {
232 *component_usage += size_diff as usize;
233 } else {
234 *component_usage = component_usage.saturating_sub((-size_diff) as usize);
235 }
236
237 let mut peak_usage = self.peak_usage.write().expect("Operation failed");
239 let peak = peak_usage.entry(event.component.clone()).or_insert(0);
240 *peak = (*peak).max(*component_usage);
241 }
242 }
243 MemoryEventType::Access | MemoryEventType::Transfer => {
244 }
246 }
247 }
248
249 pub fn get_current_usage(&self, component: &str) -> usize {
251 let current_usage = self.current_usage.read().expect("Operation failed");
252 *current_usage.get(component).unwrap_or(&0)
253 }
254
255 pub fn get_peak_usage(&self, component: &str) -> usize {
257 let peak_usage = self.peak_usage.read().expect("Operation failed");
258 *peak_usage.get(component).unwrap_or(&0)
259 }
260
261 pub fn get_total_current_usage(&self) -> usize {
263 let current_usage = self.current_usage.read().expect("Operation failed");
264 current_usage.values().sum()
265 }
266
267 pub fn get_total_peak_usage(&self) -> usize {
269 let peak_usage = self.peak_usage.read().expect("Operation failed");
270
271 let component_sum: usize = peak_usage.values().sum();
273
274 component_sum
277 }
278
279 pub fn get_allocation_stats(&self, component: &str) -> Option<AllocationStats> {
281 let allocation_count = self.allocation_count.read().expect("Operation failed");
282 let count = *allocation_count.get(component)?;
283
284 let total_allocated = self.total_allocated.read().expect("Operation failed");
285 let total = *total_allocated.get(component)?;
286
287 let avg_allocation_size = self.avg_allocation_size.read().expect("Operation failed");
288 let avg = *avg_allocation_size.get(component)?;
289
290 let peak_usage = self.peak_usage.read().expect("Operation failed");
291 let peak = *peak_usage.get(component)?;
292
293 Some(AllocationStats {
294 count,
295 total_bytes: total,
296 average_size: avg,
297 peak_usage: peak,
298 })
299 }
300
301 pub fn generate_report(&self) -> MemoryReport {
303 let current_usage = self.current_usage.read().expect("Operation failed");
304 let peak_usage = self.peak_usage.read().expect("Operation failed");
305 let allocation_count = self.allocation_count.read().expect("Operation failed");
306 let total_allocated = self.total_allocated.read().expect("Operation failed");
307 let avg_allocation_size = self.avg_allocation_size.read().expect("Operation failed");
308
309 let mut component_stats = HashMap::new();
310
311 let mut components = std::collections::HashSet::new();
313 components.extend(current_usage.keys().cloned());
314 components.extend(peak_usage.keys().cloned());
315 components.extend(allocation_count.keys().cloned());
316
317 for component in components {
319 let stats = ComponentMemoryStats {
320 current_usage: *current_usage.get(&component).unwrap_or(&0),
321 peak_usage: *peak_usage.get(&component).unwrap_or(&0),
322 allocation_count: *allocation_count.get(&component).unwrap_or(&0),
323 total_allocated: *total_allocated.get(&component).unwrap_or(&0),
324 avg_allocation_size: *avg_allocation_size.get(&component).unwrap_or(&0.0),
325 };
326
327 component_stats.insert(component, stats);
328 }
329
330 MemoryReport {
331 total_current_usage: current_usage.values().sum(),
332 total_peak_usage: self.get_total_peak_usage(),
333 total_allocation_count: allocation_count.values().sum(),
334 total_allocated_bytes: total_allocated.values().sum(),
335 component_stats,
336 duration: self.start_time.elapsed(),
337 }
338 }
339
340 pub fn reset(&self) {
342 let mut events = self.events.write().expect("Operation failed");
343 events.clear();
344
345 let mut current_usage = self.current_usage.write().expect("Operation failed");
346 current_usage.clear();
347
348 let mut peak_usage = self.peak_usage.write().expect("Operation failed");
349 peak_usage.clear();
350
351 let mut allocation_count = self.allocation_count.write().expect("Operation failed");
352 allocation_count.clear();
353
354 let mut total_allocated = self.total_allocated.write().expect("Operation failed");
355 total_allocated.clear();
356
357 let mut avg_allocation_size = self.avg_allocation_size.write().expect("Operation failed");
358 avg_allocation_size.clear();
359 }
360
361 pub fn get_events(&self) -> Vec<MemoryEvent> {
363 let events = self.events.read().expect("Operation failed");
364 events.iter().cloned().collect()
365 }
366
367 pub fn to_json(&self) -> serde_json::Value {
369 let report = self.generate_report();
371
372 let mut json_obj = serde_json::Map::new();
373
374 json_obj.insert(
375 "total_allocation_count".to_string(),
376 serde_json::Value::Number(report.total_allocation_count.into()),
377 );
378 json_obj.insert(
379 "total_peak_usage".to_string(),
380 serde_json::Value::Number(report.total_peak_usage.into()),
381 );
382 json_obj.insert(
383 "total_current_usage".to_string(),
384 serde_json::Value::Number(report.total_current_usage.into()),
385 );
386 json_obj.insert(
387 "total_allocated_bytes".to_string(),
388 serde_json::Value::Number(report.total_allocated_bytes.into()),
389 );
390
391 let component_stats: serde_json::Value = report
393 .component_stats
394 .iter()
395 .map(|(k, v)| {
396 (
397 k.clone(),
398 serde_json::json!({
399 "current_usage": v.current_usage,
400 "peak_usage": v.peak_usage,
401 "allocation_count": v.allocation_count,
402 "total_allocated": v.total_allocated,
403 "avg_allocation_size": v.avg_allocation_size
404 }),
405 )
406 })
407 .collect::<serde_json::Map<String, serde_json::Value>>()
408 .into();
409
410 json_obj.insert("component_stats".to_string(), component_stats);
411 json_obj.insert(
412 "duration_secs".to_string(),
413 serde_json::Value::Number(report.duration.as_secs().into()),
414 );
415
416 serde_json::Value::Object(json_obj)
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423 use crate::memory::metrics::event::MemoryEventType;
424
425 #[test]
426 fn test_memory_metrics_collector() {
427 let config = MemoryMetricsConfig {
428 enabled: true,
429 capture_call_stacks: false,
430 max_events: 100,
431 real_time_aggregation: true,
432 samplingrate: 1.0,
433 };
434
435 let collector = MemoryMetricsCollector::new(config);
436
437 collector.record_event(MemoryEvent::new(
439 MemoryEventType::Allocation,
440 "Component1",
441 1024,
442 0x1000,
443 ));
444
445 collector.record_event(MemoryEvent::new(
446 MemoryEventType::Allocation,
447 "Component1",
448 2048,
449 0x2000,
450 ));
451
452 collector.record_event(MemoryEvent::new(
453 MemoryEventType::Allocation,
454 "Component2",
455 4096,
456 0x3000,
457 ));
458
459 assert_eq!(collector.get_current_usage("Component1"), 3072);
461 assert_eq!(collector.get_current_usage("Component2"), 4096);
462 assert_eq!(collector.get_total_current_usage(), 7168);
463
464 collector.record_event(MemoryEvent::new(
466 MemoryEventType::Deallocation,
467 "Component1",
468 1024,
469 0x1000,
470 ));
471
472 assert_eq!(collector.get_current_usage("Component1"), 2048);
474 assert_eq!(collector.get_total_current_usage(), 6144);
475
476 let comp1_stats = collector
478 .get_allocation_stats("Component1")
479 .expect("Operation failed");
480 assert_eq!(comp1_stats.count, 2);
481 assert_eq!(comp1_stats.total_bytes, 3072);
482 assert_eq!(comp1_stats.peak_usage, 3072);
483
484 let report = collector.generate_report();
486 assert_eq!(report.total_current_usage, 6144);
487 assert_eq!(report.total_allocation_count, 3);
488
489 let comp1_report = report
491 .component_stats
492 .get("Component1")
493 .expect("Operation failed");
494 assert_eq!(comp1_report.current_usage, 2048);
495 assert_eq!(comp1_report.allocation_count, 2);
496 }
497}