From cebcc53b4bbfca76cceb3e3bc2d1fbab4d4b58c6 Mon Sep 17 00:00:00 2001 From: David Alsh Date: Mon, 9 Mar 2026 09:21:40 +1100 Subject: [PATCH 1/2] Update --- Cargo.lock | 770 +------------------------------------------------ Cargo.toml | 1 - justfile | 4 +- rust-toolchain | 2 +- src/main.rs | 147 ---------- src/plot.rs | 97 ------- 6 files changed, 18 insertions(+), 1003 deletions(-) delete mode 100644 src/plot.rs diff --git a/Cargo.lock b/Cargo.lock index b479de7..2d3942b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,27 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] +version = 4 [[package]] name = "anstream" @@ -58,7 +37,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -68,7 +47,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -77,62 +56,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "bytemuck" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cc" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets 0.52.6", -] - [[package]] name = "clap" version = "4.5.26" @@ -173,79 +102,18 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "core-text" -version = "20.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types", - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-deque" version = "0.8.3" @@ -279,259 +147,24 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading", -] - -[[package]] -name = "dwrote" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" -dependencies = [ - "lazy_static", - "libc", - "winapi", - "wio", -] - [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" -[[package]] -name = "fdeflate" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-ord" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" - -[[package]] -name = "font-kit" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64b34f4efd515f905952d91bc185039863705592c0c53ae6d979805dd154520" -dependencies = [ - "bitflags 2.5.0", - "byteorder", - "core-foundation", - "core-graphics", - "core-text", - "dirs", - "dwrote", - "float-ord", - "freetype-sys", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "freetype-sys" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "getrandom" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gif" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core 0.52.0", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "jpeg-decoder", - "num-traits", - "png", -] - -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" - -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" -[[package]] -name = "libloading" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" -dependencies = [ - "cfg-if", - "windows-targets 0.52.6", -] - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", -] - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - [[package]] name = "memchr" version = "2.7.4" @@ -547,16 +180,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "miniz_oxide" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" -dependencies = [ - "adler", - "simd-adler32", -] - [[package]] name = "ntapi" version = "0.4.1" @@ -566,111 +189,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-traits" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf07ef4804cfa9aea3b04a7bbdd5a40031dbb6b4f2cbaf2b011666c80c5b4f2" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "chrono", - "font-kit", - "image", - "lazy_static", - "num-traits", - "pathfinder_geometry", - "plotters-backend", - "plotters-bitmap", - "plotters-svg", - "ttf-parser", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-bitmap" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ce181e3f6bf82d6c1dc569103ca7b1bd964c60ba03d7e6cdfbb3e3eb7f7405" -dependencies = [ - "gif", - "image", - "plotters-backend", -] - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "png" -version = "0.17.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "proc-macro2" version = "1.0.93" @@ -685,7 +203,6 @@ name = "procmon" version = "0.2.0" dependencies = [ "clap", - "plotters", "serde", "sysinfo", ] @@ -719,47 +236,12 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom", - "libredox", - "thiserror", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "semver" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" - [[package]] name = "serde" version = "1.0.217" @@ -780,12 +262,6 @@ dependencies = [ "syn", ] -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "strsim" version = "0.11.1" @@ -817,32 +293,6 @@ dependencies = [ "windows", ] -[[package]] -name = "thiserror" -version = "1.0.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ttf-parser" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -855,92 +305,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "weezl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" - [[package]] name = "winapi" version = "0.3.9" @@ -957,15 +321,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -978,17 +333,8 @@ version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", + "windows-core", + "windows-targets", ] [[package]] @@ -1000,7 +346,7 @@ dependencies = [ "windows-implement", "windows-interface", "windows-result", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1031,16 +377,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1049,22 +386,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -1073,46 +395,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1125,70 +429,26 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - -[[package]] -name = "yeslogic-fontconfig-sys" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" -dependencies = [ - "dlib", - "once_cell", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index 110c540..a29e0ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,5 @@ edition = "2021" [dependencies] clap = { version = "4.5.26", features = ["derive", "env", "string"] } -plotters = "0.3.7" serde = { version = "1.0.217", features = ["serde_derive", "derive"] } sysinfo = "0.33.1" diff --git a/justfile b/justfile index ca961e9..f769f16 100644 --- a/justfile +++ b/justfile @@ -23,9 +23,9 @@ else \ target := \ if \ - os + arch == "linuxamd64" { "x86_64-unknown-linux-gnu" } \ + os + arch == "linuxamd64" { "x86_64-unknown-linux-musl" } \ else if \ - os + arch == "linuxarm64" { "aarch64-unknown-linux-gnu" } \ + os + arch == "linuxarm64" { "aarch64-unknown-linux-musl" } \ else if \ os + arch == "macosamd64" { "x86_64-apple-darwin" } \ else if\ diff --git a/rust-toolchain b/rust-toolchain index 79f25d0..089d189 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.84.0 \ No newline at end of file +1.94.0 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7e2b264..6a5b383 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,9 @@ #![deny(unused_crate_dependencies)] mod args; -mod plot; mod reporter; use clap::Parser; -use plot::generate_plot; use std::env; use std::process::Command; use std::process::Stdio; @@ -145,149 +143,4 @@ fn main() { h1.join().unwrap(); h0.join().unwrap(); - - generate_plot(&args.report_path, &reporter.rows.read().unwrap()).unwrap(); } - -// fn main() { -// let report_file = cfg::get_report_file_path(); -// let polling_interval = cfg::get_polling_interval(); -// let target_pid = cfg::get_pid(); -// let mem_units = cfg::get_mem_units(); -// let time_units = cfg::get_time_units(); -// let columns = cfg::get_columns(); - -// let mut handles = Vec::>::new(); -// let duration: Arc>> = Arc::new(Mutex::new(None)); -// let (sender, receiver) = channel::<(u32, Option)>(); - -// let buffer = Arc::new(Mutex::new(Vec::::new())); - -// if report_file.to_string() != "stdout" { -// println!("[procmon] Polling: {:?}", polling_interval); -// println!("[procmon] Report: {}", report_file.to_string()); -// } - -// // Monitor -// { -// let report_file = report_file.clone(); -// let buffer = buffer.clone(); -// let columns = columns.clone(); -// let time_units = time_units.clone(); - -// handles.push(thread::spawn(move || { -// let (pid, start_time) = receiver.recv().unwrap(); -// // Please note that we use "new_all" to ensure that all list of -// // components, network interfaces, disks and users are already filled! - -// if report_file.to_string() != "stdout" { -// println!("[procmon] PID: {}", pid); -// } - -// let mut sys = System::new_all(); -// let pid = Pid::from(pid as usize); - -// buffer.lock().unwrap().push(columns.get_header()); - -// while sys.refresh_process(pid) { -// let now = SystemTime::now() -// .duration_since(UNIX_EPOCH) -// .expect("Can't get the time"); - -// let p = sys.process(pid).expect("Can't get process info"); - -// let disk = p.disk_usage(); -// let memory = p.memory() / mem_units as u64; -// let cpu = (p.cpu_usage()).round() as u64; - -// let start_time_ms = match start_time { -// Some(t) => t.as_millis(), -// None => (p.start_time() * 1000) as u128, -// }; - -// let run_time = match time_units { -// cfg::TimeUnits::Seconds => (now.as_millis() - start_time_ms) as f64 / 1000 as f64, -// cfg::TimeUnits::Milliseconds => (now.as_millis() - start_time_ms) as f64, -// }; - -// buffer.lock().unwrap().push(columns.new_string( -// run_time, -// cpu, -// memory, -// disk.written_bytes, -// disk.read_bytes, -// )); - -// thread::sleep(polling_interval); -// } -// })); -// } - -// // Process -// { -// let duration = duration.clone(); - -// handles.push(thread::spawn(move || { -// if let Some(pid) = target_pid { -// println!("[procmon] Using existing PID"); -// sender.send((pid, None)).unwrap(); -// return; -// } - -// let args: Vec = env::args().collect(); -// let command_segments_raw = &args[1..]; -// let mut segments: Vec = command_segments_raw.to_vec(); - -// let first = segments.remove(0); - -// let mut command = Command::new(first); -// command.args(segments); - -// command.current_dir(env::current_dir().unwrap()); - -// command.stdout(Stdio::inherit()); -// command.stdin(Stdio::inherit()); -// command.stderr(Stdio::inherit()); - -// let start_time = SystemTime::now() -// .duration_since(UNIX_EPOCH) -// .expect("Can't get the time"); - -// let mut child = command.spawn().unwrap(); -// sender.send((child.id(), Some(start_time))).unwrap(); -// child.wait().unwrap(); - -// let end_time = SystemTime::now() -// .duration_since(UNIX_EPOCH) -// .expect("Can't get the time"); - -// let time_elapsed = end_time - start_time; -// let mut d = duration.lock().unwrap(); -// *d = Some(time_elapsed); -// })); -// } - -// for handle in handles { -// handle.join().unwrap(); -// } - -// let duration = duration.lock().unwrap().unwrap(); -// let total_duration = match time_units { -// cfg::TimeUnits::Seconds => duration.as_secs() as f64, -// cfg::TimeUnits::Milliseconds => duration.as_millis() as f64, -// }; - -// buffer -// .lock() -// .unwrap() -// .push(columns.new_string(total_duration as f64, 0, 0, 0, 0)); -// fs::write(report_file.to_string(), buffer.lock().unwrap().join("\n")).unwrap(); - -// println!("[procmon] Report: {}", report_file.to_string()); - -// if duration.as_secs() < 10 { -// println!("[procmon] Duration: {}ms", duration.as_millis()); -// } else { -// println!("[procmon] Duration: {}s", duration.as_secs()); -// } -// } diff --git a/src/plot.rs b/src/plot.rs deleted file mode 100644 index 6b35342..0000000 --- a/src/plot.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::error::Error; - -use plotters::prelude::*; - -use crate::reporter::Row; - -pub fn generate_plot( - report_path: &std::path::Path, - rows: &[Row], -) -> Result<(), Box> { - let output_path = report_path.join("report.png"); - - let data_x_axis = rows - .iter() - .map(|r| r.time.as_millis()) - .collect::>(); - let data_x_max = *data_x_axis.iter().max().unwrap(); - - let data_cpu = rows - .iter() - .map(|r| r.cpu.unwrap() as usize) - .collect::>(); - let data_y_axis_cpu_max = *data_cpu.iter().max().unwrap() as u64; - let data_y_axis_cpu = rows - .iter() - .map(|r| (r.time.as_millis(), r.cpu.unwrap())) - .collect::>(); - - let data_mem = rows - .iter() - .map(|r| (r.memory.unwrap() / 1024) as usize) - .collect::>(); - let data_y_axis_mem_max = *data_mem.iter().max().unwrap() as u64; - let data_y_axis_mem = rows - .iter() - .map(|r| (r.time.as_millis(), (r.memory.unwrap() / 1024))) - .collect::>(); - let root = BitMapBackend::new(&output_path, (1920, 1080)).into_drawing_area(); - - root.fill(&WHITE)?; - - let mut chart = ChartBuilder::on(&root) - .margin(10) - .set_label_area_size(LabelAreaPosition::Left, 60) - .set_label_area_size(LabelAreaPosition::Right, 60) - .set_label_area_size(LabelAreaPosition::Bottom, 40) - .build_cartesian_2d(0..data_x_max, 0..data_y_axis_cpu_max)? - .set_secondary_coord(0..data_x_max, 0..data_y_axis_mem_max); - - chart - .configure_secondary_axes() - .y_desc("Ram Usage (mb)") - .y_label_formatter(&|v| format!("{}", v / 1024)) - .draw()?; - - chart - .configure_mesh() - .x_desc("Time (ms)".to_string()) - .disable_x_mesh() - .disable_y_mesh() - .x_labels(round_up(data_x_max as usize) / 1000) - .x_label_formatter(&|v| format!("{}", v / 1000)) - .y_label_formatter(&|v| format!("{} %", v)) - .y_desc("CPU Usage") - .draw()?; - - chart.draw_series(LineSeries::new(data_y_axis_cpu, &BLUE))?; - - if data_x_max > 2000 { - chart - .draw_secondary_series(LineSeries::new(data_y_axis_mem, &RED))? - .label(format!("Time: {:.2} s", data_x_max as f64 / 1000.0)); - } else { - chart - .draw_secondary_series(LineSeries::new(data_y_axis_mem, &RED))? - .label(format!("Time: {} ms", data_x_max)); - } - - chart - .configure_series_labels() - .background_style(RGBColor(128, 128, 128)) - .draw()?; - - // To avoid the IO failure being ignored silently, we manually call the present function - root.present().expect("Unable to write result to file, please make sure 'plotters-doc-data' dir exists under current dir"); - Ok(()) -} - -fn round_up(i: usize) -> usize { - let mut c = i; - loop { - if c.is_multiple_of(1000) { - return c; - } - c += 1; - } -} From 9b7010dff2dacac43b930ee502a419cc423df78e Mon Sep 17 00:00:00 2001 From: David Alsh Date: Mon, 9 Mar 2026 11:24:29 +1100 Subject: [PATCH 2/2] Tidy up and musl --- .github/report.jpg | Bin 45906 -> 0 bytes .github/workflows/main.yaml | 4 +- Cargo.lock | 339 ++++++++++++++++-------------------- Cargo.toml | 10 +- README.md | 41 ++--- src/args.rs | 8 +- src/main.rs | 58 +++--- src/reporter.rs | 174 +++++++++--------- x.sh | 8 - 9 files changed, 294 insertions(+), 348 deletions(-) delete mode 100644 .github/report.jpg delete mode 100644 x.sh diff --git a/.github/report.jpg b/.github/report.jpg deleted file mode 100644 index e06aa321ff63c69c8d802bfec7e1a5594552dce0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45906 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 Output path for the generated report [env: PM_REPORT=] [default: procmon.csv] + -i, --interval How often to probe the process for details in milliseconds [env: PM_INTERVAL=] [default: 200] + -m, --mem-units What units to use for recording memory [env: PM_MEM_UNITS=] [default: mb] [possible values: mb, kb, b] + -t, --time-units What units to use for recording time [env: PM_TIME_UNITS=] [default: ms] [possible values: s, ms] + -o, --override-report Override report file if exists + --no-cpu Don't measure CPU usage [env: PM_NO_CPU=] + --no-memory Don't measure memory usage [env: PM_NO_MEMORY=] + --no-disk Don't measure disk usage [env: PM_NO_DISK=] + -h, --help Print help ``` +# Usage + ```bash procmon -- node -e "setTimeout(() => console.log('Sup'), 4000)" ``` -This will produce a file named something similar to `report.csv` +This will produce a file named `procmon.csv` ```bash -procmon --report something -- node -e "setTimeout(() => console.log('Sup'), 4000)" +procmon --report something.csv -- node -e "setTimeout(() => console.log('Sup'), 4000)" ``` -This will produce a folder containing the report - -``` -/something - report.csv - report.png -``` +This will produce a report called `something.csv` The csv file looks like: -``` +```javascript time_s,cpu,memory_mb,disk_read,disk_write 0.000,0,0,0,0 0.065,0,16,5976064,0 @@ -41,10 +50,6 @@ time_s,cpu,memory_mb,disk_read,disk_write 4.145,0,0,0,0 ``` -The png looks like: - - - # Installation ## MacOS @@ -81,10 +86,6 @@ mkdir -p $HOME/.local/procmon curl -L --url https://github.com/alshdavid/procmon/releases/latest/download/linux-amd64.tar.gz | tar -xvzf - -C $HOME/.local/procmon ``` -## Windows - -I'm not good at PowerShell - follow the same steps as the Linux/MacOS scripts - ## Credit [Matt Jones](https://github.com/mattcompiles) diff --git a/src/args.rs b/src/args.rs index 4266397..c98f7a6 100644 --- a/src/args.rs +++ b/src/args.rs @@ -13,11 +13,11 @@ pub struct Args { pub command: Vec, /// Output path for the generated report - #[arg(short = 'r', long = "report", env = "PM_REPORT", value_parser = parse_file_path, default_value = "procmon")] + #[arg(short = 'r', long = "report", env = "PM_REPORT", value_parser = parse_file_path, default_value = "procmon.csv")] pub report_path: PathBuf, /// How often to probe the process for details in milliseconds - #[arg(short = 'i', long = "interval", env = "PM_INTERVAL", value_parser = parse_duration, default_value = "250")] + #[arg(short = 'i', long = "interval", env = "PM_INTERVAL", value_parser = parse_duration, default_value = "200")] pub poll_interval: time::Duration, /// What units to use for recording memory @@ -41,8 +41,8 @@ pub struct Args { pub time_units: TimeUnits, /// Override report file if exists - #[arg(long = "no-override-report")] - pub no_override_report: bool, + #[arg(short = 'o', long = "override-report")] + pub override_report: bool, /// Don't measure CPU usage #[arg(long = "no-cpu", env = "PM_NO_CPU")] diff --git a/src/main.rs b/src/main.rs index 6a5b383..1bc0bc1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod args; mod reporter; +use anyhow::Context; use clap::Parser; use std::env; use std::process::Command; @@ -21,12 +22,12 @@ use crate::reporter::Columns; use crate::reporter::Reporter; use crate::reporter::Row; -fn main() { +fn main() -> anyhow::Result<()> { let args = Args::parse(); let reporter = match Reporter::new( &args.report_path, - &!args.no_override_report, + &args.override_report, Columns { cpu: !args.no_measure_cpu, memory: !args.no_measure_mem, @@ -36,20 +37,19 @@ fn main() { ) { Ok(v) => v, Err(msg) => { - println!("Error: {}", msg); - return; + return Err(anyhow::anyhow!("Error: {}", msg)); } }; let (sender, receiver) = channel::<(u32, Duration)>(); // Monitor - let h0 = { + let h0: thread::JoinHandle> = { let command = args.clone(); let reporter = reporter.clone(); thread::spawn(move || { - let (pid, start_time) = receiver.recv().unwrap(); + let (pid, start_time) = receiver.recv()?; let pid = Pid::from(pid as usize); // Please note that we use "new_all" to ensure that all list of @@ -60,39 +60,39 @@ fn main() { if sys.refresh_processes(ProcessesToUpdate::Some(&[pid]), true) == 0 { break; } - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Can't get the time"); + let info = sys.process(pid).context("Unable to get process")?; - let p = sys.process(pid).expect("Can't get process info"); + let now = SystemTime::now().duration_since(UNIX_EPOCH)?; let mut row = Row::default(); if !command.no_measure_cpu { - let cpu = p.cpu_usage(); + let cpu = info.cpu_usage(); row.cpu = Some(cpu.round() as u64); } if !command.no_measure_mem { - let memory = p.memory(); + let memory = info.memory(); row.memory = Some(memory); } if !command.no_measure_disk { - let disk = p.disk_usage(); + let disk = info.disk_usage(); row.disk_read = Some(disk.read_bytes); row.disk_write = Some(disk.written_bytes); } row.time = now - start_time; - reporter.write(row); + reporter.write(row)?; thread::sleep(command.poll_interval); } + + Ok(()) }) }; // Process - let h1 = { + let h1: thread::JoinHandle> = { let mut args = args.clone(); let reporter = reporter.clone(); @@ -103,7 +103,7 @@ fn main() { let mut command = Command::new(first); command.args(args.command); - command.current_dir(env::current_dir().unwrap()); + command.current_dir(env::current_dir()?); command.stdout(Stdio::inherit()); command.stdin(Stdio::inherit()); @@ -111,9 +111,7 @@ fn main() { command }; - let start_time = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Can't get the time"); + let start_time = SystemTime::now().duration_since(UNIX_EPOCH)?; reporter.write(Row { time: Duration::from_millis(0), @@ -121,15 +119,13 @@ fn main() { cpu: Some(0), disk_read: Some(0), disk_write: Some(0), - }); + })?; - let mut child = command.spawn().unwrap(); - sender.send((child.id(), start_time)).unwrap(); - child.wait().unwrap(); + let mut child = command.spawn()?; + sender.send((child.id(), start_time))?; + child.wait()?; - let end_time = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Can't get the time"); + let end_time = SystemTime::now().duration_since(UNIX_EPOCH)?; reporter.write(Row { time: end_time - start_time, @@ -137,10 +133,14 @@ fn main() { cpu: Some(0), disk_read: Some(0), disk_write: Some(0), - }); + })?; + + Ok(()) }) }; - h1.join().unwrap(); - h0.join().unwrap(); + h1.join().unwrap()?; + h0.join().unwrap()?; + + Ok(()) } diff --git a/src/reporter.rs b/src/reporter.rs index b26f33e..10e4391 100644 --- a/src/reporter.rs +++ b/src/reporter.rs @@ -1,10 +1,9 @@ -use std::fs; -use std::fs::File; use std::fs::OpenOptions; use std::io::Write; use std::path::Path; -use std::sync::Arc; -use std::sync::RwLock; +use std::sync::mpsc::Sender; +use std::sync::mpsc::channel; +use std::thread::JoinHandle; use std::time::Duration; use crate::args::Args; @@ -26,122 +25,117 @@ pub struct Row { pub disk_write: Option, } +#[derive(Clone)] pub struct Reporter { - report_file: File, - columns: Columns, - args: Args, - pub rows: RwLock>, + tx_writer: Sender, } impl Reporter { pub fn new( - report_folder: &Path, + report_file: &Path, override_report: &bool, columns: Columns, args: Args, - ) -> Result, String> { - if report_folder.exists() { - if *override_report { - fs::remove_dir_all(report_folder).unwrap(); - } else { - return Err(format!( - "Report folder {} already exists", - report_folder.to_str().unwrap() - )); - } + ) -> anyhow::Result { + if !override_report && std::fs::exists(&report_file)? { + return Err(anyhow::anyhow!("Report {:?} already exists", report_file)); } - fs::create_dir(report_folder).unwrap(); + if std::fs::exists(&report_file)? { + std::fs::remove_file(&report_file)?; + } - let report_file = OpenOptions::new() + if let Some(parent_dir) = report_file.parent() + && !std::fs::exists(parent_dir)? + { + std::fs::create_dir_all(parent_dir)?; + } + + let mut report_file = OpenOptions::new() .append(true) .create_new(true) - .open(report_folder.join("report.csv")) - .unwrap(); + .open(report_file)?; - let mut header = vec![format!("time_{}", args.time_units.get_unit())]; - if columns.cpu { - header.push("cpu".to_string()); - } - if columns.memory { - header.push(format!("memory_{}", args.mem_units.get_unit())); - } - if columns.disk { - header.push("disk_read".to_string()); - header.push("disk_write".to_string()); - } + let (tx_writer, rx_write) = channel::(); - let reporter = Self { - report_file, - columns, - args, - rows: RwLock::new(vec![]), - }; + // Sender will fail if the thread dies, don't worry about propagating error message for now + let _h0: JoinHandle> = std::thread::spawn(move || { + let mut header = vec![format!("time_{}", args.time_units.get_unit())]; + if columns.cpu { + header.push("cpu".to_string()); + } + if columns.memory { + header.push(format!("memory_{}", args.mem_units.get_unit())); + } + if columns.disk { + header.push("disk_read".to_string()); + header.push("disk_write".to_string()); + } - reporter.write_to_report_file(&header); + report_file.write_all(format!("{}\n", header.join(",")).as_bytes())?; - Ok(Arc::new(reporter)) - } + while let Ok(row) = rx_write.recv() { + let mut line = vec![]; - pub fn write( - &self, - row: Row, - ) { - let mut line = vec![]; + line.push({ + match args.time_units { + crate::args::TimeUnits::S => format!("{:.3}", args.time_units.to_f64(row.time)), + crate::args::TimeUnits::Ms => format!("{}", row.time.as_millis()), + } + }); - line.push({ - match self.args.time_units { - crate::args::TimeUnits::S => format!("{:.3}", self.args.time_units.to_f64(row.time)), - crate::args::TimeUnits::Ms => format!("{}", row.time.as_millis()), - } - }); + if columns.cpu { + if let Some(cpu) = row.cpu { + line.push(format!("{}", cpu)); + } + } - if self.columns.cpu { - if let Some(cpu) = row.cpu { - line.push(format!("{}", cpu)); - } - } + if columns.memory { + if let Some(memory) = row.memory { + match args.mem_units { + MemoryUnits::Mb => { + line.push(format!("{}", memory / 1048576_u64)); + } + MemoryUnits::Kb => { + line.push(format!("{}", memory / 1024_u64)); + } + MemoryUnits::B => { + line.push(format!("{}", memory)); + } + } + } + } - if self.columns.memory { - if let Some(memory) = row.memory { - match self.args.mem_units { - MemoryUnits::Mb => { - line.push(format!("{}", memory / 1048576_u64)); + if columns.disk { + if let Some(disk_read) = row.disk_read { + line.push(format!("{}", disk_read)); } - MemoryUnits::Kb => { - line.push(format!("{}", memory / 1024_u64)); + if let Some(disk_write) = row.disk_write { + line.push(format!("{}", disk_write)); } - MemoryUnits::B => { - line.push(format!("{}", memory)); + } + + let mut output = String::new(); + for (i, col) in line.iter().enumerate() { + output.push_str(&col.to_string()); + if i != line.len() - 1 { + output.push(','); } } - } - } - if self.columns.disk { - if let Some(disk_read) = row.disk_read { - line.push(format!("{}", disk_read)); + report_file.write_all(format!("{}\n", output).as_bytes())?; } - if let Some(disk_write) = row.disk_write { - line.push(format!("{}", disk_write)); - } - } - self.rows.write().unwrap().push(row); - self.write_to_report_file(&line); + Ok(()) + }); + + Ok(Self { tx_writer }) } - fn write_to_report_file( + pub fn write( &self, - text: &[String], - ) { - let mut output = String::new(); - for (i, col) in text.iter().enumerate() { - output.push_str(&col.to_string()); - if i != text.len() - 1 { - output.push(','); - } - } - writeln!(&self.report_file, "{}", output).expect("Unable to write to file"); + row: Row, + ) -> anyhow::Result<()> { + Ok(self.tx_writer.send(row)?) } } diff --git a/x.sh b/x.sh deleted file mode 100644 index f40c5f7..0000000 --- a/x.sh +++ /dev/null @@ -1,8 +0,0 @@ -LAST_TAG=$(curl -sSL https://api.github.com/repos/alshdavid/procmon/releases/latest | jq ".tag_name" | cut -d "." -f 3) -if [ "$LAST_TAG" = "" ]; then - LAST_TAG="0" -fi -declare -i var="$LAST_TAG" -var=$var+1 -TAG="0.0.$var" -echo $TAG \ No newline at end of file