Skip to main content

lean_ctx/core/benchmark_compare/
system_info.rs

1use std::fmt;
2
3#[derive(Debug, Clone)]
4pub struct SystemInfo {
5    pub os: String,
6    pub arch: String,
7    pub cpu_brand: String,
8    pub cpu_cores: usize,
9    pub memory_gb: f64,
10    pub lean_ctx_version: String,
11    pub rust_version: String,
12}
13
14impl fmt::Display for SystemInfo {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        write!(
17            f,
18            "{} {} | {} ({} cores) | {:.1} GB RAM",
19            self.os, self.arch, self.cpu_brand, self.cpu_cores, self.memory_gb
20        )
21    }
22}
23
24pub fn collect() -> SystemInfo {
25    SystemInfo {
26        os: std::env::consts::OS.to_string(),
27        arch: std::env::consts::ARCH.to_string(),
28        cpu_brand: read_cpu_brand(),
29        cpu_cores: std::thread::available_parallelism().map_or(1, std::num::NonZero::get),
30        memory_gb: read_memory_gb(),
31        lean_ctx_version: env!("CARGO_PKG_VERSION").to_string(),
32        rust_version: read_rust_version(),
33    }
34}
35
36fn read_cpu_brand() -> String {
37    #[cfg(target_os = "macos")]
38    {
39        std::process::Command::new("sysctl")
40            .args(["-n", "machdep.cpu.brand_string"])
41            .output()
42            .ok()
43            .and_then(|o| String::from_utf8(o.stdout).ok())
44            .map_or_else(|| "Unknown CPU".to_string(), |s| s.trim().to_string())
45    }
46    #[cfg(target_os = "linux")]
47    {
48        std::fs::read_to_string("/proc/cpuinfo")
49            .ok()
50            .and_then(|s| {
51                s.lines()
52                    .find(|l| l.starts_with("model name"))
53                    .and_then(|l| l.split(':').nth(1))
54                    .map(|v| v.trim().to_string())
55            })
56            .unwrap_or_else(|| "Unknown CPU".to_string())
57    }
58    #[cfg(target_os = "windows")]
59    {
60        std::process::Command::new("powershell")
61            .args([
62                "-NoProfile",
63                "-Command",
64                "(Get-CimInstance Win32_Processor).Name",
65            ])
66            .output()
67            .ok()
68            .and_then(|o| String::from_utf8(o.stdout).ok())
69            .map(|s| s.trim().to_string())
70            .filter(|s| !s.is_empty())
71            .unwrap_or_else(|| "Unknown CPU".to_string())
72    }
73    #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
74    {
75        "Unknown CPU".to_string()
76    }
77}
78
79fn read_memory_gb() -> f64 {
80    #[cfg(target_os = "macos")]
81    {
82        std::process::Command::new("sysctl")
83            .args(["-n", "hw.memsize"])
84            .output()
85            .ok()
86            .and_then(|o| String::from_utf8(o.stdout).ok())
87            .and_then(|s| s.trim().parse::<u64>().ok())
88            .map_or(0.0, |bytes| bytes as f64 / (1024.0 * 1024.0 * 1024.0))
89    }
90    #[cfg(target_os = "linux")]
91    {
92        std::fs::read_to_string("/proc/meminfo")
93            .ok()
94            .and_then(|s| {
95                s.lines().find(|l| l.starts_with("MemTotal")).and_then(|l| {
96                    l.split_whitespace()
97                        .nth(1)
98                        .and_then(|v| v.parse::<u64>().ok())
99                })
100            })
101            .map_or(0.0, |kb| kb as f64 / (1024.0 * 1024.0))
102    }
103    #[cfg(target_os = "windows")]
104    {
105        // Query total physical memory (bytes) via CIM; reliable on all
106        // supported Windows versions and present on CI runners.
107        std::process::Command::new("powershell")
108            .args([
109                "-NoProfile",
110                "-Command",
111                "(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory",
112            ])
113            .output()
114            .ok()
115            .and_then(|o| String::from_utf8(o.stdout).ok())
116            .and_then(|s| s.trim().parse::<u64>().ok())
117            .map_or(0.0, |bytes| bytes as f64 / (1024.0 * 1024.0 * 1024.0))
118    }
119    #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
120    {
121        0.0
122    }
123}
124
125fn read_rust_version() -> String {
126    std::process::Command::new("rustc")
127        .arg("--version")
128        .output()
129        .ok()
130        .and_then(|o| String::from_utf8(o.stdout).ok())
131        .map_or_else(|| "unknown".to_string(), |s| s.trim().to_string())
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn collect_returns_valid_info() {
140        let info = collect();
141        assert!(!info.os.is_empty());
142        assert!(!info.arch.is_empty());
143        assert!(!info.lean_ctx_version.is_empty());
144        assert!(info.cpu_cores >= 1);
145    }
146
147    #[test]
148    fn display_is_readable() {
149        let info = collect();
150        let s = format!("{info}");
151        assert!(s.contains(&info.os));
152        assert!(s.contains("cores"));
153    }
154}