1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::time::{Duration, Instant};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct SystemResourceSnapshot {
13 pub timestamp: u64,
14 pub cpu_metrics: CpuMetrics,
15 pub memory_metrics: MemoryMetrics,
16 pub gpu_metrics: Option<GpuMetrics>,
17 pub io_metrics: IoMetrics,
18 pub network_metrics: NetworkMetrics,
19 pub process_metrics: ProcessMetrics,
20 pub thread_metrics: HashMap<u64, ThreadMetrics>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct CpuMetrics {
26 pub overall_usage: f32,
28 pub core_usage: Vec<f32>,
30 pub frequency: u64,
32 pub load_average: Option<(f64, f64, f64)>,
34 pub temperature: Option<f32>,
36 pub context_switches: u64,
38 pub cache_misses: Option<u64>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct MemoryMetrics {
45 pub total_physical: u64,
47 pub available_physical: u64,
49 pub used_physical: u64,
51 pub total_virtual: u64,
53 pub available_virtual: u64,
55 pub pressure: f32,
57 pub page_faults: u64,
59 pub bandwidth_utilization: Option<f32>,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct GpuMetrics {
66 pub device_name: String,
68 pub gpu_usage: f32,
70 pub memory_used: u64,
72 pub memory_total: u64,
74 pub temperature: Option<f32>,
76 pub frequency: Option<u64>,
78 pub power_usage: Option<f32>,
80 pub compute_usage: Option<f32>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct IoMetrics {
87 pub disk_read_bps: u64,
89 pub disk_write_bps: u64,
91 pub disk_read_ops: u64,
93 pub disk_write_ops: u64,
95 pub disk_latency_us: Option<u64>,
97 pub disk_queue_depth: Option<u32>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct NetworkMetrics {
104 pub rx_bps: u64,
106 pub tx_bps: u64,
108 pub rx_pps: u64,
110 pub tx_pps: u64,
112 pub latency_ms: Option<f32>,
114 pub connections: u32,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct ProcessMetrics {
121 pub pid: u32,
123 pub name: String,
125 pub cpu_usage: f32,
127 pub memory_usage: u64,
129 pub thread_count: u32,
131 pub handle_count: u32,
133 pub priority: i32,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct ThreadMetrics {
140 pub thread_id: u64,
142 pub thread_name: Option<String>,
144 pub cpu_time_ns: u64,
146 pub state: ThreadState,
148 pub priority: i32,
150 pub cpu_affinity: Option<u64>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub enum ThreadState {
157 Running,
158 Sleeping,
159 Waiting,
160 Blocked,
161 Zombie,
162}
163
164pub struct SystemProfiler {
166 start_time: Instant,
167 #[allow(dead_code)]
168 sample_interval: Duration,
169 last_snapshot: Option<SystemResourceSnapshot>,
170 #[cfg(feature = "system-metrics")]
171 system: std::cell::RefCell<sysinfo::System>,
172}
173
174impl SystemProfiler {
175 pub fn new(sample_interval: Duration) -> Self {
177 Self {
178 start_time: Instant::now(),
179 sample_interval,
180 last_snapshot: None,
181 #[cfg(feature = "system-metrics")]
182 system: std::cell::RefCell::new(sysinfo::System::new_all()),
183 }
184 }
185
186 pub fn take_snapshot(&mut self) -> Result<SystemResourceSnapshot, Box<dyn std::error::Error>> {
188 let timestamp = self.start_time.elapsed().as_millis() as u64;
189
190 let snapshot = SystemResourceSnapshot {
191 timestamp,
192 cpu_metrics: self.collect_cpu_metrics()?,
193 memory_metrics: self.collect_memory_metrics()?,
194 gpu_metrics: self.collect_gpu_metrics()?,
195 io_metrics: self.collect_io_metrics()?,
196 network_metrics: self.collect_network_metrics()?,
197 process_metrics: self.collect_process_metrics()?,
198 thread_metrics: self.collect_thread_metrics()?,
199 };
200
201 self.last_snapshot = Some(snapshot.clone());
202 Ok(snapshot)
203 }
204
205 fn collect_cpu_metrics(&self) -> Result<CpuMetrics, Box<dyn std::error::Error>> {
207 #[cfg(feature = "system-metrics")]
208 {
209 let mut system = self.system.borrow_mut();
210 system.refresh_cpu_all();
211
212 let overall_usage = system.global_cpu_usage();
213 let core_usage: Vec<f32> = system.cpus().iter().map(|cpu| cpu.cpu_usage()).collect();
214 let load_average = sysinfo::System::load_average();
215
216 Ok(CpuMetrics {
217 overall_usage,
218 core_usage,
219 frequency: 0, load_average: Some((load_average.one, load_average.five, load_average.fifteen)),
221 temperature: None, context_switches: 0, cache_misses: None,
224 })
225 }
226
227 #[cfg(not(feature = "system-metrics"))]
228 {
229 Ok(CpuMetrics {
231 overall_usage: 0.0,
232 core_usage: vec![0.0; num_cpus::get()],
233 frequency: 0,
234 load_average: None,
235 temperature: None,
236 context_switches: 0,
237 cache_misses: None,
238 })
239 }
240 }
241
242 fn collect_memory_metrics(&self) -> Result<MemoryMetrics, Box<dyn std::error::Error>> {
244 #[cfg(feature = "system-metrics")]
245 {
246 let mut system = self.system.borrow_mut();
247 system.refresh_memory();
248
249 let total_physical = system.total_memory();
250 let available_physical = system.available_memory();
251 let used_physical = total_physical - available_physical;
252
253 let pressure = (used_physical as f32 / total_physical as f32) * 100.0;
254
255 Ok(MemoryMetrics {
256 total_physical,
257 available_physical,
258 used_physical,
259 total_virtual: system.total_swap(),
260 available_virtual: system.free_swap(),
261 pressure,
262 page_faults: 0, bandwidth_utilization: None,
264 })
265 }
266
267 #[cfg(not(feature = "system-metrics"))]
268 {
269 Ok(MemoryMetrics {
270 total_physical: 0,
271 available_physical: 0,
272 used_physical: 0,
273 total_virtual: 0,
274 available_virtual: 0,
275 pressure: 0.0,
276 page_faults: 0,
277 bandwidth_utilization: None,
278 })
279 }
280 }
281
282 fn collect_gpu_metrics(&self) -> Result<Option<GpuMetrics>, Box<dyn std::error::Error>> {
284 #[cfg(target_os = "linux")]
290 {
291 if let Ok(gpu_metrics) = self.collect_nvidia_gpu_metrics() {
293 return Ok(Some(gpu_metrics));
294 }
295 }
296
297 #[cfg(target_os = "windows")]
298 {
299 if let Ok(gpu_metrics) = self.collect_windows_gpu_metrics() {
301 return Ok(Some(gpu_metrics));
302 }
303 }
304
305 #[cfg(target_os = "macos")]
306 {
307 if let Ok(gpu_metrics) = self.collect_macos_gpu_metrics() {
309 return Ok(Some(gpu_metrics));
310 }
311 }
312
313 Ok(None)
314 }
315
316 fn collect_io_metrics(&self) -> Result<IoMetrics, Box<dyn std::error::Error>> {
318 Ok(IoMetrics {
324 disk_read_bps: 0,
325 disk_write_bps: 0,
326 disk_read_ops: 0,
327 disk_write_ops: 0,
328 disk_latency_us: None,
329 disk_queue_depth: None,
330 })
331 }
332
333 fn collect_network_metrics(&self) -> Result<NetworkMetrics, Box<dyn std::error::Error>> {
335 #[cfg(feature = "system-metrics")]
336 {
337 let total_rx = 0;
339 let total_tx = 0;
340
341 Ok(NetworkMetrics {
342 rx_bps: total_rx,
343 tx_bps: total_tx,
344 rx_pps: 0, tx_pps: 0,
346 latency_ms: None,
347 connections: 0,
348 })
349 }
350
351 #[cfg(not(feature = "system-metrics"))]
352 {
353 Ok(NetworkMetrics {
354 rx_bps: 0,
355 tx_bps: 0,
356 rx_pps: 0,
357 tx_pps: 0,
358 latency_ms: None,
359 connections: 0,
360 })
361 }
362 }
363
364 fn collect_process_metrics(&self) -> Result<ProcessMetrics, Box<dyn std::error::Error>> {
366 #[cfg(feature = "system-metrics")]
367 {
368 let mut system = self.system.borrow_mut();
369 system.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
370
371 let current_pid = sysinfo::get_current_pid()?;
372
373 if let Some(process) = system.process(current_pid) {
374 Ok(ProcessMetrics {
375 pid: current_pid.as_u32(),
376 name: process.name().to_string_lossy().to_string(),
377 cpu_usage: process.cpu_usage(),
378 memory_usage: process.memory(),
379 thread_count: 0, handle_count: 0,
381 priority: 0,
382 })
383 } else {
384 Err("Could not find current process".into())
385 }
386 }
387
388 #[cfg(not(feature = "system-metrics"))]
389 {
390 Ok(ProcessMetrics {
391 pid: std::process::id(),
392 name: "unknown".to_string(),
393 cpu_usage: 0.0,
394 memory_usage: 0,
395 thread_count: 0,
396 handle_count: 0,
397 priority: 0,
398 })
399 }
400 }
401
402 fn collect_thread_metrics(
404 &self,
405 ) -> Result<HashMap<u64, ThreadMetrics>, Box<dyn std::error::Error>> {
406 let mut thread_metrics = HashMap::new();
407
408 let current_thread_id = get_current_thread_id();
415 thread_metrics.insert(
416 current_thread_id,
417 ThreadMetrics {
418 thread_id: current_thread_id,
419 thread_name: std::thread::current().name().map(String::from),
420 cpu_time_ns: 0,
421 state: ThreadState::Running,
422 priority: 0,
423 cpu_affinity: None,
424 },
425 );
426
427 Ok(thread_metrics)
428 }
429
430 #[cfg(target_os = "linux")]
432 fn collect_nvidia_gpu_metrics(&self) -> Result<GpuMetrics, Box<dyn std::error::Error>> {
433 Err("NVIDIA GPU metrics not implemented".into())
435 }
436
437 #[cfg(target_os = "windows")]
438 fn collect_windows_gpu_metrics(&self) -> Result<GpuMetrics, Box<dyn std::error::Error>> {
439 Err("Windows GPU metrics not implemented".into())
441 }
442
443 #[cfg(target_os = "macos")]
444 fn collect_macos_gpu_metrics(&self) -> Result<GpuMetrics, Box<dyn std::error::Error>> {
445 Err("macOS GPU metrics not implemented".into())
447 }
448}
449
450fn get_current_thread_id() -> u64 {
452 #[cfg(target_os = "linux")]
453 {
454 unsafe { libc::syscall(libc::SYS_gettid) as u64 }
455 }
456
457 #[cfg(target_os = "windows")]
458 {
459 unsafe { winapi::um::processthreadsapi::GetCurrentThreadId() as u64 }
460 }
461
462 #[cfg(target_os = "macos")]
463 {
464 unsafe { libc::pthread_self() as u64 }
465 }
466
467 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
468 {
469 std::thread::current().id().as_u64()
470 }
471}
472
473pub struct ContinuousProfiler {
475 #[allow(dead_code)]
476 profiler: SystemProfiler,
477 snapshots: Vec<SystemResourceSnapshot>,
478 is_running: std::sync::Arc<std::sync::atomic::AtomicBool>,
479}
480
481impl ContinuousProfiler {
482 pub fn start_background_profiling(interval: Duration) -> Self {
484 let profiler = SystemProfiler::new(interval);
485 let is_running = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
486
487 Self {
488 profiler,
489 snapshots: Vec::new(),
490 is_running,
491 }
492 }
493
494 pub fn stop_and_collect(self) -> Vec<SystemResourceSnapshot> {
496 self.is_running
497 .store(false, std::sync::atomic::Ordering::SeqCst);
498 self.snapshots
499 }
500}