Skip to main content

solana_validator_optimizer/
system_health.rs

1use serde::Serialize;
2use std::path::Path;
3use sysinfo::{Disks, System};
4
5const GIB: u64 = 1024 * 1024 * 1024;
6
7#[derive(Debug, Clone, Serialize)]
8pub struct SystemHealthReport {
9    pub logical_cpu_count: usize,
10    pub total_memory_bytes: u64,
11    pub available_memory_bytes: u64,
12    pub disk_total_bytes: Option<u64>,
13    pub disk_available_bytes: Option<u64>,
14}
15
16impl SystemHealthReport {
17    pub fn total_memory_gib(&self) -> f64 {
18        self.total_memory_bytes as f64 / GIB as f64
19    }
20
21    pub fn available_memory_gib(&self) -> f64 {
22        self.available_memory_bytes as f64 / GIB as f64
23    }
24
25    pub fn disk_total_gib(&self) -> Option<f64> {
26        self.disk_total_bytes.map(|bytes| bytes as f64 / GIB as f64)
27    }
28
29    pub fn disk_available_gib(&self) -> Option<f64> {
30        self.disk_available_bytes
31            .map(|bytes| bytes as f64 / GIB as f64)
32    }
33}
34
35pub fn check_system_health() -> SystemHealthReport {
36    let system = System::new_all();
37
38    let logical_cpu_count = system.cpus().len();
39    let total_memory_bytes = system.total_memory();
40    let available_memory_bytes = system.available_memory();
41
42    let disks = Disks::new_with_refreshed_list();
43
44    // Prefer the filesystem containing the current working directory.
45    let current_dir = std::env::current_dir().unwrap_or_else(|_| Path::new("/").to_path_buf());
46
47    let disk = disks
48        .list()
49        .iter()
50        .filter(|disk| current_dir.starts_with(disk.mount_point()))
51        .max_by_key(|disk| disk.mount_point().components().count())
52        .or_else(|| {
53            disks
54                .list()
55                .iter()
56                .find(|disk| disk.mount_point() == Path::new("/"))
57        });
58
59    let (disk_total_bytes, disk_available_bytes) = match disk {
60        Some(disk) => (Some(disk.total_space()), Some(disk.available_space())),
61        None => (None, None),
62    };
63
64    SystemHealthReport {
65        logical_cpu_count,
66        total_memory_bytes,
67        available_memory_bytes,
68        disk_total_bytes,
69        disk_available_bytes,
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn gib_conversions_work() {
79        let report = SystemHealthReport {
80            logical_cpu_count: 8,
81            total_memory_bytes: 64 * GIB,
82            available_memory_bytes: 32 * GIB,
83            disk_total_bytes: Some(2 * 1024 * GIB),
84            disk_available_bytes: Some(500 * GIB),
85        };
86
87        assert_eq!(report.total_memory_gib(), 64.0);
88        assert_eq!(report.available_memory_gib(), 32.0);
89        assert_eq!(report.disk_total_gib(), Some(2048.0));
90        assert_eq!(report.disk_available_gib(), Some(500.0));
91    }
92}