sandbox_rs/monitoring/
ebpf.rs1use crate::errors::Result;
9use nix::unistd::Pid;
10use std::collections::HashMap;
11
12#[derive(Debug, Clone)]
14pub struct SyscallEvent {
15 pub syscall_id: u64,
17 pub syscall_name: String,
19 pub duration_us: u64,
21 pub timestamp: u64,
23 pub is_slow: bool,
25}
26
27impl SyscallEvent {
28 pub fn is_slow_syscall(&self) -> bool {
30 self.duration_us > 10_000 }
32
33 pub fn duration_ms(&self) -> f64 {
35 self.duration_us as f64 / 1000.0
36 }
37}
38
39#[derive(Debug, Clone, Default)]
41pub struct SyscallStats {
42 pub total_syscalls: u64,
44 pub slow_syscalls: u64,
46 pub total_time_us: u64,
48 pub syscalls_by_name: HashMap<String, (u64, u64)>, pub slowest_syscalls: Vec<SyscallEvent>,
52}
53
54pub struct EBpfMonitor {
56 pid: Pid,
57 events: Vec<SyscallEvent>,
58 stats: SyscallStats,
59}
60
61impl EBpfMonitor {
62 pub fn new(pid: Pid) -> Self {
64 EBpfMonitor {
65 pid,
66 events: Vec::new(),
67 stats: SyscallStats::default(),
68 }
69 }
70
71 pub fn collect_stats(&mut self) -> Result<SyscallStats> {
73 self._compute_statistics();
75 Ok(self.stats.clone())
76 }
77
78 pub fn add_event(&mut self, event: SyscallEvent) {
80 self.events.push(event);
81 self._compute_statistics();
82 }
83
84 pub fn clear(&mut self) {
86 self.events.clear();
87 self.stats = SyscallStats::default();
88 }
89
90 pub fn pid(&self) -> Pid {
92 self.pid
93 }
94
95 pub fn slow_syscall_count(&self) -> u64 {
97 self.stats.slow_syscalls
98 }
99
100 pub fn slowest_syscalls(&self, n: usize) -> Vec<SyscallEvent> {
102 self.stats
103 .slowest_syscalls
104 .iter()
105 .take(n)
106 .cloned()
107 .collect()
108 }
109
110 fn _compute_statistics(&mut self) {
112 let mut stats = SyscallStats::default();
113 let mut by_name: HashMap<String, (u64, u64)> = HashMap::new();
114 let mut slowest: Vec<SyscallEvent> = Vec::new();
115
116 for event in &self.events {
117 stats.total_syscalls += 1;
118 stats.total_time_us += event.duration_us;
119
120 if event.is_slow {
121 stats.slow_syscalls += 1;
122 }
123
124 let entry = by_name.entry(event.syscall_name.clone()).or_insert((0, 0));
126 entry.0 += 1;
127 entry.1 += event.duration_us;
128
129 slowest.push(event.clone());
131 }
132
133 slowest.sort_by(|a, b| b.duration_us.cmp(&a.duration_us));
135 slowest.truncate(10);
136
137 stats.syscalls_by_name = by_name;
138 stats.slowest_syscalls = slowest;
139
140 self.stats = stats;
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_syscall_event_is_slow() {
150 let event_slow = SyscallEvent {
151 syscall_id: 1,
152 syscall_name: "read".to_string(),
153 duration_us: 15_000, timestamp: 0,
155 is_slow: true,
156 };
157 assert!(event_slow.is_slow_syscall());
158
159 let event_fast = SyscallEvent {
160 syscall_id: 1,
161 syscall_name: "read".to_string(),
162 duration_us: 5_000, timestamp: 0,
164 is_slow: false,
165 };
166 assert!(!event_fast.is_slow_syscall());
167 }
168
169 #[test]
170 fn test_syscall_event_duration_ms() {
171 let event = SyscallEvent {
172 syscall_id: 1,
173 syscall_name: "write".to_string(),
174 duration_us: 10_000, timestamp: 0,
176 is_slow: false,
177 };
178 assert_eq!(event.duration_ms(), 10.0);
179 }
180
181 #[test]
182 fn test_ebpf_monitor_new() {
183 let pid = Pid::from_raw(std::process::id() as i32);
184 let monitor = EBpfMonitor::new(pid);
185 assert_eq!(monitor.pid(), pid);
186 assert_eq!(monitor.slow_syscall_count(), 0);
187 }
188
189 #[test]
190 fn test_ebpf_monitor_add_event() {
191 let pid = Pid::from_raw(std::process::id() as i32);
192 let mut monitor = EBpfMonitor::new(pid);
193
194 let event = SyscallEvent {
195 syscall_id: 1,
196 syscall_name: "read".to_string(),
197 duration_us: 5_000,
198 timestamp: 0,
199 is_slow: false,
200 };
201
202 monitor.add_event(event);
203 assert_eq!(monitor.stats.total_syscalls, 1);
204 assert_eq!(monitor.stats.slow_syscalls, 0);
205 }
206
207 #[test]
208 fn test_ebpf_monitor_slow_syscalls() {
209 let pid = Pid::from_raw(std::process::id() as i32);
210 let mut monitor = EBpfMonitor::new(pid);
211
212 monitor.add_event(SyscallEvent {
214 syscall_id: 1,
215 syscall_name: "read".to_string(),
216 duration_us: 5_000,
217 timestamp: 0,
218 is_slow: false,
219 });
220
221 monitor.add_event(SyscallEvent {
223 syscall_id: 2,
224 syscall_name: "write".to_string(),
225 duration_us: 15_000,
226 timestamp: 1,
227 is_slow: true,
228 });
229
230 assert_eq!(monitor.stats.total_syscalls, 2);
231 assert_eq!(monitor.stats.slow_syscalls, 1);
232 }
233
234 #[test]
235 fn test_ebpf_monitor_slowest_syscalls() {
236 let pid = Pid::from_raw(std::process::id() as i32);
237 let mut monitor = EBpfMonitor::new(pid);
238
239 for i in 0..5 {
240 monitor.add_event(SyscallEvent {
241 syscall_id: i,
242 syscall_name: format!("syscall_{}", i),
243 duration_us: (i + 1) * 1000,
244 timestamp: i,
245 is_slow: (i + 1) * 1000 > 10_000,
246 });
247 }
248
249 let slowest = monitor.slowest_syscalls(3);
250 assert_eq!(slowest.len(), 3);
251 }
252
253 #[test]
254 fn test_ebpf_monitor_clear() {
255 let pid = Pid::from_raw(std::process::id() as i32);
256 let mut monitor = EBpfMonitor::new(pid);
257
258 monitor.add_event(SyscallEvent {
259 syscall_id: 1,
260 syscall_name: "read".to_string(),
261 duration_us: 5_000,
262 timestamp: 0,
263 is_slow: false,
264 });
265
266 assert_eq!(monitor.stats.total_syscalls, 1);
267
268 monitor.clear();
269 assert_eq!(monitor.stats.total_syscalls, 0);
270 assert_eq!(monitor.stats.slow_syscalls, 0);
271 }
272}