Skip to main content

streaming_crypto/core_api/benchmarks/
bench_metadata.rs

1use std::collections::HashMap;
2use std::process::Command;
3use uuid::Uuid;
4use sysinfo::{System, Disks, get_current_pid}; // only these are needed
5
6/// Safe helpers
7
8pub fn safe_run(cmd: &[&str]) -> Option<String> {
9    if cmd.is_empty() {
10        return None;
11    }
12    let output = Command::new(cmd[0])
13        .args(&cmd[1..])
14        .output()
15        .ok()?;
16    if output.status.success() {
17        Some(String::from_utf8_lossy(&output.stdout).trim().to_string())
18    } else {
19        None
20    }
21}
22
23pub fn safe_sysinfo_call<T, F: FnOnce() -> T>(callable: F, default: T) -> T {
24    std::panic::catch_unwind(std::panic::AssertUnwindSafe(callable))
25        .unwrap_or(default)
26}
27
28/// Container detection
29
30pub fn detect_container() -> Option<String> {
31    if let Ok(data) = std::fs::read_to_string("/proc/1/cgroup") {
32        if data.contains("docker") || data.contains("containerd") {
33            return Some("docker".to_string());
34        }
35        if data.contains("podman") {
36            return Some("podman".to_string());
37        }
38    }
39    None
40}
41
42/// Git metadata
43
44pub fn collect_git_metadata() -> HashMap<String, Option<String>> {
45    let mut map = HashMap::new();
46    map.insert("commit".to_string(), safe_run(&["git", "rev-parse", "HEAD"]));
47    map.insert("branch".to_string(), safe_run(&["git", "rev-parse", "--abbrev-ref", "HEAD"]));
48    map.insert(
49        "dirty".to_string(),
50        Some(
51            safe_run(&["git", "status", "--porcelain"])
52                .map(|s| (!s.is_empty()).to_string())
53                .unwrap_or("false".to_string()),
54        ),
55    );
56    map
57}
58
59/// CPU metadata
60
61pub fn collect_cpu_metadata(sys: &System) -> HashMap<String, Option<String>> {
62    let mut map = HashMap::new();
63
64    // physical_core_count is an associated function, not a method
65    map.insert(
66        "physical_cores".to_string(),
67        Some(System::physical_core_count().unwrap_or(0).to_string()),
68    );
69
70    // logical cores from the cpus slice
71    map.insert("logical_cores".to_string(), Some(sys.cpus().len().to_string()));
72
73    // max frequency from the first CPU
74    map.insert(
75        "max_frequency_mhz".to_string(),
76        sys.cpus().iter().map(|c| c.frequency().to_string()).next(),
77    );
78
79    if cfg!(target_os = "linux") {
80        map.insert("flags".to_string(), safe_run(&["bash", "-lc", "lscpu | grep Flags"]));
81    }
82
83    map
84}
85
86/// Memory metadata
87
88pub fn collect_memory_metadata(sys: &System) -> HashMap<String, String> {
89    let mut map = HashMap::new();
90    map.insert(
91        "total_ram_mb".to_string(),
92        (sys.total_memory() / 1024).to_string(),
93    );
94    map
95}
96
97/// Disk metadata
98
99pub fn collect_disk_metadata(_sys: &System) -> HashMap<String, String> {
100    let mut map = HashMap::new();
101
102    // Create a Disks collection and refresh it
103    let mut disks = Disks::new();
104    disks.refresh(true);
105
106    let disk_names: Vec<String> = disks
107        .iter()
108        .map(|d| d.name().to_string_lossy().to_string())
109        .collect();
110
111    map.insert("disks".to_string(), format!("{:?}", disk_names));
112    map
113}
114
115
116
117/// Process metadata
118
119pub fn collect_process_metadata(sys: &System) -> HashMap<String, String> {
120    let mut map = HashMap::new();
121    if let Ok(pid) = get_current_pid() {
122        if let Some(proc) = sys.process(pid) {
123            map.insert("pid".to_string(), proc.pid().to_string());
124            map.insert("name".to_string(), proc.name().display().to_string());
125
126            // exe() returns Option<&Path>, so unwrap safely
127            let exe_str = proc.exe()
128                .map(|p| p.display().to_string()) // or p.to_string_lossy().to_string()
129                .unwrap_or_else(|| "<unknown>".to_string());
130            map.insert("exe".to_string(), exe_str);
131
132            map.insert("cpu_usage".to_string(), proc.cpu_usage().to_string());
133            map.insert("memory_kb".to_string(), proc.memory().to_string());
134        }
135    }
136    map
137}
138
139/// Environment metadata
140
141pub fn collect_environment_metadata() -> HashMap<String, String> {
142    let mut map = HashMap::new();
143    if let Ok(val) = std::env::var("VIRTUAL_ENV") {
144        map.insert("virtual_env".to_string(), val);
145    }
146    if let Ok(val) = std::env::var("CONDA_DEFAULT_ENV") {
147        map.insert("conda_env".to_string(), val);
148    }
149    for (k, v) in std::env::vars() {
150        if k.starts_with("BENCH_") || k.starts_with("APP_") || k.starts_with("ENV_") {
151            map.insert(k, v);
152        }
153    }
154    map
155}
156
157/// System metadata
158
159pub fn collect_system_metadata(sys: &System) -> HashMap<String, String> {
160    let mut map = HashMap::new();
161
162    // These are associated functions, not instance methods
163    map.insert("os".to_string(), System::name().unwrap_or_default());
164    map.insert("os_version".to_string(), System::os_version().unwrap_or_default());
165    map.insert("machine".to_string(), System::host_name().unwrap_or_default());
166
167    // Processor info from the first CPU
168    map.insert(
169        "processor".to_string(),
170        sys.cpus().get(0).map(|c| c.brand().to_string()).unwrap_or_default(),
171    );
172
173    map.insert("container".to_string(), detect_container().unwrap_or_default());
174    map
175}
176
177/// Master collector
178
179pub fn collect_metadata() -> HashMap<String, serde_json::Value> {
180    let mut sys = System::new_all();
181    sys.refresh_all();
182
183    let mut map = HashMap::new();
184    map.insert("schema_version".to_string(), serde_json::json!("2.0"));
185    map.insert("benchmark_version".to_string(), serde_json::json!("1.0"));
186    map.insert("run_id".to_string(), serde_json::json!(Uuid::new_v4().to_string()));
187    map.insert(
188        "timestamp".to_string(),
189        serde_json::json!(chrono::Utc::now().to_rfc3339()),
190    );
191
192    map.insert("system".to_string(), serde_json::json!(collect_system_metadata(&sys)));
193    map.insert("cpu".to_string(), serde_json::json!(collect_cpu_metadata(&sys)));
194    map.insert("memory".to_string(), serde_json::json!(collect_memory_metadata(&sys)));
195    map.insert("disk".to_string(), serde_json::json!(collect_disk_metadata(&sys)));
196    map.insert("process".to_string(), serde_json::json!(collect_process_metadata(&sys)));
197    map.insert("git".to_string(), serde_json::json!(collect_git_metadata()));
198    map.insert("environment".to_string(), serde_json::json!(collect_environment_metadata()));
199
200    map
201}
202
203// ### 🧩 Key Notes
204// - Uses `sysinfo` crate for CPU, memory, disk, and process metadata.
205// - Uses `uuid` and `chrono` crates for run ID and timestamp.
206// - `safe_run` wraps shell commands like `git` or `lscpu`.
207// - `detect_container` checks `/proc/1/cgroup` for Docker/Podman markers.
208// - Returns metadata as a `HashMap<String, serde_json::Value>` so we can serialize easily.