| 62829d15 Andy McCall ↶ |
1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
use crate::home::Home; |
| 6 |
use flow_core::api::Facts; |
| 7 |
use std::time::Instant; |
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
const REFERENCE_MS: f64 = 225.0; |
| 12 |
|
| 13 |
|
| 14 |
pub fn now(home: &Home, speed: Option<u32>) -> Facts { |
| 15 |
Facts { |
| 16 |
free_disk_mib: free_disk_mib(&home.root), |
| 17 |
load_x100: load_x100(), |
| 18 |
speed, |
| 19 |
speed_class: speed.map(Facts::class_of), |
| 337afb8f Andy McCall ↶ |
20 |
kernel: kernel(), |
| 62829d15 Andy McCall ↶ |
21 |
} |
| 22 |
} |
| 23 |
|
| 337afb8f Andy McCall ↶ |
24 |
|
| 25 |
|
| 26 |
pub fn kernel() -> Option<String> { |
| 27 |
let mut u: libc::utsname = unsafe { std::mem::zeroed() }; |
| 28 |
|
| 29 |
if unsafe { libc::uname(&mut u) } != 0 { |
| 30 |
return None; |
| 31 |
} |
| 32 |
|
| 33 |
let release = unsafe { std::ffi::CStr::from_ptr(u.release.as_ptr()) }; |
| 34 |
Some(release.to_string_lossy().into_owned()) |
| 35 |
} |
| 36 |
|
| 62829d15 Andy McCall ↶ |
37 |
|
| 38 |
pub fn free_disk_mib(path: &std::path::Path) -> Option<u64> { |
| 39 |
use std::os::unix::ffi::OsStrExt; |
| 40 |
let c = std::ffi::CString::new(path.as_os_str().as_bytes()).ok()?; |
| 41 |
let mut s: libc::statvfs = unsafe { std::mem::zeroed() }; |
| 42 |
|
| 43 |
if unsafe { libc::statvfs(c.as_ptr(), &mut s) } != 0 { |
| 44 |
return None; |
| 45 |
} |
| 46 |
Some((s.f_bavail as u64).saturating_mul(s.f_frsize as u64) >> 20) |
| 47 |
} |
| 48 |
|
| 8f211e20 Andy McCall ↶ |
49 |
|
| 50 |
|
| 51 |
pub fn allocated_mib(dir: &std::path::Path) -> Option<u64> { |
| 52 |
use std::os::unix::fs::MetadataExt; |
| 53 |
let mut total: u64 = 0; |
| 54 |
let mut stack = vec![dir.to_path_buf()]; |
| 55 |
while let Some(d) = stack.pop() { |
| 56 |
for e in std::fs::read_dir(&d).ok()?.flatten() { |
| 57 |
let m = std::fs::symlink_metadata(e.path()).ok()?; |
| 58 |
if m.is_dir() { |
| 59 |
stack.push(e.path()); |
| 60 |
} else { |
| 61 |
total = total.saturating_add(m.blocks() * 512); |
| 62 |
} |
| 63 |
} |
| 64 |
} |
| 65 |
Some(total >> 20) |
| 66 |
} |
| 67 |
|
| 68 |
|
| 69 |
pub fn peak_rss_mib(pid: u32) -> Option<u64> { |
| 70 |
let text = std::fs::read_to_string(format!("/proc/{pid}/status")).ok()?; |
| 71 |
let kib: u64 = text.lines().find_map(|l| l.strip_prefix("VmHWM:"))?.trim().trim_end_matches("kB").trim().parse().ok()?; |
| 72 |
Some(kib / 1024) |
| 73 |
} |
| 74 |
|
| 337afb8f Andy McCall ↶ |
75 |
|
| 76 |
|
| 62829d15 Andy McCall ↶ |
77 |
pub fn load_x100() -> Option<u32> { |
| 337afb8f Andy McCall ↶ |
78 |
let mut i: libc::sysinfo = unsafe { std::mem::zeroed() }; |
| 79 |
|
| 80 |
if unsafe { libc::sysinfo(&mut i) } != 0 { |
| 81 |
return None; |
| 82 |
} |
| 83 |
|
| 84 |
Some(((i.loads[0] as f64) / 65536.0 * 100.0).round() as u32) |
| 62829d15 Andy McCall ↶ |
85 |
} |
| 86 |
|
| 87 |
|
| 88 |
|
| 89 |
|
| 90 |
pub fn bench() -> (u32, u128) { |
| 91 |
let start = Instant::now(); |
| 92 |
let buf: Vec<u8> = (0..32u32 << 20).map(|i| (i.wrapping_mul(2_654_435_761) >> 13) as u8).collect(); |
| 93 |
let mut h = blake3::Hasher::new(); |
| 94 |
for _ in 0..8 { |
| 95 |
h.update(&buf); |
| 96 |
} |
| 97 |
std::hint::black_box(h.finalize()); |
| 98 |
drop(buf); |
| 99 |
let mut x: u64 = 0x9e37_79b9_7f4a_7c15; |
| 100 |
let mut words: Vec<u64> = (0..4u64 << 20) |
| 101 |
.map(|_| { |
| 102 |
x ^= x << 13; |
| 103 |
x ^= x >> 7; |
| 104 |
x ^= x << 17; |
| 105 |
x |
| 106 |
}) |
| 107 |
.collect(); |
| 108 |
words.sort_unstable(); |
| 109 |
std::hint::black_box(&words); |
| 110 |
let ms = start.elapsed().as_millis().max(1); |
| 111 |
((REFERENCE_MS * 100.0 / ms as f64).round() as u32, ms) |
| 112 |
} |
| 113 |
|
| 114 |
#[cfg(test)] |
| 115 |
mod tests { |
| 116 |
use super::*; |
| 117 |
|
| 118 |
#[test] |
| 119 |
fn the_cheap_facts_are_read_and_classes_double() { |
| 120 |
assert!(free_disk_mib(std::path::Path::new("/")).is_some()); |
| 337afb8f Andy McCall ↶ |
121 |
|
| 122 |
let proc_load: f64 = std::fs::read_to_string("/proc/loadavg").unwrap().split_whitespace().next().unwrap().parse().unwrap(); |
| 123 |
let load = load_x100().unwrap() as f64 / 100.0; |
| 124 |
assert!((load - proc_load).abs() < 1.0 + proc_load / 4.0, "sysinfo {load}, /proc {proc_load}"); |
| 125 |
assert_eq!(kernel().unwrap(), std::fs::read_to_string("/proc/sys/kernel/osrelease").unwrap().trim()); |
| 8f211e20 Andy McCall ↶ |
126 |
assert!(peak_rss_mib(std::process::id()).is_some_and(|m| m > 0)); |
| 127 |
let dir = tempfile::tempdir().unwrap(); |
| 128 |
let f = std::fs::File::create(dir.path().join("sparse")).unwrap(); |
| 129 |
f.set_len(1 << 30).unwrap(); |
| 130 |
std::fs::write(dir.path().join("full"), vec![7u8; 3 << 20]).unwrap(); |
| 131 |
let mib = allocated_mib(dir.path()).unwrap(); |
| 132 |
assert!((3..=4).contains(&mib), "the sparse gigabyte takes nothing, the written 3 MiB do: {mib}"); |
| 62829d15 Andy McCall ↶ |
133 |
assert_eq!(Facts::class_of(100), 3, "the reference machine"); |
| 134 |
assert_eq!((Facts::class_of(50), Facts::class_of(200), Facts::class_of(500), Facts::class_of(10)), (2, 4, 5, 1)); |
| 135 |
} |
| 136 |
} |