Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement free features #70

Merged
merged 39 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5102433
added count and seconds to free command
Apr 24, 2024
9d75109
fix for count = 1
Apr 24, 2024
28c6b4b
add --total and fix clippy warning
Apr 25, 2024
30aaeb0
fix --count, --human and swap and make clippy happy
Apr 25, 2024
c26f632
test --count and --total
Apr 25, 2024
0806fad
add --lohi and clean code
Apr 26, 2024
a18aca2
test --lohi
Apr 26, 2024
8e7a19c
mv --lohi -> --minmax & implement more correct --lohi
Apr 28, 2024
3468a0c
Merge branch 'main' into main
D3V1LC0D3R Apr 28, 2024
0381c5e
change from iter.collect() to iter.last()
Apr 28, 2024
32a6666
Merge branch 'main' of https://github.com/D3V1LC0D3R/procps
Apr 28, 2024
347d91b
remove unneeded import tests/by-util/test_free.rs
D3V1LC0D3R Apr 28, 2024
26a2b64
make free run forever if -c is not set but -s is
Apr 28, 2024
cfad26a
put the number conversion in a closure to clean up the code
Apr 29, 2024
23acca9
add --committed
Apr 29, 2024
2f582c8
add --line
Apr 29, 2024
a73b527
remove test files
Apr 29, 2024
1809553
Merge branch 'main' into main
D3V1LC0D3R Apr 30, 2024
00a7901
Merge branch 'main' into main
D3V1LC0D3R May 1, 2024
276aad9
Update src/uu/free/src/free.rs
D3V1LC0D3R May 1, 2024
1ac908a
edit comments
May 1, 2024
0aa3f90
Update src/uu/free/src/free.rs
D3V1LC0D3R May 1, 2024
b3e7f0b
use .min & .max and fix 'free memory' value for the maximum and minimum
May 1, 2024
0816661
Merge remote-tracking branch 'refs/remotes/origin/main'
May 1, 2024
c893ef7
Merge branch 'main' into main
D3V1LC0D3R May 1, 2024
d8e7f9b
rename closures
May 1, 2024
eeea730
Merge remote-tracking branch 'refs/remotes/origin/main'
May 1, 2024
37479dd
Update src/uu/free/src/free.rs
D3V1LC0D3R May 1, 2024
24294ec
replace closures with function
May 1, 2024
f6e76fd
remove cool features because the reviewers want to
May 3, 2024
51e6f4b
remove minmax test
May 3, 2024
3e3c14a
when using --lohi high should output high_free not free
May 3, 2024
8e546c3
Update src/uu/free/src/free.rs
D3V1LC0D3R May 4, 2024
1cfdaee
remove HighUsed and LowUsed as they dont actually exist
May 4, 2024
9562b0a
Merge branch 'main' of https://github.com/D3V1LC0D3R/procps
May 4, 2024
06aa9ce
set LowMem if its missing from /proc/meminfo
May 4, 2024
85f2f21
fix macos
May 5, 2024
26e29a8
Merge branch 'main' into main
D3V1LC0D3R May 6, 2024
20f2f18
replace match with if in n2s closure
May 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
283 changes: 201 additions & 82 deletions src/uu/free/src/free.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@
use std::fs;
#[cfg(target_os = "linux")]
use std::io::Error;
use std::ops::Mul;
use std::process;
use std::thread::sleep;
use std::time::Duration;
use std::u64;
use uucore::{error::UResult, format_usage, help_about, help_usage};

const ABOUT: &str = help_about!("free.md");
const USAGE: &str = help_usage!("free.md");

/// The unit of number is [UnitMultiplier::Bytes]
#[derive(Default)]
#[derive(Default, Clone)]
struct MemInfo {
total: u64,
free: u64,
Expand All @@ -43,10 +47,17 @@
swap_free: u64,
swap_used: u64,
reclaimable: u64,
low_total: u64,
low_free: u64,
high_total: u64,
high_free: u64,
commit_limit: u64,
committed: u64,
}

#[cfg(target_os = "linux")]
fn parse_meminfo() -> Result<MemInfo, Error> {
// kernel docs: https://www.kernel.org/doc/html/latest/filesystems/proc.html#meminfo
let contents = fs::read_to_string("/proc/meminfo")?;
let mut mem_info = MemInfo::default();

Expand All @@ -63,10 +74,27 @@
"SwapTotal" => mem_info.swap_total = parsed_value,
"SwapFree" => mem_info.swap_free = parsed_value,
"SReclaimable" => mem_info.reclaimable = parsed_value,
"LowTotal" => mem_info.low_total = parsed_value,
"LowFree" => mem_info.low_free = parsed_value,
"HighTotal" => mem_info.high_total = parsed_value,
"HighFree" => mem_info.high_free = parsed_value,
"CommitLimit" => mem_info.commit_limit = parsed_value,
"Committed_AS" => mem_info.committed = parsed_value,
_ => {}
}
}
}
// as far as i understand the kernel doc everything that is not highmem (out of all the memory) is lowmem
// from kernel doc: "Highmem is all memory above ~860MB of physical memory."
// it would be better to implement this via optionals, etc. but that would require a refactor so lets not do that right now

if mem_info.low_total == u64::default() {
mem_info.low_total = mem_info.total - mem_info.high_total;
}

if mem_info.low_free == u64::default() {
mem_info.low_free = mem_info.free - mem_info.high_free;
}

mem_info.swap_used = mem_info.swap_total - mem_info.swap_free;

Expand All @@ -90,6 +118,12 @@
swap_free: sys.free_swap(),
swap_used: sys.total_swap().saturating_sub(sys.free_swap()),
reclaimable: 0,
low_total: 0,
low_free: 0,
high_total: 0,
high_free: 0,
commit_limit: 0,
committed: 0,
};

Ok(mem_info)
Expand All @@ -101,88 +135,160 @@
Ok(MemInfo::default())
}

// print total - used - free combo that is used for everything except memory for now
// free can be negative if the memory is overcommitted so it has to be signed
fn tuf_combo<F>(name: &str, total: u64, used: u64, free: i128, f: F)
where
F: Fn(u64) -> String,
{
// ugly hack to convert negative values
let free_str: String = if free < 0 {
"-".to_owned() + &f((-free) as u64)
} else {

Check warning on line 147 in src/uu/free/src/free.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/free/src/free.rs#L146-L147

Added lines #L146 - L147 were not covered by tests
f(free as u64)
};

println!("{:8}{:>12}{:>12}{:>12}", name, f(total), f(used), free_str);
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args)?;
let wide = matches.get_flag("wide");

let wide = matches.get_flag("wide");
let human = matches.get_flag("human");

let si = matches.get_flag("si");
let total = matches.get_flag("total");
let lohi = matches.get_flag("lohi");
let count_flag = matches.get_one("count");
let mut count: u64 = count_flag.unwrap_or(&1_u64).to_owned();
let seconds_flag = matches.get_one("seconds");
let seconds: f64 = seconds_flag.unwrap_or(&1.0_f64).to_owned();
let committed = matches.get_flag("committed");
let one_line = matches.get_flag("line");

let dur = Duration::from_nanos(seconds.mul(1_000_000_000.0).round() as u64);
let convert = detect_unit(&matches);

match parse_meminfo() {
Ok(mem_info) => {
let buff_cache = if wide {
mem_info.buffers
} else {
mem_info.buffers + mem_info.cached
};
let cache = if wide { mem_info.cached } else { 0 };
let used = mem_info.total - mem_info.available;

if wide {
wide_header();
if human {
println!(
"{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}",
"Mem:",
humanized(mem_info.total, si),
humanized(used, si),
humanized(mem_info.free, si),
humanized(mem_info.shared, si),
humanized(buff_cache, si),
humanized(cache + mem_info.reclaimable, si),
humanized(mem_info.available, si),
)
let infinite: bool = count_flag.is_none() && seconds_flag.is_some();

while count > 0 || infinite {
// prevent underflow
if !infinite {
count -= 1;
}
match parse_meminfo() {
Ok(mem_info) => {
let buff_cache = if wide {
mem_info.buffers
} else {
mem_info.buffers + mem_info.cached
};
let cache = if wide { mem_info.cached } else { 0 };
let used = mem_info.total - mem_info.available;

// function that converts the number to the correct string
let n2s = |x| {
if human {
humanized(x, si)
} else {
convert(x).to_string()
}
};
if one_line {
println!(
"{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}",
"Mem:",
convert(mem_info.total),
convert(used),
convert(mem_info.free),
convert(mem_info.shared),
convert(buff_cache),
convert(cache + mem_info.reclaimable),
convert(mem_info.available),
)
}
} else {
header();
if human {
println!(
"{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}",
"Mem:",
humanized(mem_info.total, si),
humanized(used, si),
humanized(mem_info.free, si),
humanized(mem_info.shared, si),
humanized(buff_cache + mem_info.reclaimable, si),
humanized(mem_info.available, si),
)
"{:8}{:>12} {:8}{:>12} {:8}{:>12} {:8}{:>12}",
"SwapUse",
n2s(mem_info.swap_used),
"CacheUse",
n2s(buff_cache + mem_info.reclaimable),
"MemUse",
n2s(used),
"MemFree",
n2s(mem_info.free)
);
} else {
println!(
"{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}",
"Mem:",
convert(mem_info.total),
convert(used),
convert(mem_info.free),
convert(mem_info.shared),
convert(buff_cache + mem_info.reclaimable),
convert(mem_info.available),
)
if wide {
wide_header();
println!(
"{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}",
"Mem:",
n2s(mem_info.total),
n2s(used),
n2s(mem_info.free),
n2s(mem_info.shared),
n2s(buff_cache),
n2s(cache + mem_info.reclaimable),
n2s(mem_info.available),
);
} else {
header();
println!(
"{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}",
"Mem:",
n2s(mem_info.total),
n2s(used),
n2s(mem_info.free),
n2s(mem_info.shared),
n2s(buff_cache + mem_info.reclaimable),
n2s(mem_info.available),
)
}

if lohi {
tuf_combo(
"Low:",
mem_info.low_total,
mem_info.low_total - mem_info.low_free,
mem_info.low_free.into(),
n2s,
);
tuf_combo(
"High:",
mem_info.high_total,
mem_info.high_total - mem_info.high_free,
mem_info.high_free.into(),
n2s,
);
}

tuf_combo(
"Swap:",
mem_info.swap_total,
mem_info.swap_used,
mem_info.swap_free.into(),
n2s,
);
if total {
tuf_combo(
"Total:",
mem_info.total + mem_info.swap_total,
used + mem_info.swap_used,
(mem_info.free + mem_info.swap_free).into(),
n2s,
);
}

if committed {
tuf_combo(
"Comm:",
mem_info.commit_limit,
mem_info.committed,
(mem_info.commit_limit as i128) - (mem_info.committed as i128),
n2s,
);
}
}
}
println!(
"{:8}{:>12}{:>12}{:>12}",
"Swap:", mem_info.swap_total, mem_info.swap_used, mem_info.swap_free
);
Err(e) => {
eprintln!("free: failed to read memory info: {}", e);
process::exit(1);

Check warning on line 285 in src/uu/free/src/free.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/free/src/free.rs#L283-L285

Added lines #L283 - L285 were not covered by tests
}
}
Err(e) => {
eprintln!("free: failed to read memory info: {}", e);
process::exit(1);
if count > 0 || infinite {
// the original free prints a newline everytime before waiting for the next round
println!();
sleep(dur);
}
}

Expand All @@ -200,20 +306,33 @@
"bytes", "kilo", "mega", "giga", "tera", "peta", "kibi", "mebi", "gibi", "tebi", "pebi",
]))
.args([
arg!(-b --bytes "show output in bytes").action(ArgAction::SetTrue),
arg!( --kilo "show output in kilobytes").action(ArgAction::SetFalse),
arg!( --mega "show output in megabytes").action(ArgAction::SetTrue),
arg!( --giga "show output in gigabytes").action(ArgAction::SetTrue),
arg!( --tera "show output in terabytes").action(ArgAction::SetTrue),
arg!( --peta "show output in petabytes").action(ArgAction::SetTrue),
arg!(-k --kibi "show output in kibibytes").action(ArgAction::SetTrue),
arg!(-m --mebi "show output in mebibytes").action(ArgAction::SetTrue),
arg!(-g --gibi "show output in gibibytes").action(ArgAction::SetTrue),
arg!( --tebi "show output in tebibytes").action(ArgAction::SetTrue),
arg!( --pebi "show output in pebibytes").action(ArgAction::SetTrue),
arg!(-h --human "show human-readable output").action(ArgAction::SetTrue),
arg!( --si "use powers of 1000 not 1024").action(ArgAction::SetFalse),
// arg!(-L --line "show output on a single line"),
arg!(-b --bytes "show output in bytes").action(ArgAction::SetTrue),
arg!( --kilo "show output in kilobytes").action(ArgAction::SetFalse),
arg!( --mega "show output in megabytes").action(ArgAction::SetTrue),
arg!( --giga "show output in gigabytes").action(ArgAction::SetTrue),
arg!( --tera "show output in terabytes").action(ArgAction::SetTrue),
arg!( --peta "show output in petabytes").action(ArgAction::SetTrue),
arg!(-k --kibi "show output in kibibytes").action(ArgAction::SetTrue),
arg!(-m --mebi "show output in mebibytes").action(ArgAction::SetTrue),
arg!(-g --gibi "show output in gibibytes").action(ArgAction::SetTrue),
arg!( --tebi "show output in tebibytes").action(ArgAction::SetTrue),
arg!( --pebi "show output in pebibytes").action(ArgAction::SetTrue),
arg!(-h --human "show human-readable output").action(ArgAction::SetTrue),
arg!( --si "use powers of 1000 not 1024").action(ArgAction::SetFalse),
arg!(-l --lohi "show detailed low and high memory statistics")
.action(ArgAction::SetTrue),
arg!(-t --total "show total for RAM + swap").action(ArgAction::SetTrue),
arg!(-v --committed "show committed memory and commit limit")
.action(ArgAction::SetTrue),
// accept 1 as well as 0.5, 0.55, ...
arg!(-s --seconds "repeat printing every N seconds")
.action(ArgAction::Set)
.value_parser(clap::value_parser!(f64)),
// big int because predecesor accepts them as well (some scripts might have huge values as some sort of infinite)
arg!(-c --count "repeat printing N times, then exit")
.action(ArgAction::Set)
.value_parser(clap::value_parser!(u64)),
arg!(-L --line "show output on a single line").action(ArgAction::SetTrue),
])
.arg(
Arg::new("wide")
Expand Down
Loading
Loading