1mod hardware;
18mod parsing;
19mod system;
20mod types;
21
22pub use types::{CacheInfo, HardwareIds, SystemInfo, VectorInfo};
24
25pub use parsing::{
27 parse_extensions_compact, parse_extensions_explained, parse_vector_from_isa,
28 parse_z_extensions, parse_z_extensions_explained,
29};
30
31pub use hardware::{
33 get_board_info, get_cache_info, get_hardware_ids, get_hart_count, get_hart_count_num,
34 get_isa_string, get_vector_detail,
35};
36
37pub use system::{
39 get_kernel_info, get_memory_bytes, get_memory_info, get_os_info, get_uptime, get_uptime_seconds,
40};
41
42use std::fs;
43use std::process::Command;
44use sysinfo::System;
45
46pub fn is_riscv() -> bool {
48 if let Ok(output) = Command::new("uname").arg("-m").output() {
49 let arch = String::from_utf8_lossy(&output.stdout);
50 if arch.contains("riscv") {
51 return true;
52 }
53 }
54
55 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
56 if content.contains("riscv") || content.contains("RISC-V") {
57 return true;
58 }
59 }
60
61 false
62}
63
64pub fn get_extensions_compact() -> String {
66 parse_extensions_compact(&get_isa_string())
67}
68
69pub fn get_z_extensions() -> String {
71 parse_z_extensions(&get_isa_string())
72}
73
74pub fn get_extensions_explained() -> Vec<(String, String)> {
76 parse_extensions_explained(&get_isa_string())
77}
78
79pub fn get_z_extensions_explained() -> Vec<(String, String)> {
81 parse_z_extensions_explained(&get_isa_string())
82}
83
84pub fn collect_all_info() -> SystemInfo {
86 let mut sys = System::new();
87 sys.refresh_memory();
88 sys.refresh_cpu_all();
89
90 let isa = get_isa_string();
91 let exts: Vec<String> = get_extensions_explained()
92 .into_iter()
93 .map(|(name, _)| name)
94 .collect();
95 let z_exts: Vec<String> = get_z_extensions_explained()
96 .into_iter()
97 .map(|(name, _)| name)
98 .collect();
99
100 let hw_ids = get_hardware_ids();
101 let isa_lower = isa.to_lowercase();
102 let base = isa_lower.split('_').next().unwrap_or(&isa_lower);
103
104 SystemInfo {
105 isa,
106 extensions: exts,
107 z_extensions: z_exts,
108 vector: VectorInfo {
109 enabled: base.contains('v') || isa_lower.contains("zve"),
110 vlen: None,
111 elen: None,
112 },
113 hart_count: sys.cpus().len(),
114 hardware_ids: hw_ids,
115 cache: CacheInfo::default(),
116 board: get_board_info(),
117 memory_used_bytes: sys.used_memory(),
118 memory_total_bytes: sys.total_memory(),
119 kernel: get_kernel_info(),
120 os: get_os_info(),
121 uptime_seconds: System::uptime(),
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 const ISA_VISIONFIVE2: &str = "rv64imafdc_zicntr_zicsr_zifencei_zihpm_zba_zbb";
131 const ISA_SPACEMIT_K1: &str = "rv64imafdcv_zicbom_zicboz_zicntr_zicsr_zifencei_zihintpause_zihpm_zba_zbb_zbc_zbs_zkt_zvkt_zvl128b_zvl256b_zvl32b_zvl64b";
132 const ISA_MINIMAL: &str = "rv64imac";
133 const ISA_RV32: &str = "rv32imc";
134
135 #[test]
138 fn test_parse_extensions_compact_visionfive2() {
139 let result = parse_extensions_compact(ISA_VISIONFIVE2);
140 assert_eq!(result, "I M A F D C");
141 }
142
143 #[test]
144 fn test_parse_extensions_compact_spacemit() {
145 let result = parse_extensions_compact(ISA_SPACEMIT_K1);
146 assert_eq!(result, "I M A F D C V");
147 }
148
149 #[test]
150 fn test_parse_extensions_compact_minimal() {
151 let result = parse_extensions_compact(ISA_MINIMAL);
152 assert_eq!(result, "I M A C");
153 }
154
155 #[test]
156 fn test_parse_extensions_compact_rv32() {
157 let result = parse_extensions_compact(ISA_RV32);
158 assert_eq!(result, "I M C");
159 }
160
161 #[test]
162 fn test_parse_extensions_compact_unknown() {
163 let result = parse_extensions_compact("unknown");
164 assert_eq!(result, "");
165 }
166
167 #[test]
170 fn test_parse_z_extensions_visionfive2() {
171 let result = parse_z_extensions(ISA_VISIONFIVE2);
172 assert!(result.contains("zicntr"));
173 assert!(result.contains("zicsr"));
174 assert!(result.contains("zifencei"));
175 assert!(result.contains("zba"));
176 assert!(result.contains("zbb"));
177 }
178
179 #[test]
180 fn test_parse_z_extensions_spacemit() {
181 let result = parse_z_extensions(ISA_SPACEMIT_K1);
182 assert!(result.contains("zicbom"));
183 assert!(result.contains("zicboz"));
184 assert!(result.contains("zbc"));
185 assert!(result.contains("zbs"));
186 assert!(result.contains("zvl256b"));
187 }
188
189 #[test]
190 fn test_parse_z_extensions_minimal() {
191 let result = parse_z_extensions(ISA_MINIMAL);
192 assert!(result.is_empty());
193 }
194
195 #[test]
198 fn test_parse_extensions_explained_visionfive2() {
199 let result = parse_extensions_explained(ISA_VISIONFIVE2);
200 assert_eq!(result.len(), 6); assert!(result.iter().any(|(n, _)| n == "I"));
202 assert!(result.iter().any(|(n, _)| n == "M"));
203 assert!(result.iter().any(|(n, _)| n == "F"));
204 assert!(result.iter().any(|(n, _)| n == "D"));
205 assert!(result.iter().any(|(n, _)| n == "C"));
206 }
207
208 #[test]
209 fn test_parse_z_extensions_explained_spacemit() {
210 let result = parse_z_extensions_explained(ISA_SPACEMIT_K1);
211 assert!(result
212 .iter()
213 .any(|(n, d)| n == "Zba" && d == "Address Generation"));
214 assert!(result
215 .iter()
216 .any(|(n, d)| n == "Zbb" && d == "Basic Bit Manipulation"));
217 assert!(result
218 .iter()
219 .any(|(n, d)| n == "Zbc" && d == "Carry-less Multiply"));
220 }
221
222 #[test]
225 fn test_parse_vector_no_vector() {
226 let result = parse_vector_from_isa(ISA_VISIONFIVE2);
227 assert!(result.is_none());
228 }
229
230 #[test]
231 fn test_parse_vector_with_v() {
232 let result = parse_vector_from_isa(ISA_SPACEMIT_K1);
233 assert!(result.is_some());
234 let detail = result.unwrap();
235 assert!(detail.contains("Enabled"));
236 assert!(detail.contains("VLEN>=256"));
237 }
238
239 #[test]
240 fn test_parse_vector_zve_only() {
241 let isa = "rv64imac_zve32x";
242 let result = parse_vector_from_isa(isa);
243 assert!(result.is_some());
244 assert!(result.unwrap().contains("Enabled"));
245 }
246
247 #[test]
250 fn test_get_uptime() {
251 let uptime = get_uptime();
252 assert!(!uptime.is_empty());
253 }
254
255 #[test]
256 fn test_get_uptime_seconds() {
257 let secs = get_uptime_seconds();
258 assert!(secs > 0);
259 }
260
261 #[test]
262 fn test_get_memory_bytes() {
263 let (used, total) = get_memory_bytes();
264 assert!(total > 0);
265 assert!(used <= total);
266 }
267
268 #[test]
269 fn test_get_kernel_info() {
270 let kernel = get_kernel_info();
271 assert!(!kernel.is_empty());
272 }
273
274 #[test]
275 fn test_get_os_info() {
276 let os = get_os_info();
277 assert!(!os.is_empty());
278 }
279
280 #[test]
281 fn test_hardware_ids_default() {
282 let ids = HardwareIds::default();
283 assert!(ids.mvendorid.is_empty());
284 assert!(ids.marchid.is_empty());
285 assert!(ids.mimpid.is_empty());
286 }
287
288 #[test]
291 fn test_parse_extensions_case_insensitive() {
292 let upper = parse_extensions_compact("RV64IMAFDC");
293 let lower = parse_extensions_compact("rv64imafdc");
294 assert_eq!(upper, lower);
295 }
296
297 #[test]
298 fn test_parse_empty_isa() {
299 let result = parse_extensions_compact("");
300 assert_eq!(result, "");
301 }
302
303 #[test]
304 fn test_parse_z_extensions_order_preserved() {
305 let result = parse_z_extensions("rv64i_zba_zbb_zbc");
306 let parts: Vec<&str> = result.split(' ').collect();
307 assert_eq!(parts, vec!["zba", "zbb", "zbc"]);
308 }
309
310 mod spec_tests {
315 use super::*;
316
317 #[test]
318 fn spec_g_expansion() {
319 assert_eq!(parse_extensions_compact("rv64gc"), "I M A F D C");
320 }
321
322 #[test]
323 fn spec_g_expansion_uppercase() {
324 assert_eq!(parse_extensions_compact("RV64GC"), "I M A F D C");
325 }
326
327 #[test]
328 fn spec_e_extension() {
329 assert_eq!(parse_extensions_compact("rv32e"), "E");
330 }
331
332 #[test]
333 fn spec_e_with_c() {
334 assert_eq!(parse_extensions_compact("rv32ec"), "E C");
335 }
336
337 #[test]
338 fn spec_standard_extensions() {
339 assert_eq!(parse_extensions_compact("rv64imafdc"), "I M A F D C");
340 }
341
342 #[test]
343 fn spec_with_vector() {
344 assert_eq!(parse_extensions_compact("rv64imafdcv"), "I M A F D C V");
345 }
346
347 #[test]
348 fn spec_rv64_prefix_not_vector() {
349 let result = parse_extensions_compact("rv64imafdc");
350 assert!(!result.contains("V"));
351 }
352
353 #[test]
354 fn spec_z_extensions_ignored() {
355 assert_eq!(
356 parse_extensions_compact("rv64imafdc_zba_zbb"),
357 "I M A F D C"
358 );
359 }
360
361 #[test]
362 fn spec_empty_input() {
363 assert_eq!(parse_extensions_compact(""), "");
364 }
365
366 #[test]
367 fn spec_invalid_input() {
368 assert_eq!(parse_extensions_compact("unknown"), "");
369 }
370
371 #[test]
372 fn spec_rv64_only() {
373 assert_eq!(parse_extensions_compact("rv64"), "");
374 }
375
376 #[test]
377 fn spec_z_extensions_basic() {
378 assert_eq!(parse_z_extensions("rv64i_zicsr_zifencei"), "zicsr zifencei");
379 }
380
381 #[test]
382 fn spec_z_extensions_order() {
383 assert_eq!(parse_z_extensions("rv64i_zba_zbb_zbc"), "zba zbb zbc");
384 }
385
386 #[test]
387 fn spec_s_extensions() {
388 let result = parse_z_extensions("rv64i_sstc");
389 assert!(result.contains("sstc"));
390 }
391
392 #[test]
393 fn spec_z_extensions_none() {
394 assert_eq!(parse_z_extensions("rv64imafdc"), "");
395 }
396
397 #[test]
398 fn spec_z_extensions_g_implies() {
399 assert_eq!(parse_z_extensions("rv64gc"), "zicsr zifencei");
400 }
401
402 #[test]
403 fn spec_z_extensions_case() {
404 let result = parse_z_extensions("rv64i_Zicsr");
405 assert_eq!(result, "zicsr");
406 }
407
408 #[test]
409 fn spec_vector_with_v() {
410 let result = parse_vector_from_isa("rv64imafdcv");
411 assert!(result.is_some());
412 assert!(result.unwrap().contains("Enabled"));
413 }
414
415 #[test]
416 fn spec_vector_none() {
417 let result = parse_vector_from_isa("rv64imafdc");
418 assert!(result.is_none());
419 }
420
421 #[test]
422 fn spec_vector_zve() {
423 let result = parse_vector_from_isa("rv64imac_zve32x");
424 assert!(result.is_some());
425 }
426
427 #[test]
428 fn spec_vector_vlen_256() {
429 let result = parse_vector_from_isa("rv64imafdcv_zvl256b");
430 assert!(result.is_some());
431 assert!(result.unwrap().contains("VLEN>=256"));
432 }
433
434 #[test]
435 fn spec_vector_vlen_largest() {
436 let result = parse_vector_from_isa("rv64imafdcv_zvl128b_zvl256b");
437 assert!(result.is_some());
438 assert!(result.unwrap().contains("VLEN>=256"));
439 }
440
441 #[test]
442 fn spec_vector_no_default_vlen() {
443 let result = parse_vector_from_isa("rv64imafdcv");
444 assert!(result.is_some());
445 let detail = result.unwrap();
446 assert!(detail.contains("Enabled"));
447 assert!(!detail.contains("VLEN"));
448 }
449 }
450
451 #[cfg(target_arch = "riscv64")]
456 mod riscv_hardware_tests {
457 use super::*;
458
459 #[test]
460 fn hw_is_riscv() {
461 assert!(is_riscv());
462 }
463
464 #[test]
465 fn hw_isa_string_valid() {
466 let isa = get_isa_string();
467 assert!(isa.starts_with("rv64") || isa.starts_with("rv32"));
468 }
469
470 #[test]
471 fn hw_isa_string_has_base() {
472 let isa = get_isa_string();
473 assert!(isa.contains('i') || isa.contains('e'));
474 }
475
476 #[test]
477 fn hw_extensions_not_empty() {
478 let ext = get_extensions_compact();
479 assert!(!ext.is_empty());
480 assert!(ext.contains('I') || ext.contains('E'));
481 }
482
483 #[test]
484 fn hw_hart_count_positive() {
485 let hart_str = get_hart_count();
486 assert!(hart_str.contains("hart"));
487 let num: String = hart_str
488 .chars()
489 .take_while(|c| c.is_ascii_digit())
490 .collect();
491 let count: usize = num.parse().unwrap_or(0);
492 assert!(count > 0);
493 }
494
495 #[test]
496 fn hw_hardware_ids_present() {
497 let ids = get_hardware_ids();
498 let has_any =
499 !ids.mvendorid.is_empty() || !ids.marchid.is_empty() || !ids.mimpid.is_empty();
500 assert!(has_any);
501 }
502
503 #[test]
504 fn hw_collect_all_info() {
505 let info = collect_all_info();
506 assert!(!info.isa.is_empty());
507 assert!(!info.extensions.is_empty());
508 assert!(info.hart_count > 0);
509 }
510 }
511}