1mod hardware;
18mod parsing;
19mod system;
20mod types;
21
22pub use types::{CacheInfo, HardwareIds, RiscvInfo, 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_riscv_info() -> RiscvInfo {
86 let mut sys = System::new();
87 sys.refresh_cpu_all();
88
89 let isa = get_isa_string();
90 let exts: Vec<String> = get_extensions_explained()
91 .into_iter()
92 .map(|(name, _)| name)
93 .collect();
94 let z_exts: Vec<String> = get_z_extensions_explained()
95 .into_iter()
96 .map(|(name, _)| name)
97 .collect();
98
99 let hw_ids = get_hardware_ids();
100 let isa_lower = isa.to_lowercase();
101 let base = isa_lower.split('_').next().unwrap_or(&isa_lower);
102
103 RiscvInfo {
104 isa,
105 extensions: exts,
106 z_extensions: z_exts,
107 vector: VectorInfo {
108 enabled: base.contains('v') || isa_lower.contains("zve"),
109 vlen: None,
110 elen: None,
111 },
112 hart_count: sys.cpus().len(),
113 hardware_ids: hw_ids,
114 cache: CacheInfo::default(),
115 }
116}
117
118pub fn collect_all_info() -> SystemInfo {
120 let mut sys = System::new();
121 sys.refresh_memory();
122 sys.refresh_cpu_all();
123
124 let isa = get_isa_string();
125 let exts: Vec<String> = get_extensions_explained()
126 .into_iter()
127 .map(|(name, _)| name)
128 .collect();
129 let z_exts: Vec<String> = get_z_extensions_explained()
130 .into_iter()
131 .map(|(name, _)| name)
132 .collect();
133
134 let hw_ids = get_hardware_ids();
135 let isa_lower = isa.to_lowercase();
136 let base = isa_lower.split('_').next().unwrap_or(&isa_lower);
137
138 SystemInfo {
139 isa,
140 extensions: exts,
141 z_extensions: z_exts,
142 vector: VectorInfo {
143 enabled: base.contains('v') || isa_lower.contains("zve"),
144 vlen: None,
145 elen: None,
146 },
147 hart_count: sys.cpus().len(),
148 hardware_ids: hw_ids,
149 cache: CacheInfo::default(),
150 board: get_board_info(),
151 memory_used_bytes: sys.used_memory(),
152 memory_total_bytes: sys.total_memory(),
153 kernel: get_kernel_info(),
154 os: get_os_info(),
155 uptime_seconds: System::uptime(),
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 const ISA_VISIONFIVE2: &str = "rv64imafdc_zicntr_zicsr_zifencei_zihpm_zba_zbb";
165 const ISA_SPACEMIT_K1: &str = "rv64imafdcv_zicbom_zicboz_zicntr_zicsr_zifencei_zihintpause_zihpm_zba_zbb_zbc_zbs_zkt_zvkt_zvl128b_zvl256b_zvl32b_zvl64b";
166 const ISA_MINIMAL: &str = "rv64imac";
167 const ISA_RV32: &str = "rv32imc";
168
169 #[test]
172 fn test_parse_extensions_compact_visionfive2() {
173 let result = parse_extensions_compact(ISA_VISIONFIVE2);
174 assert_eq!(result, "I M A F D C");
175 }
176
177 #[test]
178 fn test_parse_extensions_compact_spacemit() {
179 let result = parse_extensions_compact(ISA_SPACEMIT_K1);
180 assert_eq!(result, "I M A F D C V");
181 }
182
183 #[test]
184 fn test_parse_extensions_compact_minimal() {
185 let result = parse_extensions_compact(ISA_MINIMAL);
186 assert_eq!(result, "I M A C");
187 }
188
189 #[test]
190 fn test_parse_extensions_compact_rv32() {
191 let result = parse_extensions_compact(ISA_RV32);
192 assert_eq!(result, "I M C");
193 }
194
195 #[test]
196 fn test_parse_extensions_compact_unknown() {
197 let result = parse_extensions_compact("unknown");
198 assert_eq!(result, "");
199 }
200
201 #[test]
204 fn test_parse_z_extensions_visionfive2() {
205 let result = parse_z_extensions(ISA_VISIONFIVE2);
206 assert!(result.contains("zicntr"));
207 assert!(result.contains("zicsr"));
208 assert!(result.contains("zifencei"));
209 assert!(result.contains("zba"));
210 assert!(result.contains("zbb"));
211 }
212
213 #[test]
214 fn test_parse_z_extensions_spacemit() {
215 let result = parse_z_extensions(ISA_SPACEMIT_K1);
216 assert!(result.contains("zicbom"));
217 assert!(result.contains("zicboz"));
218 assert!(result.contains("zbc"));
219 assert!(result.contains("zbs"));
220 assert!(result.contains("zvl256b"));
221 }
222
223 #[test]
224 fn test_parse_z_extensions_minimal() {
225 let result = parse_z_extensions(ISA_MINIMAL);
226 assert!(result.is_empty());
227 }
228
229 #[test]
232 fn test_parse_extensions_explained_visionfive2() {
233 let result = parse_extensions_explained(ISA_VISIONFIVE2);
234 assert_eq!(result.len(), 6); assert!(result.iter().any(|(n, _)| n == "I"));
236 assert!(result.iter().any(|(n, _)| n == "M"));
237 assert!(result.iter().any(|(n, _)| n == "F"));
238 assert!(result.iter().any(|(n, _)| n == "D"));
239 assert!(result.iter().any(|(n, _)| n == "C"));
240 }
241
242 #[test]
243 fn test_parse_z_extensions_explained_spacemit() {
244 let result = parse_z_extensions_explained(ISA_SPACEMIT_K1);
245 assert!(result
246 .iter()
247 .any(|(n, d)| n == "Zba" && d == "Address Generation"));
248 assert!(result
249 .iter()
250 .any(|(n, d)| n == "Zbb" && d == "Basic Bit Manipulation"));
251 assert!(result
252 .iter()
253 .any(|(n, d)| n == "Zbc" && d == "Carry-less Multiply"));
254 }
255
256 #[test]
259 fn test_parse_vector_no_vector() {
260 let result = parse_vector_from_isa(ISA_VISIONFIVE2);
261 assert!(result.is_none());
262 }
263
264 #[test]
265 fn test_parse_vector_with_v() {
266 let result = parse_vector_from_isa(ISA_SPACEMIT_K1);
267 assert!(result.is_some());
268 let detail = result.unwrap();
269 assert!(detail.contains("Enabled"));
270 assert!(detail.contains("VLEN>=256"));
271 }
272
273 #[test]
274 fn test_parse_vector_zve_only() {
275 let isa = "rv64imac_zve32x";
276 let result = parse_vector_from_isa(isa);
277 assert!(result.is_some());
278 assert!(result.unwrap().contains("Enabled"));
279 }
280
281 #[test]
284 fn test_get_uptime() {
285 let uptime = get_uptime();
286 assert!(!uptime.is_empty());
287 }
288
289 #[test]
290 fn test_get_uptime_seconds() {
291 let secs = get_uptime_seconds();
292 assert!(secs > 0);
293 }
294
295 #[test]
296 fn test_get_memory_bytes() {
297 let (used, total) = get_memory_bytes();
298 assert!(total > 0);
299 assert!(used <= total);
300 }
301
302 #[test]
303 fn test_get_kernel_info() {
304 let kernel = get_kernel_info();
305 assert!(!kernel.is_empty());
306 }
307
308 #[test]
309 fn test_get_os_info() {
310 let os = get_os_info();
311 assert!(!os.is_empty());
312 }
313
314 #[test]
315 fn test_hardware_ids_default() {
316 let ids = HardwareIds::default();
317 assert!(ids.mvendorid.is_empty());
318 assert!(ids.marchid.is_empty());
319 assert!(ids.mimpid.is_empty());
320 }
321
322 #[test]
325 fn test_parse_extensions_case_insensitive() {
326 let upper = parse_extensions_compact("RV64IMAFDC");
327 let lower = parse_extensions_compact("rv64imafdc");
328 assert_eq!(upper, lower);
329 }
330
331 #[test]
332 fn test_parse_empty_isa() {
333 let result = parse_extensions_compact("");
334 assert_eq!(result, "");
335 }
336
337 #[test]
338 fn test_parse_z_extensions_order_preserved() {
339 let result = parse_z_extensions("rv64i_zba_zbb_zbc");
340 let parts: Vec<&str> = result.split(' ').collect();
341 assert_eq!(parts, vec!["zba", "zbb", "zbc"]);
342 }
343
344 mod spec_tests {
349 use super::*;
350
351 #[test]
352 fn spec_g_expansion() {
353 assert_eq!(parse_extensions_compact("rv64gc"), "I M A F D C");
354 }
355
356 #[test]
357 fn spec_g_expansion_uppercase() {
358 assert_eq!(parse_extensions_compact("RV64GC"), "I M A F D C");
359 }
360
361 #[test]
362 fn spec_e_extension() {
363 assert_eq!(parse_extensions_compact("rv32e"), "E");
364 }
365
366 #[test]
367 fn spec_e_with_c() {
368 assert_eq!(parse_extensions_compact("rv32ec"), "E C");
369 }
370
371 #[test]
372 fn spec_standard_extensions() {
373 assert_eq!(parse_extensions_compact("rv64imafdc"), "I M A F D C");
374 }
375
376 #[test]
377 fn spec_with_vector() {
378 assert_eq!(parse_extensions_compact("rv64imafdcv"), "I M A F D C V");
379 }
380
381 #[test]
382 fn spec_rv64_prefix_not_vector() {
383 let result = parse_extensions_compact("rv64imafdc");
384 assert!(!result.contains("V"));
385 }
386
387 #[test]
388 fn spec_z_extensions_ignored() {
389 assert_eq!(
390 parse_extensions_compact("rv64imafdc_zba_zbb"),
391 "I M A F D C"
392 );
393 }
394
395 #[test]
396 fn spec_empty_input() {
397 assert_eq!(parse_extensions_compact(""), "");
398 }
399
400 #[test]
401 fn spec_invalid_input() {
402 assert_eq!(parse_extensions_compact("unknown"), "");
403 }
404
405 #[test]
406 fn spec_rv64_only() {
407 assert_eq!(parse_extensions_compact("rv64"), "");
408 }
409
410 #[test]
411 fn spec_z_extensions_basic() {
412 assert_eq!(parse_z_extensions("rv64i_zicsr_zifencei"), "zicsr zifencei");
413 }
414
415 #[test]
416 fn spec_z_extensions_order() {
417 assert_eq!(parse_z_extensions("rv64i_zba_zbb_zbc"), "zba zbb zbc");
418 }
419
420 #[test]
421 fn spec_s_extensions() {
422 let result = parse_z_extensions("rv64i_sstc");
423 assert!(result.contains("sstc"));
424 }
425
426 #[test]
427 fn spec_z_extensions_none() {
428 assert_eq!(parse_z_extensions("rv64imafdc"), "");
429 }
430
431 #[test]
432 fn spec_z_extensions_g_implies() {
433 assert_eq!(parse_z_extensions("rv64gc"), "zicsr zifencei");
434 }
435
436 #[test]
437 fn spec_z_extensions_case() {
438 let result = parse_z_extensions("rv64i_Zicsr");
439 assert_eq!(result, "zicsr");
440 }
441
442 #[test]
443 fn spec_vector_with_v() {
444 let result = parse_vector_from_isa("rv64imafdcv");
445 assert!(result.is_some());
446 assert!(result.unwrap().contains("Enabled"));
447 }
448
449 #[test]
450 fn spec_vector_none() {
451 let result = parse_vector_from_isa("rv64imafdc");
452 assert!(result.is_none());
453 }
454
455 #[test]
456 fn spec_vector_zve() {
457 let result = parse_vector_from_isa("rv64imac_zve32x");
458 assert!(result.is_some());
459 }
460
461 #[test]
462 fn spec_vector_vlen_256() {
463 let result = parse_vector_from_isa("rv64imafdcv_zvl256b");
464 assert!(result.is_some());
465 assert!(result.unwrap().contains("VLEN>=256"));
466 }
467
468 #[test]
469 fn spec_vector_vlen_largest() {
470 let result = parse_vector_from_isa("rv64imafdcv_zvl128b_zvl256b");
471 assert!(result.is_some());
472 assert!(result.unwrap().contains("VLEN>=256"));
473 }
474
475 #[test]
476 fn spec_vector_no_default_vlen() {
477 let result = parse_vector_from_isa("rv64imafdcv");
478 assert!(result.is_some());
479 let detail = result.unwrap();
480 assert!(detail.contains("Enabled"));
481 assert!(!detail.contains("VLEN"));
482 }
483 }
484
485 #[cfg(target_arch = "riscv64")]
490 mod riscv_hardware_tests {
491 use super::*;
492
493 #[test]
494 fn hw_is_riscv() {
495 assert!(is_riscv());
496 }
497
498 #[test]
499 fn hw_isa_string_valid() {
500 let isa = get_isa_string();
501 assert!(isa.starts_with("rv64") || isa.starts_with("rv32"));
502 }
503
504 #[test]
505 fn hw_isa_string_has_base() {
506 let isa = get_isa_string();
507 assert!(isa.contains('i') || isa.contains('e'));
508 }
509
510 #[test]
511 fn hw_extensions_not_empty() {
512 let ext = get_extensions_compact();
513 assert!(!ext.is_empty());
514 assert!(ext.contains('I') || ext.contains('E'));
515 }
516
517 #[test]
518 fn hw_hart_count_positive() {
519 let hart_str = get_hart_count();
520 assert!(hart_str.contains("hart"));
521 let num: String = hart_str
522 .chars()
523 .take_while(|c| c.is_ascii_digit())
524 .collect();
525 let count: usize = num.parse().unwrap_or(0);
526 assert!(count > 0);
527 }
528
529 #[test]
530 fn hw_hardware_ids_present() {
531 let ids = get_hardware_ids();
532 let has_any =
533 !ids.mvendorid.is_empty() || !ids.marchid.is_empty() || !ids.mimpid.is_empty();
534 assert!(has_any);
535 }
536
537 #[test]
538 fn hw_collect_all_info() {
539 let info = collect_all_info();
540 assert!(!info.isa.is_empty());
541 assert!(!info.extensions.is_empty());
542 assert!(info.hart_count > 0);
543 }
544
545 #[test]
546 fn hw_collect_riscv_info() {
547 let info = collect_riscv_info();
548 assert!(!info.isa.is_empty());
549 assert!(!info.extensions.is_empty());
550 assert!(info.hart_count > 0);
551 }
552
553 #[test]
554 fn hw_riscv_info_excludes_system_fields() {
555 let riscv_info = collect_riscv_info();
556 let all_info = collect_all_info();
557
558 assert_eq!(riscv_info.isa, all_info.isa);
560 assert_eq!(riscv_info.extensions, all_info.extensions);
561 assert_eq!(riscv_info.z_extensions, all_info.z_extensions);
562 assert_eq!(riscv_info.hart_count, all_info.hart_count);
563
564 }
568 }
569}