1use crate::{HardwareQueryError, Result};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use sysinfo::System;
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub enum CPUVendor {
9 Intel,
10 AMD,
11 ARM,
12 Apple,
13 Unknown(String),
14}
15
16impl fmt::Display for CPUVendor {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 CPUVendor::Intel => write!(f, "Intel"),
20 CPUVendor::AMD => write!(f, "AMD"),
21 CPUVendor::ARM => write!(f, "ARM"),
22 CPUVendor::Apple => write!(f, "Apple"),
23 CPUVendor::Unknown(name) => write!(f, "{name}"),
24 }
25 }
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub enum CPUFeature {
31 AVX,
32 AVX2,
33 AVX512,
34 SSE,
35 SSE2,
36 SSE3,
37 SSE41,
38 SSE42,
39 FMA,
40 AES,
41 SHA,
42 BMI1,
43 BMI2,
44 RDRAND,
45 RDSEED,
46 POPCNT,
47 LZCNT,
48 MOVBE,
49 PREFETCHWT1,
50 CLFLUSHOPT,
51 CLWB,
52 XSAVE,
53 XSAVEOPT,
54 XSAVEC,
55 XSAVES,
56 FSGSBASE,
57 RDTSCP,
58 F16C,
59 Unknown(String),
60}
61
62impl fmt::Display for CPUFeature {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match self {
65 CPUFeature::AVX => write!(f, "AVX"),
66 CPUFeature::AVX2 => write!(f, "AVX2"),
67 CPUFeature::AVX512 => write!(f, "AVX512"),
68 CPUFeature::SSE => write!(f, "SSE"),
69 CPUFeature::SSE2 => write!(f, "SSE2"),
70 CPUFeature::SSE3 => write!(f, "SSE3"),
71 CPUFeature::SSE41 => write!(f, "SSE4.1"),
72 CPUFeature::SSE42 => write!(f, "SSE4.2"),
73 CPUFeature::FMA => write!(f, "FMA"),
74 CPUFeature::AES => write!(f, "AES"),
75 CPUFeature::SHA => write!(f, "SHA"),
76 CPUFeature::BMI1 => write!(f, "BMI1"),
77 CPUFeature::BMI2 => write!(f, "BMI2"),
78 CPUFeature::RDRAND => write!(f, "RDRAND"),
79 CPUFeature::RDSEED => write!(f, "RDSEED"),
80 CPUFeature::POPCNT => write!(f, "POPCNT"),
81 CPUFeature::LZCNT => write!(f, "LZCNT"),
82 CPUFeature::MOVBE => write!(f, "MOVBE"),
83 CPUFeature::PREFETCHWT1 => write!(f, "PREFETCHWT1"),
84 CPUFeature::CLFLUSHOPT => write!(f, "CLFLUSHOPT"),
85 CPUFeature::CLWB => write!(f, "CLWB"),
86 CPUFeature::XSAVE => write!(f, "XSAVE"),
87 CPUFeature::XSAVEOPT => write!(f, "XSAVEOPT"),
88 CPUFeature::XSAVEC => write!(f, "XSAVEC"),
89 CPUFeature::XSAVES => write!(f, "XSAVES"),
90 CPUFeature::FSGSBASE => write!(f, "FSGSBASE"),
91 CPUFeature::RDTSCP => write!(f, "RDTSCP"),
92 CPUFeature::F16C => write!(f, "F16C"),
93 CPUFeature::Unknown(name) => write!(f, "{name}"),
94 }
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct CPUInfo {
101 pub vendor: CPUVendor,
103 pub model_name: String,
105 pub brand: String,
107 pub physical_cores: u32,
109 pub logical_cores: u32,
111 pub base_frequency: u32,
113 pub max_frequency: u32,
115 pub l1_cache_kb: u32,
117 pub l2_cache_kb: u32,
119 pub l3_cache_kb: u32,
121 pub features: Vec<CPUFeature>,
123 pub architecture: String,
125 pub core_usage: Vec<f32>,
127 pub temperature: Option<f32>,
129 pub power_consumption: Option<f32>,
131 pub stepping: Option<u32>,
133 pub family: Option<u32>,
135 pub model: Option<u32>,
137 pub microcode: Option<String>,
139 pub vulnerabilities: Vec<String>,
141}
142
143impl CPUInfo {
144 pub fn query() -> Result<Self> {
146 let mut system = System::new_all();
147 system.refresh_all();
148
149 let cpus = system.cpus();
150 if cpus.is_empty() {
151 return Err(HardwareQueryError::system_info_unavailable(
152 "No CPU information available",
153 ));
154 }
155
156 let cpu = &cpus[0];
157 let brand = cpu.brand().to_string();
158 let vendor = Self::parse_vendor(&brand);
159
160 Ok(Self {
161 vendor,
162 model_name: Self::extract_model_name(&brand),
163 brand,
164 physical_cores: Self::detect_physical_cores()?,
165 logical_cores: system.cpus().len() as u32,
166 base_frequency: cpu.frequency() as u32,
167 max_frequency: Self::detect_max_frequency()?,
168 l1_cache_kb: Self::detect_l1_cache()?,
169 l2_cache_kb: Self::detect_l2_cache()?,
170 l3_cache_kb: Self::detect_l3_cache()?,
171 features: Self::detect_features()?,
172 architecture: Self::detect_architecture(),
173 core_usage: cpus.iter().map(|cpu| cpu.cpu_usage()).collect(),
174 temperature: Self::detect_temperature(),
175 power_consumption: Self::detect_power_consumption(),
176 stepping: Self::detect_stepping()?,
177 family: Self::detect_family()?,
178 model: Self::detect_model()?,
179 microcode: Self::detect_microcode(),
180 vulnerabilities: Self::detect_vulnerabilities()?,
181 })
182 }
183
184 pub fn vendor(&self) -> &CPUVendor {
186 &self.vendor
187 }
188
189 pub fn model_name(&self) -> &str {
191 &self.model_name
192 }
193
194 pub fn brand(&self) -> &str {
196 &self.brand
197 }
198
199 pub fn physical_cores(&self) -> u32 {
201 self.physical_cores
202 }
203
204 pub fn logical_cores(&self) -> u32 {
206 self.logical_cores
207 }
208
209 pub fn base_frequency(&self) -> u32 {
211 self.base_frequency
212 }
213
214 pub fn max_frequency(&self) -> u32 {
216 self.max_frequency
217 }
218
219 pub fn l1_cache_kb(&self) -> u32 {
221 self.l1_cache_kb
222 }
223
224 pub fn l2_cache_kb(&self) -> u32 {
226 self.l2_cache_kb
227 }
228
229 pub fn l3_cache_kb(&self) -> u32 {
231 self.l3_cache_kb
232 }
233
234 pub fn features(&self) -> &[CPUFeature] {
236 &self.features
237 }
238
239 pub fn has_feature(&self, feature: &str) -> bool {
241 self.features
242 .iter()
243 .any(|f| f.to_string().to_lowercase() == feature.to_lowercase())
244 }
245
246 pub fn architecture(&self) -> &str {
248 &self.architecture
249 }
250
251 pub fn core_usage(&self) -> &[f32] {
253 &self.core_usage
254 }
255
256 pub fn temperature(&self) -> Option<f32> {
258 self.temperature
259 }
260
261 pub fn power_consumption(&self) -> Option<f32> {
263 self.power_consumption
264 }
265
266 fn parse_vendor(brand: &str) -> CPUVendor {
267 let brand_lower = brand.to_lowercase();
268 if brand_lower.contains("intel") {
269 CPUVendor::Intel
270 } else if brand_lower.contains("amd") {
271 CPUVendor::AMD
272 } else if brand_lower.contains("arm") {
273 CPUVendor::ARM
274 } else if brand_lower.contains("apple") {
275 CPUVendor::Apple
276 } else {
277 CPUVendor::Unknown(brand.to_string())
278 }
279 }
280
281 fn extract_model_name(brand: &str) -> String {
282 brand.split('@').next().unwrap_or(brand).trim().to_string()
284 }
285
286 fn detect_physical_cores() -> Result<u32> {
287 #[cfg(target_os = "windows")]
288 {
289 Self::detect_physical_cores_windows()
290 }
291 #[cfg(target_os = "linux")]
292 {
293 Self::detect_physical_cores_linux()
294 }
295 #[cfg(target_os = "macos")]
296 {
297 Self::detect_physical_cores_macos()
298 }
299 #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
300 {
301 Ok((num_cpus::get() / 2) as u32)
303 }
304 }
305
306 fn detect_max_frequency() -> Result<u32> {
307 #[cfg(target_os = "windows")]
308 {
309 Self::detect_max_frequency_windows()
310 }
311 #[cfg(target_os = "linux")]
312 {
313 Self::detect_max_frequency_linux()
314 }
315 #[cfg(target_os = "macos")]
316 {
317 Self::detect_max_frequency_macos()
318 }
319 #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
320 {
321 Ok(3000) }
323 }
324
325 fn detect_l1_cache() -> Result<u32> {
326 #[cfg(target_os = "windows")]
327 {
328 Self::detect_l1_cache_windows()
329 }
330 #[cfg(target_os = "linux")]
331 {
332 Self::detect_l1_cache_linux()
333 }
334 #[cfg(target_os = "macos")]
335 {
336 Self::detect_l1_cache_macos()
337 }
338 #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
339 {
340 Ok(32) }
342 }
343
344 fn detect_l2_cache() -> Result<u32> {
345 #[cfg(target_os = "windows")]
346 {
347 Self::detect_l2_cache_windows()
348 }
349 #[cfg(target_os = "linux")]
350 {
351 Self::detect_l2_cache_linux()
352 }
353 #[cfg(target_os = "macos")]
354 {
355 Self::detect_l2_cache_macos()
356 }
357 #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
358 {
359 Ok(256) }
361 }
362
363 fn detect_l3_cache() -> Result<u32> {
364 #[cfg(target_os = "windows")]
365 {
366 Self::detect_l3_cache_windows()
367 }
368 #[cfg(target_os = "linux")]
369 {
370 Self::detect_l3_cache_linux()
371 }
372 #[cfg(target_os = "macos")]
373 {
374 Self::detect_l3_cache_macos()
375 }
376 #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
377 {
378 Ok(8192) }
380 }
381
382 fn detect_features() -> Result<Vec<CPUFeature>> {
383 #[cfg(target_os = "windows")]
384 {
385 Self::detect_features_windows()
386 }
387 #[cfg(target_os = "linux")]
388 {
389 Self::detect_features_linux()
390 }
391 #[cfg(target_os = "macos")]
392 {
393 Self::detect_features_macos()
394 }
395 #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
396 {
397 Ok(vec![
398 CPUFeature::SSE,
399 CPUFeature::SSE2,
400 CPUFeature::SSE3,
401 CPUFeature::SSE41,
402 CPUFeature::SSE42,
403 CPUFeature::AVX,
404 CPUFeature::AVX2,
405 ])
406 }
407 }
408
409 fn detect_stepping() -> Result<Option<u32>> {
410 #[cfg(target_os = "linux")]
411 {
412 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
413 for line in content.lines() {
414 if line.starts_with("stepping") {
415 if let Some(value) = line.split(':').nth(1) {
416 return Ok(value.trim().parse().ok());
417 }
418 }
419 }
420 }
421 }
422 Ok(None)
423 }
424
425 fn detect_family() -> Result<Option<u32>> {
426 #[cfg(target_os = "linux")]
427 {
428 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
429 for line in content.lines() {
430 if line.starts_with("cpu family") {
431 if let Some(value) = line.split(':').nth(1) {
432 return Ok(value.trim().parse().ok());
433 }
434 }
435 }
436 }
437 }
438 Ok(None)
439 }
440
441 fn detect_model() -> Result<Option<u32>> {
442 #[cfg(target_os = "linux")]
443 {
444 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
445 for line in content.lines() {
446 if line.starts_with("model") && !line.starts_with("model name") {
447 if let Some(value) = line.split(':').nth(1) {
448 return Ok(value.trim().parse().ok());
449 }
450 }
451 }
452 }
453 }
454 Ok(None)
455 }
456
457 fn detect_microcode() -> Option<String> {
458 #[cfg(target_os = "linux")]
459 {
460 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
461 for line in content.lines() {
462 if line.starts_with("microcode") {
463 if let Some(value) = line.split(':').nth(1) {
464 return Some(value.trim().to_string());
465 }
466 }
467 }
468 }
469 }
470 None
471 }
472
473 fn detect_vulnerabilities() -> Result<Vec<String>> {
474 let vulnerabilities = Vec::new();
475
476 #[cfg(target_os = "linux")]
477 {
478 if let Ok(entries) = fs::read_dir("/sys/devices/system/cpu/vulnerabilities") {
479 for entry in entries.flatten() {
480 if let Ok(name) = entry.file_name().into_string() {
481 if let Ok(status) = fs::read_to_string(entry.path()) {
482 let status = status.trim();
483 if !status.starts_with("Not affected")
484 && !status.starts_with("Mitigation")
485 {
486 vulnerabilities.push(format!("{}: {}", name, status));
487 }
488 }
489 }
490 }
491 }
492 }
493
494 Ok(vulnerabilities)
495 }
496
497 #[cfg(target_os = "windows")]
499 fn detect_physical_cores_windows() -> Result<u32> {
500 match wmi::WMIConnection::new(wmi::COMLibrary::new()?) {
501 Ok(wmi_con) => {
502 let results: Vec<std::collections::HashMap<String, wmi::Variant>> = wmi_con
503 .raw_query("SELECT NumberOfCores FROM Win32_Processor")
504 .map_err(|e| {
505 HardwareQueryError::system_info_unavailable(format!(
506 "WMI query failed: {e}"
507 ))
508 })?;
509
510 if let Some(result) = results.first() {
511 if let Some(wmi::Variant::UI4(cores)) = result.get("NumberOfCores") {
512 return Ok(*cores);
513 }
514 }
515 Err(HardwareQueryError::system_info_unavailable(
516 "Could not get core count from WMI",
517 ))
518 }
519 Err(_) => Ok((num_cpus::get() / 2) as u32), }
521 }
522
523 #[cfg(target_os = "windows")]
524 fn detect_max_frequency_windows() -> Result<u32> {
525 match wmi::WMIConnection::new(wmi::COMLibrary::new()?) {
526 Ok(wmi_con) => {
527 let results: Vec<std::collections::HashMap<String, wmi::Variant>> = wmi_con
528 .raw_query("SELECT MaxClockSpeed FROM Win32_Processor")
529 .map_err(|e| {
530 HardwareQueryError::system_info_unavailable(format!(
531 "WMI query failed: {e}"
532 ))
533 })?;
534
535 if let Some(result) = results.first() {
536 if let Some(wmi::Variant::UI4(freq)) = result.get("MaxClockSpeed") {
537 return Ok(*freq);
538 }
539 }
540 Ok(3000) }
542 Err(_) => Ok(3000), }
544 }
545
546 #[cfg(target_os = "windows")]
547 fn detect_l1_cache_windows() -> Result<u32> {
548 match wmi::WMIConnection::new(wmi::COMLibrary::new()?) {
549 Ok(wmi_con) => {
550 let results: Vec<std::collections::HashMap<String, wmi::Variant>> = wmi_con
551 .raw_query("SELECT MaxCacheSize FROM Win32_CacheMemory WHERE Level = 3")
552 .map_err(|e| {
553 HardwareQueryError::system_info_unavailable(format!(
554 "WMI query failed: {e}"
555 ))
556 })?;
557
558 if let Some(result) = results.first() {
559 if let Some(wmi::Variant::UI4(size)) = result.get("MaxCacheSize") {
560 return Ok(*size);
561 }
562 }
563 Ok(32) }
565 Err(_) => Ok(32), }
567 }
568
569 #[cfg(target_os = "windows")]
570 fn detect_l2_cache_windows() -> Result<u32> {
571 Ok(256) }
573
574 #[cfg(target_os = "windows")]
575 fn detect_l3_cache_windows() -> Result<u32> {
576 Ok(8192) }
578
579 #[cfg(target_os = "windows")]
580 fn detect_features_windows() -> Result<Vec<CPUFeature>> {
581 Ok(vec![
584 CPUFeature::SSE,
585 CPUFeature::SSE2,
586 CPUFeature::SSE3,
587 CPUFeature::SSE41,
588 CPUFeature::SSE42,
589 CPUFeature::AVX,
590 CPUFeature::AVX2,
591 ])
592 }
593
594 #[cfg(target_os = "linux")]
596 fn detect_physical_cores_linux() -> Result<u32> {
597 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
598 let mut core_ids = std::collections::HashSet::new();
599 for line in content.lines() {
600 if line.starts_with("core id") {
601 if let Some(value) = line.split(':').nth(1) {
602 if let Ok(id) = value.trim().parse::<u32>() {
603 core_ids.insert(id);
604 }
605 }
606 }
607 }
608 if !core_ids.is_empty() {
609 return Ok(core_ids.len() as u32);
610 }
611 }
612 Ok((num_cpus::get() / 2) as u32) }
614
615 #[cfg(target_os = "linux")]
616 fn detect_max_frequency_linux() -> Result<u32> {
617 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
618 for line in content.lines() {
619 if line.starts_with("cpu MHz") {
620 if let Some(value) = line.split(':').nth(1) {
621 if let Ok(freq) = value.trim().parse::<f32>() {
622 return Ok(freq as u32);
623 }
624 }
625 }
626 }
627 }
628
629 if let Ok(content) =
631 fs::read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")
632 {
633 if let Ok(freq) = content.trim().parse::<u32>() {
634 return Ok(freq / 1000); }
636 }
637
638 Ok(3000) }
640
641 #[cfg(target_os = "linux")]
642 fn detect_l1_cache_linux() -> Result<u32> {
643 if let Ok(content) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index0/size") {
644 if let Ok(size_str) = content.trim().parse::<String>() {
645 if let Ok(size) = size_str.trim_end_matches('K').parse::<u32>() {
646 return Ok(size);
647 }
648 }
649 }
650 Ok(32) }
652
653 #[cfg(target_os = "linux")]
654 fn detect_l2_cache_linux() -> Result<u32> {
655 if let Ok(content) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index1/size") {
656 if let Ok(size_str) = content.trim().parse::<String>() {
657 if let Ok(size) = size_str.trim_end_matches('K').parse::<u32>() {
658 return Ok(size);
659 }
660 }
661 }
662 Ok(256) }
664
665 #[cfg(target_os = "linux")]
666 fn detect_l3_cache_linux() -> Result<u32> {
667 if let Ok(content) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index2/size") {
668 if let Ok(size_str) = content.trim().parse::<String>() {
669 if let Ok(size) = size_str.trim_end_matches('K').parse::<u32>() {
670 return Ok(size);
671 }
672 }
673 }
674 Ok(8192) }
676
677 #[cfg(target_os = "linux")]
678 fn detect_features_linux() -> Result<Vec<CPUFeature>> {
679 let mut features = Vec::new();
680
681 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
682 for line in content.lines() {
683 if line.starts_with("flags") {
684 if let Some(flags_str) = line.split(':').nth(1) {
685 let flags: Vec<&str> = flags_str.split_whitespace().collect();
686
687 for flag in flags {
688 match flag {
689 "sse" => features.push(CPUFeature::SSE),
690 "sse2" => features.push(CPUFeature::SSE2),
691 "sse3" => features.push(CPUFeature::SSE3),
692 "sse4_1" => features.push(CPUFeature::SSE41),
693 "sse4_2" => features.push(CPUFeature::SSE42),
694 "avx" => features.push(CPUFeature::AVX),
695 "avx2" => features.push(CPUFeature::AVX2),
696 "avx512f" => features.push(CPUFeature::AVX512),
697 "fma" => features.push(CPUFeature::FMA),
698 "aes" => features.push(CPUFeature::AES),
699 "sha_ni" => features.push(CPUFeature::SHA),
700 "bmi1" => features.push(CPUFeature::BMI1),
701 "bmi2" => features.push(CPUFeature::BMI2),
702 "rdrand" => features.push(CPUFeature::RDRAND),
703 "rdseed" => features.push(CPUFeature::RDSEED),
704 "popcnt" => features.push(CPUFeature::POPCNT),
705 "lzcnt" => features.push(CPUFeature::LZCNT),
706 _ => {}
707 }
708 }
709 break;
710 }
711 }
712 }
713 }
714
715 Ok(features)
716 }
717
718 #[cfg(target_os = "macos")]
720 fn detect_physical_cores_macos() -> Result<u32> {
721 use std::process::Command;
722
723 let output = Command::new("sysctl")
724 .arg("-n")
725 .arg("hw.physicalcpu")
726 .output()
727 .map_err(|e| {
728 HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
729 })?;
730
731 if output.status.success() {
732 let cores_str = String::from_utf8_lossy(&output.stdout);
733 if let Ok(cores) = cores_str.trim().parse::<u32>() {
734 return Ok(cores);
735 }
736 }
737
738 Ok((num_cpus::get() / 2) as u32) }
740
741 #[cfg(target_os = "macos")]
742 fn detect_max_frequency_macos() -> Result<u32> {
743 use std::process::Command;
744
745 let output = Command::new("sysctl")
746 .arg("-n")
747 .arg("hw.cpufrequency_max")
748 .output()
749 .map_err(|e| {
750 HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
751 })?;
752
753 if output.status.success() {
754 let freq_str = String::from_utf8_lossy(&output.stdout);
755 if let Ok(freq) = freq_str.trim().parse::<u64>() {
756 return Ok((freq / 1_000_000) as u32); }
758 }
759
760 Ok(3000) }
762
763 #[cfg(target_os = "macos")]
764 fn detect_l1_cache_macos() -> Result<u32> {
765 use std::process::Command;
766
767 let output = Command::new("sysctl")
768 .arg("-n")
769 .arg("hw.l1dcachesize")
770 .output()
771 .map_err(|e| {
772 HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
773 })?;
774
775 if output.status.success() {
776 let cache_str = String::from_utf8_lossy(&output.stdout);
777 if let Ok(cache) = cache_str.trim().parse::<u32>() {
778 return Ok(cache / 1024); }
780 }
781
782 Ok(32) }
784
785 #[cfg(target_os = "macos")]
786 fn detect_l2_cache_macos() -> Result<u32> {
787 use std::process::Command;
788
789 let output = Command::new("sysctl")
790 .arg("-n")
791 .arg("hw.l2cachesize")
792 .output()
793 .map_err(|e| {
794 HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
795 })?;
796
797 if output.status.success() {
798 let cache_str = String::from_utf8_lossy(&output.stdout);
799 if let Ok(cache) = cache_str.trim().parse::<u32>() {
800 return Ok(cache / 1024); }
802 }
803
804 Ok(256) }
806
807 #[cfg(target_os = "macos")]
808 fn detect_l3_cache_macos() -> Result<u32> {
809 use std::process::Command;
810
811 let output = Command::new("sysctl")
812 .arg("-n")
813 .arg("hw.l3cachesize")
814 .output()
815 .map_err(|e| {
816 HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
817 })?;
818
819 if output.status.success() {
820 let cache_str = String::from_utf8_lossy(&output.stdout);
821 if let Ok(cache) = cache_str.trim().parse::<u32>() {
822 return Ok(cache / 1024); }
824 }
825
826 Ok(8192) }
828
829 #[cfg(target_os = "macos")]
830 fn detect_features_macos() -> Result<Vec<CPUFeature>> {
831 use std::process::Command;
832
833 let mut features = Vec::new();
834
835 let feature_checks = vec![
837 ("hw.optional.sse", CPUFeature::SSE),
838 ("hw.optional.sse2", CPUFeature::SSE2),
839 ("hw.optional.sse3", CPUFeature::SSE3),
840 ("hw.optional.sse4_1", CPUFeature::SSE41),
841 ("hw.optional.sse4_2", CPUFeature::SSE42),
842 ("hw.optional.avx1_0", CPUFeature::AVX),
843 ("hw.optional.avx2_0", CPUFeature::AVX2),
844 ("hw.optional.aes", CPUFeature::AES),
845 ];
846
847 for (sysctl_name, feature) in feature_checks {
848 let output = Command::new("sysctl").arg("-n").arg(sysctl_name).output();
849
850 if let Ok(output) = output {
851 if output.status.success() {
852 let value_str = String::from_utf8_lossy(&output.stdout);
853 if value_str.trim() == "1" {
854 features.push(feature);
855 }
856 }
857 }
858 }
859
860 Ok(features)
861 }
862
863 fn detect_architecture() -> String {
864 if cfg!(target_arch = "x86_64") {
865 "x86_64".to_string()
866 } else if cfg!(target_arch = "aarch64") {
867 "aarch64".to_string()
868 } else if cfg!(target_arch = "arm") {
869 "arm".to_string()
870 } else {
871 std::env::consts::ARCH.to_string()
872 }
873 }
874
875 fn detect_temperature() -> Option<f32> {
876 None
878 }
879
880 fn detect_power_consumption() -> Option<f32> {
881 None
883 }
884}