From 0bd5c994a55f4ef26ce657faed6c493226e37344 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Tue, 14 Mar 2023 10:52:55 -0500 Subject: [PATCH 1/5] Implementation working --- src/windows/mod.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 74fd1686..bde509c9 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -10,9 +10,11 @@ use windows::{ core::PSTR, Win32::System::Power::GetSystemPowerStatus, Win32::System::Power::SYSTEM_POWER_STATUS, Win32::System::SystemInformation::GetComputerNameExA, + Win32::System::SystemInformation::GetLogicalProcessorInformation, Win32::System::SystemInformation::GetTickCount64, Win32::System::SystemInformation::GlobalMemoryStatusEx, Win32::System::SystemInformation::MEMORYSTATUSEX, + Win32::System::SystemInformation::RelationProcessorCore, Win32::System::WindowsProgramming::GetUserNameA, }; @@ -295,11 +297,76 @@ impl GeneralReadout for WindowsGeneralReadout { } fn cpu_physical_cores(&self) -> Result { - Err(ReadoutError::NotImplemented) + // Source: https://github.com/seanmonstar/num_cpus/blob/master/src/lib.rs#L129 + #[allow(non_camel_case_types, dead_code)] + struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION { + mask: usize, + relationship: u32, + _unused: [u64; 2] + } + + // The required size of the buffer, in bytes + let mut needed_size = 0; + + // Get the required size of the buffer + unsafe { + GetLogicalProcessorInformation(std::ptr::null_mut(), &mut needed_size); + } + + let struct_size = std::mem::size_of::() as u32; + + // Could be 0, or some other bogus size + if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 { + return Err(ReadoutError::Other( + "Call to \"GetLogicalProcessorInformation\" returned an invalid size.".to_string() + )); + } + + let count = needed_size / struct_size; + + // Allocate some memory where we will store the processor info + let mut buf = Vec::with_capacity(count as usize); + + let result; + + // Populate the buffer with processor information + unsafe { + result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size); + } + + // If failed for some reason + if result.as_bool() == false { + return Err(ReadoutError::Other( + "Call to \"GetLogicalProcessorInformation\" failed.".to_string() + ))?; + } + + // Number of logical processor entries + let count = needed_size / struct_size; + + unsafe { + buf.set_len(count as usize); + } + + let phys_proc_count = buf.iter() + // Only interested in processor packages (physical processors.) + .filter(|proc_info| proc_info.Relationship == RelationProcessorCore) + .count(); + + return if phys_proc_count == 0 { + Err(ReadoutError::Other( + "No physical processor cores found.".to_string() + ))? + } else { + Ok(phys_proc_count) + } } fn cpu_cores(&self) -> Result { - Err(ReadoutError::NotImplemented) + // Open the registry key containing the CPUs information. + let cpu_info = RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor")?; + // Count the number of subkeys, which is the number of CPU logical processors. + Ok(cpu_info.enum_keys().count()) } fn uptime(&self) -> Result { From 3660f9d1fbfc32c4bd2be0189b28f86f54539500 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Mon, 20 Mar 2023 11:15:31 -0500 Subject: [PATCH 2/5] fmt and clippy --- src/windows/mod.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/windows/mod.rs b/src/windows/mod.rs index bde509c9..c1991cfa 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -13,8 +13,8 @@ use windows::{ Win32::System::SystemInformation::GetLogicalProcessorInformation, Win32::System::SystemInformation::GetTickCount64, Win32::System::SystemInformation::GlobalMemoryStatusEx, - Win32::System::SystemInformation::MEMORYSTATUSEX, Win32::System::SystemInformation::RelationProcessorCore, + Win32::System::SystemInformation::MEMORYSTATUSEX, Win32::System::WindowsProgramming::GetUserNameA, }; @@ -302,7 +302,7 @@ impl GeneralReadout for WindowsGeneralReadout { struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION { mask: usize, relationship: u32, - _unused: [u64; 2] + _unused: [u64; 2], } // The required size of the buffer, in bytes @@ -318,7 +318,7 @@ impl GeneralReadout for WindowsGeneralReadout { // Could be 0, or some other bogus size if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 { return Err(ReadoutError::Other( - "Call to \"GetLogicalProcessorInformation\" returned an invalid size.".to_string() + "Call to \"GetLogicalProcessorInformation\" returned an invalid size.".to_string(), )); } @@ -335,9 +335,9 @@ impl GeneralReadout for WindowsGeneralReadout { } // If failed for some reason - if result.as_bool() == false { + if !result.as_bool() { return Err(ReadoutError::Other( - "Call to \"GetLogicalProcessorInformation\" failed.".to_string() + "Call to \"GetLogicalProcessorInformation\" failed.".to_string(), ))?; } @@ -348,14 +348,15 @@ impl GeneralReadout for WindowsGeneralReadout { buf.set_len(count as usize); } - let phys_proc_count = buf.iter() + let phys_proc_count = buf + .iter() // Only interested in processor packages (physical processors.) .filter(|proc_info| proc_info.Relationship == RelationProcessorCore) .count(); - return if phys_proc_count == 0 { + if phys_proc_count == 0 { Err(ReadoutError::Other( - "No physical processor cores found.".to_string() + "No physical processor cores found.".to_string(), ))? } else { Ok(phys_proc_count) @@ -364,7 +365,8 @@ impl GeneralReadout for WindowsGeneralReadout { fn cpu_cores(&self) -> Result { // Open the registry key containing the CPUs information. - let cpu_info = RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor")?; + let cpu_info = RegKey::predef(HKEY_LOCAL_MACHINE) + .open_subkey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor")?; // Count the number of subkeys, which is the number of CPU logical processors. Ok(cpu_info.enum_keys().count()) } From bd909f937a62f0ec77e5ac7cf45303adc8f93eb8 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Mon, 27 Mar 2023 15:50:36 -0500 Subject: [PATCH 3/5] Add alternative implementations --- src/windows/mod.rs | 111 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 11 deletions(-) diff --git a/src/windows/mod.rs b/src/windows/mod.rs index c1991cfa..1c8cefce 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -7,14 +7,13 @@ use wmi::WMIResult; use wmi::{COMLibrary, Variant, WMIConnection}; use windows::{ - core::PSTR, Win32::System::Power::GetSystemPowerStatus, + core::PSTR, + Win32::System::Power::GetSystemPowerStatus, Win32::System::Power::SYSTEM_POWER_STATUS, - Win32::System::SystemInformation::GetComputerNameExA, - Win32::System::SystemInformation::GetLogicalProcessorInformation, - Win32::System::SystemInformation::GetTickCount64, - Win32::System::SystemInformation::GlobalMemoryStatusEx, - Win32::System::SystemInformation::RelationProcessorCore, - Win32::System::SystemInformation::MEMORYSTATUSEX, + Win32::System::SystemInformation::{ + GetComputerNameExA, GetLogicalProcessorInformation, GetTickCount64, GlobalMemoryStatusEx, + RelationCache, RelationProcessorCore, RelationProcessorPackage, MEMORYSTATUSEX, + }, Win32::System::WindowsProgramming::GetUserNameA, }; @@ -364,11 +363,101 @@ impl GeneralReadout for WindowsGeneralReadout { } fn cpu_cores(&self) -> Result { + // Source: https://github.com/seanmonstar/num_cpus/blob/master/src/lib.rs#L129 + #[allow(non_camel_case_types, dead_code)] + struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION { + mask: usize, + relationship: u32, + _unused: [u64; 2], + } + + // The required size of the buffer, in bytes + let mut needed_size = 0; + + // Get the required size of the buffer + unsafe { + GetLogicalProcessorInformation(std::ptr::null_mut(), &mut needed_size); + } + + let struct_size = std::mem::size_of::() as u32; + + // Could be 0, or some other bogus size + if !(needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0) { + let count = needed_size / struct_size; + + // Allocate some memory where we will store the processor info + let mut buf = Vec::with_capacity(count as usize); + + let result; + + // Populate the buffer with processor information + unsafe { + result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size); + } + + if result.as_bool() { + // Number of logical processor entries + let count = needed_size / struct_size; + + unsafe { + buf.set_len(count as usize); + } + + let proc_cache = buf + .iter() + // Processors that share a cache + .filter(|proc_info| proc_info.Relationship == RelationCache) + .count(); + let proc_core = buf + .iter() + // Processors that share a core + .filter(|proc_info| proc_info.Relationship == RelationProcessorCore) + .count(); + let proc_package = buf + .iter() + // Processors that share a package + .filter(|proc_info| proc_info.Relationship == RelationProcessorPackage) + .count(); + + // Get the number of logical processors + let logical_core_count = proc_cache - proc_core - proc_package; + + if logical_core_count > 0 { + return Ok(logical_core_count); + } + } + } + + // Alternative Implementation 1 + // Get the number of logical processors from an environment variable. + if let Ok(val) = std::env::var("NUMBER_OF_PROCESSORS") { + // Convert the String to a usize. + if let Ok(val) = val.parse::() { + return Ok(val); + } + } + + // Alternative Implementation 2 // Open the registry key containing the CPUs information. - let cpu_info = RegKey::predef(HKEY_LOCAL_MACHINE) - .open_subkey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor")?; - // Count the number of subkeys, which is the number of CPU logical processors. - Ok(cpu_info.enum_keys().count()) + if let Ok(cpu_info) = RegKey::predef(HKEY_LOCAL_MACHINE) + .open_subkey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor") + { + // Count the number of subkeys, which is the number of CPU logical processors. + return Ok(cpu_info.enum_keys().count()); + } + + // Alternative Implementation 3 + // Get the number of logical processors by getting the number of threads that can be run in parallel. + if let Ok(cores) = std::thread::available_parallelism() { + // Doesn't work properly on systems with more than 64 logical cores. + if cores.get() < 64 { + return Ok(cores.get()); + } + } + + Err(ReadoutError::Other( + "Failed to get the number of logical processors.".to_string(), + )) } fn uptime(&self) -> Result { From 79f0b249feb16e79474f0f57569037d72ff97060 Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Mon, 27 Mar 2023 16:14:53 -0500 Subject: [PATCH 4/5] Replace available_parallelism with GetNativeSystemInfo --- Cargo.toml | 3 ++- src/windows/mod.rs | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ef3d1af9..41df3774 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,9 +47,10 @@ wmi = "0.12.0" winreg = "0.10.1" windows = { version = "0.39.0", features = [ "Win32_Foundation", + "Win32_System_Diagnostics_Debug", "Win32_System_Power", "Win32_System_SystemInformation", - "Win32_System_WindowsProgramming" + "Win32_System_WindowsProgramming", ]} [target.'cfg(not(target_os = "windows"))'.dependencies] diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 1c8cefce..7fff942e 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -11,8 +11,9 @@ use windows::{ Win32::System::Power::GetSystemPowerStatus, Win32::System::Power::SYSTEM_POWER_STATUS, Win32::System::SystemInformation::{ - GetComputerNameExA, GetLogicalProcessorInformation, GetTickCount64, GlobalMemoryStatusEx, - RelationCache, RelationProcessorCore, RelationProcessorPackage, MEMORYSTATUSEX, + GetComputerNameExA, GetLogicalProcessorInformation, GetNativeSystemInfo, GetTickCount64, + GlobalMemoryStatusEx, RelationCache, RelationProcessorCore, RelationProcessorPackage, + MEMORYSTATUSEX, SYSTEM_INFO, }, Win32::System::WindowsProgramming::GetUserNameA, }; @@ -447,14 +448,16 @@ impl GeneralReadout for WindowsGeneralReadout { } // Alternative Implementation 3 - // Get the number of logical processors by getting the number of threads that can be run in parallel. - if let Ok(cores) = std::thread::available_parallelism() { - // Doesn't work properly on systems with more than 64 logical cores. - if cores.get() < 64 { - return Ok(cores.get()); - } + // Source: https://github.com/rust-lang/rust/blob/master/library/std/src/sys/windows/thread.rs#L101 + // Get the number of logical processors from the system info. + let mut sys_info = SYSTEM_INFO::default(); + unsafe { GetNativeSystemInfo(&mut sys_info) } + + if sys_info.dwNumberOfProcessors > 0 { + return Ok(sys_info.dwNumberOfProcessors as usize); } + // If all else fails, return an error. Err(ReadoutError::Other( "Failed to get the number of logical processors.".to_string(), )) From 89a6bd462cf315aa3a58a88cfff69f3e547c5a5e Mon Sep 17 00:00:00 2001 From: Carterpersall Date: Wed, 12 Apr 2023 17:21:01 -0500 Subject: [PATCH 5/5] Make compatible with >32 Core systems --- src/windows/mod.rs | 239 +++++++++++++++++++++------------------------ 1 file changed, 114 insertions(+), 125 deletions(-) diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 7fff942e..f9f7b9af 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -11,9 +11,9 @@ use windows::{ Win32::System::Power::GetSystemPowerStatus, Win32::System::Power::SYSTEM_POWER_STATUS, Win32::System::SystemInformation::{ - GetComputerNameExA, GetLogicalProcessorInformation, GetNativeSystemInfo, GetTickCount64, - GlobalMemoryStatusEx, RelationCache, RelationProcessorCore, RelationProcessorPackage, - MEMORYSTATUSEX, SYSTEM_INFO, + GetComputerNameExA, GetLogicalProcessorInformationEx, GetTickCount64, GlobalMemoryStatusEx, + RelationProcessorCore, GROUP_AFFINITY, MEMORYSTATUSEX, + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, }, Win32::System::WindowsProgramming::GetUserNameA, }; @@ -297,148 +297,132 @@ impl GeneralReadout for WindowsGeneralReadout { } fn cpu_physical_cores(&self) -> Result { - // Source: https://github.com/seanmonstar/num_cpus/blob/master/src/lib.rs#L129 - #[allow(non_camel_case_types, dead_code)] - struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION { - mask: usize, - relationship: u32, - _unused: [u64; 2], - } + // Source: https://github.com/AFLplusplus/LibAFL/blob/main/libafl/src/bolts/core_affinity.rs#L423 - // The required size of the buffer, in bytes + // Get the required size of the buffer, in bytes let mut needed_size = 0; - - // Get the required size of the buffer unsafe { - GetLogicalProcessorInformation(std::ptr::null_mut(), &mut needed_size); + GetLogicalProcessorInformationEx( + RelationProcessorCore, + std::ptr::null_mut(), + &mut needed_size, + ); } - let struct_size = std::mem::size_of::() as u32; - // Could be 0, or some other bogus size - if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 { - return Err(ReadoutError::Other( - "Call to \"GetLogicalProcessorInformation\" returned an invalid size.".to_string(), - )); - } - - let count = needed_size / struct_size; - - // Allocate some memory where we will store the processor info - let mut buf = Vec::with_capacity(count as usize); - - let result; - - // Populate the buffer with processor information - unsafe { - result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size); - } - - // If failed for some reason - if !result.as_bool() { - return Err(ReadoutError::Other( - "Call to \"GetLogicalProcessorInformation\" failed.".to_string(), - ))?; - } - - // Number of logical processor entries - let count = needed_size / struct_size; + if needed_size != 0 { + // Allocate memory where we will store the processor info. + let mut buffer = vec![0u8; needed_size as usize]; - unsafe { - buf.set_len(count as usize); - } - - let phys_proc_count = buf - .iter() - // Only interested in processor packages (physical processors.) - .filter(|proc_info| proc_info.Relationship == RelationProcessorCore) - .count(); + // Populate the buffer with processor information + let result = unsafe { + GetLogicalProcessorInformationEx( + RelationProcessorCore, + buffer.as_mut_ptr() as *mut _, // cast to *mut _ to avoid type mismatch + &mut needed_size, + ) + }; + if result.as_bool() { + let mut n_cores: usize = 0; + + let mut byte_offset: usize = 0; + while byte_offset < needed_size as usize { + unsafe { + // Interpret the byte-array as a SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct + let part_ptr_raw = buffer.as_ptr().add(byte_offset); + let part_ptr: *const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX = + part_ptr_raw as *const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; + let part: &SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX = &*part_ptr; + + // We are only interested in RelationProcessorCore information + if part.Relationship == RelationProcessorCore { + n_cores += 1; + } + + // Set the pointer to the next part as indicated by the size of this part + byte_offset += part.Size as usize; + } + } - if phys_proc_count == 0 { - Err(ReadoutError::Other( - "No physical processor cores found.".to_string(), - ))? + Ok(n_cores) + } else { + Err(ReadoutError::Other(String::from( + "Second call to \"GetLogicalProcessorInformationEx\" failed.", + ))) + } } else { - Ok(phys_proc_count) + Err(ReadoutError::Other(String::from( + "First call to \"GetLogicalProcessorInformationEx\" failed.", + ))) } } fn cpu_cores(&self) -> Result { - // Source: https://github.com/seanmonstar/num_cpus/blob/master/src/lib.rs#L129 - #[allow(non_camel_case_types, dead_code)] - struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION { - mask: usize, - relationship: u32, - _unused: [u64; 2], - } + // Source: https://github.com/AFLplusplus/LibAFL/blob/main/libafl/src/bolts/core_affinity.rs#L423 - // The required size of the buffer, in bytes + // Get the required size of the buffer, in bytes let mut needed_size = 0; - - // Get the required size of the buffer unsafe { - GetLogicalProcessorInformation(std::ptr::null_mut(), &mut needed_size); + GetLogicalProcessorInformationEx( + RelationProcessorCore, + std::ptr::null_mut(), + &mut needed_size, + ); } - let struct_size = std::mem::size_of::() as u32; - // Could be 0, or some other bogus size - if !(needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0) { - let count = needed_size / struct_size; - - // Allocate some memory where we will store the processor info - let mut buf = Vec::with_capacity(count as usize); - - let result; + if needed_size != 0 { + // Allocate memory where we will store the processor info. + let mut buffer = vec![0u8; needed_size as usize]; // Populate the buffer with processor information - unsafe { - result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size); - } - + let result = unsafe { + GetLogicalProcessorInformationEx( + RelationProcessorCore, + buffer.as_mut_ptr() as *mut _, // cast to *mut _ to avoid type mismatch + &mut needed_size, + ) + }; if result.as_bool() { - // Number of logical processor entries - let count = needed_size / struct_size; - - unsafe { - buf.set_len(count as usize); + let mut n_logical_procs: usize = 0; + + let mut byte_offset: usize = 0; + while byte_offset < needed_size as usize { + unsafe { + // Interpret the byte-array as a SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct + let part_ptr_raw = buffer.as_ptr().add(byte_offset); + let part_ptr: *const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX = + part_ptr_raw as *const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; + let part: &SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX = &*part_ptr; + + // We are only interested in RelationProcessorCore information + if part.Relationship == RelationProcessorCore { + // The number of GROUP_AFFINITY structs in the array will be specified in the 'groupCount' + // We tentatively use the first element to get the pointer to it and reinterpret the + // entire slice with the groupCount + let groupmasks_slice: &[GROUP_AFFINITY] = std::slice::from_raw_parts( + part.Anonymous.Processor.GroupMask.as_ptr(), + part.Anonymous.Processor.GroupCount as usize, + ); + + // Count the local logical processors of the group and accumulate + let n_local_procs: usize = groupmasks_slice + .iter() + .map(|g| g.Mask.count_ones() as usize) + .sum::(); + n_logical_procs += n_local_procs; + } + + // Set the pointer to the next part as indicated by the size of this part + byte_offset += part.Size as usize; + } } - let proc_cache = buf - .iter() - // Processors that share a cache - .filter(|proc_info| proc_info.Relationship == RelationCache) - .count(); - let proc_core = buf - .iter() - // Processors that share a core - .filter(|proc_info| proc_info.Relationship == RelationProcessorCore) - .count(); - let proc_package = buf - .iter() - // Processors that share a package - .filter(|proc_info| proc_info.Relationship == RelationProcessorPackage) - .count(); - - // Get the number of logical processors - let logical_core_count = proc_cache - proc_core - proc_package; - - if logical_core_count > 0 { - return Ok(logical_core_count); - } + return Ok(n_logical_procs); } } // Alternative Implementation 1 - // Get the number of logical processors from an environment variable. - if let Ok(val) = std::env::var("NUMBER_OF_PROCESSORS") { - // Convert the String to a usize. - if let Ok(val) = val.parse::() { - return Ok(val); - } - } - - // Alternative Implementation 2 // Open the registry key containing the CPUs information. if let Ok(cpu_info) = RegKey::predef(HKEY_LOCAL_MACHINE) .open_subkey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor") @@ -447,14 +431,19 @@ impl GeneralReadout for WindowsGeneralReadout { return Ok(cpu_info.enum_keys().count()); } - // Alternative Implementation 3 - // Source: https://github.com/rust-lang/rust/blob/master/library/std/src/sys/windows/thread.rs#L101 - // Get the number of logical processors from the system info. - let mut sys_info = SYSTEM_INFO::default(); - unsafe { GetNativeSystemInfo(&mut sys_info) } - - if sys_info.dwNumberOfProcessors > 0 { - return Ok(sys_info.dwNumberOfProcessors as usize); + // Alternative Implementation 2 + // Use WMI to get the number of logical processors. + if let Ok(wmi_con) = wmi_connection() { + let results: Vec> = + wmi_con.raw_query("SELECT NumberOfLogicalProcessors FROM Win32_Processor")?; + + if let Some(result) = results.first() { + if let Some(Variant::String(val)) = result.get("NumberOfLogicalProcessors") { + if let Ok(out) = val.clone().parse::() { + return Ok(out); + } + } + } } // If all else fails, return an error.