use std::time::{Duration, Instant};
use serde::{Serialize, Deserialize};
use crate::error::ResourceKind;
use crate::InstanceId;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetailedResourceUsage {
pub memory: MemoryUsage,
pub cpu: CpuUsage,
pub io: IoUsage,
pub timeline: Vec<ResourceSnapshot>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryUsage {
pub current_bytes: usize,
pub peak_bytes: usize,
pub allocations: u64,
pub deallocations: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuUsage {
pub time_spent: Duration,
pub instructions_executed: u64,
pub function_calls: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IoUsage {
pub files_opened: u64,
pub bytes_read: u64,
pub bytes_written: u64,
pub network_requests: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceSnapshot {
pub timestamp: u64, pub memory_bytes: usize,
pub cpu_time_ms: u64,
pub active_handles: u32,
}
#[derive(Debug)]
pub struct ResourceMonitor {
_instance_id: Option<InstanceId>,
start_time: Instant,
memory_usage: MemoryUsage,
cpu_usage: CpuUsage,
io_usage: IoUsage,
snapshots: Vec<ResourceSnapshot>,
snapshot_interval: Duration,
last_snapshot: Instant,
}
impl ResourceMonitor {
pub fn new(instance_id: Option<InstanceId>) -> Self {
let now = Instant::now();
Self {
_instance_id: instance_id,
start_time: now,
memory_usage: MemoryUsage {
current_bytes: 0,
peak_bytes: 0,
allocations: 0,
deallocations: 0,
},
cpu_usage: CpuUsage {
time_spent: Duration::from_millis(0),
instructions_executed: 0,
function_calls: 0,
},
io_usage: IoUsage {
files_opened: 0,
bytes_read: 0,
bytes_written: 0,
network_requests: 0,
},
snapshots: Vec::new(),
snapshot_interval: Duration::from_secs(1),
last_snapshot: now,
}
}
pub fn update_memory(&mut self, current_bytes: usize) {
self.memory_usage.current_bytes = current_bytes;
if current_bytes > self.memory_usage.peak_bytes {
self.memory_usage.peak_bytes = current_bytes;
}
self.take_snapshot_if_needed();
}
pub fn record_allocation(&mut self, _bytes: usize) {
self.memory_usage.allocations += 1;
}
pub fn record_deallocation(&mut self, _bytes: usize) {
self.memory_usage.deallocations += 1;
}
pub fn update_cpu_time(&mut self, time_spent: Duration) {
self.cpu_usage.time_spent = time_spent;
self.take_snapshot_if_needed();
}
pub fn record_function_call(&mut self) {
self.cpu_usage.function_calls += 1;
}
pub fn record_instructions(&mut self, count: u64) {
self.cpu_usage.instructions_executed += count;
}
pub fn record_file_open(&mut self) {
self.io_usage.files_opened += 1;
}
pub fn record_file_read(&mut self, bytes: u64) {
self.io_usage.bytes_read += bytes;
}
pub fn record_file_write(&mut self, bytes: u64) {
self.io_usage.bytes_written += bytes;
}
pub fn record_network_request(&mut self) {
self.io_usage.network_requests += 1;
}
fn take_snapshot_if_needed(&mut self) {
let now = Instant::now();
if now.duration_since(self.last_snapshot) >= self.snapshot_interval {
self.take_snapshot();
self.last_snapshot = now;
}
}
pub fn take_snapshot(&mut self) {
let snapshot = ResourceSnapshot {
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64,
memory_bytes: self.memory_usage.current_bytes,
cpu_time_ms: self.cpu_usage.time_spent.as_millis() as u64,
active_handles: (self.io_usage.files_opened - self.io_usage.bytes_written.min(self.io_usage.files_opened)) as u32,
};
self.snapshots.push(snapshot);
if self.snapshots.len() > 100 {
self.snapshots.remove(0);
}
}
pub fn get_detailed_usage(&self) -> DetailedResourceUsage {
DetailedResourceUsage {
memory: self.memory_usage.clone(),
cpu: self.cpu_usage.clone(),
io: self.io_usage.clone(),
timeline: self.snapshots.clone(),
}
}
pub fn get_current_usage(&self) -> DetailedResourceUsage {
DetailedResourceUsage {
memory: self.memory_usage.clone(),
cpu: self.cpu_usage.clone(),
io: self.io_usage.clone(),
timeline: self.snapshots.clone(),
}
}
pub fn check_resource_limit(&self, kind: &ResourceKind, limit: u64) -> Option<(u64, String)> {
match kind {
ResourceKind::Memory => {
let used = self.memory_usage.current_bytes as u64;
if used > limit {
Some((used, format!("Memory usage {} bytes exceeds limit {} bytes", used, limit)))
} else {
None
}
},
ResourceKind::CpuTime => {
let used = self.cpu_usage.time_spent.as_millis() as u64;
if used > limit {
Some((used, format!("CPU time {} ms exceeds limit {} ms", used, limit)))
} else {
None
}
},
ResourceKind::ExecutionTime => {
let used = Instant::now().duration_since(self.start_time).as_millis() as u64;
if used > limit {
Some((used, format!("Execution time {} ms exceeds limit {} ms", used, limit)))
} else {
None
}
},
ResourceKind::FileHandles => {
let used = self.io_usage.files_opened;
if used > limit {
Some((used, format!("File handles {} exceeds limit {}", used, limit)))
} else {
None
}
},
ResourceKind::NetworkConnections => {
let used = self.io_usage.network_requests;
if used > limit {
Some((used, format!("Network requests {} exceeds limit {}", used, limit)))
} else {
None
}
},
_ => None,
}
}
pub fn get_utilization(&self, kind: &ResourceKind, limit: u64) -> f64 {
if limit == 0 {
return 0.0;
}
let used = match kind {
ResourceKind::Memory => self.memory_usage.current_bytes as u64,
ResourceKind::CpuTime => self.cpu_usage.time_spent.as_millis() as u64,
ResourceKind::ExecutionTime => Instant::now().duration_since(self.start_time).as_millis() as u64,
ResourceKind::FileHandles => self.io_usage.files_opened,
ResourceKind::NetworkConnections => self.io_usage.network_requests,
_ => 0,
};
(used as f64 / limit as f64 * 100.0).min(100.0)
}
pub fn reset(&mut self) {
self.start_time = Instant::now();
self.memory_usage = MemoryUsage {
current_bytes: 0,
peak_bytes: 0,
allocations: 0,
deallocations: 0,
};
self.cpu_usage = CpuUsage {
time_spent: Duration::from_millis(0),
instructions_executed: 0,
function_calls: 0,
};
self.io_usage = IoUsage {
files_opened: 0,
bytes_read: 0,
bytes_written: 0,
network_requests: 0,
};
self.snapshots.clear();
self.last_snapshot = Instant::now();
}
}