From 910d71f08cb734fbcff371b8e3722b2c5cec2581 Mon Sep 17 00:00:00 2001 From: Benjamin Vaisvil Date: Mon, 12 Jan 2026 07:20:25 -0600 Subject: [PATCH 1/8] feat: update sysinfo from forked 0.15.x to official 0.37 Migrate from the forked sysinfo (zenith_changes_15.1_mem_fix branch) to the official sysinfo 0.37 crate from crates.io. Key changes: - Remove *Ext traits (SystemExt, ProcessExt, DiskExt, etc.) - methods are now directly on structs - Rename Processor to Cpu, get_processors() to cpus() - Add separate Components, Disks, Networks types to CPUTimeApp - Update all method calls (get_* prefix removed) - Handle Option return types for temperature/max - Convert Pid wrapper type to i32 for internal use - Use procfs crate on Linux for priority, nice, threads_total (these fields were removed from sysinfo) Note: On macOS, priority/nice/threads_total default to 0/0/1 as these fields are not available in sysinfo 0.37. A TODO comment marks this for future platform-specific implementation. --- Cargo.lock | 292 ++++++++++++++++++++++++++++------------ Cargo.toml | 3 +- src/metrics/disk.rs | 12 +- src/metrics/mod.rs | 136 +++++++++++-------- src/metrics/zprocess.rs | 61 +++++---- 5 files changed, 325 insertions(+), 179 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f41b1d4..3e44ed1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,7 +111,7 @@ checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock 2.8.0", "autocfg", - "cfg-if 1.0.0", + "cfg-if", "concurrent-queue", "futures-lite 1.13.0", "log", @@ -130,7 +130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock 3.4.0", - "cfg-if 1.0.0", + "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.6.0", @@ -183,7 +183,7 @@ dependencies = [ "async-lock 2.8.0", "async-signal", "blocking", - "cfg-if 1.0.0", + "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", "rustix 0.38.44", @@ -199,7 +199,7 @@ dependencies = [ "async-io 2.4.0", "async-lock 3.4.0", "atomic-waker", - "cfg-if 1.0.0", + "cfg-if", "futures-core", "futures-io", "rustix 0.38.44", @@ -413,12 +413,6 @@ dependencies = [ "nom", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -463,7 +457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" dependencies = [ "castaway", - "cfg-if 1.0.0", + "cfg-if", "itoa", "rustversion", "ryu", @@ -511,26 +505,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", + "cfg-if", ] [[package]] @@ -629,7 +604,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "dirs-sys-next", ] @@ -644,12 +619,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "either" version = "1.15.0" @@ -901,7 +870,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] @@ -980,7 +949,7 @@ name = "heim-common" version = "0.1.0-rc.1" source = "git+https://github.com/bvaisvil/heim.git?branch=zenith_changes#f8a384aceff44e1da490bddaf615ca3272256ede" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "core-foundation 0.9.4", "futures-core", "futures-util", @@ -998,7 +967,7 @@ name = "heim-cpu" version = "0.1.0-rc.1" source = "git+https://github.com/bvaisvil/heim.git?branch=zenith_changes#f8a384aceff44e1da490bddaf615ca3272256ede" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "futures", "glob", "heim-common", @@ -1006,7 +975,7 @@ dependencies = [ "lazy_static", "libc", "mach", - "ntapi", + "ntapi 0.3.7", "smol", "winapi", ] @@ -1017,7 +986,7 @@ version = "0.1.0-rc.1" source = "git+https://github.com/bvaisvil/heim.git?branch=zenith_changes#f8a384aceff44e1da490bddaf615ca3272256ede" dependencies = [ "bitflags 1.3.2", - "cfg-if 1.0.0", + "cfg-if", "core-foundation 0.9.4", "heim-common", "heim-runtime", @@ -1032,14 +1001,14 @@ name = "heim-host" version = "0.1.0-rc.1" source = "git+https://github.com/bvaisvil/heim.git?branch=zenith_changes#f8a384aceff44e1da490bddaf615ca3272256ede" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "heim-common", "heim-runtime", "lazy_static", "libc", "log", "mach", - "ntapi", + "ntapi 0.3.7", "platforms", "winapi", ] @@ -1049,7 +1018,7 @@ name = "heim-memory" version = "0.1.0-rc.1" source = "git+https://github.com/bvaisvil/heim.git?branch=zenith_changes#f8a384aceff44e1da490bddaf615ca3272256ede" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "heim-common", "heim-runtime", "lazy_static", @@ -1064,7 +1033,7 @@ version = "0.1.0-rc.1" source = "git+https://github.com/bvaisvil/heim.git?branch=zenith_changes#f8a384aceff44e1da490bddaf615ca3272256ede" dependencies = [ "bitflags 1.3.2", - "cfg-if 1.0.0", + "cfg-if", "heim-common", "heim-runtime", "libc", @@ -1080,7 +1049,7 @@ version = "0.1.1-rc.1" source = "git+https://github.com/bvaisvil/heim.git?branch=zenith_changes#f8a384aceff44e1da490bddaf615ca3272256ede" dependencies = [ "async-trait", - "cfg-if 1.0.0", + "cfg-if", "darwin-libproc", "futures", "heim-common", @@ -1092,7 +1061,7 @@ dependencies = [ "libc", "mach", "memchr", - "ntapi", + "ntapi 0.3.7", "ordered-float", "smol", "winapi", @@ -1114,7 +1083,7 @@ name = "heim-sensors" version = "0.1.0-rc.1" source = "git+https://github.com/bvaisvil/heim.git?branch=zenith_changes#f8a384aceff44e1da490bddaf615ca3272256ede" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "heim-common", "heim-runtime", ] @@ -1124,7 +1093,7 @@ name = "heim-virt" version = "0.1.0-rc.1" source = "git+https://github.com/bvaisvil/heim.git?branch=zenith_changes#f8a384aceff44e1da490bddaf615ca3272256ede" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "futures", "heim-common", "heim-runtime", @@ -1143,6 +1112,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "home" version = "0.5.11" @@ -1163,7 +1138,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -1216,7 +1191,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1288,7 +1263,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "windows-targets 0.52.6", ] @@ -1437,7 +1412,7 @@ checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ "bitflags 1.3.2", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "memoffset", ] @@ -1449,7 +1424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.9.0", - "cfg-if 1.0.0", + "cfg-if", "cfg_aliases", "libc", ] @@ -1473,6 +1448,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "ntapi" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +dependencies = [ + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1542,6 +1526,25 @@ dependencies = [ "libloading", ] +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "once_cell" version = "1.21.1" @@ -1579,7 +1582,7 @@ version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", "smallvec", @@ -1642,7 +1645,7 @@ checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags 1.3.2", - "cfg-if 1.0.0", + "cfg-if", "concurrent-queue", "libc", "log", @@ -1656,7 +1659,7 @@ version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", @@ -1708,6 +1711,31 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "procfs" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" +dependencies = [ + "bitflags 2.9.0", + "chrono", + "flate2", + "hex", + "procfs-core", + "rustix 0.38.44", +] + +[[package]] +name = "procfs-core" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" +dependencies = [ + "bitflags 2.9.0", + "chrono", + "hex", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -1812,26 +1840,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.5.10" @@ -2114,7 +2122,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce0bc92586a2cba9cd189520c457b95e50f5565c3011484a5c83949be2f9fdcc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "core-foundation 0.10.0", "lazycell", "libc", @@ -2184,16 +2192,16 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.15.1" -source = "git+https://github.com/bvaisvil/sysinfo.git?branch=zenith_changes_15.1_mem_fix#671c4e14a0ff9c653fc842c83fabb5be4cae25c1" +version = "0.37.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" dependencies = [ - "cfg-if 0.1.10", - "doc-comment", "libc", - "ntapi", - "once_cell", - "rayon", - "winapi", + "memchr", + "ntapi 0.4.2", + "objc2-core-foundation", + "objc2-io-kit", + "windows", ] [[package]] @@ -2409,7 +2417,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -2501,6 +2509,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -2510,12 +2540,86 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "windows-link" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2574,6 +2678,15 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -2714,6 +2827,7 @@ dependencies = [ "num-derive", "num-traits", "nvml-wrapper", + "procfs", "ratatui", "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index 9c8583c..228fa56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ heim = { git = "https://github.com/bvaisvil/heim.git", branch = "zenith_changes" futures = "0.3.31" gumdrop = { version = "~0.8.1", features = ["default_expr"] } chrono = "~0.4.39" -sysinfo = { git = "https://github.com/bvaisvil/sysinfo.git", branch = "zenith_changes_15.1_mem_fix" } +sysinfo = "0.37" dirs-next = "2.0.0" serde = { version = "~1.0.217", features = ["derive"] } serde_derive = "~1.0.217" @@ -44,3 +44,4 @@ nvml-wrapper = { version = "0.10.0", optional = true } unicode-width = "0.2.0" [target.'cfg(target_os = "linux")'.dependencies] linux-taskstats = { version = "0.7.0", default-features = false } +procfs = "0.17" diff --git a/src/metrics/disk.rs b/src/metrics/disk.rs index 048ec8d..e1a1d3e 100644 --- a/src/metrics/disk.rs +++ b/src/metrics/disk.rs @@ -11,7 +11,7 @@ use std::fs::{canonicalize, read_link}; use std::ops; use std::path::PathBuf; use std::time::Duration; -use sysinfo::{Disk, DiskExt}; +use sysinfo::Disk; #[derive(PartialEq, Copy, Clone, Debug)] pub struct IoMetrics { @@ -81,11 +81,11 @@ impl ZDisk { pub fn from_disk(d: &Disk) -> ZDisk { ZDisk { - mount_point: d.get_mount_point().to_path_buf(), - available_bytes: d.get_available_space(), - size_bytes: d.get_total_space(), - name: get_device_name(d.get_name()), - file_system: String::from_utf8_lossy(d.get_file_system()).into_owned(), + mount_point: d.mount_point().to_path_buf(), + available_bytes: d.available_space(), + size_bytes: d.total_space(), + name: get_device_name(d.name()), + file_system: d.file_system().to_string_lossy().into_owned(), previous_io: IoMetrics { read_bytes: 0, write_bytes: 0, diff --git a/src/metrics/mod.rs b/src/metrics/mod.rs index f0237d8..c19b9c0 100644 --- a/src/metrics/mod.rs +++ b/src/metrics/mod.rs @@ -31,9 +31,9 @@ use nvml::{cuda_driver_version_major, cuda_driver_version_minor}; use std::fs; use std::path::{Path, PathBuf}; -use sysinfo::{ - Component, ComponentExt, Disk, DiskExt, NetworkExt, ProcessExt, ProcessorExt, System, SystemExt, -}; +use sysinfo::{Components, Disk, Disks, Networks, System}; +#[cfg(target_os = "linux")] +use procfs; use uzers::{Users, UsersCache}; #[cfg(all(feature = "nvidia", not(target_os = "linux")))] @@ -123,10 +123,10 @@ pub trait DiskFreeSpaceExt { impl DiskFreeSpaceExt for Disk { fn get_perc_free_space(&self) -> f32 { - if self.get_total_space() < 1 { + if self.total_space() < 1 { return 0.0; } - percent_of(self.get_available_space(), self.get_total_space()) + percent_of(self.available_space(), self.total_space()) } } #[allow(dead_code)] @@ -143,13 +143,13 @@ pub struct Sensor { pub high: f32, } -impl From<&Component> for Sensor { - fn from(c: &Component) -> Sensor { +impl From<&sysinfo::Component> for Sensor { + fn from(c: &sysinfo::Component) -> Sensor { Sensor { - name: c.get_label().to_owned(), - current_temp: c.get_temperature(), - critical: c.get_critical().unwrap_or(0.0), - high: c.get_max(), + name: c.label().to_owned(), + current_temp: c.temperature().unwrap_or(0.0), + critical: c.critical().unwrap_or(0.0), + high: c.max().unwrap_or(0.0), } } } @@ -233,6 +233,9 @@ pub struct CPUTimeApp { pub disk_read: u64, pub cpus: Vec<(String, u64)>, pub system: System, + pub components: Components, + pub disks_cache: Disks, + pub networks: Networks, pub net_in: u64, pub net_out: u64, pub processes: Vec, @@ -314,6 +317,9 @@ impl CPUTimeApp { histogram_map, cpus: vec![], system: System::new_all(), + components: Components::new_with_refreshed_list(), + disks_cache: Disks::new_with_refreshed_list(), + networks: Networks::new_with_refreshed_list(), cpu_utilization: 0, mem_utilization: 0, mem_total: 0, @@ -459,13 +465,14 @@ impl CPUTimeApp { async fn update_sensors(&mut self) { self.sensors.clear(); - for t in self.system.get_components() { + self.components.refresh(false); + for t in self.components.iter() { if cfg!(target_os = "linux") { - if t.get_label().contains("Package id") { + if t.label().contains("Package id") { debug!("{:?}", t); self.sensors.push(Sensor::from(t)); } - } else if cfg!(target_os = "macos") && t.get_label().contains("CPU") { + } else if cfg!(target_os = "macos") && t.label().contains("CPU") { self.sensors.push(Sensor::from(t)); } } @@ -478,7 +485,7 @@ impl CPUTimeApp { fn update_process_list(&mut self, keep_order: bool) { debug!("Updating Process List"); - let process_list = self.system.get_processes(); + let process_list = self.system.processes(); #[cfg(target_os = "linux")] let client = &self.netlink_client; let mut current_pids: HashSet = HashSet::with_capacity(process_list.len()); @@ -492,7 +499,10 @@ impl CPUTimeApp { self.threads_total = 0; for (pid, process) in process_list { - if let Some(zp) = self.process_map.get_mut(pid) { + let pid_i32 = pid.as_u32() as i32; + let uid = process.user_id().map(|u| **u).unwrap_or(0); + + if let Some(zp) = self.process_map.get_mut(&pid_i32) { if zp.start_time == process.start_time() { let disk_usage = process.disk_usage(); // check for PID reuse @@ -500,10 +510,21 @@ impl CPUTimeApp { zp.cpu_usage = process.cpu_usage(); zp.cum_cpu_usage += zp.cpu_usage as f64; zp.status = process.status(); - zp.priority = process.priority; - zp.nice = process.nice; + + // Get priority, nice, threads_total from procfs on Linux + #[cfg(target_os = "linux")] + { + if let Ok(proc) = procfs::process::Process::new(pid_i32) { + if let Ok(stat) = proc.stat() { + zp.priority = stat.priority as i32; + zp.nice = stat.nice as i32; + zp.threads_total = stat.num_threads as u64; + } + } + } + // TODO: macOS - priority, nice, threads_total not available in sysinfo 0.33 + zp.virtual_memory = process.virtual_memory(); - zp.threads_total = process.threads_total; self.threads_total += zp.threads_total as usize; zp.prev_read_bytes = zp.read_bytes; zp.prev_write_bytes = zp.write_bytes; @@ -517,10 +538,10 @@ impl CPUTimeApp { } else { let user_name = self .user_cache - .get_user_by_uid(process.uid) + .get_user_by_uid(uid) .map(|user| user.name().to_string_lossy().to_string()) - .unwrap_or(format!("{:}", process.uid)); - let zprocess = ZProcess::from_user_and_process(user_name, process); + .unwrap_or(format!("{:}", uid)); + let zprocess = ZProcess::from_user_and_process(user_name, process, uid); self.threads_total += zprocess.threads_total as usize; top.update(zp, &self.histogram_map.tick); @@ -530,11 +551,11 @@ impl CPUTimeApp { } else { let user_name = self .user_cache - .get_user_by_uid(process.uid) + .get_user_by_uid(uid) .map(|user| user.name().to_string_lossy().to_string()) - .unwrap_or(format!("{:}", process.uid)); + .unwrap_or(format!("{:}", uid)); #[allow(unused_mut)] - let mut zprocess = ZProcess::from_user_and_process(user_name, process); + let mut zprocess = ZProcess::from_user_and_process(user_name, process, uid); #[cfg(target_os = "linux")] zprocess.update_delay(client); @@ -544,7 +565,7 @@ impl CPUTimeApp { self.process_map.insert(zprocess.pid, zprocess); } - current_pids.insert(*pid); + current_pids.insert(pid_i32); } if keep_order { @@ -620,18 +641,18 @@ impl CPUTimeApp { async fn update_disk(&mut self) { debug!("Updating Disks"); - static IGNORED_FILE_SYSTEMS: &[&[u8]] = &[ - b"sysfs", - b"proc", - b"tmpfs", - b"cgroup", - b"cgroup2", - b"pstore", - b"squashfs", - b"iso9660", + static IGNORED_FILE_SYSTEMS: &[&str] = &[ + "sysfs", + "proc", + "tmpfs", + "cgroup", + "cgroup2", + "pstore", + "squashfs", + "iso9660", ]; - self.system.refresh_disks_list(); + self.disks_cache.refresh(true); let mut updated: HashMap = HashMap::with_capacity(self.disks.len()); for k in self.disks.keys() { if k == "Total" { @@ -643,12 +664,12 @@ impl CPUTimeApp { let mut total_available = 0; let mut total_space = 0; - for d in self.system.get_disks().iter() { - let name = d.get_name().to_string_lossy(); - let mp = d.get_mount_point().to_string_lossy(); + for d in self.disks_cache.list().iter() { + let name = d.name().to_string_lossy(); + let mp = d.mount_point().to_string_lossy(); if cfg!(target_os = "linux") { - let fs = d.get_file_system(); - if IGNORED_FILE_SYSTEMS.iter().any(|ignored| &fs == ignored) { + let fs = d.file_system().to_string_lossy(); + if IGNORED_FILE_SYSTEMS.iter().any(|ignored| fs.as_ref() == *ignored) { continue; } if mp.starts_with("/sys") @@ -661,10 +682,10 @@ impl CPUTimeApp { continue; } } - let name = get_device_name(d.get_name()); + let name = get_device_name(d.name()); let zd = self.disks.entry(name).or_insert(ZDisk::from_disk(d)); - zd.size_bytes = d.get_total_space(); - zd.available_bytes = d.get_available_space(); + zd.size_bytes = d.total_space(); + zd.available_bytes = d.available_space(); total_available += zd.available_bytes; total_space += zd.size_bytes; updated.insert(zd.name.to_string(), true); @@ -754,15 +775,15 @@ impl CPUTimeApp { pub async fn update_cpu(&mut self) { debug!("Updating CPU"); - let procs = self.system.get_processors(); + let cpus = self.system.cpus(); let mut usage: f32 = 0.0; self.cpus.clear(); let mut usagev: Vec = vec![]; - for (i, p) in procs.iter().enumerate() { + for (i, cpu) in cpus.iter().enumerate() { if i == 0 { - self.processor_name = p.get_name().to_owned(); + self.processor_name = cpu.name().to_owned(); } - let mut u = p.get_cpu_usage(); + let mut u = cpu.cpu_usage(); if u.is_nan() { u = 0.0; } @@ -770,10 +791,10 @@ impl CPUTimeApp { usage += u; usagev.push(u); } - if procs.is_empty() { + if cpus.is_empty() { self.cpu_utilization = 0; } else { - usage /= procs.len() as f32; + usage /= cpus.len() as f32; self.cpu_utilization = usage as u64; } self.histogram_map @@ -781,12 +802,13 @@ impl CPUTimeApp { } pub async fn update_networks(&mut self) { + self.networks.refresh(false); let mut net_in = 0; let mut net_out = 0; - for (_iface, data) in self.system.get_networks() { + for (_iface, data) in self.networks.iter() { debug!("iface: {}", _iface); - net_in += data.get_received(); - net_out += data.get_transmitted(); + net_in += data.received(); + net_out += data.transmitted(); } self.net_in = net_in; self.net_out = net_out; @@ -802,15 +824,15 @@ impl CPUTimeApp { self.update_cpu().await; self.update_sensors().await; - self.mem_utilization = self.system.get_used_memory(); - self.mem_total = self.system.get_total_memory(); + self.mem_utilization = self.system.used_memory(); + self.mem_total = self.system.total_memory(); let mem = percent_of(self.mem_utilization, self.mem_total) as u64; self.histogram_map.add_value_to(&HistogramKind::Mem, mem); - self.swap_utilization = self.system.get_used_swap(); - self.swap_total = self.system.get_total_swap(); + self.swap_utilization = self.system.used_swap(); + self.swap_total = self.system.total_swap(); self.update_networks().await; self.update_process_list(keep_order); diff --git a/src/metrics/zprocess.rs b/src/metrics/zprocess.rs index 1103da9..29f2002 100644 --- a/src/metrics/zprocess.rs +++ b/src/metrics/zprocess.rs @@ -10,12 +10,12 @@ use libc::{id_t, setpriority}; #[cfg(target_os = "linux")] use linux_taskstats::Client; +#[cfg(target_os = "linux")] +use procfs; -use std::cmp::Ordering::{self, Equal}; +use std::cmp::Ordering; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use sysinfo::Process; -use sysinfo::ProcessExt; -use sysinfo::ProcessStatus; +use sysinfo::{Process, ProcessStatus}; use chrono::prelude::DateTime; use chrono::Duration as CDuration; @@ -77,23 +77,43 @@ pub struct ZProcess { } impl ZProcess { - pub fn from_user_and_process(user_name: String, process: &Process) -> Self { + pub fn from_user_and_process(user_name: String, process: &Process, uid: u32) -> Self { let disk_usage = process.disk_usage(); + let pid_i32 = process.pid().as_u32() as i32; + + // Get priority, nice, threads_total from procfs on Linux + #[cfg(target_os = "linux")] + let (priority, nice, threads_total) = { + if let Ok(proc) = procfs::process::Process::new(pid_i32) { + if let Ok(stat) = proc.stat() { + (stat.priority as i32, stat.nice as i32, stat.num_threads as u64) + } else { + (0, 0, 1) + } + } else { + (0, 0, 1) + } + }; + + #[cfg(not(target_os = "linux"))] + let (priority, nice, threads_total) = (0, 0, 1); + // TODO: macOS - priority, nice, threads_total not available in sysinfo 0.33 + ZProcess { - uid: process.uid, + uid, user_name, - pid: process.pid(), + pid: pid_i32, memory: process.memory(), cpu_usage: process.cpu_usage(), - command: process.cmd().to_vec(), + command: process.cmd().iter().map(|s| s.to_string_lossy().to_string()).collect(), status: process.status(), - exe: format!("{}", process.exe().display()), - name: process.name().to_string(), + exe: process.exe().map(|p| format!("{}", p.display())).unwrap_or_default(), + name: process.name().to_string_lossy().to_string(), cum_cpu_usage: process.cpu_usage() as f64, - priority: process.priority, - nice: process.nice, + priority, + nice, virtual_memory: process.virtual_memory(), - threads_total: process.threads_total, + threads_total, read_bytes: disk_usage.total_read_bytes, write_bytes: disk_usage.total_written_bytes, prev_read_bytes: disk_usage.total_read_bytes, @@ -407,19 +427,6 @@ pub trait ProcessStatusExt { } impl ProcessStatusExt for ProcessStatus { - #[cfg(target_os = "macos")] - fn to_single_char(&self) -> &str { - match *self { - ProcessStatus::Idle => "I", - ProcessStatus::Run => "R", - ProcessStatus::Sleep => "S", - ProcessStatus::Stop => "T", - ProcessStatus::Zombie => "Z", - ProcessStatus::Unknown(_) => "U", - } - } - - #[cfg(all(unix, not(target_os = "macos")))] fn to_single_char(&self) -> &str { match *self { ProcessStatus::Idle => "I", @@ -432,6 +439,8 @@ impl ProcessStatusExt for ProcessStatus { ProcessStatus::Wakekill => "K", ProcessStatus::Waking => "W", ProcessStatus::Parked => "P", + ProcessStatus::UninterruptibleDiskSleep => "D", + ProcessStatus::LockBlocked => "L", ProcessStatus::Unknown(_) => "U", } } From 5d5d5fb2681d47e527308b64aebd89511fc46c8b Mon Sep 17 00:00:00 2001 From: Benjamin Vaisvil Date: Mon, 12 Jan 2026 07:31:23 -0600 Subject: [PATCH 2/8] style: run cargo fmt --- src/metrics/mod.rs | 18 +++++++----------- src/metrics/zprocess.rs | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/metrics/mod.rs b/src/metrics/mod.rs index c19b9c0..168e501 100644 --- a/src/metrics/mod.rs +++ b/src/metrics/mod.rs @@ -29,11 +29,11 @@ use nvml::error::NvmlError; #[cfg(all(feature = "nvidia", target_os = "linux"))] use nvml::{cuda_driver_version_major, cuda_driver_version_minor}; +#[cfg(target_os = "linux")] +use procfs; use std::fs; use std::path::{Path, PathBuf}; use sysinfo::{Components, Disk, Disks, Networks, System}; -#[cfg(target_os = "linux")] -use procfs; use uzers::{Users, UsersCache}; #[cfg(all(feature = "nvidia", not(target_os = "linux")))] @@ -642,14 +642,7 @@ impl CPUTimeApp { debug!("Updating Disks"); static IGNORED_FILE_SYSTEMS: &[&str] = &[ - "sysfs", - "proc", - "tmpfs", - "cgroup", - "cgroup2", - "pstore", - "squashfs", - "iso9660", + "sysfs", "proc", "tmpfs", "cgroup", "cgroup2", "pstore", "squashfs", "iso9660", ]; self.disks_cache.refresh(true); @@ -669,7 +662,10 @@ impl CPUTimeApp { let mp = d.mount_point().to_string_lossy(); if cfg!(target_os = "linux") { let fs = d.file_system().to_string_lossy(); - if IGNORED_FILE_SYSTEMS.iter().any(|ignored| fs.as_ref() == *ignored) { + if IGNORED_FILE_SYSTEMS + .iter() + .any(|ignored| fs.as_ref() == *ignored) + { continue; } if mp.starts_with("/sys") diff --git a/src/metrics/zprocess.rs b/src/metrics/zprocess.rs index 29f2002..3047465 100644 --- a/src/metrics/zprocess.rs +++ b/src/metrics/zprocess.rs @@ -86,7 +86,11 @@ impl ZProcess { let (priority, nice, threads_total) = { if let Ok(proc) = procfs::process::Process::new(pid_i32) { if let Ok(stat) = proc.stat() { - (stat.priority as i32, stat.nice as i32, stat.num_threads as u64) + ( + stat.priority as i32, + stat.nice as i32, + stat.num_threads as u64, + ) } else { (0, 0, 1) } @@ -105,9 +109,16 @@ impl ZProcess { pid: pid_i32, memory: process.memory(), cpu_usage: process.cpu_usage(), - command: process.cmd().iter().map(|s| s.to_string_lossy().to_string()).collect(), + command: process + .cmd() + .iter() + .map(|s| s.to_string_lossy().to_string()) + .collect(), status: process.status(), - exe: process.exe().map(|p| format!("{}", p.display())).unwrap_or_default(), + exe: process + .exe() + .map(|p| format!("{}", p.display())) + .unwrap_or_default(), name: process.name().to_string_lossy().to_string(), cum_cpu_usage: process.cpu_usage() as f64, priority, From 380f69cdcbc71594624f6a3736be892a870db9d6 Mon Sep 17 00:00:00 2001 From: Benjamin Vaisvil Date: Mon, 12 Jan 2026 07:45:19 -0600 Subject: [PATCH 3/8] fix: address clippy warnings - Remove empty lines after doc comments - Use is_multiple_of() instead of manual modulo check - Use is_some_and() instead of map_or(false, ...) - Remove unused lifetime parameter - Add #[allow] for too_many_arguments on existing functions --- src/main.rs | 2 +- src/metrics/graphics/device.rs | 1 - src/renderer/mod.rs | 2 +- src/renderer/process.rs | 1 + src/util.rs | 4 ++-- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 900025c..de4265c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ /** * Copyright 2019-2020, Benjamin Vaisvil and the zenith contributors */ - #[macro_use] extern crate num_derive; #[macro_use] @@ -160,6 +159,7 @@ fn create_geometry( geometry } +#[allow(clippy::too_many_arguments)] fn start_zenith( rate: u64, cpu_height: u16, diff --git a/src/metrics/graphics/device.rs b/src/metrics/graphics/device.rs index e69ecbc..10d1898 100644 --- a/src/metrics/graphics/device.rs +++ b/src/metrics/graphics/device.rs @@ -1,7 +1,6 @@ /** * Copyright 2019-2020, Benjamin Vaisvil and the zenith contributors */ - pub trait GraphicsExt { fn update_gfx_devices(&mut self); #[allow(dead_code)] diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 00905b8..b98ede7 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -218,7 +218,7 @@ pub struct TerminalRenderer<'a> { recompute_constraints_on_start_up: bool, } -impl<'a> TerminalRenderer<'_> { +impl TerminalRenderer<'_> { pub fn new( tick_rate: u64, section_geometry: &'_ [(Section, f64)], diff --git a/src/renderer/process.rs b/src/renderer/process.rs index 08d4552..40673f0 100644 --- a/src/renderer/process.rs +++ b/src/renderer/process.rs @@ -16,6 +16,7 @@ use ratatui::Frame; use std::borrow::Cow; use std::time::{Duration, UNIX_EPOCH}; +#[allow(clippy::too_many_arguments)] pub fn render_process_table( app: &CPUTimeApp, process_table: &[i32], diff --git a/src/util.rs b/src/util.rs index 476e30b..bf584b1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -74,7 +74,7 @@ impl Events { loop { tx.send(Event::Tick).expect("Couldn't send event."); count += 1; - if count % 60 == 0 { + if count.is_multiple_of(60) { tx.send(Event::Save).expect("Couldn't send event"); } thread::sleep(config.tick_rate); @@ -150,7 +150,7 @@ impl Drop for Lockfile { async fn is_zenith_running(path: &Path) -> bool { name_of_process_for_pidfile(path) .await - .map_or(false, |name| name == "zenith") + .is_some_and(|name| name == "zenith") } async fn name_of_process_for_pidfile(path: &Path) -> Option { From 4ff830e7064656fcda444888a82c8071504b8d9e Mon Sep 17 00:00:00 2001 From: Benjamin Vaisvil Date: Mon, 12 Jan 2026 08:07:36 -0600 Subject: [PATCH 4/8] fix: restore Equal import needed for Linux builds The Equal variant from Ordering is used in Linux-specific field comparators but was accidentally removed. Add #[allow(unused_imports)] since it's only used on Linux. --- src/metrics/zprocess.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/metrics/zprocess.rs b/src/metrics/zprocess.rs index 3047465..26ed965 100644 --- a/src/metrics/zprocess.rs +++ b/src/metrics/zprocess.rs @@ -13,7 +13,8 @@ use linux_taskstats::Client; #[cfg(target_os = "linux")] use procfs; -use std::cmp::Ordering; +#[allow(unused_imports)] +use std::cmp::Ordering::{self, Equal}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use sysinfo::{Process, ProcessStatus}; From b4167b67f05e96927b8b09a97a7595f9b34d1cff Mon Sep 17 00:00:00 2001 From: Benjamin Vaisvil Date: Mon, 12 Jan 2026 20:24:41 -0600 Subject: [PATCH 5/8] feat: add macOS support for priority, nice, and threads_total Use libc::getpriority() to get the nice value and proc_pidinfo() with PROC_PIDTASKINFO to get the thread count on macOS. This removes the TODO comments and provides full functionality on both Linux and macOS for these process fields. --- src/metrics/mod.rs | 10 +++++- src/metrics/zprocess.rs | 76 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/metrics/mod.rs b/src/metrics/mod.rs index 168e501..4fe2d53 100644 --- a/src/metrics/mod.rs +++ b/src/metrics/mod.rs @@ -9,6 +9,8 @@ pub mod zprocess; use crate::metrics::disk::{get_device_name, get_disk_io_metrics, IoMetrics, ZDisk}; use crate::metrics::graphics::device::{GraphicsDevice, GraphicsExt}; use crate::metrics::histogram::{HistogramKind, HistogramMap}; +#[cfg(target_os = "macos")] +use crate::metrics::zprocess::get_macos_process_info; use crate::metrics::zprocess::ZProcess; use crate::util::percent_of; @@ -522,7 +524,13 @@ impl CPUTimeApp { } } } - // TODO: macOS - priority, nice, threads_total not available in sysinfo 0.33 + #[cfg(target_os = "macos")] + { + let (priority, nice, threads_total) = get_macos_process_info(pid_i32); + zp.priority = priority; + zp.nice = nice; + zp.threads_total = threads_total; + } zp.virtual_memory = process.virtual_memory(); self.threads_total += zp.threads_total as usize; diff --git a/src/metrics/zprocess.rs b/src/metrics/zprocess.rs index 26ed965..e244c38 100644 --- a/src/metrics/zprocess.rs +++ b/src/metrics/zprocess.rs @@ -4,8 +4,9 @@ use crate::metrics::ProcessTableSortBy; use heim::process; use heim::process::ProcessError; -#[cfg(target_os = "linux")] use libc::getpriority; +#[cfg(target_os = "macos")] +use libc::{c_int, c_void, pid_t}; use libc::{id_t, setpriority}; #[cfg(target_os = "linux")] @@ -22,6 +23,74 @@ use chrono::prelude::DateTime; use chrono::Duration as CDuration; use chrono::Local; +#[cfg(target_os = "macos")] +const PROC_PIDTASKINFO: c_int = 4; + +#[cfg(target_os = "macos")] +#[repr(C)] +struct ProcTaskInfo { + pti_virtual_size: u64, + pti_resident_size: u64, + pti_total_user: u64, + pti_total_system: u64, + pti_threads_user: u64, + pti_threads_system: u64, + pti_policy: i32, + pti_faults: i32, + pti_pageins: i32, + pti_cow_faults: i32, + pti_messages_sent: i32, + pti_messages_received: i32, + pti_syscalls_mach: i32, + pti_syscalls_unix: i32, + pti_csw: i32, + pti_threadnum: i32, + pti_numrunning: i32, + pti_priority: i32, +} + +#[cfg(target_os = "macos")] +extern "C" { + fn proc_pidinfo( + pid: pid_t, + flavor: c_int, + arg: u64, + buffer: *mut c_void, + buffersize: c_int, + ) -> c_int; +} + +#[cfg(target_os = "macos")] +pub fn get_macos_process_info(pid: i32) -> (i32, i32, u64) { + use std::mem; + + // Get nice value using getpriority + let nice = unsafe { getpriority(0, pid as u32) }; + let priority = nice + 20; // Convert nice to priority (Linux convention) + + // Get thread count using proc_pidinfo + let mut task_info: ProcTaskInfo = unsafe { mem::zeroed() }; + let size = mem::size_of::() as c_int; + + let ret = unsafe { + proc_pidinfo( + pid, + PROC_PIDTASKINFO, + 0, + &mut task_info as *mut _ as *mut c_void, + size, + ) + }; + + let threads = if ret > 0 { + task_info.pti_threadnum as u64 + } else { + 1 + }; + + (priority, nice, threads) +} + macro_rules! convert_result_to_string { ($x:expr) => { match $x { @@ -100,9 +169,8 @@ impl ZProcess { } }; - #[cfg(not(target_os = "linux"))] - let (priority, nice, threads_total) = (0, 0, 1); - // TODO: macOS - priority, nice, threads_total not available in sysinfo 0.33 + #[cfg(target_os = "macos")] + let (priority, nice, threads_total) = get_macos_process_info(pid_i32); ZProcess { uid, From 7a39007ade155e3b0b087077c423465297bfcf32 Mon Sep 17 00:00:00 2001 From: Benjamin Vaisvil Date: Tue, 13 Jan 2026 09:46:22 -0600 Subject: [PATCH 6/8] fix: use pti_priority from proc_pidinfo for correct macOS priority Instead of calculating priority from nice value, use the actual pti_priority field from ProcTaskInfo. Also properly handle getpriority errors by checking errno. --- src/metrics/zprocess.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/metrics/zprocess.rs b/src/metrics/zprocess.rs index e244c38..ea3c04f 100644 --- a/src/metrics/zprocess.rs +++ b/src/metrics/zprocess.rs @@ -64,11 +64,17 @@ extern "C" { pub fn get_macos_process_info(pid: i32) -> (i32, i32, u64) { use std::mem; - // Get nice value using getpriority + // Get nice value using getpriority (returns -1 on error, but -1 is also valid nice) + // We need to clear errno first to distinguish errors + unsafe { *libc::__error() = 0 }; let nice = unsafe { getpriority(0, pid as u32) }; - let priority = nice + 20; // Convert nice to priority (Linux convention) + let nice = if nice == -1 && unsafe { *libc::__error() } != 0 { + 0 // Error occurred, use default + } else { + nice + }; - // Get thread count using proc_pidinfo + // Get thread count and priority using proc_pidinfo let mut task_info: ProcTaskInfo = unsafe { mem::zeroed() }; let size = mem::size_of::() as c_int; @@ -82,10 +88,10 @@ pub fn get_macos_process_info(pid: i32) -> (i32, i32, u64) { ) }; - let threads = if ret > 0 { - task_info.pti_threadnum as u64 + let (priority, threads) = if ret > 0 { + (task_info.pti_priority, task_info.pti_threadnum as u64) } else { - 1 + (0, 1) }; (priority, nice, threads) From 38f5a72f2df046102267a1537ff3cf8605cc43cf Mon Sep 17 00:00:00 2001 From: Benjamin Vaisvil Date: Mon, 19 Jan 2026 16:25:57 -0600 Subject: [PATCH 7/8] fix memory display for processes --- src/renderer/process.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/process.rs b/src/renderer/process.rs index 40673f0..978f8cf 100644 --- a/src/renderer/process.rs +++ b/src/renderer/process.rs @@ -195,7 +195,7 @@ fn render_rows<'a>( app.top_pids.mem.pid, format!( "{:>8}", - float_to_byte_string!(p.memory as f64, Unit::KB).replace('B', "") + float_to_byte_string!(p.memory as f64, Unit::B).replace('B', "") ), ), set_process_row_style( @@ -203,7 +203,7 @@ fn render_rows<'a>( app.top_pids.virt.pid, format!( "{:>8}", - float_to_byte_string!(p.virtual_memory as f64, Unit::KB).replace('B', "") + float_to_byte_string!(p.virtual_memory as f64, Unit::B).replace('B', "") ), ), Cell::from(format!("{:1}", p.status.to_single_char())), @@ -382,7 +382,7 @@ pub fn render_process( Line::from(vec![ Span::raw("Total Memory: "), Span::styled( - format!("{:>10}", float_to_byte_string!(p.memory as f64, Unit::KB)), + format!("{:>10}", float_to_byte_string!(p.memory as f64, Unit::B)), rhs_style, ), ]), From 7654f520c3f6bb736dd6ca24b9b892070d778862 Mon Sep 17 00:00:00 2001 From: Benjamin Vaisvil Date: Mon, 19 Jan 2026 19:39:30 -0600 Subject: [PATCH 8/8] using host_page_size() from the Mach API to get the correct page size, with fallback to sysconf(_SC_PAGESIZE) --- src/metrics/mod.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/src/metrics/mod.rs b/src/metrics/mod.rs index 4fe2d53..4f893f5 100644 --- a/src/metrics/mod.rs +++ b/src/metrics/mod.rs @@ -38,6 +38,119 @@ use std::path::{Path, PathBuf}; use sysinfo::{Components, Disk, Disks, Networks, System}; use uzers::{Users, UsersCache}; +#[cfg(target_os = "macos")] +mod macos_mem { + #[allow(deprecated)] + use libc::{c_int, c_void, mach_host_self, mach_port_t, sysctlbyname}; + use std::convert::TryInto; + use std::ffi::CString; + use std::mem; + + const HOST_VM_INFO64: c_int = 4; + const HOST_VM_INFO64_COUNT: u32 = + (mem::size_of::() / mem::size_of::()) as u32; + const KERN_SUCCESS: c_int = 0; + + #[repr(C)] + struct vm_statistics64 { + free_count: u32, + active_count: u32, + inactive_count: u32, + wire_count: u32, + zero_fill_count: u64, + reactivations: u64, + pageins: u64, + pageouts: u64, + faults: u64, + cow_faults: u64, + lookups: u64, + hits: u64, + purges: u64, + purgeable_count: u32, + speculative_count: u32, + decompressions: u64, + compressions: u64, + swapins: u64, + swapouts: u64, + compressor_page_count: u32, + throttled_count: u32, + external_page_count: u32, + internal_page_count: u32, + total_uncompressed_pages_in_compressor: u64, + } + + extern "C" { + fn host_statistics64( + host_priv: mach_port_t, + flavor: c_int, + host_info_out: *mut c_int, + host_info_outCnt: *mut u32, + ) -> c_int; + + fn host_page_size(host: mach_port_t, out_page_size: *mut usize) -> c_int; + } + + fn get_vm_page_pageable_internal_count() -> Option { + let mut buf: Vec = vec![0; 8]; + let c = CString::new("vm.page_pageable_internal_count").ok()?; + let mut len: usize = 8; + unsafe { + if sysctlbyname( + c.as_ptr(), + buf.as_mut_ptr() as *mut c_void, + &mut len, + std::ptr::null_mut(), + 0, + ) != 0 + { + return None; + } + Some(u64::from_ne_bytes(buf[..8].try_into().ok()?)) + } + } + + pub fn get_macos_memory_used() -> Option { + unsafe { + #[allow(deprecated)] + let host_port = mach_host_self(); + let mut vm_stat: vm_statistics64 = mem::zeroed(); + let mut count = HOST_VM_INFO64_COUNT; + + let ret = host_statistics64( + host_port, + HOST_VM_INFO64, + &mut vm_stat as *mut _ as *mut c_int, + &mut count, + ); + + if ret != KERN_SUCCESS { + return None; + } + + // Get page size using host_page_size, fallback to sysconf + let mut page_size: usize = 0; + let res = host_page_size(host_port, &mut page_size); + if res != KERN_SUCCESS { + page_size = libc::sysconf(libc::_SC_PAGESIZE) as usize; + } + + // Get pageable internal count for accurate app memory + let pageable_internal = get_vm_page_pageable_internal_count()?; + + let app_mem = pageable_internal.saturating_sub(vm_stat.purgeable_count as u64); + let wired_mem = vm_stat.wire_count as u64; + let compressed_mem = vm_stat.compressor_page_count as u64; + + Some( + app_mem + .saturating_add(compressed_mem) + .saturating_add(wired_mem) + .saturating_mul(page_size as u64), + ) + } + } +} + #[cfg(all(feature = "nvidia", not(target_os = "linux")))] #[derive(FromPrimitive, PartialEq, Copy, Clone)] pub enum ProcessTableSortBy { @@ -828,7 +941,15 @@ impl CPUTimeApp { self.update_cpu().await; self.update_sensors().await; - self.mem_utilization = self.system.used_memory(); + #[cfg(target_os = "macos")] + { + self.mem_utilization = + macos_mem::get_macos_memory_used().unwrap_or_else(|| self.system.used_memory()); + } + #[cfg(not(target_os = "macos"))] + { + self.mem_utilization = self.system.used_memory(); + } self.mem_total = self.system.total_memory(); let mem = percent_of(self.mem_utilization, self.mem_total) as u64;