Skip to main content

torsh_utils/
collect_env.rs

1//! # Environment Information Collection
2//!
3//! This module provides comprehensive system and environment information collection
4//! for debugging, issue reproduction, and system requirements verification.
5//!
6//! ## Features
7//!
8//! - **System Information**: OS, architecture, CPU details
9//! - **Hardware Detection**: CPU features (AVX, SSE, etc.), GPU information
10//! - **Memory Information**: Total and available RAM
11//! - **Software Environment**: Rust version, ToRSh version, dependencies
12//! - **GPU Detection**: NVIDIA CUDA, Apple Metal support
13//! - **Python Integration**: Python version and installed packages (if available)
14//! - **Environment Variables**: Relevant environment variables for debugging
15//!
16//! ## Quick Start
17//!
18//! ### Basic Usage
19//!
20//! ```rust,no_run
21//! use torsh_utils::collect_env::{collect_env, print_env_info};
22//!
23//! # fn example() -> Result<(), torsh_core::TorshError> {
24//! // Collect environment information
25//! let env_info = collect_env()?;
26//!
27//! // Print formatted report
28//! print_env_info(&env_info);
29//!
30//! // Access specific information
31//! println!("ToRSh version: {}", env_info.torsh_version);
32//! println!("Rust version: {}", env_info.rust_version);
33//! println!("OS: {}", env_info.os);
34//! # Ok(())
35//! # }
36//! ```
37//!
38//! ### Hardware Information
39//!
40//! ```rust,no_run
41//! use torsh_utils::collect_env::collect_env;
42//!
43//! # fn example() -> Result<(), torsh_core::TorshError> {
44//! let env_info = collect_env()?;
45//!
46//! // CPU information
47//! println!("CPU: {}", env_info.cpu_info.brand);
48//! println!("CPU cores: {} physical, {} logical",
49//!     env_info.cpu_info.cores,
50//!     env_info.cpu_info.threads
51//! );
52//! println!("CPU features: {}", env_info.cpu_info.features.join(", "));
53//!
54//! // Memory information
55//! println!("Total RAM: {} MB", env_info.memory_info.total_mb);
56//! println!("Available RAM: {} MB", env_info.memory_info.available_mb);
57//!
58//! // GPU information
59//! if !env_info.gpu_info.is_empty() {
60//!     for (i, gpu) in env_info.gpu_info.iter().enumerate() {
61//!         println!("GPU {}: {} ({} MB)",
62//!             i,
63//!             gpu.name,
64//!             gpu.memory_mb
65//!         );
66//!         if let Some(cuda) = &gpu.cuda_version {
67//!             println!("  CUDA version: {}", cuda);
68//!         }
69//!     }
70//! } else {
71//!     println!("No GPUs detected");
72//! }
73//! # Ok(())
74//! # }
75//! ```
76//!
77//! ### Environment Variables
78//!
79//! ```rust,no_run
80//! use torsh_utils::collect_env::collect_env;
81//!
82//! # fn example() -> Result<(), torsh_core::TorshError> {
83//! let env_info = collect_env()?;
84//!
85//! // Check CUDA configuration
86//! if let Some(cuda_home) = env_info.env_vars.get("CUDA_HOME") {
87//!     println!("CUDA_HOME: {}", cuda_home);
88//! }
89//!
90//! if let Some(cuda_path) = env_info.env_vars.get("CUDA_PATH") {
91//!     println!("CUDA_PATH: {}", cuda_path);
92//! }
93//!
94//! // Check library paths
95//! if let Some(ld_path) = env_info.env_vars.get("LD_LIBRARY_PATH") {
96//!     println!("LD_LIBRARY_PATH: {}", ld_path);
97//! }
98//! # Ok(())
99//! # }
100//! ```
101//!
102//! ### Export to JSON
103//!
104//! ```rust,no_run
105//! use torsh_utils::collect_env::collect_env;
106//! use std::fs;
107//!
108//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
109//! let env_info = collect_env()?;
110//!
111//! // Serialize to JSON
112//! let json = serde_json::to_string_pretty(&env_info)?;
113//!
114//! // Save to file
115//! fs::write("environment_info.json", json)?;
116//!
117//! println!("Environment info saved to environment_info.json");
118//! # Ok(())
119//! # }
120//! ```
121//!
122//! ### Python Environment Detection
123//!
124//! ```rust,no_run
125//! use torsh_utils::collect_env::collect_env;
126//!
127//! # fn example() -> Result<(), torsh_core::TorshError> {
128//! let env_info = collect_env()?;
129//!
130//! if let Some(python) = &env_info.python_info {
131//!     println!("Python version: {}", python.version);
132//!     println!("Python executable: {}", python.executable);
133//!
134//!     // Check for specific packages
135//!     if let Some(torch_version) = python.packages.get("torch") {
136//!         println!("PyTorch installed: {}", torch_version);
137//!     }
138//!
139//!     if let Some(numpy_version) = python.packages.get("numpy") {
140//!         println!("NumPy installed: {}", numpy_version);
141//!     }
142//! } else {
143//!     println!("Python not detected");
144//! }
145//! # Ok(())
146//! # }
147//! ```
148//!
149//! ## Use Cases
150//!
151//! ### 1. Bug Reports
152//!
153//! Include environment information in bug reports for easier reproduction:
154//!
155//! ```rust,no_run
156//! use torsh_utils::collect_env::{collect_env, print_env_info};
157//!
158//! # fn example() -> Result<(), torsh_core::TorshError> {
159//! println!("=== Bug Report ===");
160//! println!("Issue: Model crashes during training");
161//! println!();
162//! println!("=== Environment ===");
163//!
164//! let env_info = collect_env()?;
165//! print_env_info(&env_info);
166//! # Ok(())
167//! # }
168//! ```
169//!
170//! ### 2. System Requirements Check
171//!
172//! Verify system meets minimum requirements:
173//!
174//! ```rust,no_run
175//! use torsh_utils::collect_env::collect_env;
176//!
177//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
178//! let env_info = collect_env()?;
179//!
180//! // Check minimum requirements
181//! let min_ram_mb = 4096; // 4GB
182//! let min_cpu_cores = 2;
183//!
184//! if env_info.memory_info.total_mb < min_ram_mb {
185//!     eprintln!("⚠️  WARNING: Insufficient RAM. Required: {} MB, Available: {} MB",
186//!         min_ram_mb, env_info.memory_info.total_mb);
187//! }
188//!
189//! if env_info.cpu_info.cores < min_cpu_cores {
190//!     eprintln!("⚠️  WARNING: Insufficient CPU cores. Required: {}, Available: {}",
191//!         min_cpu_cores, env_info.cpu_info.cores);
192//! }
193//!
194//! // Check for required CPU features
195//! let required_features = vec!["SSE2", "AVX"];
196//! for feature in &required_features {
197//!     if !env_info.cpu_info.features.contains(&feature.to_string()) {
198//!         eprintln!("⚠️  WARNING: Missing CPU feature: {}", feature);
199//!     }
200//! }
201//!
202//! // Check for CUDA if GPU training is needed
203//! let needs_cuda = true;
204//! if needs_cuda && env_info.gpu_info.is_empty() {
205//!     eprintln!("⚠️  WARNING: No CUDA GPUs detected");
206//! }
207//! # Ok(())
208//! # }
209//! ```
210//!
211//! ### 3. CI/CD Integration
212//!
213//! Log environment information in CI pipelines:
214//!
215//! ```rust,no_run
216//! use torsh_utils::collect_env::{collect_env, print_env_info};
217//!
218//! # fn example() -> Result<(), torsh_core::TorshError> {
219//! // In CI script
220//! println!("=== CI Environment Information ===");
221//! let env_info = collect_env()?;
222//! print_env_info(&env_info);
223//!
224//! // Save for artifacts
225//! let json = serde_json::to_string_pretty(&env_info).unwrap();
226//! std::fs::write("ci_environment.json", json).unwrap();
227//! # Ok(())
228//! # }
229//! ```
230//!
231//! ## Collected Information
232//!
233//! ### System Information
234//! - Operating system name and version
235//! - CPU architecture (x86_64, ARM, etc.)
236//! - OS family (Unix, Windows, etc.)
237//!
238//! ### CPU Information
239//! - Brand/model name
240//! - Number of physical cores
241//! - Number of logical threads (with hyperthreading)
242//! - Supported instruction sets (SSE, AVX, AVX2, AVX512)
243//!
244//! ### GPU Information
245//! - NVIDIA GPUs via nvidia-smi:
246//!   - GPU name/model
247//!   - Total memory
248//!   - Driver version
249//!   - CUDA version (if available)
250//! - Apple Metal GPUs (on macOS)
251//!
252//! ### Memory Information
253//! - Total system RAM
254//! - Available RAM
255//!
256//! ### Software Versions
257//! - ToRSh version
258//! - Rust compiler version
259//! - Installed Rust packages
260//!
261//! ### Python Environment (if available)
262//! - Python version
263//! - Python executable path
264//! - Installed Python packages (via pip)
265//!
266//! ### Environment Variables
267//! Collects relevant variables including:
268//! - `CUDA_HOME`, `CUDA_PATH`
269//! - `CUDNN_PATH`
270//! - `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`
271//! - `PATH`
272//! - `RUST_BACKTRACE`, `RUST_LOG`
273//! - `OMP_NUM_THREADS`, `MKL_NUM_THREADS`
274//!
275//! ## Privacy Considerations
276//!
277//! The collected information includes:
278//! - ✅ Hardware specifications (safe to share)
279//! - ✅ Software versions (safe to share)
280//! - ⚠️  Environment variables (may contain paths)
281//! - ⚠️  Installed packages (usually safe)
282//!
283//! **Recommendation**: Review the output before sharing publicly, especially
284//! environment variables that might contain sensitive paths or credentials.
285//!
286//! ## Best Practices
287//!
288//! 1. **Always Include in Bug Reports**: Helps maintainers reproduce issues
289//! 2. **Version Compatibility**: Check before deploying to new environments
290//! 3. **Automated Checks**: Integrate into CI/CD for consistent environments
291//! 4. **Documentation**: Include in deployment documentation
292//! 5. **Review Before Sharing**: Remove sensitive information if present
293//!
294//! ## Platform Support
295//!
296//! | Platform | System Info | CPU Info | GPU Detection | Python Detection |
297//! |----------|-------------|----------|---------------|------------------|
298//! | Linux    | ✅ Full     | ✅ Full  | ✅ NVIDIA     | ✅ Full          |
299//! | macOS    | ✅ Full     | ✅ Full  | ✅ Metal      | ✅ Full          |
300//! | Windows  | ✅ Full     | ✅ Full  | ✅ NVIDIA     | ✅ Full          |
301//!
302//! ## Comparison with PyTorch
303//!
304//! Similar to `torch.utils.collect_env.get_pretty_env_info()`, but provides:
305//! - More detailed CPU feature detection
306//! - Rust ecosystem information
307//! - Better structured output (JSON serializable)
308//! - Lower overhead (no Python runtime required)
309//!
310//! ## See Also
311//!
312//! - [`benchmark`](crate::benchmark): For performance benchmarking
313//! - [`bottleneck`](crate::bottleneck): For performance profiling
314//! - [Tutorial Guide](https://docs.torsh.rs/tutorial#environment)
315//! - [Troubleshooting Guide](https://docs.torsh.rs/troubleshooting)
316
317// Framework infrastructure - components designed for future use
318#![allow(dead_code)]
319use serde::{Deserialize, Serialize};
320use std::collections::HashMap;
321use std::env;
322use torsh_core::error::Result;
323
324/// Environment information
325#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct EnvironmentInfo {
327    pub torsh_version: String,
328    pub rust_version: String,
329    pub os: String,
330    pub cpu_info: CpuInfo,
331    pub gpu_info: Vec<GpuInfo>,
332    pub memory_info: MemoryInfo,
333    pub python_info: Option<PythonInfo>,
334    pub env_vars: HashMap<String, String>,
335    pub installed_packages: HashMap<String, String>,
336}
337
338/// CPU information
339#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct CpuInfo {
341    pub brand: String,
342    pub cores: usize,
343    pub threads: usize,
344    pub features: Vec<String>,
345}
346
347/// GPU information
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct GpuInfo {
350    pub name: String,
351    pub memory_mb: usize,
352    pub driver_version: String,
353    pub cuda_version: Option<String>,
354}
355
356/// Memory information
357#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct MemoryInfo {
359    pub total_mb: usize,
360    pub available_mb: usize,
361}
362
363/// Python environment information
364#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct PythonInfo {
366    pub version: String,
367    pub executable: String,
368    pub packages: HashMap<String, String>,
369}
370
371/// Collect environment information
372pub fn collect_env() -> Result<EnvironmentInfo> {
373    let torsh_version = env!("CARGO_PKG_VERSION").to_string();
374    let rust_version = get_rust_version();
375    let os = get_os_info();
376    let cpu_info = get_cpu_info();
377    let gpu_info = get_gpu_info();
378    let memory_info = get_memory_info();
379    let python_info = get_python_info();
380    let env_vars = get_relevant_env_vars();
381    let installed_packages = get_installed_packages();
382
383    Ok(EnvironmentInfo {
384        torsh_version,
385        rust_version,
386        os,
387        cpu_info,
388        gpu_info,
389        memory_info,
390        python_info,
391        env_vars,
392        installed_packages,
393    })
394}
395
396/// Get Rust version
397fn get_rust_version() -> String {
398    env::var("RUSTC_VERSION")
399        .or_else(|_| {
400            std::process::Command::new("rustc")
401                .arg("--version")
402                .output()
403                .map(|output| String::from_utf8_lossy(&output.stdout).to_string())
404                .map_err(|_| std::env::VarError::NotPresent)
405        })
406        .unwrap_or_else(|_| "unknown".to_string())
407}
408
409/// Get OS information
410fn get_os_info() -> String {
411    format!(
412        "{} {} ({})",
413        env::consts::OS,
414        env::consts::ARCH,
415        env::consts::FAMILY
416    )
417}
418
419/// Get CPU information
420fn get_cpu_info() -> CpuInfo {
421    #[cfg(feature = "collect_env")]
422    {
423        use sysinfo::System;
424
425        let mut sys = System::new();
426        sys.refresh_cpu_all();
427
428        let cpus = sys.cpus();
429        let brand = cpus
430            .first()
431            .map(|cpu| cpu.brand().to_string())
432            .unwrap_or_else(|| "Unknown".to_string());
433
434        let cores = num_cpus::get_physical();
435        let threads = num_cpus::get();
436
437        let features = get_cpu_features();
438
439        CpuInfo {
440            brand,
441            cores,
442            threads,
443            features,
444        }
445    }
446
447    #[cfg(not(feature = "collect_env"))]
448    {
449        CpuInfo {
450            brand: "Unknown".to_string(),
451            cores: num_cpus::get_physical(),
452            threads: num_cpus::get(),
453            features: vec![],
454        }
455    }
456}
457
458/// Get CPU features
459fn get_cpu_features() -> Vec<String> {
460    #[cfg_attr(not(target_arch = "x86_64"), allow(unused_mut))]
461    let mut features = vec![];
462
463    #[cfg(target_arch = "x86_64")]
464    {
465        if is_x86_feature_detected!("avx") {
466            features.push("AVX".to_string());
467        }
468        if is_x86_feature_detected!("avx2") {
469            features.push("AVX2".to_string());
470        }
471        if is_x86_feature_detected!("avx512f") {
472            features.push("AVX512F".to_string());
473        }
474        if is_x86_feature_detected!("sse") {
475            features.push("SSE".to_string());
476        }
477        if is_x86_feature_detected!("sse2") {
478            features.push("SSE2".to_string());
479        }
480        if is_x86_feature_detected!("sse3") {
481            features.push("SSE3".to_string());
482        }
483        if is_x86_feature_detected!("sse4.1") {
484            features.push("SSE4.1".to_string());
485        }
486        if is_x86_feature_detected!("sse4.2") {
487            features.push("SSE4.2".to_string());
488        }
489    }
490
491    features
492}
493
494/// Get GPU information
495fn get_gpu_info() -> Vec<GpuInfo> {
496    let mut gpus = vec![];
497
498    // Check for NVIDIA GPUs
499    if let Ok(output) = std::process::Command::new("nvidia-smi")
500        .args([
501            "--query-gpu=name,memory.total,driver_version",
502            "--format=csv,noheader,nounits",
503        ])
504        .output()
505    {
506        if output.status.success() {
507            let output_str = String::from_utf8_lossy(&output.stdout);
508            for line in output_str.lines() {
509                let parts: Vec<&str> = line.split(',').map(|s| s.trim()).collect();
510                if parts.len() >= 3 {
511                    gpus.push(GpuInfo {
512                        name: parts[0].to_string(),
513                        memory_mb: parts[1].parse().unwrap_or(0),
514                        driver_version: parts[2].to_string(),
515                        cuda_version: get_cuda_version(),
516                    });
517                }
518            }
519        }
520    }
521
522    // Check for Metal (macOS)
523    #[cfg(target_os = "macos")]
524    {
525        if let Ok(_output) = std::process::Command::new("system_profiler")
526            .args(&["SPDisplaysDataType", "-json"])
527            .output()
528        {
529            // Parse JSON output for GPU info
530            // This is simplified - real implementation would parse properly
531            gpus.push(GpuInfo {
532                name: "Apple Metal GPU".to_string(),
533                memory_mb: 0,
534                driver_version: "Metal".to_string(),
535                cuda_version: None,
536            });
537        }
538    }
539
540    gpus
541}
542
543/// Get CUDA version
544fn get_cuda_version() -> Option<String> {
545    if let Ok(output) = std::process::Command::new("nvcc")
546        .args(["--version"])
547        .output()
548    {
549        if output.status.success() {
550            let output_str = String::from_utf8_lossy(&output.stdout);
551            for line in output_str.lines() {
552                if line.contains("release") {
553                    return line
554                        .split_whitespace()
555                        .find(|s| s.starts_with("V"))
556                        .map(|s| s.trim_start_matches('V').to_string());
557                }
558            }
559        }
560    }
561    None
562}
563
564/// Get memory information
565fn get_memory_info() -> MemoryInfo {
566    #[cfg(feature = "collect_env")]
567    {
568        use sysinfo::System;
569
570        let mut sys = System::new();
571        sys.refresh_memory();
572
573        MemoryInfo {
574            total_mb: (sys.total_memory() / 1024 / 1024) as usize,
575            available_mb: (sys.available_memory() / 1024 / 1024) as usize,
576        }
577    }
578
579    #[cfg(not(feature = "collect_env"))]
580    {
581        MemoryInfo {
582            total_mb: 0,
583            available_mb: 0,
584        }
585    }
586}
587
588/// Get Python information
589fn get_python_info() -> Option<PythonInfo> {
590    if let Ok(output) = std::process::Command::new("python3")
591        .args(["--version"])
592        .output()
593    {
594        if output.status.success() {
595            let version = String::from_utf8_lossy(&output.stdout).trim().to_string();
596
597            let executable = which::which("python3").ok()?.to_string_lossy().to_string();
598
599            // Get installed packages
600            let mut packages = HashMap::new();
601            if let Ok(pip_output) = std::process::Command::new("python3")
602                .args(["-m", "pip", "list", "--format=json"])
603                .output()
604            {
605                if pip_output.status.success() {
606                    if let Ok(json) = serde_json::from_slice::<Vec<PipPackage>>(&pip_output.stdout)
607                    {
608                        for pkg in json {
609                            packages.insert(pkg.name, pkg.version);
610                        }
611                    }
612                }
613            }
614
615            return Some(PythonInfo {
616                version,
617                executable,
618                packages,
619            });
620        }
621    }
622    None
623}
624
625#[derive(Deserialize)]
626struct PipPackage {
627    name: String,
628    version: String,
629}
630
631/// Get relevant environment variables
632fn get_relevant_env_vars() -> HashMap<String, String> {
633    let mut vars = HashMap::new();
634
635    let relevant_vars = vec![
636        "CUDA_HOME",
637        "CUDA_PATH",
638        "CUDNN_PATH",
639        "LD_LIBRARY_PATH",
640        "DYLD_LIBRARY_PATH",
641        "PATH",
642        "RUST_BACKTRACE",
643        "RUST_LOG",
644        "OMP_NUM_THREADS",
645        "MKL_NUM_THREADS",
646        "TORCH_NUM_THREADS",
647    ];
648
649    for var in relevant_vars {
650        if let Ok(value) = env::var(var) {
651            vars.insert(var.to_string(), value);
652        }
653    }
654
655    vars
656}
657
658/// Get installed Rust packages
659fn get_installed_packages() -> HashMap<String, String> {
660    let mut packages = HashMap::new();
661
662    // Add core ToRSh packages
663    packages.insert("torsh".to_string(), env!("CARGO_PKG_VERSION").to_string());
664
665    // Get cargo dependencies
666    if let Ok(output) = std::process::Command::new("cargo")
667        .args(["tree", "--depth", "1", "--format", "{p}"])
668        .current_dir(env!("CARGO_MANIFEST_DIR"))
669        .output()
670    {
671        if output.status.success() {
672            let output_str = String::from_utf8_lossy(&output.stdout);
673            for line in output_str.lines() {
674                if let Some((name, version)) = parse_cargo_tree_line(line) {
675                    packages.insert(name, version);
676                }
677            }
678        }
679    }
680
681    packages
682}
683
684/// Parse cargo tree output line
685fn parse_cargo_tree_line(line: &str) -> Option<(String, String)> {
686    let parts: Vec<&str> = line.split_whitespace().collect();
687    if parts.len() >= 2 {
688        let name = parts[0].to_string();
689        let version = parts[1].trim_start_matches('v').to_string();
690        Some((name, version))
691    } else {
692        None
693    }
694}
695
696/// Pretty print environment information
697pub fn print_env_info(info: &EnvironmentInfo) {
698    println!("=== ToRSh Environment Information ===");
699    println!();
700    println!("ToRSh Version: {}", info.torsh_version);
701    println!("Rust Version: {}", info.rust_version);
702    println!("OS: {}", info.os);
703    println!();
704
705    println!("CPU Information:");
706    println!("  Brand: {}", info.cpu_info.brand);
707    println!(
708        "  Cores: {} physical, {} logical",
709        info.cpu_info.cores, info.cpu_info.threads
710    );
711    println!("  Features: {}", info.cpu_info.features.join(", "));
712    println!();
713
714    if !info.gpu_info.is_empty() {
715        println!("GPU Information:");
716        for (i, gpu) in info.gpu_info.iter().enumerate() {
717            println!("  GPU {}: {}", i, gpu.name);
718            println!("    Memory: {} MB", gpu.memory_mb);
719            println!("    Driver: {}", gpu.driver_version);
720            if let Some(cuda) = &gpu.cuda_version {
721                println!("    CUDA: {}", cuda);
722            }
723        }
724        println!();
725    }
726
727    println!("Memory:");
728    println!("  Total: {} MB", info.memory_info.total_mb);
729    println!("  Available: {} MB", info.memory_info.available_mb);
730    println!();
731
732    if let Some(python) = &info.python_info {
733        println!("Python:");
734        println!("  Version: {}", python.version);
735        println!("  Executable: {}", python.executable);
736        if !python.packages.is_empty() {
737            println!("  Key packages:");
738            for (name, version) in &python.packages {
739                if name.contains("torch") || name.contains("numpy") || name.contains("scipy") {
740                    println!("    {}: {}", name, version);
741                }
742            }
743        }
744        println!();
745    }
746
747    if !info.env_vars.is_empty() {
748        println!("Environment Variables:");
749        for (key, value) in &info.env_vars {
750            if key.contains("CUDA") || key.contains("PATH") {
751                println!("  {}: {}", key, value);
752            }
753        }
754    }
755}
756
757#[cfg(test)]
758mod tests {
759    use super::*;
760
761    #[test]
762    fn test_collect_env() {
763        let info = collect_env().unwrap();
764
765        assert!(!info.torsh_version.is_empty());
766        assert!(!info.rust_version.is_empty());
767        assert!(!info.os.is_empty());
768        assert!(info.cpu_info.cores > 0);
769        assert!(info.cpu_info.threads > 0);
770        // Note: threads is typically >= cores with hyperthreading,
771        // but detection may vary across systems
772    }
773
774    #[test]
775    fn test_cpu_features() {
776        let features = get_cpu_features();
777        // At least SSE2 should be available on modern x86_64
778        #[cfg(target_arch = "x86_64")]
779        assert!(!features.is_empty());
780        // On other architectures, just verify the function works
781        #[cfg(not(target_arch = "x86_64"))]
782        let _ = features;
783    }
784}