From 3bd07dfaf7d7692677040b8045986c8355ab5a9b Mon Sep 17 00:00:00 2001 From: Reagan Bohan Date: Thu, 10 Oct 2024 10:28:28 +0000 Subject: [PATCH] free: add Windows support Values are virtual the same as resmon. Unfortunately, it is slightly hacky to get the page "Swap" file sizes. --- Cargo.lock | 73 ++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/uu/free/Cargo.toml | 1 + src/uu/free/src/free.rs | 39 ++++++++++++++++- src/uu/free/src/windows_util.rs | 74 +++++++++++++++++++++++++++++++++ 5 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 src/uu/free/src/windows_util.rs diff --git a/Cargo.lock b/Cargo.lock index d197e48..481634e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -877,7 +877,7 @@ dependencies = [ "memchr", "ntapi", "rayon", - "windows", + "windows 0.57.0", ] [[package]] @@ -1021,6 +1021,7 @@ dependencies = [ "clap", "sysinfo", "uucore", + "windows 0.58.0", ] [[package]] @@ -1292,6 +1293,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -1307,9 +1318,22 @@ version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings", "windows-targets 0.52.6", ] @@ -1324,6 +1348,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-interface" version = "0.57.0" @@ -1335,6 +1370,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -1344,6 +1390,25 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 5b0eb11..9dad462 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ chrono = { version = "0.4.38", default-features = false, features = ["clock"] } walkdir = "2.5.0" prettytable-rs = "0.10.0" nix = { version = "0.29", default-features = false, features = ["process"] } +windows = { version = "0.58.0" } [dependencies] clap = { workspace = true } diff --git a/src/uu/free/Cargo.toml b/src/uu/free/Cargo.toml index e04bb5e..d4dac6e 100644 --- a/src/uu/free/Cargo.toml +++ b/src/uu/free/Cargo.toml @@ -18,6 +18,7 @@ clap = { workspace = true } bytesize = { workspace = true } sysinfo = { workspace = true } +windows = { workspace = true, features = ["Wdk_System_SystemInformation", "Win32_System_ProcessStatus", "Win32_System_SystemInformation"] } [lib] path = "src/free.rs" diff --git a/src/uu/free/src/free.rs b/src/uu/free/src/free.rs index 1b5ebf1..dc9c3c0 100644 --- a/src/uu/free/src/free.rs +++ b/src/uu/free/src/free.rs @@ -3,6 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +#[cfg(target_os = "windows")] +mod windows_util; + use bytesize::{ByteSize, GB, GIB, KB, KIB, MB, MIB, PB, PIB, TB, TIB}; use clap::{arg, crate_version, ArgAction, ArgGroup, ArgMatches, Command}; use std::env; @@ -118,10 +121,42 @@ fn parse_meminfo() -> Result> { Ok(mem_info) } -// TODO: implement function #[cfg(target_os = "windows")] fn parse_meminfo() -> Result> { - Ok(MemInfo::default()) + use std::mem::size_of; + use windows::Win32::System::{ + ProcessStatus::{GetPerformanceInfo, PERFORMANCE_INFORMATION}, + SystemInformation::{GlobalMemoryStatusEx, MEMORYSTATUSEX}, + }; + + let (pagefile_used, pagefile_total) = windows_util::get_pagefile_usage()?; + + let mut status = MEMORYSTATUSEX { + dwLength: size_of::() as u32, + ..Default::default() + }; + unsafe { GlobalMemoryStatusEx(&mut status)? } + + let mut perf_info = PERFORMANCE_INFORMATION { + cb: size_of::() as u32, + ..Default::default() + }; + unsafe { GetPerformanceInfo(&mut perf_info, perf_info.cb)? } + + let mem_info = MemInfo { + total: status.ullTotalPhys / 1024, + free: (status.ullAvailPhys - (perf_info.SystemCache * perf_info.PageSize) as u64) / 1024, + available: status.ullAvailPhys / 1024, + cached: (perf_info.SystemCache * perf_info.PageSize) as u64 / 1024, + swap_total: (pagefile_total as u64 * perf_info.PageSize as u64) / 1024, + swap_free: ((pagefile_total - pagefile_used) as u64 * perf_info.PageSize as u64) / 1024, + swap_used: (pagefile_used as u64 * perf_info.PageSize as u64) / 1024, + commit_limit: (perf_info.CommitLimit * perf_info.PageSize) as u64 / 1024, + committed: (perf_info.CommitTotal * perf_info.PageSize) as u64 / 1024, + ..Default::default() + }; + + Ok(mem_info) } // print total - used - free combo that is used for everything except memory for now diff --git a/src/uu/free/src/windows_util.rs b/src/uu/free/src/windows_util.rs new file mode 100644 index 0000000..4144a7a --- /dev/null +++ b/src/uu/free/src/windows_util.rs @@ -0,0 +1,74 @@ +use std::ffi::c_void; + +use windows::{ + core::Result, + Wdk::System::SystemInformation::{NtQuerySystemInformation, SYSTEM_INFORMATION_CLASS}, + Win32::Foundation::{STATUS_INFO_LENGTH_MISMATCH, STATUS_SUCCESS, UNICODE_STRING}, +}; + +#[repr(C)] +#[derive(Default, Debug)] +#[allow(non_snake_case)] +struct SYSTEM_PAGEFILE_INFORMATION { + NextEntryOffset: u32, + TotalSize: u32, + TotalInUse: u32, + PeakUsage: u32, + PageFileName: UNICODE_STRING, +} + +/// Get the usage and total size of all page files. +pub(crate) fn get_pagefile_usage() -> Result<(u32, u32)> { + let mut buf: Vec = Vec::new(); + + let mut return_len: u32 = 0; + + loop { + let status = unsafe { + NtQuerySystemInformation( + // SystemPageFileInformation + SYSTEM_INFORMATION_CLASS(0x12), + buf.as_mut_ptr() as *mut c_void, + buf.len() as u32, + &mut return_len, + ) + }; + + debug_assert!(buf.len() <= return_len as usize); + + if status == STATUS_INFO_LENGTH_MISMATCH { + debug_assert!(return_len > buf.len() as u32); + buf.resize(return_len as usize, 0); + continue; + } else if status == STATUS_SUCCESS { + debug_assert!(return_len == buf.len() as u32); + break; + } else { + return Err(status.into()); + } + } + + let mut usage = 0; + let mut total = 0; + + if return_len > 0 { + let ptr = buf.as_ptr(); + let mut offset = 0; + loop { + let ptr_offset = + unsafe { ptr.byte_offset(offset) } as *const SYSTEM_PAGEFILE_INFORMATION; + let record = unsafe { std::ptr::read(ptr_offset) }; + + usage += record.TotalInUse; + total += record.TotalSize; + + offset = record.NextEntryOffset as isize; + + if offset == 0 { + break; + } + } + } + + Ok((usage, total)) +}