free: add Windows support

Values are virtual the same as resmon. Unfortunately, it is slightly
hacky to get the page "Swap" file sizes.
This commit is contained in:
Reagan Bohan
2024-10-10 10:28:28 +00:00
parent 851ec314fc
commit 3bd07dfaf7
5 changed files with 182 additions and 6 deletions
Generated
+69 -4
View File
@@ -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"
+1
View File
@@ -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 }
+1
View File
@@ -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"
+37 -2
View File
@@ -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<MemInfo, Box<dyn std::error::Error>> {
Ok(mem_info)
}
// TODO: implement function
#[cfg(target_os = "windows")]
fn parse_meminfo() -> Result<MemInfo, Box<dyn std::error::Error>> {
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::<MEMORYSTATUSEX>() as u32,
..Default::default()
};
unsafe { GlobalMemoryStatusEx(&mut status)? }
let mut perf_info = PERFORMANCE_INFORMATION {
cb: size_of::<PERFORMANCE_INFORMATION>() 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
+74
View File
@@ -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<u8> = 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))
}