wasm_sandbox/
monitoring.rs

1//! Resource monitoring and usage tracking
2
3use std::time::{Duration, Instant};
4use serde::{Serialize, Deserialize};
5use crate::error::ResourceKind;
6use crate::InstanceId;
7
8/// Detailed resource usage information with timeline
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct DetailedResourceUsage {
11    pub memory: MemoryUsage,
12    pub cpu: CpuUsage,
13    pub io: IoUsage,
14    pub timeline: Vec<ResourceSnapshot>,
15}
16
17/// Memory usage statistics
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct MemoryUsage {
20    pub current_bytes: usize,
21    pub peak_bytes: usize,
22    pub allocations: u64,
23    pub deallocations: u64,
24}
25
26/// CPU usage statistics
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct CpuUsage {
29    pub time_spent: Duration,
30    pub instructions_executed: u64,
31    pub function_calls: u64,
32}
33
34/// I/O usage statistics
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct IoUsage {
37    pub files_opened: u64,
38    pub bytes_read: u64,
39    pub bytes_written: u64,
40    pub network_requests: u64,
41}
42
43/// Resource snapshot at a point in time
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct ResourceSnapshot {
46    pub timestamp: u64, // milliseconds since epoch
47    pub memory_bytes: usize,
48    pub cpu_time_ms: u64,
49    pub active_handles: u32,
50}
51
52/// Resource monitor for tracking usage patterns
53#[derive(Debug)]
54pub struct ResourceMonitor {
55    _instance_id: Option<InstanceId>,
56    start_time: Instant,
57    memory_usage: MemoryUsage,
58    cpu_usage: CpuUsage,
59    io_usage: IoUsage,
60    snapshots: Vec<ResourceSnapshot>,
61    snapshot_interval: Duration,
62    last_snapshot: Instant,
63}
64
65impl ResourceMonitor {
66    /// Create a new resource monitor
67    pub fn new(instance_id: Option<InstanceId>) -> Self {
68        let now = Instant::now();
69        Self {
70            _instance_id: instance_id,
71            start_time: now,
72            memory_usage: MemoryUsage {
73                current_bytes: 0,
74                peak_bytes: 0,
75                allocations: 0,
76                deallocations: 0,
77            },
78            cpu_usage: CpuUsage {
79                time_spent: Duration::from_millis(0),
80                instructions_executed: 0,
81                function_calls: 0,
82            },
83            io_usage: IoUsage {
84                files_opened: 0,
85                bytes_read: 0,
86                bytes_written: 0,
87                network_requests: 0,
88            },
89            snapshots: Vec::new(),
90            snapshot_interval: Duration::from_secs(1),
91            last_snapshot: now,
92        }
93    }
94
95    /// Update memory usage
96    pub fn update_memory(&mut self, current_bytes: usize) {
97        self.memory_usage.current_bytes = current_bytes;
98        if current_bytes > self.memory_usage.peak_bytes {
99            self.memory_usage.peak_bytes = current_bytes;
100        }
101        self.take_snapshot_if_needed();
102    }
103
104    /// Record memory allocation
105    pub fn record_allocation(&mut self, _bytes: usize) {
106        self.memory_usage.allocations += 1;
107    }
108
109    /// Record memory deallocation
110    pub fn record_deallocation(&mut self, _bytes: usize) {
111        self.memory_usage.deallocations += 1;
112    }
113
114    /// Update CPU usage
115    pub fn update_cpu_time(&mut self, time_spent: Duration) {
116        self.cpu_usage.time_spent = time_spent;
117        self.take_snapshot_if_needed();
118    }
119
120    /// Record function call
121    pub fn record_function_call(&mut self) {
122        self.cpu_usage.function_calls += 1;
123    }
124
125    /// Record instructions executed
126    pub fn record_instructions(&mut self, count: u64) {
127        self.cpu_usage.instructions_executed += count;
128    }
129
130    /// Record file operation
131    pub fn record_file_open(&mut self) {
132        self.io_usage.files_opened += 1;
133    }
134
135    /// Record file read
136    pub fn record_file_read(&mut self, bytes: u64) {
137        self.io_usage.bytes_read += bytes;
138    }
139
140    /// Record file write
141    pub fn record_file_write(&mut self, bytes: u64) {
142        self.io_usage.bytes_written += bytes;
143    }
144
145    /// Record network request
146    pub fn record_network_request(&mut self) {
147        self.io_usage.network_requests += 1;
148    }
149
150    /// Take a snapshot if enough time has passed
151    fn take_snapshot_if_needed(&mut self) {
152        let now = Instant::now();
153        if now.duration_since(self.last_snapshot) >= self.snapshot_interval {
154            self.take_snapshot();
155            self.last_snapshot = now;
156        }
157    }
158
159    /// Take a resource snapshot
160    pub fn take_snapshot(&mut self) {
161        let snapshot = ResourceSnapshot {
162            timestamp: std::time::SystemTime::now()
163                .duration_since(std::time::UNIX_EPOCH)
164                .unwrap_or_default()
165                .as_millis() as u64,
166            memory_bytes: self.memory_usage.current_bytes,
167            cpu_time_ms: self.cpu_usage.time_spent.as_millis() as u64,
168            active_handles: (self.io_usage.files_opened - self.io_usage.bytes_written.min(self.io_usage.files_opened)) as u32,
169        };
170        
171        self.snapshots.push(snapshot);
172        
173        // Keep only last 100 snapshots to prevent memory growth
174        if self.snapshots.len() > 100 {
175            self.snapshots.remove(0);
176        }
177    }
178
179    /// Get detailed resource usage
180    pub fn get_detailed_usage(&self) -> DetailedResourceUsage {
181        DetailedResourceUsage {
182            memory: self.memory_usage.clone(),
183            cpu: self.cpu_usage.clone(),
184            io: self.io_usage.clone(),
185            timeline: self.snapshots.clone(),
186        }
187    }
188
189    /// Get current resource usage
190    pub fn get_current_usage(&self) -> DetailedResourceUsage {
191        DetailedResourceUsage {
192            memory: self.memory_usage.clone(),
193            cpu: self.cpu_usage.clone(),
194            io: self.io_usage.clone(),
195            timeline: self.snapshots.clone(),
196        }
197    }
198
199    /// Check if resource limit is exceeded
200    pub fn check_resource_limit(&self, kind: &ResourceKind, limit: u64) -> Option<(u64, String)> {
201        match kind {
202            ResourceKind::Memory => {
203                let used = self.memory_usage.current_bytes as u64;
204                if used > limit {
205                    Some((used, format!("Memory usage {} bytes exceeds limit {} bytes", used, limit)))
206                } else {
207                    None
208                }
209            },
210            ResourceKind::CpuTime => {
211                let used = self.cpu_usage.time_spent.as_millis() as u64;
212                if used > limit {
213                    Some((used, format!("CPU time {} ms exceeds limit {} ms", used, limit)))
214                } else {
215                    None
216                }
217            },
218            ResourceKind::ExecutionTime => {
219                let used = Instant::now().duration_since(self.start_time).as_millis() as u64;
220                if used > limit {
221                    Some((used, format!("Execution time {} ms exceeds limit {} ms", used, limit)))
222                } else {
223                    None
224                }
225            },
226            ResourceKind::FileHandles => {
227                let used = self.io_usage.files_opened;
228                if used > limit {
229                    Some((used, format!("File handles {} exceeds limit {}", used, limit)))
230                } else {
231                    None
232                }
233            },
234            ResourceKind::NetworkConnections => {
235                let used = self.io_usage.network_requests;
236                if used > limit {
237                    Some((used, format!("Network requests {} exceeds limit {}", used, limit)))
238                } else {
239                    None
240                }
241            },
242            _ => None,
243        }
244    }
245
246    /// Get utilization percentage for a resource
247    pub fn get_utilization(&self, kind: &ResourceKind, limit: u64) -> f64 {
248        if limit == 0 {
249            return 0.0;
250        }
251        
252        let used = match kind {
253            ResourceKind::Memory => self.memory_usage.current_bytes as u64,
254            ResourceKind::CpuTime => self.cpu_usage.time_spent.as_millis() as u64,
255            ResourceKind::ExecutionTime => Instant::now().duration_since(self.start_time).as_millis() as u64,
256            ResourceKind::FileHandles => self.io_usage.files_opened,
257            ResourceKind::NetworkConnections => self.io_usage.network_requests,
258            _ => 0,
259        };
260        
261        (used as f64 / limit as f64 * 100.0).min(100.0)
262    }
263
264    /// Reset usage statistics
265    pub fn reset(&mut self) {
266        self.start_time = Instant::now();
267        self.memory_usage = MemoryUsage {
268            current_bytes: 0,
269            peak_bytes: 0,
270            allocations: 0,
271            deallocations: 0,
272        };
273        self.cpu_usage = CpuUsage {
274            time_spent: Duration::from_millis(0),
275            instructions_executed: 0,
276            function_calls: 0,
277        };
278        self.io_usage = IoUsage {
279            files_opened: 0,
280            bytes_read: 0,
281            bytes_written: 0,
282            network_requests: 0,
283        };
284        self.snapshots.clear();
285        self.last_snapshot = Instant::now();
286    }
287}