1mod extensions;
18mod hardware;
19mod parsing;
20mod system;
21mod types;
22
23pub use types::{CacheInfo, HardwareIds, RiscvInfo, SystemInfo, VectorInfo};
25
26pub use extensions::{
28 STANDARD_EXTENSIONS, S_CATEGORY_NAMES, S_EXTENSIONS, Z_CATEGORY_NAMES, Z_EXTENSIONS,
29};
30
31pub use parsing::{
33 get_all_s_extensions_with_status, get_all_standard_extensions_with_status,
34 get_all_z_extensions_with_status, get_s_category_name, get_z_category_name, group_by_category,
35 parse_extensions_compact, parse_extensions_explained, parse_s_extensions,
36 parse_s_extensions_explained, parse_s_extensions_with_category, parse_vector_from_isa,
37 parse_z_extensions, parse_z_extensions_explained, parse_z_extensions_with_category,
38 ExtensionInfo,
39};
40
41pub use hardware::{
43 get_board_info, get_cache_info, get_hardware_ids, get_hart_count, get_hart_count_num,
44 get_isa_string, get_vector_detail,
45};
46
47pub use system::{
49 get_kernel_info, get_memory_bytes, get_memory_info, get_os_info, get_uptime, get_uptime_seconds,
50};
51
52use std::fs;
53use std::process::Command;
54use sysinfo::System;
55
56#[must_use]
58pub fn is_riscv() -> bool {
59 if let Ok(output) = Command::new("uname").arg("-m").output() {
60 let arch = String::from_utf8_lossy(&output.stdout);
61 if arch.contains("riscv") {
62 return true;
63 }
64 }
65
66 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
67 if content.contains("riscv") || content.contains("RISC-V") {
68 return true;
69 }
70 }
71
72 false
73}
74
75#[must_use]
77pub fn get_extensions_compact() -> String {
78 parse_extensions_compact(&get_isa_string())
79}
80
81#[must_use]
83pub fn get_z_extensions() -> String {
84 parse_z_extensions(&get_isa_string())
85}
86
87#[must_use]
89pub fn get_extensions_explained() -> Vec<(String, String)> {
90 parse_extensions_explained(&get_isa_string())
91}
92
93#[must_use]
95pub fn get_z_extensions_explained() -> Vec<(String, String)> {
96 parse_z_extensions_explained(&get_isa_string())
97}
98
99#[must_use]
101pub fn get_s_extensions() -> String {
102 parse_s_extensions(&get_isa_string())
103}
104
105#[must_use]
107pub fn get_s_extensions_explained() -> Vec<(String, String)> {
108 parse_s_extensions_explained(&get_isa_string())
109}
110
111#[must_use]
113pub fn get_z_extensions_with_category() -> Vec<ExtensionInfo> {
114 parse_z_extensions_with_category(&get_isa_string())
115}
116
117#[must_use]
119pub fn get_s_extensions_with_category() -> Vec<ExtensionInfo> {
120 parse_s_extensions_with_category(&get_isa_string())
121}
122
123#[must_use]
125pub fn collect_riscv_info() -> RiscvInfo {
126 let mut sys = System::new();
127 sys.refresh_cpu_all();
128
129 let isa = get_isa_string();
130 let exts: Vec<String> = get_extensions_explained()
131 .into_iter()
132 .map(|(name, _)| name)
133 .collect();
134 let z_exts: Vec<String> = get_z_extensions_explained()
135 .into_iter()
136 .map(|(name, _)| name)
137 .collect();
138
139 let hw_ids = get_hardware_ids();
140 let isa_lower = isa.to_lowercase();
141 let base = isa_lower.split('_').next().unwrap_or(&isa_lower);
142
143 RiscvInfo {
144 isa,
145 extensions: exts,
146 z_extensions: z_exts,
147 vector: VectorInfo {
148 enabled: base.contains('v') || isa_lower.contains("zve"),
149 vlen: None,
150 elen: None,
151 },
152 hart_count: sys.cpus().len(),
153 hardware_ids: hw_ids,
154 cache: CacheInfo::default(),
155 }
156}
157
158#[must_use]
160pub fn collect_all_info() -> SystemInfo {
161 let mut sys = System::new();
162 sys.refresh_memory();
163 sys.refresh_cpu_all();
164
165 let isa = get_isa_string();
166 let exts: Vec<String> = get_extensions_explained()
167 .into_iter()
168 .map(|(name, _)| name)
169 .collect();
170 let z_exts: Vec<String> = get_z_extensions_explained()
171 .into_iter()
172 .map(|(name, _)| name)
173 .collect();
174 let s_exts: Vec<String> = get_s_extensions_explained()
175 .into_iter()
176 .map(|(name, _)| name)
177 .collect();
178
179 let hw_ids = get_hardware_ids();
180 let isa_lower = isa.to_lowercase();
181 let base = isa_lower.split('_').next().unwrap_or(&isa_lower);
182
183 SystemInfo {
184 isa,
185 extensions: exts,
186 z_extensions: z_exts,
187 s_extensions: s_exts,
188 vector: VectorInfo {
189 enabled: base.contains('v') || isa_lower.contains("zve"),
190 vlen: None,
191 elen: None,
192 },
193 hart_count: sys.cpus().len(),
194 hardware_ids: hw_ids,
195 cache: CacheInfo::default(),
196 board: get_board_info(),
197 memory_used_bytes: sys.used_memory(),
198 memory_total_bytes: sys.total_memory(),
199 kernel: get_kernel_info(),
200 os: get_os_info(),
201 uptime_seconds: System::uptime(),
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
212 fn test_get_uptime() {
213 let uptime = get_uptime();
214 assert!(!uptime.is_empty());
215 }
216
217 #[test]
218 fn test_get_uptime_seconds() {
219 let secs = get_uptime_seconds();
220 assert!(secs > 0);
221 }
222
223 #[test]
224 fn test_get_memory_bytes() {
225 let (used, total) = get_memory_bytes();
226 assert!(total > 0);
227 assert!(used <= total);
228 }
229
230 #[test]
231 fn test_get_kernel_info() {
232 let kernel = get_kernel_info();
233 assert!(!kernel.is_empty());
234 }
235
236 #[test]
237 fn test_get_os_info() {
238 let os = get_os_info();
239 assert!(!os.is_empty());
240 }
241
242 #[test]
243 fn test_hardware_ids_default() {
244 let ids = HardwareIds::default();
245 assert!(ids.mvendorid.is_empty());
246 assert!(ids.marchid.is_empty());
247 assert!(ids.mimpid.is_empty());
248 }
249
250 #[cfg(target_arch = "riscv64")]
253 mod riscv_hardware_tests {
254 use super::*;
255
256 #[test]
257 fn hw_is_riscv() {
258 assert!(is_riscv());
259 }
260
261 #[test]
262 fn hw_isa_string_valid() {
263 let isa = get_isa_string();
264 assert!(isa.starts_with("rv64") || isa.starts_with("rv32"));
265 }
266
267 #[test]
268 fn hw_isa_string_has_base() {
269 let isa = get_isa_string();
270 assert!(isa.contains('i') || isa.contains('e'));
271 }
272
273 #[test]
274 fn hw_extensions_not_empty() {
275 let ext = get_extensions_compact();
276 assert!(!ext.is_empty());
277 assert!(ext.contains('I') || ext.contains('E'));
278 }
279
280 #[test]
281 fn hw_hart_count_positive() {
282 let hart_str = get_hart_count();
283 assert!(hart_str.contains("hart"));
284 let num: String = hart_str
285 .chars()
286 .take_while(|c| c.is_ascii_digit())
287 .collect();
288 let count: usize = num.parse().unwrap_or(0);
289 assert!(count > 0);
290 }
291
292 #[test]
293 fn hw_hardware_ids_present() {
294 let ids = get_hardware_ids();
295 let has_any =
296 !ids.mvendorid.is_empty() || !ids.marchid.is_empty() || !ids.mimpid.is_empty();
297 assert!(has_any);
298 }
299
300 #[test]
301 fn hw_collect_all_info() {
302 let info = collect_all_info();
303 assert!(!info.isa.is_empty());
304 assert!(!info.extensions.is_empty());
305 assert!(info.hart_count > 0);
306 }
307
308 #[test]
309 fn hw_collect_riscv_info() {
310 let info = collect_riscv_info();
311 assert!(!info.isa.is_empty());
312 assert!(!info.extensions.is_empty());
313 assert!(info.hart_count > 0);
314 }
315
316 #[test]
317 fn hw_riscv_info_excludes_system_fields() {
318 let riscv_info = collect_riscv_info();
319 let all_info = collect_all_info();
320
321 assert_eq!(riscv_info.isa, all_info.isa);
323 assert_eq!(riscv_info.extensions, all_info.extensions);
324 assert_eq!(riscv_info.z_extensions, all_info.z_extensions);
325 assert_eq!(riscv_info.hart_count, all_info.hart_count);
326
327 }
331 }
332}