use std::collections::{HashMap, VecDeque};
use std::sync::{Mutex, RwLock};
use std::time::{Duration, Instant};
use crate::memory::metrics::event::{MemoryEvent, MemoryEventType};
use rand::rngs::StdRng;
use rand::{Rng, RngExt, SeedableRng};
#[cfg(feature = "memory_metrics")]
#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
struct Random {
rng: StdRng,
}
impl Default for Random {
fn default() -> Self {
Self {
rng: StdRng::seed_from_u64(0), }
}
}
impl Random {
fn gen_range(&mut self, range: std::ops::Range<f64>) -> f64 {
self.rng.random_range(range)
}
fn random_range(&mut self, range: std::ops::Range<f64>) -> f64 {
self.rng.random_range(range)
}
}
#[derive(Debug, Clone)]
pub struct MemoryMetricsConfig {
pub enabled: bool,
pub capture_call_stacks: bool,
pub max_events: usize,
pub real_time_aggregation: bool,
pub samplingrate: f64,
}
impl Default for MemoryMetricsConfig {
fn default() -> Self {
Self {
enabled: true,
capture_call_stacks: cfg!(feature = "memory_call_stack"),
max_events: 10000,
real_time_aggregation: true,
samplingrate: 1.0,
}
}
}
#[derive(Debug, Clone)]
pub struct AllocationStats {
pub count: usize,
pub total_bytes: usize,
pub average_size: f64,
pub peak_usage: usize,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
pub struct ComponentMemoryStats {
pub current_usage: usize,
pub peak_usage: usize,
pub allocation_count: usize,
pub total_allocated: usize,
pub avg_allocation_size: f64,
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "memory_metrics",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct MemoryReport {
pub total_current_usage: usize,
pub total_peak_usage: usize,
pub total_allocation_count: usize,
pub total_allocated_bytes: usize,
pub component_stats: HashMap<String, ComponentMemoryStats>,
pub duration: Duration,
}
pub struct MemoryMetricsCollector {
config: MemoryMetricsConfig,
events: RwLock<VecDeque<MemoryEvent>>,
current_usage: RwLock<HashMap<String, usize>>,
peak_usage: RwLock<HashMap<String, usize>>,
allocation_count: RwLock<HashMap<String, usize>>,
total_allocated: RwLock<HashMap<String, usize>>,
avg_allocation_size: RwLock<HashMap<String, f64>>,
start_time: Instant,
rng: Mutex<Random>,
}
impl MemoryMetricsCollector {
pub fn new(config: MemoryMetricsConfig) -> Self {
Self {
config,
events: RwLock::new(VecDeque::with_capacity(1000)),
current_usage: RwLock::new(HashMap::new()),
peak_usage: RwLock::new(HashMap::new()),
allocation_count: RwLock::new(HashMap::new()),
total_allocated: RwLock::new(HashMap::new()),
avg_allocation_size: RwLock::new(HashMap::new()),
start_time: Instant::now(),
rng: Mutex::new(Random::default()),
}
}
pub fn record_event(&self, event: MemoryEvent) {
if !self.config.enabled {
return;
}
if self.config.samplingrate < 1.0 {
let mut rng = self.rng.lock().expect("Operation failed");
if rng.random_range(0.0..1.0) > self.config.samplingrate {
return;
}
}
if self.config.real_time_aggregation {
self.update_metrics(&event);
}
if self.config.max_events > 0 {
let mut events = self.events.write().expect("Operation failed");
events.push_back(event);
while events.len() > self.config.max_events {
events.pop_front();
}
}
}
fn update_metrics(&self, event: &MemoryEvent) {
match event.event_type {
MemoryEventType::Allocation => {
let mut current_usage = self.current_usage.write().expect("Operation failed");
let component_usage = current_usage.entry(event.component.clone()).or_insert(0);
*component_usage += event.size;
let mut peak_usage = self.peak_usage.write().expect("Operation failed");
let peak = peak_usage.entry(event.component.clone()).or_insert(0);
*peak = (*peak).max(*component_usage);
let mut allocation_count = self.allocation_count.write().expect("Operation failed");
let count = allocation_count.entry(event.component.clone()).or_insert(0);
*count += 1;
let mut total_allocated = self.total_allocated.write().expect("Operation failed");
let total = total_allocated.entry(event.component.clone()).or_insert(0);
*total += event.size;
let mut avg_allocation_size =
self.avg_allocation_size.write().expect("Operation failed");
let avg = avg_allocation_size
.entry(event.component.clone())
.or_insert(0.0);
*avg = (*avg * (*count as f64 - 1.0) + event.size as f64) / *count as f64;
}
MemoryEventType::Deallocation => {
let mut current_usage = self.current_usage.write().expect("Operation failed");
let component_usage = current_usage.entry(event.component.clone()).or_insert(0);
*component_usage = component_usage.saturating_sub(event.size);
}
MemoryEventType::Resize => {
if let Some(old_size) = event
.metadata
.get("old_size")
.and_then(|s| s.parse::<usize>().ok())
{
let size_diff = event.size as isize - old_size as isize;
let mut current_usage = self.current_usage.write().expect("Operation failed");
let component_usage = current_usage.entry(event.component.clone()).or_insert(0);
if size_diff > 0 {
*component_usage += size_diff as usize;
} else {
*component_usage = component_usage.saturating_sub((-size_diff) as usize);
}
let mut peak_usage = self.peak_usage.write().expect("Operation failed");
let peak = peak_usage.entry(event.component.clone()).or_insert(0);
*peak = (*peak).max(*component_usage);
}
}
MemoryEventType::Access | MemoryEventType::Transfer => {
}
}
}
pub fn get_current_usage(&self, component: &str) -> usize {
let current_usage = self.current_usage.read().expect("Operation failed");
*current_usage.get(component).unwrap_or(&0)
}
pub fn get_peak_usage(&self, component: &str) -> usize {
let peak_usage = self.peak_usage.read().expect("Operation failed");
*peak_usage.get(component).unwrap_or(&0)
}
pub fn get_total_current_usage(&self) -> usize {
let current_usage = self.current_usage.read().expect("Operation failed");
current_usage.values().sum()
}
pub fn get_total_peak_usage(&self) -> usize {
let peak_usage = self.peak_usage.read().expect("Operation failed");
let component_sum: usize = peak_usage.values().sum();
component_sum
}
pub fn get_allocation_stats(&self, component: &str) -> Option<AllocationStats> {
let allocation_count = self.allocation_count.read().expect("Operation failed");
let count = *allocation_count.get(component)?;
let total_allocated = self.total_allocated.read().expect("Operation failed");
let total = *total_allocated.get(component)?;
let avg_allocation_size = self.avg_allocation_size.read().expect("Operation failed");
let avg = *avg_allocation_size.get(component)?;
let peak_usage = self.peak_usage.read().expect("Operation failed");
let peak = *peak_usage.get(component)?;
Some(AllocationStats {
count,
total_bytes: total,
average_size: avg,
peak_usage: peak,
})
}
pub fn generate_report(&self) -> MemoryReport {
let current_usage = self.current_usage.read().expect("Operation failed");
let peak_usage = self.peak_usage.read().expect("Operation failed");
let allocation_count = self.allocation_count.read().expect("Operation failed");
let total_allocated = self.total_allocated.read().expect("Operation failed");
let avg_allocation_size = self.avg_allocation_size.read().expect("Operation failed");
let mut component_stats = HashMap::new();
let mut components = std::collections::HashSet::new();
components.extend(current_usage.keys().cloned());
components.extend(peak_usage.keys().cloned());
components.extend(allocation_count.keys().cloned());
for component in components {
let stats = ComponentMemoryStats {
current_usage: *current_usage.get(&component).unwrap_or(&0),
peak_usage: *peak_usage.get(&component).unwrap_or(&0),
allocation_count: *allocation_count.get(&component).unwrap_or(&0),
total_allocated: *total_allocated.get(&component).unwrap_or(&0),
avg_allocation_size: *avg_allocation_size.get(&component).unwrap_or(&0.0),
};
component_stats.insert(component, stats);
}
MemoryReport {
total_current_usage: current_usage.values().sum(),
total_peak_usage: self.get_total_peak_usage(),
total_allocation_count: allocation_count.values().sum(),
total_allocated_bytes: total_allocated.values().sum(),
component_stats,
duration: self.start_time.elapsed(),
}
}
pub fn reset(&self) {
let mut events = self.events.write().expect("Operation failed");
events.clear();
let mut current_usage = self.current_usage.write().expect("Operation failed");
current_usage.clear();
let mut peak_usage = self.peak_usage.write().expect("Operation failed");
peak_usage.clear();
let mut allocation_count = self.allocation_count.write().expect("Operation failed");
allocation_count.clear();
let mut total_allocated = self.total_allocated.write().expect("Operation failed");
total_allocated.clear();
let mut avg_allocation_size = self.avg_allocation_size.write().expect("Operation failed");
avg_allocation_size.clear();
}
pub fn get_events(&self) -> Vec<MemoryEvent> {
let events = self.events.read().expect("Operation failed");
events.iter().cloned().collect()
}
pub fn to_json(&self) -> serde_json::Value {
let report = self.generate_report();
let mut json_obj = serde_json::Map::new();
json_obj.insert(
"total_allocation_count".to_string(),
serde_json::Value::Number(report.total_allocation_count.into()),
);
json_obj.insert(
"total_peak_usage".to_string(),
serde_json::Value::Number(report.total_peak_usage.into()),
);
json_obj.insert(
"total_current_usage".to_string(),
serde_json::Value::Number(report.total_current_usage.into()),
);
json_obj.insert(
"total_allocated_bytes".to_string(),
serde_json::Value::Number(report.total_allocated_bytes.into()),
);
let component_stats: serde_json::Value = report
.component_stats
.iter()
.map(|(k, v)| {
(
k.clone(),
serde_json::json!({
"current_usage": v.current_usage,
"peak_usage": v.peak_usage,
"allocation_count": v.allocation_count,
"total_allocated": v.total_allocated,
"avg_allocation_size": v.avg_allocation_size
}),
)
})
.collect::<serde_json::Map<String, serde_json::Value>>()
.into();
json_obj.insert("component_stats".to_string(), component_stats);
json_obj.insert(
"duration_secs".to_string(),
serde_json::Value::Number(report.duration.as_secs().into()),
);
serde_json::Value::Object(json_obj)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::memory::metrics::event::MemoryEventType;
#[test]
fn test_memory_metrics_collector() {
let config = MemoryMetricsConfig {
enabled: true,
capture_call_stacks: false,
max_events: 100,
real_time_aggregation: true,
samplingrate: 1.0,
};
let collector = MemoryMetricsCollector::new(config);
collector.record_event(MemoryEvent::new(
MemoryEventType::Allocation,
"Component1",
1024,
0x1000,
));
collector.record_event(MemoryEvent::new(
MemoryEventType::Allocation,
"Component1",
2048,
0x2000,
));
collector.record_event(MemoryEvent::new(
MemoryEventType::Allocation,
"Component2",
4096,
0x3000,
));
assert_eq!(collector.get_current_usage("Component1"), 3072);
assert_eq!(collector.get_current_usage("Component2"), 4096);
assert_eq!(collector.get_total_current_usage(), 7168);
collector.record_event(MemoryEvent::new(
MemoryEventType::Deallocation,
"Component1",
1024,
0x1000,
));
assert_eq!(collector.get_current_usage("Component1"), 2048);
assert_eq!(collector.get_total_current_usage(), 6144);
let comp1_stats = collector
.get_allocation_stats("Component1")
.expect("Operation failed");
assert_eq!(comp1_stats.count, 2);
assert_eq!(comp1_stats.total_bytes, 3072);
assert_eq!(comp1_stats.peak_usage, 3072);
let report = collector.generate_report();
assert_eq!(report.total_current_usage, 6144);
assert_eq!(report.total_allocation_count, 3);
let comp1_report = report
.component_stats
.get("Component1")
.expect("Operation failed");
assert_eq!(comp1_report.current_usage, 2048);
assert_eq!(comp1_report.allocation_count, 2);
}
}