Repository: uutils/procps
Pull request #362 (2025-03-23 16:00:53)

tload: Basic implementation of `tload`
This pr add `tload` command with `modern` tui.

The `legacy` tui will be implemented in another pr.

And also our fresh look:

[![asciicast](https://asciinema.org/a/z8obn9c5JlNFd8HIHGx6YdYh4.svg)](https://asciinema.org/a/z8obn9c5JlNFd8HIHGx6YdYh4)


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 613f483..1b7c277 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11,6 +11,12 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "allocator-api2"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+
 [[package]]
 name = "android-tzdata"
 version = "0.1.1"
@@ -104,6 +110,21 @@ version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a3c8f83209414aacf0eeae3cf730b18d6981697fba62f200fcfb92b9f082acba"
 
+[[package]]
+name = "cassowary"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
+
+[[package]]
+name = "castaway"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
+dependencies = [
+ "rustversion",
+]
+
 [[package]]
 name = "cc"
 version = "1.0.92"
@@ -208,6 +229,20 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 
+[[package]]
+name = "compact_str"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
+dependencies = [
+ "castaway",
+ "cfg-if",
+ "itoa",
+ "rustversion",
+ "ryu",
+ "static_assertions",
+]
+
 [[package]]
 name = "core-foundation-sys"
 version = "0.8.7"
@@ -239,6 +274,31 @@ version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
 
+[[package]]
+name = "crossterm"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
+dependencies = [
+ "bitflags",
+ "crossterm_winapi",
+ "mio",
+ "parking_lot",
+ "rustix 0.38.40",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "csv"
 version = "1.3.0"
@@ -260,6 +320,41 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "darling"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "deranged"
 version = "0.3.11"
@@ -320,6 +415,12 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
 
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
 [[package]]
 name = "errno"
 version = "0.3.10"
@@ -336,6 +437,18 @@ version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
 
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
 [[package]]
 name = "getrandom"
 version = "0.2.14"
@@ -365,6 +478,23 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
 [[package]]
 name = "hermit-abi"
 version = "0.3.9"
@@ -394,6 +524,32 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "indoc"
+version = "2.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
+
+[[package]]
+name = "instability"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac48900be4ab1c0dd6f1c2553d86ef371eda69c52b97ffd22af3e4f0a1771eb8"
+dependencies = [
+ "darling",
+ "indoc",
+ "pretty_assertions",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "is-terminal"
 version = "0.4.12"
@@ -405,6 +561,15 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.11"
@@ -454,18 +619,49 @@ version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9"
 
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
 [[package]]
 name = "log"
 version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
 
+[[package]]
+name = "lru"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
+dependencies = [
+ "hashbrown",
+]
+
 [[package]]
 name = "memchr"
 version = "2.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
 
+[[package]]
+name = "mio"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
+dependencies = [
+ "libc",
+ "log",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "nix"
 version = "0.29.0"
@@ -529,7 +725,30 @@ version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a6229bad892b46b0dcfaaeb18ad0d2e56400f5aaea05b768bde96e73676cf75"
 dependencies = [
- "unicode-width 0.1.11",
+ "unicode-width 0.1.14",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -541,6 +760,12 @@ dependencies = [
  "regex",
 ]
 
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
 [[package]]
 name = "phf"
 version = "0.11.3"
@@ -618,7 +843,7 @@ dependencies = [
  "is-terminal",
  "lazy_static",
  "term",
- "unicode-width 0.1.11",
+ "unicode-width 0.1.14",
 ]
 
 [[package]]
@@ -659,6 +884,7 @@ dependencies = [
  "uu_slabtop",
  "uu_snice",
  "uu_sysctl",
+ "uu_tload",
  "uu_top",
  "uu_w",
  "uu_watch",
@@ -721,6 +947,27 @@ dependencies = [
  "zerocopy 0.8.14",
 ]
 
+[[package]]
+name = "ratatui"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
+dependencies = [
+ "bitflags",
+ "cassowary",
+ "compact_str",
+ "crossterm",
+ "indoc",
+ "instability",
+ "itertools",
+ "lru",
+ "paste",
+ "strum",
+ "unicode-segmentation",
+ "unicode-truncate",
+ "unicode-width 0.2.0",
+]
+
 [[package]]
 name = "rayon"
 version = "1.10.0"
@@ -741,6 +988,15 @@ dependencies = [
  "crossbeam-utils",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "redox_users"
 version = "0.4.5"
@@ -843,6 +1099,12 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
 [[package]]
 name = "serde"
 version = "1.0.197"
@@ -863,12 +1125,48 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "signal-hook"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-mio"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "siphasher"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
 
+[[package]]
+name = "smallvec"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
+
 [[package]]
 name = "smawk"
 version = "0.3.2"
@@ -885,12 +1183,40 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
 [[package]]
 name = "strsim"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
+[[package]]
+name = "strum"
+version = "0.26.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
+dependencies = [
+ "strum_macros",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
 [[package]]
 name = "syn"
 version = "2.0.87"
@@ -1047,11 +1373,28 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
 
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "unicode-truncate"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
+dependencies = [
+ "itertools",
+ "unicode-segmentation",
+ "unicode-width 0.1.14",
+]
+
 [[package]]
 name = "unicode-width"
-version = "0.1.11"
+version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
 
 [[package]]
 name = "unicode-width"
@@ -1204,6 +1547,16 @@ dependencies = [
  "walkdir",
 ]
 
+[[package]]
+name = "uu_tload"
+version = "0.0.1"
+dependencies = [
+ "clap",
+ "crossterm",
+ "ratatui",
+ "uucore",
+]
+
 [[package]]
 name = "uu_top"
 version = "0.0.1"
diff --git a/Cargo.toml b/Cargo.toml
index af100e5..c518520 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,13 +30,14 @@ feat_common_core = [
     "pgrep",
     "pidof",
     "pidwait",
+    "pkill",
     "pmap",
     "ps",
     "pwdx",
     "slabtop",
     "snice",
-    "pkill",
     "sysctl",
+    "tload",
     "top",
     "w",
     "watch",
@@ -48,12 +49,14 @@ chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
 clap = { version = "4.5.4", features = ["wrap_help", "cargo"] }
 clap_complete = "4.5.2"
 clap_mangen = "0.2.20"
+crossterm = "0.28.1"
 libc = "0.2.154"
 nix = { version = "0.29", default-features = false, features = ["process"] }
 phf = "0.11.2"
 phf_codegen = "0.11.2"
 prettytable-rs = "0.10.0"
 rand = { version = "0.9.0", features = ["small_rng"] }
+ratatui = "0.29.0"
 regex = "1.10.4"
 sysinfo = "0.33.0"
 tempfile = "3.10.1"
@@ -80,13 +83,14 @@ free = { optional = true, version = "0.0.1", package = "uu_free", path = "src/uu
 pgrep = { optional = true, version = "0.0.1", package = "uu_pgrep", path = "src/uu/pgrep" }
 pidof = { optional = true, version = "0.0.1", package = "uu_pidof", path = "src/uu/pidof" }
 pidwait = { optional = true, version = "0.0.1", package = "uu_pidwait", path = "src/uu/pidwait" }
+pkill = { optional = true, version = "0.0.1", package = "uu_pkill", path = "src/uu/pkill" }
 pmap = { optional = true, version = "0.0.1", package = "uu_pmap", path = "src/uu/pmap" }
 ps = { optional = true, version = "0.0.1", package = "uu_ps", path = "src/uu/ps" }
 pwdx = { optional = true, version = "0.0.1", package = "uu_pwdx", path = "src/uu/pwdx" }
 slabtop = { optional = true, version = "0.0.1", package = "uu_slabtop", path = "src/uu/slabtop" }
 snice = { optional = true, version = "0.0.1", package = "uu_snice", path = "src/uu/snice" }
-pkill = { optional = true, version = "0.0.1", package = "uu_pkill", path = "src/uu/pkill" }
 sysctl = { optional = true, version = "0.0.1", package = "uu_sysctl", path = "src/uu/sysctl" }
+tload = { optional = true, version = "0.0.1", package = "uu_tload", path = "src/uu/tload" }
 top = { optional = true, version = "0.0.1", package = "uu_top", path = "src/uu/top" }
 w = { optional = true, version = "0.0.1", package = "uu_w", path = "src/uu/w" }
 watch = { optional = true, version = "0.0.1", package = "uu_watch", path = "src/uu/watch" }
diff --git a/README.md b/README.md
index efb62a3..9a33ad2 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ Ongoing:
 * `slabtop`: Displays detailed kernel slab cache information in real time.
 * `snice`: Changes the scheduling priority of a running process.
 * `sysctl`: Read or write kernel parameters at run-time.
+* `tload`: Prints a graphical representation of system load average to the terminal.
 * `top`: Displays real-time information about system processes.
 * `w`: Shows who is logged on and what they are doing.
 * `watch`: Executes a program periodically, showing output fullscreen.
@@ -30,7 +31,6 @@ Ongoing:
 TODO:
 * `hugetop`: Report hugepage usage of processes and the system as a whole.
 * `skill`: Sends a signal to processes based on criteria like user, terminal, etc.
-* `tload`: Prints a graphical representation of system load average to the terminal.
 * `vmstat`: Reports information about processes, memory, paging, block IO, traps, and CPU activity.
 
 Elsewhere:
diff --git a/src/uu/tload/Cargo.toml b/src/uu/tload/Cargo.toml
new file mode 100644
index 0000000..a1b584a
--- /dev/null
+++ b/src/uu/tload/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "uu_tload"
+version = "0.0.1"
+edition = "2021"
+authors = ["uutils developers"]
+license = "MIT"
+description = "tload ~ (uutils) graphic representation of system load average"
+
+homepage = "https://github.com/uutils/procps"
+repository = "https://github.com/uutils/procps/tree/main/src/uu/tload"
+keywords = ["acl", "uutils", "cross-platform", "cli", "utility"]
+categories = ["command-line-utilities"]
+
+[dependencies]
+clap = { workspace = true }
+crossterm = { workspace = true }
+ratatui = { workspace = true }
+uucore = { workspace = true }
+
+[lib]
+path = "src/tload.rs"
+
+[[bin]]
+name = "tload"
+path = "src/main.rs"
diff --git a/src/uu/tload/src/main.rs b/src/uu/tload/src/main.rs
new file mode 100644
index 0000000..5c5c627
--- /dev/null
+++ b/src/uu/tload/src/main.rs
@@ -0,0 +1,1 @@
+uucore::bin!(uu_tload);
diff --git a/src/uu/tload/src/tload.rs b/src/uu/tload/src/tload.rs
new file mode 100644
index 0000000..bd80530
--- /dev/null
+++ b/src/uu/tload/src/tload.rs
@@ -0,0 +1,172 @@
+// This file is part of the uutils procps package.
+//
+// For the full copyright and license information, please view the LICENSE
+// file that was distributed with this source code.
+
+use std::collections::VecDeque;
+use std::sync::{Arc, RwLock};
+use std::thread::{self, sleep};
+use std::time::Duration;
+
+use clap::{arg, crate_version, value_parser, ArgAction, ArgMatches, Command};
+use crossterm::event::{self, KeyCode, KeyEvent, KeyModifiers};
+use tui::{LegacyTui, ModernTui};
+use uucore::{error::UResult, format_usage, help_about, help_usage};
+
+const ABOUT: &str = help_about!("tload.md");
+const USAGE: &str = help_usage!("tload.md");
+
+mod tui;
+
+#[derive(Debug, Default, Clone)]
+struct SystemLoadAvg {
+    pub(crate) last_1: f32,
+    pub(crate) last_5: f32,
+    pub(crate) last_10: f32,
+}
+
+impl SystemLoadAvg {
+    #[cfg(target_os = "linux")]
+    fn new() -> UResult<SystemLoadAvg> {
+        use std::fs;
+        use uucore::error::USimpleError;
+
+        let result = fs::read_to_string("/proc/loadavg")?;
+        let split = result.split(" ").collect::<Vec<_>>();
+
+        // Helper function to keep code clean
+        fn f(s: &str) -> UResult<f32> {
+            s.parse::<f32>()
+                .map_err(|e| USimpleError::new(1, e.to_string()))
+        }
+
+        Ok(SystemLoadAvg {
+            last_1: f(split[0])?,
+            last_5: f(split[1])?,
+            last_10: f(split[2])?,
+        })
+    }
+
+    #[cfg(not(target_os = "linux"))]
+    fn new() -> UResult<SystemLoadAvg> {
+        Ok(SystemLoadAvg::default())
+    }
+}
+
+#[allow(unused)]
+#[derive(Debug)]
+struct Settings {
+    delay: u64,
+    scale: usize, // Not used
+
+    is_modern: bool, // For modern display
+}
+
+impl Settings {
+    fn new(matches: &ArgMatches) -> Settings {
+        Settings {
+            delay: matches.get_one("delay").cloned().unwrap(),
+            scale: matches.get_one("scale").cloned().unwrap(),
+            is_modern: matches.get_flag("modern"),
+        }
+    }
+}
+
+#[uucore::main]
+pub fn uumain(args: impl uucore::Args) -> UResult<()> {
+    let matches = uu_app().try_get_matches_from(args)?;
+    let settings = Settings::new(&matches);
+
+    let mut terminal = ratatui::init();
+
+    let data = {
+        // Why 10240?
+        //
+        // Emm, maybe there will be some terminal can display more than 10000 char?
+        let data = Arc::new(RwLock::new(VecDeque::with_capacity(10240)));
+        data.write()
+            .unwrap()
+            .push_back(SystemLoadAvg::new().unwrap());
+        data
+    };
+    let cloned_data = data.clone();
+    thread::spawn(move || loop {
+        sleep(Duration::from_secs(settings.delay));
+
+        let mut data = cloned_data.write().unwrap();
+        if data.iter().len() >= 10240 {
+            // Keep this VecDeque smaller than 10240
+            data.pop_front();
+        }
+        data.push_back(SystemLoadAvg::new().unwrap());
+    });
+
+    loop {
+        // Now only accept `Ctrl+C` for compatibility with the original implementation
+        //
+        // Use `event::poll` for non-blocking event reading
+        if let Ok(true) = event::poll(Duration::from_millis(10)) {
+            // If event available, break this loop
+            if let Ok(event::Event::Key(KeyEvent {
+                code: KeyCode::Char('c'),
+                modifiers: KeyModifiers::CONTROL,
+                ..
+            })) = event::read()
+            {
+                // compatibility with the original implementation
+                uucore::error::set_exit_code(130);
+                break;
+            }
+        }
+
+        terminal.draw(|frame| {
+            let data = &data.read().unwrap();
+            let data = data.iter().cloned().collect::<Vec<_>>();
+            frame.render_widget(
+                if settings.is_modern {
+                    ModernTui::new(&data)
+                } else {
+                    LegacyTui::new(&data)
+                },
+                frame.area(),
+            );
+        })?;
+
+        std::thread::sleep(Duration::from_millis(10));
+    }
+
+    ratatui::restore();
+    Ok(())
+}
+
+#[allow(clippy::cognitive_complexity)]
+pub fn uu_app() -> Command {
+    Command::new(uucore::util_name())
+        .version(crate_version!())
+        .about(ABOUT)
+        .override_usage(format_usage(USAGE))
+        .infer_long_args(true)
+        .args([
+            arg!(-d --delay     <secs>  "update delay in seconds")
+                .value_parser(value_parser!(u64))
+                .default_value("5")
+                .hide_default_value(true),
+            arg!(-m --modern            "modern look").action(ArgAction::SetTrue),
+            // TODO: Implement this arg
+            arg!(-s --scale <num>       "vertical scale")
+                .value_parser(value_parser!(usize))
+                .default_value("5")
+                .hide_default_value(true),
+        ])
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    // It's just a test to make sure if can parsing correctly.
+    #[test]
+    fn test_system_load_avg() {
+        let _ = SystemLoadAvg::new().expect("SystemLoadAvg::new");
+    }
+}
diff --git a/src/uu/tload/src/tui.rs b/src/uu/tload/src/tui.rs
new file mode 100644
index 0000000..62ebdc3
--- /dev/null
+++ b/src/uu/tload/src/tui.rs
@@ -0,0 +1,119 @@
+// This file is part of the uutils procps package.
+//
+// For the full copyright and license information, please view the LICENSE
+// file that was distributed with this source code.
+
+use ratatui::{
+    buffer::Buffer,
+    layout::{Constraint, Direction, Layout, Rect},
+    style::{Style, Stylize},
+    symbols::Marker,
+    text::{Line, Text},
+    widgets::{Axis, Block, Borders, Chart, Dataset, GraphType, Paragraph, Widget},
+};
+
+use crate::SystemLoadAvg;
+
+pub(crate) struct ModernTui<'a>(&'a [SystemLoadAvg]);
+
+impl ModernTui<'_> {
+    fn render_header(&self, area: Rect, buf: &mut Buffer) {
+        let text = Text::from(vec![
+            Line::from(format!(
+                "Last 1 min load:   {:>5}",
+                self.0.last().unwrap().last_1
+            )),
+            Line::from(format!(
+                "Last 5 min load:   {:>5}",
+                self.0.last().unwrap().last_5
+            )),
+            Line::from(format!(
+                "Last 10 min load:  {:>5}",
+                self.0.last().unwrap().last_10
+            )),
+        ]);
+
+        Paragraph::new(text)
+            .style(Style::default().bold().italic())
+            .block(
+                Block::new()
+                    .borders(Borders::ALL)
+                    .title("System load history"),
+            )
+            .render(area, buf);
+    }
+
+    fn render_chart(&self, area: Rect, buf: &mut Buffer) {
+        let result = &self.0[self.0.len().saturating_sub(area.width.into())..]
+            .iter()
+            .enumerate()
+            .map(|(index, load)| (index as f64, load.last_1 as f64))
+            .collect::<Vec<_>>();
+
+        let data = Dataset::default()
+            .graph_type(GraphType::Line)
+            .marker(Marker::Braille)
+            .data(result);
+
+        let x_axis = {
+            let start = Line::from("0");
+            let middle = Line::from((area.width / 2).to_string());
+            let end = Line::from(area.width.to_string());
+            Axis::default()
+                .title("Time(per delay)")
+                .bounds([0.0, area.width.into()])
+                .labels(vec![start, middle, end])
+        };
+
+        // Why this tweak?
+        //
+        // Sometime the chart cannot display all the line because of max height are equals the max
+        // load of system in the history, so I add 0.2*{max_load} to the height of chart make it
+        // display beautiful
+        let y_axis_upper_bound = result.iter().map(|it| it.1).reduce(f64::max).unwrap_or(0.0);
+        let y_axis_upper_bound = y_axis_upper_bound + y_axis_upper_bound * 0.2;
+        let label = {
+            let min = "0.0".to_owned();
+            let mid = format!("{:.1}", y_axis_upper_bound / 2.0);
+            let max = format!("{:.1}", y_axis_upper_bound);
+            vec![min, mid, max]
+        };
+        let y_axis = Axis::default()
+            .bounds([0.0, y_axis_upper_bound])
+            .labels(label)
+            .title("System Load");
+
+        Chart::new(vec![data])
+            .x_axis(x_axis)
+            .y_axis(y_axis)
+            .render(area, buf);
+    }
+}
+
+impl ModernTui<'_> {
+    pub(crate) fn new(input: &[SystemLoadAvg]) -> ModernTui<'_> {
+        ModernTui(input)
+    }
+}
+
+impl Widget for ModernTui<'_> {
+    fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
+    where
+        Self: Sized,
+    {
+        let layout = Layout::new(
+            Direction::Vertical,
+            [Constraint::Length(5), Constraint::Min(0)],
+        )
+        .split(area);
+
+        let header = layout[0];
+        let chart = layout[1];
+
+        self.render_header(header, buf);
+        self.render_chart(chart, buf);
+    }
+}
+
+// TODO: Implemented LegacyTui
+pub(crate) type LegacyTui<'a> = ModernTui<'a>;
diff --git a/src/uu/tload/tload.md b/src/uu/tload/tload.md
new file mode 100644
index 0000000..eea64d2
--- /dev/null
+++ b/src/uu/tload/tload.md
@@ -0,0 +1,7 @@
+# tload
+
+```
+tload [options] [tty]
+```
+
+tload prints a graph of the current system load average to the specified tty (or the tty of the tload process if none is specified).


Test patch:
diff --git a/tests/by-util/test_tload.rs b/tests/by-util/test_tload.rs
new file mode 100644
index 0000000..85ca6f4
--- /dev/null
+++ b/tests/by-util/test_tload.rs
@@ -0,0 +1,11 @@
+// This file is part of the uutils procps package.
+//
+// For the full copyright and license information, please view the LICENSE
+// file that was distributed with this source code.
+
+use crate::common::util::TestScenario;
+
+#[test]
+fn test_invalid_arg() {
+    new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
+}
diff --git a/tests/tests.rs b/tests/tests.rs
index 2749cba..62f7e81 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -60,3 +60,7 @@ mod test_pkill;
 #[cfg(feature = "sysctl")]
 #[path = "by-util/test_sysctl.rs"]
 mod test_sysctl;
+
+#[cfg(feature = "tload")]
+#[path = "by-util/test_tload.rs"]
+mod test_tload;