use std::io;
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use std::time::{Duration, Instant};
#[cfg(not(target_os = "linux"))]
use sysinfo::{get_current_pid, CpuExt, ProcessExt, System, SystemExt};
#[repr(align(64))]
pub struct SystemHealth {
system_cpu: AtomicU32,
process_cpu: AtomicU32,
system_memory_mb: AtomicU64,
process_memory_mb: AtomicU64,
load_average: AtomicU32,
thread_count: AtomicU32,
fd_count: AtomicU32,
health_score: AtomicU32,
last_update: AtomicU64,
update_interval_ms: u64,
created_at: Instant,
#[cfg(not(target_os = "linux"))]
sys: std::sync::Mutex<System>,
#[cfg(not(target_os = "linux"))]
pid: Option<sysinfo::Pid>,
}
#[derive(Debug, Clone)]
pub struct SystemSnapshot {
pub system_cpu_percent: f64,
pub process_cpu_percent: f64,
pub system_memory_mb: u64,
pub process_memory_mb: u64,
pub load_average: f64,
pub thread_count: u32,
pub fd_count: u32,
pub health_score: f64,
pub last_update: Duration,
}
#[derive(Debug, Clone)]
pub struct ProcessStats {
pub cpu_percent: f64,
pub memory_mb: f64,
pub threads: u32,
pub file_handles: u32,
pub uptime: Duration,
}
impl SystemHealth {
#[inline]
pub fn new() -> Self {
let instance = Self {
system_cpu: AtomicU32::new(0),
process_cpu: AtomicU32::new(0),
system_memory_mb: AtomicU64::new(0),
process_memory_mb: AtomicU64::new(0),
load_average: AtomicU32::new(0),
thread_count: AtomicU32::new(0),
fd_count: AtomicU32::new(0),
health_score: AtomicU32::new(10000), last_update: AtomicU64::new(0),
update_interval_ms: 1000, created_at: Instant::now(),
#[cfg(not(target_os = "linux"))]
sys: std::sync::Mutex::new(System::new()),
#[cfg(not(target_os = "linux"))]
pid: get_current_pid().ok(),
};
instance.update_metrics();
instance
}
#[inline]
pub fn with_interval(interval: Duration) -> Self {
let mut instance = Self::new();
instance.update_interval_ms = interval.as_millis() as u64;
instance
}
#[inline]
pub fn cpu_used(&self) -> f64 {
self.maybe_update();
self.system_cpu.load(Ordering::Relaxed) as f64 / 100.0
}
#[inline]
pub fn cpu_free(&self) -> f64 {
100.0 - self.cpu_used()
}
#[inline]
pub fn mem_used_mb(&self) -> f64 {
self.maybe_update();
self.system_memory_mb.load(Ordering::Relaxed) as f64
}
#[inline]
pub fn mem_used_gb(&self) -> f64 {
self.mem_used_mb() / 1024.0
}
#[inline]
pub fn process_cpu_used(&self) -> f64 {
self.maybe_update();
self.process_cpu.load(Ordering::Relaxed) as f64 / 100.0
}
#[inline]
pub fn process_mem_used_mb(&self) -> f64 {
self.maybe_update();
self.process_memory_mb.load(Ordering::Relaxed) as f64
}
#[inline]
pub fn load_avg(&self) -> f64 {
self.maybe_update();
self.load_average.load(Ordering::Relaxed) as f64 / 100.0
}
#[inline]
pub fn thread_count(&self) -> u32 {
self.maybe_update();
self.thread_count.load(Ordering::Relaxed)
}
#[inline]
pub fn fd_count(&self) -> u32 {
self.maybe_update();
self.fd_count.load(Ordering::Relaxed)
}
#[inline]
pub fn health_score(&self) -> f64 {
self.maybe_update();
self.health_score.load(Ordering::Relaxed) as f64 / 100.0
}
#[inline(always)]
pub fn quick_check(&self) -> HealthStatus {
let score = self.health_score();
if score >= 80.0 {
HealthStatus::Healthy
} else if score >= 60.0 {
HealthStatus::Warning
} else if score >= 40.0 {
HealthStatus::Degraded
} else {
HealthStatus::Critical
}
}
#[inline]
pub fn update(&self) {
self.update_metrics();
}
pub fn snapshot(&self) -> SystemSnapshot {
self.maybe_update();
let last_update_ns = self.last_update.load(Ordering::Relaxed);
let last_update = if last_update_ns > 0 {
Duration::from_nanos(last_update_ns)
} else {
Duration::ZERO
};
SystemSnapshot {
system_cpu_percent: self.system_cpu.load(Ordering::Relaxed) as f64 / 100.0,
process_cpu_percent: self.process_cpu.load(Ordering::Relaxed) as f64 / 100.0,
system_memory_mb: self.system_memory_mb.load(Ordering::Relaxed),
process_memory_mb: self.process_memory_mb.load(Ordering::Relaxed),
load_average: self.load_average.load(Ordering::Relaxed) as f64 / 100.0,
thread_count: self.thread_count.load(Ordering::Relaxed),
fd_count: self.fd_count.load(Ordering::Relaxed),
health_score: self.health_score.load(Ordering::Relaxed) as f64 / 100.0,
last_update,
}
}
pub fn process(&self) -> ProcessStats {
self.maybe_update();
ProcessStats {
cpu_percent: self.process_cpu.load(Ordering::Relaxed) as f64 / 100.0,
memory_mb: self.process_memory_mb.load(Ordering::Relaxed) as f64,
threads: self.thread_count.load(Ordering::Relaxed),
file_handles: self.fd_count.load(Ordering::Relaxed),
uptime: self.created_at.elapsed(),
}
}
#[inline]
fn maybe_update(&self) {
let now = self.created_at.elapsed().as_millis() as u64;
let last_update = self.last_update.load(Ordering::Relaxed);
if now >= last_update && (now - last_update) > self.update_interval_ms {
self.update_metrics();
}
}
fn update_metrics(&self) {
let now_ns = self.created_at.elapsed().as_nanos() as u64;
if let Ok(cpu) = self.get_system_cpu() {
self.system_cpu
.store((cpu * 100.0) as u32, Ordering::Relaxed);
}
if let Ok(memory_mb) = self.get_system_memory_mb() {
self.system_memory_mb.store(memory_mb, Ordering::Relaxed);
}
if let Ok(load) = self.get_load_average() {
self.load_average
.store((load * 100.0) as u32, Ordering::Relaxed);
}
if let Ok(cpu) = self.get_process_cpu() {
self.process_cpu
.store((cpu * 100.0) as u32, Ordering::Relaxed);
}
if let Ok(memory_mb) = self.get_process_memory_mb() {
self.process_memory_mb.store(memory_mb, Ordering::Relaxed);
}
if let Ok(threads) = self.get_thread_count() {
self.thread_count.store(threads, Ordering::Relaxed);
}
if let Ok(fds) = self.get_fd_count() {
self.fd_count.store(fds, Ordering::Relaxed);
}
let health = self.calculate_health_score();
self.health_score
.store((health * 100.0) as u32, Ordering::Relaxed);
self.last_update.store(now_ns, Ordering::Relaxed);
}
fn calculate_health_score(&self) -> f64 {
let mut score: f64 = 100.0;
let system_cpu = self.system_cpu.load(Ordering::Relaxed) as f64 / 100.0;
if system_cpu > 80.0 {
score -= 30.0; } else if system_cpu > 60.0 {
score -= 15.0;
} else if system_cpu > 40.0 {
score -= 5.0;
}
let load = self.load_average.load(Ordering::Relaxed) as f64 / 100.0;
let cpu_count = num_cpus::get() as f64;
if load > cpu_count * 2.0 {
score -= 25.0;
} else if load > cpu_count * 1.5 {
score -= 10.0;
} else if load > cpu_count {
score -= 5.0;
}
let process_cpu = self.process_cpu.load(Ordering::Relaxed) as f64 / 100.0;
if process_cpu > 50.0 {
score -= 15.0;
} else if process_cpu > 25.0 {
score -= 8.0;
}
let memory_gb = self.system_memory_mb.load(Ordering::Relaxed) as f64 / 1024.0;
if memory_gb > 16.0 {
score -= 10.0;
} else if memory_gb > 8.0 {
score -= 5.0;
}
let threads = self.thread_count.load(Ordering::Relaxed);
if threads > 1000 {
score -= 20.0;
} else if threads > 500 {
score -= 10.0;
} else if threads > 200 {
score -= 5.0;
}
let fds = self.fd_count.load(Ordering::Relaxed);
if fds > 10000 {
score -= 15.0;
} else if fds > 5000 {
score -= 8.0;
} else if fds > 1000 {
score -= 3.0;
}
score.max(0.0)
}
#[cfg(target_os = "linux")]
fn get_system_cpu(&self) -> io::Result<f64> {
let contents = std::fs::read_to_string("/proc/stat")?;
if let Some(line) = contents.lines().next() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 5 && parts[0] == "cpu" {
let user: u64 = parts[1].parse().unwrap_or(0);
let nice: u64 = parts[2].parse().unwrap_or(0);
let system: u64 = parts[3].parse().unwrap_or(0);
let idle: u64 = parts[4].parse().unwrap_or(0);
let total = user + nice + system + idle;
let used = user + nice + system;
if total > 0 {
return Ok(used as f64 / total as f64 * 100.0);
}
}
}
Ok(0.0)
}
#[cfg(not(target_os = "linux"))]
fn get_system_cpu(&self) -> io::Result<f64> {
let mut guard = self.sys.lock().unwrap();
guard.refresh_cpu();
Ok(guard.global_cpu_info().cpu_usage() as f64)
}
#[cfg(target_os = "linux")]
fn get_system_memory_mb(&self) -> io::Result<u64> {
let contents = std::fs::read_to_string("/proc/meminfo")?;
let mut total_kb = 0u64;
let mut free_kb = 0u64;
let mut available_kb = 0u64;
for line in contents.lines() {
if line.starts_with("MemTotal:") {
total_kb = line
.split_whitespace()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(0);
} else if line.starts_with("MemFree:") {
free_kb = line
.split_whitespace()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(0);
} else if line.starts_with("MemAvailable:") {
available_kb = line
.split_whitespace()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(0);
}
}
let used_kb = if available_kb > 0 {
total_kb - available_kb
} else {
total_kb - free_kb
};
Ok(used_kb / 1024) }
#[cfg(not(target_os = "linux"))]
fn get_system_memory_mb(&self) -> io::Result<u64> {
let mut guard = self.sys.lock().unwrap();
guard.refresh_memory();
let used_kib = guard.used_memory();
Ok(used_kib / 1024)
}
#[cfg(target_os = "linux")]
fn get_load_average(&self) -> io::Result<f64> {
let contents = std::fs::read_to_string("/proc/loadavg")?;
if let Some(first) = contents.split_whitespace().next() {
return first
.parse()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid load average"));
}
Ok(0.0)
}
#[cfg(not(target_os = "linux"))]
fn get_load_average(&self) -> io::Result<f64> {
let guard = self.sys.lock().unwrap();
let la = guard.load_average();
Ok(la.one)
}
#[cfg(target_os = "linux")]
fn get_process_cpu(&self) -> io::Result<f64> {
let contents = std::fs::read_to_string("/proc/self/stat")?;
let parts: Vec<&str> = contents.split_whitespace().collect();
if parts.len() >= 15 {
let utime: u64 = parts[13].parse().unwrap_or(0);
let stime: u64 = parts[14].parse().unwrap_or(0);
let total = utime + stime;
Ok(total as f64 * 0.01) } else {
Ok(0.0)
}
}
#[cfg(not(target_os = "linux"))]
fn get_process_cpu(&self) -> io::Result<f64> {
let mut guard = self.sys.lock().unwrap();
if let Some(pid) = self.pid {
guard.refresh_process(pid);
if let Some(proc_) = guard.process(pid) {
let raw = proc_.cpu_usage() as f64;
let cores = num_cpus::get() as f64;
let norm = if cores > 0.0 { raw / cores } else { raw };
return Ok(norm.clamp(0.0, 100.0));
}
}
Ok(0.0)
}
#[cfg(target_os = "linux")]
fn get_process_memory_mb(&self) -> io::Result<u64> {
let contents = std::fs::read_to_string("/proc/self/status")?;
for line in contents.lines() {
if line.starts_with("VmRSS:") {
if let Some(kb_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = kb_str.parse::<u64>() {
return Ok(kb / 1024); }
}
}
}
Ok(0)
}
#[cfg(not(target_os = "linux"))]
fn get_process_memory_mb(&self) -> io::Result<u64> {
let mut guard = self.sys.lock().unwrap();
if let Some(pid) = self.pid {
guard.refresh_process(pid);
if let Some(proc_) = guard.process(pid) {
return Ok(proc_.memory() / 1024);
}
}
Ok(0)
}
#[cfg(target_os = "linux")]
fn get_thread_count(&self) -> io::Result<u32> {
let contents = std::fs::read_to_string("/proc/self/status")?;
for line in contents.lines() {
if line.starts_with("Threads:") {
if let Some(count_str) = line.split_whitespace().nth(1) {
if let Ok(count) = count_str.parse() {
return Ok(count);
}
}
}
}
Ok(1) }
#[cfg(not(target_os = "linux"))]
fn get_thread_count(&self) -> io::Result<u32> {
Ok(1)
}
#[cfg(target_os = "linux")]
fn get_fd_count(&self) -> io::Result<u32> {
match std::fs::read_dir("/proc/self/fd") {
Ok(entries) => Ok(entries.count() as u32),
Err(_) => Ok(0),
}
}
#[cfg(not(target_os = "linux"))]
fn get_fd_count(&self) -> io::Result<u32> {
Ok(0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HealthStatus {
Healthy,
Warning,
Degraded,
Critical,
}
impl HealthStatus {
#[inline]
pub fn is_degraded(&self) -> bool {
matches!(self, Self::Degraded | Self::Critical)
}
#[inline]
pub fn is_healthy(&self) -> bool {
matches!(self, Self::Healthy)
}
#[inline]
pub fn has_issues(&self) -> bool {
!matches!(self, Self::Healthy)
}
}
impl Default for SystemHealth {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for SystemHealth {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let snapshot = self.snapshot();
write!(
f,
"SystemHealth(CPU: {:.1}%, Mem: {} MB, Health: {:.1}%)",
snapshot.system_cpu_percent, snapshot.system_memory_mb, snapshot.health_score
)
}
}
impl std::fmt::Debug for SystemHealth {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let snapshot = self.snapshot();
f.debug_struct("SystemHealth")
.field("system_cpu", &snapshot.system_cpu_percent)
.field("process_cpu", &snapshot.process_cpu_percent)
.field("system_memory_mb", &snapshot.system_memory_mb)
.field("process_memory_mb", &snapshot.process_memory_mb)
.field("load_average", &snapshot.load_average)
.field("threads", &snapshot.thread_count)
.field("fds", &snapshot.fd_count)
.field("health_score", &snapshot.health_score)
.finish()
}
}
impl std::fmt::Display for HealthStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Healthy => write!(f, "Healthy"),
Self::Warning => write!(f, "Warning"),
Self::Degraded => write!(f, "Degraded"),
Self::Critical => write!(f, "Critical"),
}
}
}
unsafe impl Send for SystemHealth {}
unsafe impl Sync for SystemHealth {}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_basic_functionality() {
let health = SystemHealth::new();
let _cpu = health.cpu_used();
let _mem = health.mem_used_mb();
let _process_cpu = health.process_cpu_used();
let _process_mem = health.process_mem_used_mb();
let _load = health.load_avg();
let _threads = health.thread_count();
let _fds = health.fd_count();
let _score = health.health_score();
let status = health.quick_check();
assert!(matches!(
status,
HealthStatus::Healthy
| HealthStatus::Warning
| HealthStatus::Degraded
| HealthStatus::Critical
));
}
#[test]
fn test_cpu_free() {
let health = SystemHealth::new();
let used = health.cpu_used();
let free = health.cpu_free();
assert!((used + free - 100.0).abs() < 0.1);
}
#[test]
fn test_memory_units() {
let health = SystemHealth::new();
let mb = health.mem_used_mb();
let gb = health.mem_used_gb();
if mb > 0.0 {
assert!((gb * 1024.0 - mb).abs() < 1.0);
}
}
#[test]
fn test_snapshot() {
let health = SystemHealth::new();
let snapshot = health.snapshot();
assert!(snapshot.system_cpu_percent >= 0.0);
assert!(snapshot.system_cpu_percent <= 100.0);
assert!(snapshot.health_score >= 0.0);
assert!(snapshot.health_score <= 100.0);
assert!(snapshot.thread_count > 0); }
#[test]
fn test_process_stats() {
let health = SystemHealth::new();
let stats = health.process();
assert!(stats.threads > 0); assert!(stats.uptime > Duration::ZERO);
assert!(stats.cpu_percent >= 0.0);
assert!(stats.memory_mb >= 0.0);
}
#[test]
fn test_health_status() {
let healthy = HealthStatus::Healthy;
let warning = HealthStatus::Warning;
let degraded = HealthStatus::Degraded;
let critical = HealthStatus::Critical;
assert!(healthy.is_healthy());
assert!(!healthy.is_degraded());
assert!(!healthy.has_issues());
assert!(!warning.is_healthy());
assert!(!warning.is_degraded());
assert!(warning.has_issues());
assert!(!degraded.is_healthy());
assert!(degraded.is_degraded());
assert!(degraded.has_issues());
assert!(!critical.is_healthy());
assert!(critical.is_degraded());
assert!(critical.has_issues());
}
#[test]
fn test_custom_interval() {
let health = SystemHealth::with_interval(Duration::from_millis(500));
let _cpu = health.cpu_used();
let _score = health.health_score();
}
#[test]
fn test_force_update() {
let health = SystemHealth::new();
let score_before = health.health_score();
health.update();
let score_after = health.health_score();
assert!(score_before >= 0.0);
assert!(score_after >= 0.0);
}
#[test]
fn test_concurrent_access() {
let health = std::sync::Arc::new(SystemHealth::new());
let mut handles = vec![];
for _ in 0..10 {
let health_clone = health.clone();
let handle = thread::spawn(move || {
for _ in 0..100 {
let _cpu = health_clone.cpu_used();
let _mem = health_clone.mem_used_mb();
let _status = health_clone.quick_check();
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let final_score = health.health_score();
assert!((0.0..=100.0).contains(&final_score));
}
#[test]
fn test_display_formatting() {
let health = SystemHealth::new();
let display_str = format!("{health}");
assert!(display_str.contains("SystemHealth"));
assert!(display_str.contains("CPU"));
assert!(display_str.contains("Mem"));
let debug_str = format!("{health:?}");
assert!(debug_str.contains("SystemHealth"));
let status = health.quick_check();
let status_str = format!("{status}");
assert!(!status_str.is_empty());
}
}
#[cfg(all(test, feature = "bench-tests", not(tarpaulin)))]
#[allow(unused_imports)]
mod benchmarks {
use super::*;
use std::time::Instant;
#[cfg_attr(not(feature = "bench-tests"), ignore)]
#[test]
fn bench_quick_check() {
let health = SystemHealth::new();
let iterations = 1_000_000;
let start = Instant::now();
for _ in 0..iterations {
let _ = health.quick_check();
}
let elapsed = start.elapsed();
println!(
"SystemHealth quick_check: {:.2} ns/op",
elapsed.as_nanos() as f64 / iterations as f64
);
assert!(elapsed.as_nanos() / iterations < 200);
}
#[cfg_attr(not(feature = "bench-tests"), ignore)]
#[test]
fn bench_cached_metrics() {
let health = SystemHealth::new();
let iterations = 1_000_000;
let start = Instant::now();
for _ in 0..iterations {
let _ = health.cpu_used();
let _ = health.mem_used_mb();
let _ = health.health_score();
}
let elapsed = start.elapsed();
println!(
"SystemHealth cached metrics: {:.2} ns/op",
elapsed.as_nanos() as f64 / iterations as f64 / 3.0
);
assert!(elapsed.as_nanos() / iterations < 1000);
}
#[cfg_attr(not(feature = "bench-tests"), ignore)]
#[test]
fn bench_force_update() {
let health = SystemHealth::new();
let iterations = 1000;
let start = Instant::now();
for _ in 0..iterations {
health.update();
}
let elapsed = start.elapsed();
println!(
"SystemHealth force update: {:.2} μs/op",
elapsed.as_micros() as f64 / iterations as f64
);
assert!(elapsed.as_millis() < 2000);
}
}