| 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 |
|
| 3554afef Andy McCall ↶ |
26 |
#[cfg(unix)] |
| 337afb8f Andy McCall ↶ |
27 |
pub fn kernel() -> Option<String> { |
| 28 |
let mut u: libc::utsname = unsafe { std::mem::zeroed() }; |
| 29 |
|
| 30 |
if unsafe { libc::uname(&mut u) } != 0 { |
| 31 |
return None; |
| 32 |
} |
| 33 |
|
| 34 |
let release = unsafe { std::ffi::CStr::from_ptr(u.release.as_ptr()) }; |
| 35 |
Some(release.to_string_lossy().into_owned()) |
| 36 |
} |
| 37 |
|
| f4dfd742 Andy McCall ↶ |
38 |
|
| 39 |
|
| 40 |
#[cfg(windows)] |
| 3554afef Andy McCall ↶ |
41 |
pub fn kernel() -> Option<String> { |
| f4dfd742 Andy McCall ↶ |
42 |
use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW; |
| 43 |
|
| 44 |
let mut v: OSVERSIONINFOW = unsafe { std::mem::zeroed() }; |
| 45 |
v.dwOSVersionInfoSize = std::mem::size_of::<OSVERSIONINFOW>() as u32; |
| 46 |
|
| 47 |
let status = unsafe { windows_sys::Wdk::System::SystemServices::RtlGetVersion(&mut v) }; |
| 48 |
(status == 0).then(|| format!("Windows {}.{}.{}", v.dwMajorVersion, v.dwMinorVersion, v.dwBuildNumber)) |
| 3554afef Andy McCall ↶ |
49 |
} |
| 50 |
|
| 51 |
|
| 6f66e397 Andy McCall ↶ |
52 |
|
| 3554afef Andy McCall ↶ |
53 |
#[cfg(unix)] |
| 62829d15 Andy McCall ↶ |
54 |
pub fn free_disk_mib(path: &std::path::Path) -> Option<u64> { |
| 55 |
use std::os::unix::ffi::OsStrExt; |
| 56 |
let c = std::ffi::CString::new(path.as_os_str().as_bytes()).ok()?; |
| 57 |
let mut s: libc::statvfs = unsafe { std::mem::zeroed() }; |
| 58 |
|
| 59 |
if unsafe { libc::statvfs(c.as_ptr(), &mut s) } != 0 { |
| 60 |
return None; |
| 61 |
} |
| 62 |
Some((s.f_bavail as u64).saturating_mul(s.f_frsize as u64) >> 20) |
| 3554afef Andy McCall ↶ |
63 |
} |
| 64 |
|
| 6f66e397 Andy McCall ↶ |
65 |
#[cfg(windows)] |
| 66 |
pub fn free_disk_mib(path: &std::path::Path) -> Option<u64> { |
| 67 |
use std::os::windows::ffi::OsStrExt; |
| 68 |
let wide: Vec<u16> = path.as_os_str().encode_wide().chain(Some(0)).collect(); |
| 69 |
let mut free: u64 = 0; |
| 70 |
|
| 71 |
|
| 72 |
let ok = unsafe { |
| 73 |
windows_sys::Win32::Storage::FileSystem::GetDiskFreeSpaceExW(wide.as_ptr(), &mut free, std::ptr::null_mut(), std::ptr::null_mut()) |
| 74 |
}; |
| 75 |
(ok != 0).then_some(free >> 20) |
| 62829d15 Andy McCall ↶ |
76 |
} |
| 77 |
|
| 8f211e20 Andy McCall ↶ |
78 |
|
| 79 |
|
| 80 |
pub fn allocated_mib(dir: &std::path::Path) -> Option<u64> { |
| 81 |
let mut total: u64 = 0; |
| 82 |
let mut stack = vec![dir.to_path_buf()]; |
| 83 |
while let Some(d) = stack.pop() { |
| 84 |
for e in std::fs::read_dir(&d).ok()?.flatten() { |
| 85 |
let m = std::fs::symlink_metadata(e.path()).ok()?; |
| 86 |
if m.is_dir() { |
| 87 |
stack.push(e.path()); |
| 88 |
} else { |
| 3554afef Andy McCall ↶ |
89 |
total = total.saturating_add(crate::portable::disk_bytes(&m)); |
| 8f211e20 Andy McCall ↶ |
90 |
} |
| 91 |
} |
| 92 |
} |
| 93 |
Some(total >> 20) |
| 94 |
} |
| 95 |
|
| 96 |
|
| 97 |
pub fn peak_rss_mib(pid: u32) -> Option<u64> { |
| 98 |
let text = std::fs::read_to_string(format!("/proc/{pid}/status")).ok()?; |
| 99 |
let kib: u64 = text.lines().find_map(|l| l.strip_prefix("VmHWM:"))?.trim().trim_end_matches("kB").trim().parse().ok()?; |
| 100 |
Some(kib / 1024) |
| 101 |
} |
| 102 |
|
| 337afb8f Andy McCall ↶ |
103 |
|
| 3554afef Andy McCall ↶ |
104 |
|
| 105 |
|
| 106 |
#[cfg(not(unix))] |
| 107 |
pub fn load_x100() -> Option<u32> { |
| 108 |
None |
| 109 |
} |
| 110 |
|
| 111 |
#[cfg(unix)] |
| 62829d15 Andy McCall ↶ |
112 |
pub fn load_x100() -> Option<u32> { |
| 337afb8f Andy McCall ↶ |
113 |
let mut i: libc::sysinfo = unsafe { std::mem::zeroed() }; |
| 114 |
|
| 115 |
if unsafe { libc::sysinfo(&mut i) } != 0 { |
| 116 |
return None; |
| 117 |
} |
| 118 |
|
| 119 |
Some(((i.loads[0] as f64) / 65536.0 * 100.0).round() as u32) |
| 62829d15 Andy McCall ↶ |
120 |
} |
| 121 |
|
| 122 |
|
| 123 |
|
| 124 |
|
| 125 |
pub fn bench() -> (u32, u128) { |
| 126 |
let start = Instant::now(); |
| 127 |
let buf: Vec<u8> = (0..32u32 << 20).map(|i| (i.wrapping_mul(2_654_435_761) >> 13) as u8).collect(); |
| 128 |
let mut h = blake3::Hasher::new(); |
| 129 |
for _ in 0..8 { |
| 130 |
h.update(&buf); |
| 131 |
} |
| 132 |
std::hint::black_box(h.finalize()); |
| 133 |
drop(buf); |
| 134 |
let mut x: u64 = 0x9e37_79b9_7f4a_7c15; |
| 135 |
let mut words: Vec<u64> = (0..4u64 << 20) |
| 136 |
.map(|_| { |
| 137 |
x ^= x << 13; |
| 138 |
x ^= x >> 7; |
| 139 |
x ^= x << 17; |
| 140 |
x |
| 141 |
}) |
| 142 |
.collect(); |
| 143 |
words.sort_unstable(); |
| 144 |
std::hint::black_box(&words); |
| 145 |
let ms = start.elapsed().as_millis().max(1); |
| 146 |
((REFERENCE_MS * 100.0 / ms as f64).round() as u32, ms) |
| 147 |
} |
| 148 |
|
| 149 |
#[cfg(test)] |
| 150 |
mod tests { |
| 151 |
use super::*; |
| 152 |
|
| 153 |
#[test] |
| 154 |
fn the_cheap_facts_are_read_and_classes_double() { |
| 155 |
assert!(free_disk_mib(std::path::Path::new("/")).is_some()); |
| 337afb8f Andy McCall ↶ |
156 |
|
| 157 |
let proc_load: f64 = std::fs::read_to_string("/proc/loadavg").unwrap().split_whitespace().next().unwrap().parse().unwrap(); |
| 158 |
let load = load_x100().unwrap() as f64 / 100.0; |
| 159 |
assert!((load - proc_load).abs() < 1.0 + proc_load / 4.0, "sysinfo {load}, /proc {proc_load}"); |
| 160 |
assert_eq!(kernel().unwrap(), std::fs::read_to_string("/proc/sys/kernel/osrelease").unwrap().trim()); |
| 8f211e20 Andy McCall ↶ |
161 |
assert!(peak_rss_mib(std::process::id()).is_some_and(|m| m > 0)); |
| 162 |
let dir = tempfile::tempdir().unwrap(); |
| 163 |
let f = std::fs::File::create(dir.path().join("sparse")).unwrap(); |
| 164 |
f.set_len(1 << 30).unwrap(); |
| 165 |
std::fs::write(dir.path().join("full"), vec![7u8; 3 << 20]).unwrap(); |
| 166 |
let mib = allocated_mib(dir.path()).unwrap(); |
| 167 |
assert!((3..=4).contains(&mib), "the sparse gigabyte takes nothing, the written 3 MiB do: {mib}"); |
| 62829d15 Andy McCall ↶ |
168 |
assert_eq!(Facts::class_of(100), 3, "the reference machine"); |
| 169 |
assert_eq!((Facts::class_of(50), Facts::class_of(200), Facts::class_of(500), Facts::class_of(10)), (2, 4, 5, 1)); |
| 170 |
} |
| 171 |
} |