Skip to main content

binius_utils/
platform_diagnostics.rs

1// Copyright 2025 Irreducible Inc.
2use std::{collections::BTreeMap, sync::OnceLock};
3
4use regex::Regex;
5
6// Build-time constants from environment variables
7const BUILD_TARGET: &str = env!("BUILD_TARGET");
8const BUILD_RUSTFLAGS: &str = env!("BUILD_RUSTFLAGS");
9const COMPILE_TIME_FEATURES_STR: &str = env!("COMPILE_TIME_FEATURES");
10
11// Lazy-initialized regex patterns for codebase scanning
12static CFG_REGEX: OnceLock<Regex> = OnceLock::new();
13static DETECT_REGEX: OnceLock<Regex> = OnceLock::new();
14
15/// Creates a regex pattern that matches Rust `#[cfg(target_feature = "...")]` attributes.
16///
17/// This pattern is used to scan Rust source files and extract CPU features that are
18/// conditionally compiled based on the target platform's capabilities.
19///
20/// # Pattern Details
21/// - Matches: `target_feature = "feature_name"`
22/// - Captures: The feature name (without quotes)
23/// - Handles: Variable whitespace around `=`
24///
25/// # Example Matches
26/// - `#[cfg(target_feature = "neon")]`
27/// - `#[cfg(all(target_arch = "x86_64", target_feature = "avx2"))]`
28fn cfg_feature_regex() -> &'static Regex {
29	CFG_REGEX.get_or_init(|| {
30		Regex::new(r#"target_feature\s*=\s*"([^"]+)""#)
31			.expect("Failed to compile cfg feature regex")
32	})
33}
34
35/// Creates a regex pattern that matches Rust feature detection macro calls.
36///
37/// This pattern is used to find runtime CPU feature detection in the codebase,
38/// such as `is_x86_feature_detected!("avx")` or `is_aarch64_feature_detected!("neon")`.
39///
40/// # Pattern Details
41/// - Matches: `is_*_feature_detected!("feature_name")`
42/// - Captures: The feature name (without quotes)
43/// - Handles: Any architecture prefix (x86, aarch64, etc.)
44///
45/// # Example Matches
46/// - `is_x86_feature_detected!("avx2")`
47/// - `is_aarch64_feature_detected!("neon")`
48/// - `if is_x86_feature_detected!("gfni") { ... }`
49fn runtime_detection_regex() -> &'static Regex {
50	DETECT_REGEX.get_or_init(|| {
51		Regex::new(r#"is_\w+_feature_detected!\s*\(\s*"([^"]+)"\s*\)"#)
52			.expect("Failed to compile runtime detection regex")
53	})
54}
55
56// Configuration constants
57mod config {
58	// CPU vendor detection strings
59	pub const VENDOR_PATTERNS: &[(&str, &str)] = &[
60		("apple", "Apple"),
61		("graviton", "AWS"),
62		("ampere", "Ampere"),
63		("intel", "Intel"),
64		("amd", "AMD"),
65	];
66	pub const VENDOR_GENERIC: &str = "Generic";
67
68	// Architecture names (used for testing)
69	#[cfg(test)]
70	pub const KNOWN_ARCHITECTURES: &[&str] =
71		&["x86_64", "aarch64", "arm", "riscv64", "wasm32", "wasm64"];
72
73	// Operating systems (used for testing)
74	#[cfg(test)]
75	pub const KNOWN_OS: &[&str] = &[
76		"linux", "macos", "windows", "freebsd", "openbsd", "netbsd", "android", "ios",
77	];
78
79	// Features to categorize as SIMD (for display purposes)
80	// Note: Features not in these lists will be categorized as "Other"
81	pub const SIMD_FEATURES: &[&str] = &[
82		"neon", "sve", "sve2", "dotprod", "fp16", "bf16", "i8mm", "f32mm", "f64mm", "fcma",
83	];
84
85	// Features to categorize as Crypto (for display purposes)
86	pub const CRYPTO_FEATURES: &[&str] = &["aes", "sha2", "sha3", "crc", "pmuv3"];
87
88	// Display settings
89	pub const MAX_DIRS_TO_SHOW: usize = 5;
90	pub const MAX_FILES_IN_DIR: usize = 10;
91
92	// Default values
93	pub const UNKNOWN_CPU: &str = "Unknown CPU";
94	pub const UNKNOWN_VERSION: &str = "unknown";
95
96	// Directory names to skip
97	pub const SKIP_DIRS: &[&str] = &["target", ".git", "node_modules"];
98	pub const RUST_FILE_EXT: &str = "rs";
99	pub const ARCH_DIR_NAME: &str = "arch";
100
101	// CPU target strategies
102	pub const CPU_TARGET_NATIVE: &str = "native";
103	pub const CPU_TARGET_GENERIC: &str = "generic";
104}
105
106pub struct PlatformDiagnostics {
107	hardware: HardwareInfo,
108	os_runtime: OSRuntimeInfo,
109	llvm_config: LLVMConfig,
110	code_features: CodeFeatures,
111	codebase_usage: CodebaseUsage,
112}
113
114#[derive(Debug)]
115struct HardwareInfo {
116	cpu_model: String,
117	architecture: &'static str,
118	vendor: String,
119	core_count: usize,
120}
121
122#[derive(Debug)]
123struct OSRuntimeInfo {
124	os: &'static str,
125	kernel_version: String,
126	runtime_features: BTreeMap<&'static str, bool>,
127}
128
129#[derive(Debug)]
130struct LLVMConfig {
131	target_triple: String,
132	target_cpu: String,
133}
134
135#[derive(Debug)]
136struct CodeFeatures {
137	compile_time_features: Vec<String>,
138}
139
140#[derive(Debug)]
141struct CodebaseUsage {
142	cfg_features: BTreeMap<String, Vec<String>>, // feature -> files using it
143	runtime_detections: BTreeMap<String, Vec<String>>, // feature -> files using runtime detection
144	arch_modules: Vec<String>,                   // architecture-specific modules found
145}
146
147// ANSI color codes
148const GREEN: &str = "\x1b[32m";
149const YELLOW: &str = "\x1b[33m";
150const BLUE: &str = "\x1b[34m";
151const RED: &str = "\x1b[31m";
152const CYAN: &str = "\x1b[36m";
153const MAGENTA: &str = "\x1b[35m";
154const RESET: &str = "\x1b[0m";
155const BOLD: &str = "\x1b[1m";
156const DIM: &str = "\x1b[2m";
157
158// Platform detection helper functions
159#[cfg(target_os = "macos")]
160fn get_macos_cpu_brand() -> Option<String> {
161	std::process::Command::new("sysctl")
162		.args(["-n", "machdep.cpu.brand_string"])
163		.output()
164		.ok()
165		.and_then(|o| String::from_utf8(o.stdout).ok())
166		.map(|s| s.trim().to_string())
167}
168
169#[cfg(target_os = "macos")]
170fn get_kernel_version_via_uname() -> Option<String> {
171	std::process::Command::new("uname")
172		.arg("-r")
173		.output()
174		.ok()
175		.and_then(|o| String::from_utf8(o.stdout).ok())
176		.map(|s| s.trim().to_string())
177}
178
179// Runtime feature detection functions
180#[cfg(target_arch = "aarch64")]
181fn detect_aarch64_features() -> BTreeMap<&'static str, bool> {
182	use std::arch::is_aarch64_feature_detected;
183	let mut features = BTreeMap::new();
184
185	// Note: We can't use a loop here because the macro requires literal strings
186	features.insert("neon", is_aarch64_feature_detected!("neon"));
187	features.insert("aes", is_aarch64_feature_detected!("aes"));
188	features.insert("sha2", is_aarch64_feature_detected!("sha2"));
189	features.insert("sha3", is_aarch64_feature_detected!("sha3"));
190	features.insert("crc", is_aarch64_feature_detected!("crc"));
191	features.insert("lse", is_aarch64_feature_detected!("lse"));
192	features.insert("dotprod", is_aarch64_feature_detected!("dotprod"));
193	features.insert("fp16", is_aarch64_feature_detected!("fp16"));
194	features.insert("sve", is_aarch64_feature_detected!("sve"));
195	features.insert("sve2", is_aarch64_feature_detected!("sve2"));
196	features.insert("fcma", is_aarch64_feature_detected!("fcma"));
197	features.insert("rcpc", is_aarch64_feature_detected!("rcpc"));
198	features.insert("rcpc2", is_aarch64_feature_detected!("rcpc2"));
199	features.insert("dpb", is_aarch64_feature_detected!("dpb"));
200	features.insert("dpb2", is_aarch64_feature_detected!("dpb2"));
201	features.insert("bf16", is_aarch64_feature_detected!("bf16"));
202	features.insert("i8mm", is_aarch64_feature_detected!("i8mm"));
203	features.insert("f32mm", is_aarch64_feature_detected!("f32mm"));
204	features.insert("f64mm", is_aarch64_feature_detected!("f64mm"));
205
206	features
207}
208
209#[cfg(target_arch = "x86_64")]
210fn detect_x86_64_features() -> BTreeMap<&'static str, bool> {
211	use std::arch::is_x86_feature_detected;
212	let mut features = BTreeMap::new();
213
214	// Note: We can't use a loop here because the macro requires literal strings
215	features.insert("avx", is_x86_feature_detected!("avx"));
216	features.insert("avx2", is_x86_feature_detected!("avx2"));
217	features.insert("avx512f", is_x86_feature_detected!("avx512f"));
218	features.insert("gfni", is_x86_feature_detected!("gfni"));
219	features.insert("aes", is_x86_feature_detected!("aes"));
220	features.insert("pclmulqdq", is_x86_feature_detected!("pclmulqdq"));
221	features.insert("sha", is_x86_feature_detected!("sha"));
222	features.insert("vaes", is_x86_feature_detected!("vaes"));
223	features.insert("vpclmulqdq", is_x86_feature_detected!("vpclmulqdq"));
224
225	features
226}
227
228impl PlatformDiagnostics {
229	#[must_use]
230	pub fn gather() -> Self {
231		Self {
232			hardware: Self::detect_hardware(),
233			os_runtime: Self::detect_os_runtime(),
234			llvm_config: Self::parse_llvm_config(),
235			code_features: Self::analyze_code_features(),
236			codebase_usage: Self::scan_codebase_usage(),
237		}
238	}
239
240	fn detect_hardware() -> HardwareInfo {
241		let cpu_model = Self::get_cpu_model();
242		let vendor = Self::detect_vendor(&cpu_model);
243		let core_count = std::thread::available_parallelism()
244			.map(std::num::NonZeroUsize::get)
245			.unwrap_or(1);
246
247		HardwareInfo {
248			cpu_model,
249			architecture: std::env::consts::ARCH,
250			vendor,
251			core_count,
252		}
253	}
254
255	fn detect_vendor(cpu_model: &str) -> String {
256		let model_lower = cpu_model.to_lowercase();
257		for (pattern, vendor) in config::VENDOR_PATTERNS {
258			if model_lower.contains(pattern) {
259				return vendor.to_string();
260			}
261		}
262		config::VENDOR_GENERIC.to_string()
263	}
264
265	fn get_cpu_model() -> String {
266		#[cfg(target_os = "linux")]
267		{
268			if let Ok(cpuinfo) = std::fs::read_to_string("/proc/cpuinfo") {
269				// For x86_64
270				if let Some(line) = cpuinfo.lines().find(|l| l.starts_with("model name")) {
271					return line.split(':').nth(1).unwrap_or("").trim().to_string();
272				}
273				// For ARM
274				if let Some(line) = cpuinfo.lines().find(|l| l.starts_with("CPU implementer")) {
275					let implementer = line.split(':').nth(1).unwrap_or("").trim();
276					if let Some(part_line) = cpuinfo.lines().find(|l| l.starts_with("CPU part")) {
277						let part = part_line.split(':').nth(1).unwrap_or("").trim();
278						return format!("ARM implementer {implementer} part {part}");
279					}
280				}
281			}
282		}
283
284		#[cfg(target_os = "macos")]
285		{
286			if let Some(cpu_brand) = get_macos_cpu_brand() {
287				return cpu_brand;
288			}
289		}
290
291		config::UNKNOWN_CPU.to_string()
292	}
293
294	fn detect_os_runtime() -> OSRuntimeInfo {
295		#[cfg(target_arch = "aarch64")]
296		let runtime_features = detect_aarch64_features();
297
298		#[cfg(target_arch = "x86_64")]
299		let runtime_features = detect_x86_64_features();
300
301		#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
302		let runtime_features = BTreeMap::new();
303
304		let kernel_version = Self::get_kernel_version();
305
306		OSRuntimeInfo {
307			os: std::env::consts::OS,
308			kernel_version,
309			runtime_features,
310		}
311	}
312
313	fn get_kernel_version() -> String {
314		#[cfg(target_os = "linux")]
315		{
316			std::fs::read_to_string("/proc/version")
317				.ok()
318				.and_then(|s| s.split_whitespace().nth(2).map(|s| s.to_string()))
319				.unwrap_or_else(|| config::UNKNOWN_VERSION.to_string())
320		}
321
322		#[cfg(target_os = "macos")]
323		{
324			get_kernel_version_via_uname().unwrap_or_else(|| config::UNKNOWN_VERSION.to_string())
325		}
326
327		#[cfg(not(any(target_os = "linux", target_os = "macos")))]
328		{
329			config::UNKNOWN_VERSION.to_string()
330		}
331	}
332
333	fn parse_llvm_config() -> LLVMConfig {
334		// Parse target-cpu from RUSTFLAGS
335		// Handles both "-C target-cpu=native" and "-Ctarget-cpu=native" formats
336		let mut target_cpu = config::CPU_TARGET_GENERIC.to_string();
337
338		// Try to find target-cpu in RUSTFLAGS
339		for (i, part) in BUILD_RUSTFLAGS.split_whitespace().enumerate() {
340			if part == "-C" {
341				// Check next part for "target-cpu=value"
342				if let Some(next) = BUILD_RUSTFLAGS.split_whitespace().nth(i + 1)
343					&& let Some(cpu) = next.strip_prefix("target-cpu=")
344				{
345					target_cpu = cpu.to_string();
346					break;
347				}
348			} else if let Some(rest) = part.strip_prefix("-C") {
349				// Handle "-Ctarget-cpu=value" (no space)
350				if let Some(cpu) = rest.strip_prefix("target-cpu=") {
351					target_cpu = cpu.to_string();
352					break;
353				}
354			}
355		}
356
357		LLVMConfig {
358			target_triple: BUILD_TARGET.to_string(),
359			target_cpu,
360		}
361	}
362
363	fn analyze_code_features() -> CodeFeatures {
364		let compile_time_features = COMPILE_TIME_FEATURES_STR
365			.split(',')
366			.filter(|s| !s.is_empty())
367			.map(|s| s.to_string())
368			.collect();
369
370		CodeFeatures {
371			compile_time_features,
372		}
373	}
374
375	fn scan_codebase_usage() -> CodebaseUsage {
376		let mut cfg_features = BTreeMap::new();
377		let mut runtime_detections = BTreeMap::new();
378		let mut arch_modules = Vec::new();
379
380		// Try to find the workspace root
381		let workspace_root = std::env::var("CARGO_MANIFEST_DIR").ok().and_then(|dir| {
382			let path = std::path::Path::new(&dir);
383			// Walk up to find workspace root (has Cargo.toml with [workspace])
384			let mut current = Some(path);
385			while let Some(p) = current {
386				let cargo_toml = p.join("Cargo.toml");
387				if cargo_toml.exists()
388					&& let Ok(content) = std::fs::read_to_string(&cargo_toml)
389					&& content.contains("[workspace]")
390				{
391					return p.to_str().map(std::string::ToString::to_string);
392				}
393				current = p.parent();
394			}
395			None
396		});
397
398		if let Some(root) = workspace_root {
399			// Get regex patterns for scanning
400			let cfg_regex = cfg_feature_regex();
401			let detect_regex = runtime_detection_regex();
402
403			// Scan for arch modules and features
404			Self::scan_directory_with_regex(
405				std::path::Path::new(&root),
406				&mut cfg_features,
407				&mut runtime_detections,
408				&mut arch_modules,
409				&root,
410				cfg_regex,
411				detect_regex,
412			);
413
414			arch_modules.sort();
415			arch_modules.dedup();
416		}
417
418		CodebaseUsage {
419			cfg_features,
420			runtime_detections,
421			arch_modules,
422		}
423	}
424
425	fn scan_directory_with_regex(
426		dir: &std::path::Path,
427		cfg_features: &mut BTreeMap<String, Vec<String>>,
428		runtime_detections: &mut BTreeMap<String, Vec<String>>,
429		arch_modules: &mut Vec<String>,
430		root: &str,
431		cfg_regex: &Regex,
432		detect_regex: &Regex,
433	) {
434		// Skip common non-source directories
435		if let Some(name) = dir.file_name().and_then(|n| n.to_str())
436			&& (config::SKIP_DIRS.contains(&name) || name.starts_with('.'))
437		{
438			return;
439		}
440
441		if let Ok(entries) = std::fs::read_dir(dir) {
442			for entry in entries.flatten() {
443				let path = entry.path();
444
445				if path.is_dir() {
446					// Check if this is an arch module
447					if path.file_name() == Some(std::ffi::OsStr::new(config::ARCH_DIR_NAME)) {
448						// List subdirectories as arch modules
449						if let Ok(arch_entries) = std::fs::read_dir(&path) {
450							for arch_entry in arch_entries.flatten() {
451								if arch_entry.path().is_dir()
452									&& let Some(name) = arch_entry.file_name().to_str()
453								{
454									arch_modules.push(name.to_string());
455								}
456							}
457						}
458					}
459
460					// Recurse into subdirectory
461					Self::scan_directory_with_regex(
462						&path,
463						cfg_features,
464						runtime_detections,
465						arch_modules,
466						root,
467						cfg_regex,
468						detect_regex,
469					);
470				} else if path.extension() == Some(std::ffi::OsStr::new(config::RUST_FILE_EXT)) {
471					// Scan Rust file for features
472					if let Ok(content) = std::fs::read_to_string(&path) {
473						let relative_path = path
474							.strip_prefix(root)
475							.unwrap_or(&path)
476							.to_string_lossy()
477							.to_string();
478
479						// Find all cfg features
480						for cap in cfg_regex.captures_iter(&content) {
481							if let Some(feature_match) = cap.get(1) {
482								let feature = feature_match.as_str();
483								// Skip invalid feature names
484								if !feature.is_empty() && !feature.contains('.') {
485									cfg_features
486										.entry(feature.to_string())
487										.or_default()
488										.push(relative_path.clone());
489								}
490							}
491						}
492
493						// Find all runtime detections
494						for cap in detect_regex.captures_iter(&content) {
495							if let Some(feature_match) = cap.get(1) {
496								let feature = feature_match.as_str();
497								// Skip invalid feature names and generic placeholders
498								if !feature.is_empty()
499									&& !feature.contains('.') && feature != "feature"
500								{
501									runtime_detections
502										.entry(feature.to_string())
503										.or_default()
504										.push(relative_path.clone());
505								}
506							}
507						}
508					}
509				}
510			}
511		}
512	}
513
514	pub fn print(&self) {
515		println!("\n{BOLD}Platform Feature Report{RESET}\n");
516
517		// 1. Hardware
518		self.print_hardware();
519		println!();
520
521		// 2. OS/Runtime
522		self.print_os_runtime();
523		println!();
524
525		// 3. Compilation Target (LLVM)
526		self.print_llvm();
527		println!();
528
529		// 4. Available CPU Instructions
530		self.print_available_instructions();
531		println!();
532
533		// 5. Codebase Usage
534		self.print_codebase_usage();
535	}
536
537	fn print_hardware(&self) {
538		println!(
539			"{BOLD}{CYAN}Hardware:{RESET} {} {} ({} cores)",
540			self.hardware.vendor, self.hardware.architecture, self.hardware.core_count
541		);
542		println!("{CYAN}CPU:{RESET} {}", self.hardware.cpu_model);
543
544		match self.hardware.vendor.as_str() {
545			"Apple" => {
546				println!(
547					"{CYAN}Features:{RESET} {GREEN}✓{RESET}AMX, {GREEN}✓{RESET}Neural Engine, {GREEN}✓{RESET}P+E cores, {RED}✗{RESET}SVE/SVE2, {GREEN}✓{RESET}NEON"
548				);
549			}
550			"AWS" => {
551				println!(
552					"{CYAN}Features:{RESET} {GREEN}✓{RESET}SVE-256bit, {GREEN}✓{RESET}Server memory, {GREEN}✓{RESET}Large cache, {RED}✗{RESET}AMX, {GREEN}✓{RESET}NEON"
553				);
554			}
555			_ => {
556				println!(
557					"{CYAN}Features:{RESET} {YELLOW}?{RESET}Vendor-specific, {GREEN}✓{RESET}NEON, {YELLOW}?{RESET}Crypto"
558				);
559			}
560		}
561	}
562
563	fn print_os_runtime(&self) {
564		println!(
565			"{BOLD}{CYAN}OS/Runtime:{RESET} {} (kernel {})",
566			self.os_runtime.os, self.os_runtime.kernel_version
567		);
568
569		// Group features by status
570		let detected: Vec<&str> = self
571			.os_runtime
572			.runtime_features
573			.iter()
574			.filter(|(_, v)| **v)
575			.map(|(k, _)| *k)
576			.collect();
577		let not_found: Vec<&str> = self
578			.os_runtime
579			.runtime_features
580			.iter()
581			.filter(|(_, v)| !**v)
582			.map(|(k, _)| *k)
583			.collect();
584
585		if !detected.is_empty() {
586			println!("{GREEN}Detected:{RESET} {}", detected.join(", "));
587		}
588		if !not_found.is_empty() {
589			println!("{DIM}Not available:{RESET} {}", not_found.join(", "));
590		}
591	}
592
593	fn print_llvm(&self) {
594		println!("{BOLD}{CYAN}Compilation Target:{RESET}");
595		println!("{CYAN}Triple:{RESET} {}", self.llvm_config.target_triple);
596		println!("{CYAN}CPU:{RESET} {}", self.llvm_config.target_cpu);
597
598		match self.llvm_config.target_cpu.as_str() {
599			config::CPU_TARGET_NATIVE => {
600				println!(
601					"{CYAN}Strategy:{RESET} {YELLOW}Native{RESET} - Optimized for this specific CPU"
602				);
603				println!("{DIM}         Binary only runs on CPUs with same features{RESET}");
604			}
605			config::CPU_TARGET_GENERIC => {
606				println!(
607					"{CYAN}Strategy:{RESET} {GREEN}Generic{RESET} - Portable across all {} CPUs",
608					if self.llvm_config.target_triple.contains("aarch64") {
609						"ARM64"
610					} else if self.llvm_config.target_triple.contains("x86_64") {
611						"x86-64"
612					} else {
613						"target"
614					}
615				);
616				println!(
617					"{DIM}         Uses explicit features but no CPU-specific scheduling{RESET}"
618				);
619			}
620			cpu if cpu.starts_with("apple-") => {
621				println!(
622					"{CYAN}Strategy:{RESET} {MAGENTA}Apple Silicon{RESET} - Optimized for {cpu}"
623				);
624				println!("{DIM}         Enables AMX, disables SVE{RESET}");
625			}
626			cpu if cpu.contains("neoverse") => {
627				println!("{CYAN}Strategy:{RESET} {BLUE}Server ARM{RESET} - Optimized for {cpu}");
628				println!("{DIM}         Enables SVE, optimized for cloud workloads{RESET}");
629			}
630			_ => {
631				println!("{CYAN}Strategy:{RESET} Custom CPU target");
632			}
633		}
634	}
635
636	fn print_available_instructions(&self) {
637		println!("{BOLD}{CYAN}Available CPU Instructions:{RESET}");
638
639		// Group features by category
640		let mut simd_features = Vec::new();
641		let mut crypto_features = Vec::new();
642		let mut arch_features = Vec::new();
643
644		for feature in &self.code_features.compile_time_features {
645			if config::SIMD_FEATURES.contains(&feature.as_str()) {
646				simd_features.push(feature.as_str());
647			} else if config::CRYPTO_FEATURES.contains(&feature.as_str()) {
648				crypto_features.push(feature.as_str());
649			} else if !feature.starts_with("v8.") && feature != "vh" {
650				arch_features.push(feature.as_str());
651			}
652		}
653
654		println!(
655			"{CYAN}Total:{RESET} {} CPU features available to compiler",
656			self.code_features.compile_time_features.len()
657		);
658
659		if !simd_features.is_empty() {
660			simd_features.sort_unstable();
661			println!("  {GREEN}SIMD:{RESET} {}", simd_features.join(", "));
662		}
663		if !crypto_features.is_empty() {
664			crypto_features.sort_unstable();
665			println!("  {GREEN}Crypto:{RESET} {}", crypto_features.join(", "));
666		}
667		if !arch_features.is_empty() {
668			arch_features.sort_unstable();
669			// Always show the features, but wrap if too many
670			if arch_features.len() <= 6 {
671				println!("  {GREEN}Other:{RESET} {}", arch_features.join(", "));
672			} else {
673				// Show in multiple lines for readability
674				println!("  {GREEN}Other:{RESET}");
675				for chunk in arch_features.chunks(8) {
676					println!("    {}", chunk.join(", "));
677				}
678			}
679		}
680
681		// Show important missing features
682		#[cfg(target_arch = "aarch64")]
683		{
684			let important_missing = vec!["sve", "sve2"]
685				.into_iter()
686				.filter(|f| {
687					!self
688						.code_features
689						.compile_time_features
690						.iter()
691						.any(|feature| feature == f)
692				})
693				.collect::<Vec<_>>();
694			if !important_missing.is_empty() {
695				println!(
696					"  {DIM}Not available:{RESET} {} (code paths requiring these are excluded)",
697					important_missing.join(", ")
698				);
699			}
700		}
701	}
702
703	fn print_feature_locations(&self, _feature: &str, locations: &[String]) {
704		// Group files by directory
705		let mut by_dir: BTreeMap<String, Vec<String>> = BTreeMap::new();
706		for loc in locations {
707			if let Some(slash_pos) = loc.rfind('/') {
708				let dir = loc[..slash_pos].to_string();
709				let file = loc[slash_pos + 1..].to_string();
710				let files = by_dir.entry(dir).or_default();
711				if !files.contains(&file) {
712					files.push(file);
713				}
714			} else {
715				let files = by_dir.entry(String::new()).or_default();
716				if !files.contains(loc) {
717					files.push(loc.clone());
718				}
719			}
720		}
721
722		let mut shown = 0;
723		for (dir_count, (dir, files)) in by_dir.iter().enumerate() {
724			if dir_count >= config::MAX_DIRS_TO_SHOW && by_dir.len() > config::MAX_DIRS_TO_SHOW {
725				println!("    ... in {} more files", locations.len() - shown);
726				break;
727			}
728
729			if files.len() == 1 {
730				println!("    {}/{}", dir, files[0]);
731				shown += 1;
732			} else if files.len() <= config::MAX_FILES_IN_DIR {
733				// List all files if 10 or fewer
734				println!("    {}/: {}", dir, files.join(", "));
735				shown += files.len();
736			} else {
737				// Show first 10 files and indicate there are more
738				let first_10: Vec<_> = files
739					.iter()
740					.take(config::MAX_FILES_IN_DIR)
741					.cloned()
742					.collect();
743				println!(
744					"    {}/: {} (and {} more)",
745					dir,
746					first_10.join(", "),
747					files.len() - config::MAX_FILES_IN_DIR
748				);
749				shown += files.len();
750			}
751		}
752	}
753
754	fn print_codebase_usage(&self) {
755		// Always show the codebase section header
756		println!("{BOLD}{CYAN}Codebase Analysis:{RESET}");
757
758		if self.codebase_usage.cfg_features.is_empty()
759			&& self.codebase_usage.runtime_detections.is_empty()
760			&& self.codebase_usage.arch_modules.is_empty()
761		{
762			println!("{DIM}  No feature usage detected{RESET}");
763			return;
764		}
765
766		// Show arch modules first
767		if !self.codebase_usage.arch_modules.is_empty() {
768			println!(
769				"{MAGENTA}Arch modules:{RESET} {}",
770				self.codebase_usage.arch_modules.join(", ")
771			);
772		}
773
774		if !self.codebase_usage.cfg_features.is_empty() {
775			// Check which used features are enabled vs disabled
776			let mut enabled_used = Vec::new();
777			let mut disabled_used = Vec::new();
778
779			for feature in self.codebase_usage.cfg_features.keys() {
780				// Check if feature is enabled at compile time
781				if self.code_features.compile_time_features.contains(feature) {
782					enabled_used.push(feature.clone());
783				} else {
784					disabled_used.push(feature.clone());
785				}
786			}
787
788			if !enabled_used.is_empty() {
789				println!("{GREEN}Used & Enabled:{RESET}");
790				for feature in &enabled_used {
791					if let Some(locations) = self.codebase_usage.cfg_features.get(feature) {
792						println!("  {GREEN}{feature}:{RESET}");
793						self.print_feature_locations(feature, locations);
794					}
795				}
796			}
797
798			if !disabled_used.is_empty() {
799				println!("{DIM}Used but NOT Enabled:{RESET}");
800				for feature in &disabled_used {
801					if let Some(locations) = self.codebase_usage.cfg_features.get(feature) {
802						println!("  {DIM}{feature}:{RESET}");
803						self.print_feature_locations(feature, locations);
804					}
805				}
806			}
807		}
808
809		if !self.codebase_usage.runtime_detections.is_empty() {
810			let detections: Vec<String> = self
811				.codebase_usage
812				.runtime_detections
813				.keys()
814				.cloned()
815				.collect();
816			println!("{BLUE}Runtime detections:{RESET} {}", detections.join(", "));
817		}
818	}
819
820	/// Generate a feature suffix for benchmark names based on platform diagnostics
821	#[must_use]
822	pub fn get_feature_suffix(&self) -> String {
823		let mut suffix_parts = Vec::new();
824
825		// Threading - check if rayon feature is enabled
826		#[cfg(feature = "rayon")]
827		suffix_parts.push("mt");
828		#[cfg(not(feature = "rayon"))]
829		suffix_parts.push("st");
830
831		// Architecture
832		#[cfg(target_arch = "x86_64")]
833		{
834			suffix_parts.push("x86");
835			// Add key features based on compile-time features
836			#[cfg(target_feature = "gfni")]
837			suffix_parts.push("gfni");
838			#[cfg(target_feature = "avx512f")]
839			suffix_parts.push("avx512");
840			#[cfg(all(not(target_feature = "avx512f"), target_feature = "avx2"))]
841			suffix_parts.push("avx2");
842		}
843
844		#[cfg(target_arch = "aarch64")]
845		{
846			suffix_parts.push("arm64");
847			// Check for NEON and AES
848			#[cfg(all(target_feature = "neon", target_feature = "aes"))]
849			suffix_parts.push("neon_aes");
850			#[cfg(all(target_feature = "neon", not(target_feature = "aes")))]
851			suffix_parts.push("neon");
852		}
853
854		suffix_parts.join("_")
855	}
856}
857
858#[cfg(test)]
859mod tests {
860	use super::*;
861
862	#[test]
863	fn test_platform_diagnostics() {
864		let diag = PlatformDiagnostics::gather();
865		diag.print();
866	}
867
868	#[test]
869	fn test_sanity() {
870		// Test that PlatformDiagnostics can be created without panicking
871		let diag = PlatformDiagnostics::gather();
872
873		// Test hardware info
874		assert!(!diag.hardware.cpu_model.is_empty(), "CPU model should not be empty");
875		assert!(!diag.hardware.vendor.is_empty(), "Vendor should not be empty");
876		assert!(diag.hardware.core_count >= 1, "Should have at least 1 core");
877		assert!(
878			config::KNOWN_ARCHITECTURES.contains(&diag.hardware.architecture),
879			"Architecture should be a known value"
880		);
881
882		// Test OS runtime info
883		assert!(!diag.os_runtime.kernel_version.is_empty(), "Kernel version should not be empty");
884		assert!(config::KNOWN_OS.contains(&diag.os_runtime.os), "OS should be a known value");
885
886		// Test LLVM config
887		assert!(!diag.llvm_config.target_triple.is_empty(), "Target triple should not be empty");
888		assert!(!diag.llvm_config.target_cpu.is_empty(), "Target CPU should not be empty");
889
890		// Test code features
891		// Compile-time features can be empty on some platforms
892		assert!(
893			diag.code_features.compile_time_features.is_empty()
894				|| diag
895					.code_features
896					.compile_time_features
897					.iter()
898					.all(|f| !f.is_empty()),
899			"All feature names should be non-empty"
900		);
901
902		// Test that print() doesn't panic
903		// Redirect output to avoid cluttering test output
904		let _output = std::panic::catch_unwind(|| {
905			diag.print();
906		});
907		assert!(_output.is_ok(), "print() should not panic");
908	}
909
910	#[test]
911	fn test_detect_vendor() {
912		assert_eq!(PlatformDiagnostics::detect_vendor("Apple M1 Pro"), "Apple");
913		assert_eq!(PlatformDiagnostics::detect_vendor("Intel Core i7"), "Intel");
914		assert_eq!(PlatformDiagnostics::detect_vendor("AMD Ryzen 9"), "AMD");
915		assert_eq!(PlatformDiagnostics::detect_vendor("AWS Graviton3"), "AWS");
916		assert_eq!(PlatformDiagnostics::detect_vendor("Ampere Altra"), "Ampere");
917		assert_eq!(PlatformDiagnostics::detect_vendor("Unknown CPU"), "Generic");
918	}
919}