lsmem: Impl lsmem
This commit is contained in:
144
Cargo.lock
generated
144
Cargo.lock
generated
@@ -71,6 +71,12 @@ version = "2.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytecount"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -195,6 +201,12 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
@@ -212,6 +224,12 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@@ -235,6 +253,12 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -307,6 +331,17 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "papergrid"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7419ad52a7de9b60d33e11085a0fe3df1fbd5926aa3f93d3dd53afbc9e86725"
|
||||||
|
dependencies = [
|
||||||
|
"bytecount",
|
||||||
|
"fnv",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@@ -361,6 +396,30 @@ dependencies = [
|
|||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.78"
|
version = "1.0.78"
|
||||||
@@ -523,6 +582,44 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.205"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.205"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.58",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.122"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
@@ -541,6 +638,17 @@ version = "0.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.58"
|
version = "2.0.58"
|
||||||
@@ -566,6 +674,29 @@ dependencies = [
|
|||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tabled"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77c9303ee60b9bedf722012ea29ae3711ba13a67c9b9ae28993838b63057cb1b"
|
||||||
|
dependencies = [
|
||||||
|
"papergrid",
|
||||||
|
"tabled_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tabled_derive"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf0fb8bfdc709786c154e24a66777493fb63ae97e3036d914c8666774c477069"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.10.1"
|
version = "3.10.1"
|
||||||
@@ -683,6 +814,9 @@ name = "uu_lsmem"
|
|||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tabled",
|
||||||
"uucore",
|
"uucore",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -736,6 +870,12 @@ version = "0.0.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d841f8408028085ca65896cdd60b9925d4e407cb69989a64889f2bebbb51147b"
|
checksum = "d841f8408028085ca65896cdd60b9925d4e407cb69989a64889f2bebbb51147b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
@@ -803,7 +943,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -814,7 +954,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@@ -47,6 +47,9 @@ textwrap = { version = "0.16.0", features = ["terminal_size"] }
|
|||||||
xattr = "1.3.1"
|
xattr = "1.3.1"
|
||||||
tempfile = "3.9.0"
|
tempfile = "3.9.0"
|
||||||
rand = { version = "0.8", features = ["small_rng"] }
|
rand = { version = "0.8", features = ["small_rng"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0.122"
|
||||||
|
tabled = "0.16.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
@@ -56,7 +59,6 @@ uucore = { workspace = true }
|
|||||||
phf = { workspace = true }
|
phf = { workspace = true }
|
||||||
textwrap = { workspace = true }
|
textwrap = { workspace = true }
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" }
|
lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" }
|
||||||
lsmem = { optional = true, version = "0.0.1", package = "uu_lsmem", path = "src/uu/lsmem" }
|
lsmem = { optional = true, version = "0.0.1", package = "uu_lsmem", path = "src/uu/lsmem" }
|
||||||
|
@@ -13,3 +13,6 @@ path = "src/main.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
uucore = { workspace = true }
|
uucore = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
tabled = { workspace = true }
|
||||||
|
@@ -3,15 +3,535 @@
|
|||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
|
mod utils;
|
||||||
|
|
||||||
use clap::{crate_version, Command};
|
use clap::{crate_version, Command};
|
||||||
|
use clap::{Arg, ArgAction};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::fs;
|
||||||
|
use std::io::{self, BufRead, BufReader};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::str::FromStr;
|
||||||
use uucore::{error::UResult, format_usage, help_about, help_usage};
|
use uucore::{error::UResult, format_usage, help_about, help_usage};
|
||||||
|
|
||||||
|
use tabled::{
|
||||||
|
settings::{location::ByColumnName, object, Alignment, Disable, Modify, Style},
|
||||||
|
Table, Tabled,
|
||||||
|
};
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("lsmem.md");
|
const ABOUT: &str = help_about!("lsmem.md");
|
||||||
const USAGE: &str = help_usage!("lsmem.md");
|
const USAGE: &str = help_usage!("lsmem.md");
|
||||||
|
|
||||||
|
mod options {
|
||||||
|
pub const BYTES: &str = "bytes";
|
||||||
|
}
|
||||||
|
|
||||||
|
// const BUFSIZ: usize = 1024;
|
||||||
|
|
||||||
|
const PATH_SYS_MEMORY: &str = "/sys/devices/system/memory";
|
||||||
|
const PATH_BLOCK_SIZE_BYTES: &str = "/sys/devices/system/memory/block_size_bytes";
|
||||||
|
const PATH_VALID_ZONES: &str = "/sys/devices/system/memory/valid_zones";
|
||||||
|
const PATH_SUB_REMOVABLE: &str = "removable";
|
||||||
|
const PATH_SUB_STATE: &str = "state";
|
||||||
|
const NAME_MEMORY: &str = "memory";
|
||||||
|
|
||||||
|
// struct ColDesc {
|
||||||
|
// name: &'static str, // Rust's equivalent to `const char *`
|
||||||
|
// whint: f64, // Rust uses `f64` for double precision floating-point numbers
|
||||||
|
// flags: i32, // Using `i32` for integers
|
||||||
|
// help: &'static str, // Rust's equivalent to `const char *`
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
enum Columns {
|
||||||
|
#[serde(rename = "RANGE")]
|
||||||
|
Range,
|
||||||
|
#[serde(rename = "SIZE")]
|
||||||
|
Size,
|
||||||
|
#[serde(rename = "STATE")]
|
||||||
|
State,
|
||||||
|
#[serde(rename = "REMOVABLE")]
|
||||||
|
Removable,
|
||||||
|
#[serde(rename = "BLOCK")]
|
||||||
|
Block,
|
||||||
|
#[serde(rename = "NODE")]
|
||||||
|
Node,
|
||||||
|
#[serde(rename = "ZONES")]
|
||||||
|
Zones,
|
||||||
|
}
|
||||||
|
// const SCOLS_FL_RIGHT: i32 = 1;
|
||||||
|
|
||||||
|
// static COLDESCS: [ColDesc; 7] = [
|
||||||
|
// ColDesc {
|
||||||
|
// name: "RANGE",
|
||||||
|
// whint: 0.0,
|
||||||
|
// flags: 0,
|
||||||
|
// help: "start and end address of the memory range",
|
||||||
|
// },
|
||||||
|
// ColDesc {
|
||||||
|
// name: "SIZE",
|
||||||
|
// whint: 5.0,
|
||||||
|
// flags: SCOLS_FL_RIGHT,
|
||||||
|
// help: "size of the memory range",
|
||||||
|
// },
|
||||||
|
// ColDesc {
|
||||||
|
// name: "STATE",
|
||||||
|
// whint: 0.0,
|
||||||
|
// flags: SCOLS_FL_RIGHT,
|
||||||
|
// help: "online status of the memory range",
|
||||||
|
// },
|
||||||
|
// ColDesc {
|
||||||
|
// name: "REMOVABLE",
|
||||||
|
// whint: 0.0,
|
||||||
|
// flags: SCOLS_FL_RIGHT,
|
||||||
|
// help: "memory is removable",
|
||||||
|
// },
|
||||||
|
// ColDesc {
|
||||||
|
// name: "BLOCK",
|
||||||
|
// whint: 0.0,
|
||||||
|
// flags: SCOLS_FL_RIGHT,
|
||||||
|
// help: "memory block number or blocks range",
|
||||||
|
// },
|
||||||
|
// ColDesc {
|
||||||
|
// name: "NODE",
|
||||||
|
// whint: 0.0,
|
||||||
|
// flags: SCOLS_FL_RIGHT,
|
||||||
|
// help: "numa node of memory",
|
||||||
|
// },
|
||||||
|
// ColDesc {
|
||||||
|
// name: "ZONES",
|
||||||
|
// whint: 0.0,
|
||||||
|
// flags: SCOLS_FL_RIGHT,
|
||||||
|
// help: "valid zones for the memory range",
|
||||||
|
// },
|
||||||
|
// ];
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq, Clone, Copy)]
|
||||||
|
enum ZoneId {
|
||||||
|
#[serde(rename = "ZONE_DMA")]
|
||||||
|
ZoneDma,
|
||||||
|
#[serde(rename = "ZONE_DMA32")]
|
||||||
|
ZoneDma32,
|
||||||
|
#[serde(rename = "ZONE_NORMAL")]
|
||||||
|
ZoneNormal,
|
||||||
|
#[serde(rename = "ZONE_HIGHMEM")]
|
||||||
|
ZoneHighmem,
|
||||||
|
#[serde(rename = "ZONE_MOVABLE")]
|
||||||
|
ZoneMovable,
|
||||||
|
#[serde(rename = "ZONE_DEVICE")]
|
||||||
|
ZoneDevice,
|
||||||
|
#[serde(rename = "ZONE_NONE")]
|
||||||
|
ZoneNone,
|
||||||
|
#[serde(rename = "ZONE_UNKNOWN")]
|
||||||
|
ZoneUnknown,
|
||||||
|
#[serde(rename = "MAX_NR_ZONES")]
|
||||||
|
MaxNrZones,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
enum MemoryState {
|
||||||
|
Online,
|
||||||
|
Offline,
|
||||||
|
GoingOffline,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for MemoryState {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
MemoryState::Online => write!(f, "online"),
|
||||||
|
MemoryState::Offline => write!(f, "offline"),
|
||||||
|
MemoryState::GoingOffline => write!(f, "going-offline"),
|
||||||
|
MemoryState::Unknown => write!(f, "unknown"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for MemoryState {
|
||||||
|
type Err = ();
|
||||||
|
fn from_str(input: &str) -> Result<MemoryState, Self::Err> {
|
||||||
|
match input {
|
||||||
|
"online" => Ok(MemoryState::Online),
|
||||||
|
"offline" => Ok(MemoryState::Offline),
|
||||||
|
"going-offline" => Ok(MemoryState::GoingOffline),
|
||||||
|
"unknown" => Ok(MemoryState::Unknown),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct MemoryBlock {
|
||||||
|
index: u64,
|
||||||
|
count: u64,
|
||||||
|
state: MemoryState,
|
||||||
|
node: i32,
|
||||||
|
nr_zones: usize,
|
||||||
|
zones: [ZoneId; ZoneId::MaxNrZones as usize],
|
||||||
|
removable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryBlock {
|
||||||
|
fn new() -> Self {
|
||||||
|
MemoryBlock {
|
||||||
|
index: 0,
|
||||||
|
count: 0,
|
||||||
|
state: MemoryState::Unknown,
|
||||||
|
node: 0,
|
||||||
|
nr_zones: 0,
|
||||||
|
zones: [ZoneId::ZoneUnknown; ZoneId::MaxNrZones as usize],
|
||||||
|
removable: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Tabled, Default)]
|
||||||
|
struct TableRow {
|
||||||
|
#[tabled(rename = "RANGE")]
|
||||||
|
range: String,
|
||||||
|
#[tabled(rename = "SIZE")]
|
||||||
|
size: String,
|
||||||
|
#[tabled(rename = "STATE")]
|
||||||
|
state: String,
|
||||||
|
#[tabled(rename = "REMOVABLE")]
|
||||||
|
removable: String,
|
||||||
|
#[tabled(rename = "BLOCK")]
|
||||||
|
block: String,
|
||||||
|
#[tabled(rename = "NODE")]
|
||||||
|
node: String,
|
||||||
|
#[tabled(rename = "ZONES")]
|
||||||
|
zones: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Options {
|
||||||
|
have_nodes: bool,
|
||||||
|
// raw: bool,
|
||||||
|
// export: bool,
|
||||||
|
// json: bool,
|
||||||
|
// noheadings: bool,
|
||||||
|
// summary: bool,
|
||||||
|
list_all: bool,
|
||||||
|
bytes: bool,
|
||||||
|
want_summary: bool,
|
||||||
|
want_table: bool,
|
||||||
|
split_by_node: bool,
|
||||||
|
split_by_state: bool,
|
||||||
|
split_by_removable: bool,
|
||||||
|
split_by_zones: bool,
|
||||||
|
have_zones: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Lsmem {
|
||||||
|
ndirs: usize,
|
||||||
|
dirs: Vec<PathBuf>,
|
||||||
|
blocks: Vec<MemoryBlock>,
|
||||||
|
nblocks: usize,
|
||||||
|
block_size: u64,
|
||||||
|
mem_online: u64,
|
||||||
|
mem_offline: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lsmem {
|
||||||
|
fn new() -> Lsmem {
|
||||||
|
Lsmem {
|
||||||
|
ndirs: 0,
|
||||||
|
dirs: Vec::default(),
|
||||||
|
blocks: Vec::default(),
|
||||||
|
nblocks: 0,
|
||||||
|
block_size: 0,
|
||||||
|
mem_online: 0,
|
||||||
|
mem_offline: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Options {
|
||||||
|
fn new() -> Options {
|
||||||
|
Options {
|
||||||
|
have_nodes: false,
|
||||||
|
// raw: false,
|
||||||
|
// export: false,
|
||||||
|
// json: false,
|
||||||
|
// noheadings: false,
|
||||||
|
// summary: false,
|
||||||
|
list_all: false,
|
||||||
|
bytes: false,
|
||||||
|
want_summary: true, // default true
|
||||||
|
want_table: true, // default true
|
||||||
|
split_by_node: false,
|
||||||
|
split_by_state: false,
|
||||||
|
split_by_removable: false,
|
||||||
|
split_by_zones: false,
|
||||||
|
have_zones: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_info(lsmem: &mut Lsmem, opts: &mut Options) {
|
||||||
|
lsmem.block_size = u64::from_str_radix(
|
||||||
|
&read_file_content::<String>(Path::new(PATH_BLOCK_SIZE_BYTES)).unwrap(),
|
||||||
|
16,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
lsmem.dirs = get_block_paths();
|
||||||
|
lsmem.dirs.sort_by(|a, b| {
|
||||||
|
let filename_a = a.to_str().unwrap().split('/').last().unwrap();
|
||||||
|
let filename_b = b.to_str().unwrap().split('/').last().unwrap();
|
||||||
|
let idx_a: u64 = filename_a[NAME_MEMORY.len()..].parse().unwrap();
|
||||||
|
let idx_b: u64 = filename_b[NAME_MEMORY.len()..].parse().unwrap();
|
||||||
|
idx_a.cmp(&idx_b)
|
||||||
|
});
|
||||||
|
lsmem.ndirs = lsmem.dirs.len();
|
||||||
|
for path in lsmem.dirs.iter() {
|
||||||
|
if memory_block_get_node(path).is_ok() {
|
||||||
|
opts.have_nodes = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut p = path.clone();
|
||||||
|
p.push("valid_zones");
|
||||||
|
if fs::read_dir(p).is_ok() {
|
||||||
|
opts.have_zones = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.have_nodes && opts.have_zones {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..lsmem.ndirs {
|
||||||
|
let blk = memory_block_read_attrs(opts, &lsmem.dirs[i]);
|
||||||
|
if blk.state == MemoryState::Online {
|
||||||
|
lsmem.mem_online += lsmem.block_size;
|
||||||
|
} else {
|
||||||
|
lsmem.mem_offline += lsmem.block_size;
|
||||||
|
}
|
||||||
|
if is_mergeable(lsmem, opts, &blk) {
|
||||||
|
lsmem.blocks[lsmem.nblocks - 1].count += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lsmem.nblocks += 1;
|
||||||
|
lsmem.blocks.push(blk.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_block_paths() -> Vec<PathBuf> {
|
||||||
|
let mut paths = Vec::<PathBuf>::new();
|
||||||
|
for entry in fs::read_dir(PATH_SYS_MEMORY).unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
let path = entry.path();
|
||||||
|
let filename = path.to_str().unwrap().split('/').last().unwrap();
|
||||||
|
if path.is_dir() && filename.starts_with(NAME_MEMORY) {
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paths
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_mergeable(lsmem: &Lsmem, opts: &Options, blk: &MemoryBlock) -> bool {
|
||||||
|
if lsmem.nblocks == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let curr_block = &lsmem.blocks[lsmem.nblocks - 1];
|
||||||
|
if opts.list_all {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if curr_block.index + curr_block.count != blk.index {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if opts.split_by_state && curr_block.state != blk.state {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if opts.split_by_removable && curr_block.removable != blk.removable {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if opts.split_by_node && opts.have_nodes && (curr_block.node != blk.node) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if opts.split_by_zones && opts.have_zones {
|
||||||
|
if curr_block.nr_zones != blk.nr_zones {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..curr_block.nr_zones {
|
||||||
|
if curr_block.zones[i] == ZoneId::ZoneUnknown || curr_block.zones[i] != blk.zones[i] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memory_block_get_node(path: &PathBuf) -> Result<i32, <i32 as FromStr>::Err> {
|
||||||
|
for entry in fs::read_dir(path).unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
let path = entry.path();
|
||||||
|
let filename = path.to_str().unwrap().split('/').last().unwrap();
|
||||||
|
if path.is_dir() && filename.starts_with("node") {
|
||||||
|
return filename["node".len()..].parse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memory_block_read_attrs(opts: &Options, path: &PathBuf) -> MemoryBlock {
|
||||||
|
let mut blk = MemoryBlock::new();
|
||||||
|
blk.count = 1;
|
||||||
|
blk.state = MemoryState::Unknown;
|
||||||
|
let filename = path.to_str().unwrap().split('/').last().unwrap();
|
||||||
|
blk.index = filename[NAME_MEMORY.len()..].parse().unwrap();
|
||||||
|
|
||||||
|
let mut removable_path = path.clone();
|
||||||
|
removable_path.push(PATH_SUB_REMOVABLE);
|
||||||
|
blk.removable = read_file_content::<i32>(&removable_path).is_ok();
|
||||||
|
|
||||||
|
let mut state_path = path.clone();
|
||||||
|
state_path.push(PATH_SUB_STATE);
|
||||||
|
if let Ok(state_raw) = read_file_content::<String>(&state_path) {
|
||||||
|
blk.state = MemoryState::from_str(&state_raw).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.have_nodes {
|
||||||
|
blk.node = memory_block_get_node(path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
blk.nr_zones = 0;
|
||||||
|
if opts.have_zones {
|
||||||
|
if let Ok(raw_content) = read_file_content::<String>(Path::new(PATH_VALID_ZONES)) {
|
||||||
|
let zone_toks = raw_content.split(' ').collect::<Vec<&str>>();
|
||||||
|
for (i, zone_tok) in zone_toks
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.take(std::cmp::min(zone_toks.len(), ZoneId::MaxNrZones as usize))
|
||||||
|
{
|
||||||
|
blk.zones[i] = serde_json::from_str(zone_tok).unwrap();
|
||||||
|
blk.nr_zones += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blk
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_table(lsmem: &Lsmem, opts: &Options) -> tabled::Table {
|
||||||
|
let mut table = Vec::<TableRow>::new();
|
||||||
|
|
||||||
|
for i in 0..lsmem.nblocks {
|
||||||
|
let mut row = TableRow::default();
|
||||||
|
|
||||||
|
let blk = lsmem.blocks[i].borrow();
|
||||||
|
|
||||||
|
// Range
|
||||||
|
let start = blk.index * lsmem.block_size;
|
||||||
|
let size = blk.count * lsmem.block_size;
|
||||||
|
row.range = format!("0x{:016x}-0x{:016x}", start, start + size - 1);
|
||||||
|
|
||||||
|
// Size
|
||||||
|
row.size = if opts.bytes {
|
||||||
|
format!("{}", blk.count * lsmem.block_size)
|
||||||
|
} else {
|
||||||
|
utils::size_to_human_string(blk.count * lsmem.block_size)
|
||||||
|
};
|
||||||
|
|
||||||
|
// State
|
||||||
|
row.state = match blk.state {
|
||||||
|
MemoryState::Online => MemoryState::Online.to_string(),
|
||||||
|
MemoryState::Offline => MemoryState::Offline.to_string(),
|
||||||
|
MemoryState::GoingOffline => MemoryState::GoingOffline.to_string(),
|
||||||
|
MemoryState::Unknown => "?".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Removable
|
||||||
|
row.removable = if blk.removable {
|
||||||
|
"yes".to_string()
|
||||||
|
} else {
|
||||||
|
"no".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Block
|
||||||
|
row.block = if blk.count == 1 {
|
||||||
|
format!("{}", blk.index)
|
||||||
|
} else {
|
||||||
|
format!("{}-{}", blk.index, blk.index + blk.count - 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Node
|
||||||
|
if opts.have_nodes {
|
||||||
|
row.node = format!("{}", blk.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
table.push(row);
|
||||||
|
}
|
||||||
|
Table::new(table)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_table(lsmem: &Lsmem, opts: &Options) {
|
||||||
|
let mut table = create_table(lsmem, opts);
|
||||||
|
table
|
||||||
|
.with(Style::blank())
|
||||||
|
.with(Modify::new(object::Columns::new(1..)).with(Alignment::right()));
|
||||||
|
|
||||||
|
// the default version
|
||||||
|
table.with(Disable::column(ByColumnName::new("NODE")));
|
||||||
|
table.with(Disable::column(ByColumnName::new("ZONES")));
|
||||||
|
|
||||||
|
println!("{table}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_summary(lsmem: &Lsmem, opts: &Options) {
|
||||||
|
if opts.bytes {
|
||||||
|
println!("{:<23} {:>15}", "Memory block size:", lsmem.block_size);
|
||||||
|
println!("{:<23} {:>15}", "Total online memory:", lsmem.mem_online);
|
||||||
|
println!("{:<23} {:>15}", "Total offline memory:", lsmem.mem_offline);
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"{:<23} {:>15}",
|
||||||
|
"Memory block size:",
|
||||||
|
utils::size_to_human_string(lsmem.block_size)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"{:<23} {:>15}",
|
||||||
|
"Total online memory:",
|
||||||
|
utils::size_to_human_string(lsmem.mem_online)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"{:<23} {:>15}",
|
||||||
|
"Total offline memory:",
|
||||||
|
utils::size_to_human_string(lsmem.mem_offline)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_file_content<T: core::str::FromStr>(path: &Path) -> io::Result<T>
|
||||||
|
where
|
||||||
|
T::Err: std::fmt::Debug, // Required to unwrap the result of T::from_str
|
||||||
|
{
|
||||||
|
let file = fs::File::open(path)?;
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
let mut content = String::new();
|
||||||
|
reader.read_line(&mut content)?;
|
||||||
|
Ok(content.trim().to_string().parse().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let _matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?;
|
let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?;
|
||||||
|
let opt_bytes = matches.get_flag(options::BYTES);
|
||||||
|
|
||||||
|
let mut lsmem = Lsmem::new();
|
||||||
|
let mut opts = Options::new();
|
||||||
|
opts.bytes = opt_bytes;
|
||||||
|
|
||||||
|
read_info(&mut lsmem, &mut opts);
|
||||||
|
|
||||||
|
if opts.want_table {
|
||||||
|
print_table(&lsmem, &opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.want_summary {
|
||||||
|
print_summary(&lsmem, &opts);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,4 +541,11 @@ pub fn uu_app() -> Command {
|
|||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.override_usage(format_usage(USAGE))
|
.override_usage(format_usage(USAGE))
|
||||||
.infer_long_args(true)
|
.infer_long_args(true)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::BYTES)
|
||||||
|
.short('b')
|
||||||
|
.long("bytes")
|
||||||
|
.help("print SIZE in bytes rather than in human readable format")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
79
src/uu/lsmem/src/utils.rs
Normal file
79
src/uu/lsmem/src/utils.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// enum HumanStringSize {
|
||||||
|
// Suffix1Letter = 0,
|
||||||
|
// Suffix3Letter = (1 << 0),
|
||||||
|
// SuffixSpace = (1 << 1),
|
||||||
|
// Decimal2Digits = (1 << 2),
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn size_to_human_string(bytes: u64) -> String {
|
||||||
|
let mut buf = String::with_capacity(32);
|
||||||
|
let mut dec;
|
||||||
|
let mut frac;
|
||||||
|
let letters = "BKMGTPE";
|
||||||
|
let mut suffix = String::with_capacity(4);
|
||||||
|
|
||||||
|
let exp = get_exp(bytes);
|
||||||
|
let c = letters
|
||||||
|
.chars()
|
||||||
|
.nth(if exp != 0 { exp / 10 } else { 0 })
|
||||||
|
.unwrap_or('B');
|
||||||
|
dec = if exp != 0 {
|
||||||
|
bytes / (1_u64 << exp)
|
||||||
|
} else {
|
||||||
|
bytes
|
||||||
|
};
|
||||||
|
frac = if exp != 0 { bytes % (1_u64 << exp) } else { 0 };
|
||||||
|
|
||||||
|
suffix.push(c);
|
||||||
|
|
||||||
|
// Rounding logic
|
||||||
|
if frac != 0 {
|
||||||
|
if frac >= u64::MAX / 1000 {
|
||||||
|
frac = ((frac / 1024) * 1000) / (1 << (exp - 10));
|
||||||
|
} else {
|
||||||
|
frac = (frac * 1000) / (1 << exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round to 1 decimal place
|
||||||
|
frac = ((frac + 50) / 100) * 10;
|
||||||
|
|
||||||
|
// Check for overflow due to rounding
|
||||||
|
if frac == 100 {
|
||||||
|
dec += 1;
|
||||||
|
frac = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the result
|
||||||
|
if frac != 0 {
|
||||||
|
let decimal_point = ".";
|
||||||
|
buf = format!("{}{}{:02}", dec, decimal_point, frac);
|
||||||
|
if buf.ends_with('0') {
|
||||||
|
buf.pop(); // Remove extraneous zero
|
||||||
|
}
|
||||||
|
buf += &suffix;
|
||||||
|
} else {
|
||||||
|
buf += &format!("{dec}");
|
||||||
|
buf += &suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_exp(n: u64) -> usize {
|
||||||
|
let mut shft = 10;
|
||||||
|
while shft <= 60 {
|
||||||
|
if n < (1 << shft) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
shft += 10;
|
||||||
|
}
|
||||||
|
shft - 10
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_size_to_human_string() {
|
||||||
|
assert_eq!("11.7K", size_to_human_string(12000));
|
||||||
|
assert_eq!("11.4M", size_to_human_string(12000000));
|
||||||
|
assert_eq!("11.2G", size_to_human_string(12000000000));
|
||||||
|
}
|
@@ -2,3 +2,10 @@
|
|||||||
//
|
//
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// 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);
|
||||||
|
}
|
||||||
|
@@ -9,6 +9,10 @@ mod common;
|
|||||||
#[path = "by-util/test_lscpu.rs"]
|
#[path = "by-util/test_lscpu.rs"]
|
||||||
mod test_lscpu;
|
mod test_lscpu;
|
||||||
|
|
||||||
|
#[cfg(feature = "lsmem")]
|
||||||
|
#[path = "by-util/test_lsmem.rs"]
|
||||||
|
mod test_lsmem;
|
||||||
|
|
||||||
#[cfg(feature = "mountpoint")]
|
#[cfg(feature = "mountpoint")]
|
||||||
#[path = "by-util/test_mountpoint.rs"]
|
#[path = "by-util/test_mountpoint.rs"]
|
||||||
mod test_mountpoint;
|
mod test_mountpoint;
|
||||||
|
Reference in New Issue
Block a user