mirror of
https://github.com/NotAShelf/microfetch.git
synced 2026-05-21 13:56:31 +00:00
Compare commits
7 commits
b385714d88
...
136d5bcb58
| Author | SHA1 | Date | |
|---|---|---|---|
|
136d5bcb58 |
|||
|
543a1597ba |
|||
|
09b9398938 |
|||
|
|
9712430fc2 |
||
|
|
a41511a3b4 |
||
|
|
e434985d00 |
||
|
|
93b7af0da8 |
8 changed files with 417 additions and 60 deletions
42
Cargo.lock
generated
42
Cargo.lock
generated
|
|
@ -448,9 +448,9 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hotpath"
|
name = "hotpath"
|
||||||
version = "0.14.1"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7d9982fcb4356a5260502f0e646411ec1feb2962cc98c230777104a8d1c5ed3"
|
checksum = "28594d8a3d30371e1488dcd6ba545bf07e9ce9dff6d8a57d70dbbaa221d3246d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|
@ -460,7 +460,8 @@ dependencies = [
|
||||||
"eyre",
|
"eyre",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hdrhistogram",
|
"hdrhistogram",
|
||||||
"hotpath-macros 0.14.1",
|
"hotpath-macros",
|
||||||
|
"hotpath-meta",
|
||||||
"libc",
|
"libc",
|
||||||
"mach2",
|
"mach2",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
|
@ -474,33 +475,16 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hotpath"
|
name = "hotpath-macros"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28594d8a3d30371e1488dcd6ba545bf07e9ce9dff6d8a57d70dbbaa221d3246d"
|
checksum = "c7b87d58f92c09858091f328521db329955e9fc960c3c9c55e5dc0ef228a37b0"
|
||||||
dependencies = [
|
|
||||||
"hotpath-macros 0.15.0",
|
|
||||||
"hotpath-meta",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hotpath-macros"
|
|
||||||
version = "0.14.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4398eddb78466298f1ddcc739abbbd8a942e541d1972c7590bd83de364e626e0"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hotpath-macros"
|
|
||||||
version = "0.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c7b87d58f92c09858091f328521db329955e9fc960c3c9c55e5dc0ef228a37b0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hotpath-macros-meta"
|
name = "hotpath-macros-meta"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
|
|
@ -611,9 +595,9 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "microfetch"
|
name = "microfetch"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hotpath 0.15.0",
|
"hotpath",
|
||||||
"microfetch-alloc",
|
"microfetch-alloc",
|
||||||
"microfetch-asm",
|
"microfetch-asm",
|
||||||
"microfetch-lib",
|
"microfetch-lib",
|
||||||
|
|
@ -621,15 +605,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "microfetch-alloc"
|
name = "microfetch-alloc"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "microfetch-asm"
|
name = "microfetch-asm"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "microfetch-bench"
|
name = "microfetch-bench"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"criterion-cycles-per-byte",
|
"criterion-cycles-per-byte",
|
||||||
|
|
@ -638,9 +622,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "microfetch-lib"
|
name = "microfetch-lib"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hotpath 0.14.1",
|
"hotpath",
|
||||||
"microfetch-asm",
|
"microfetch-asm",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ authors = [ "NotAShelf <raf@notashelf.dev>" ]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
rust-version = "1.92.0"
|
rust-version = "1.92.0"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
microfetch-alloc = { path = "./crates/alloc" }
|
microfetch-alloc = { path = "./crates/alloc" }
|
||||||
|
|
|
||||||
|
|
@ -731,6 +731,80 @@ pub unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Direct `sched_getaffinity(2)` syscall
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// On success, the number of bytes written to the mask buffer (always a
|
||||||
|
/// multiple of `sizeof(long)`). On error, a negative errno.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure that `mask` points to a buffer of at least
|
||||||
|
/// `mask_size` bytes.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn sys_sched_getaffinity(
|
||||||
|
pid: i32,
|
||||||
|
mask_size: usize,
|
||||||
|
mask: *mut u8,
|
||||||
|
) -> i32 {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
unsafe {
|
||||||
|
let ret: i64;
|
||||||
|
core::arch::asm!(
|
||||||
|
"syscall",
|
||||||
|
in("rax") 204i64, // __NR_sched_getaffinity
|
||||||
|
in("rdi") pid,
|
||||||
|
in("rsi") mask_size,
|
||||||
|
in("rdx") mask,
|
||||||
|
lateout("rax") ret,
|
||||||
|
lateout("rcx") _,
|
||||||
|
lateout("r11") _,
|
||||||
|
options(nostack)
|
||||||
|
);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
{
|
||||||
|
ret as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
unsafe {
|
||||||
|
let ret: i64;
|
||||||
|
core::arch::asm!(
|
||||||
|
"svc #0",
|
||||||
|
in("x8") 123i64, // __NR_sched_getaffinity
|
||||||
|
in("x0") pid,
|
||||||
|
in("x1") mask_size,
|
||||||
|
in("x2") mask,
|
||||||
|
lateout("x0") ret,
|
||||||
|
options(nostack)
|
||||||
|
);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
{
|
||||||
|
ret as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
unsafe {
|
||||||
|
let ret: i64;
|
||||||
|
core::arch::asm!(
|
||||||
|
"ecall",
|
||||||
|
in("a7") 123i64, // __NR_sched_getaffinity
|
||||||
|
in("a0") pid,
|
||||||
|
in("a1") mask_size,
|
||||||
|
in("a2") mask,
|
||||||
|
lateout("a0") ret,
|
||||||
|
options(nostack)
|
||||||
|
);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
{
|
||||||
|
ret as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Direct syscall to exit the process
|
/// Direct syscall to exit the process
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ license.workspace = true
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hotpath = { optional = true, version = "0.14.0" }
|
hotpath = { optional = true, version = "0.15.0" }
|
||||||
microfetch-asm.workspace = true
|
microfetch-asm.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
||||||
286
crates/lib/src/cpu.rs
Normal file
286
crates/lib/src/cpu.rs
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
use alloc::string::String;
|
||||||
|
|
||||||
|
use crate::{Error, syscall::read_file_fast, system::write_u64};
|
||||||
|
|
||||||
|
/// Gets CPU model name (trimmed), or empty string if unavailable.
|
||||||
|
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||||
|
pub fn get_cpu_name() -> String {
|
||||||
|
get_model_name().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets CPU core/thread info as a string.
|
||||||
|
///
|
||||||
|
/// Format: `{cores} cores ({p}p/{e}e), {threads} threads` on hybrid Intel,
|
||||||
|
/// `{cores} cores, {threads} threads` otherwise.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if the thread count cannot be determined.
|
||||||
|
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||||
|
pub fn get_cpu_cores() -> Result<String, Error> {
|
||||||
|
let threads = get_thread_count()?;
|
||||||
|
let cores = get_core_count(threads);
|
||||||
|
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
write_u64(&mut result, u64::from(cores));
|
||||||
|
result.push_str(" cores");
|
||||||
|
|
||||||
|
if let Some((p, e)) = get_pe_cores() {
|
||||||
|
result.push_str(" (");
|
||||||
|
write_u64(&mut result, u64::from(p));
|
||||||
|
result.push_str("p/");
|
||||||
|
write_u64(&mut result, u64::from(e));
|
||||||
|
result.push_str("e)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if threads != cores {
|
||||||
|
result.push_str(", ");
|
||||||
|
write_u64(&mut result, u64::from(threads));
|
||||||
|
result.push_str(" threads");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Count online threads via `sched_getaffinity(2)`.
|
||||||
|
fn get_thread_count() -> Result<u32, Error> {
|
||||||
|
let mut mask = [0u8; 128];
|
||||||
|
let ret = unsafe {
|
||||||
|
crate::syscall::sys_sched_getaffinity(0, mask.len(), mask.as_mut_ptr())
|
||||||
|
};
|
||||||
|
if ret < 0 {
|
||||||
|
return Err(Error::from_raw_os_error(-ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
let bytes = ret as usize;
|
||||||
|
let mut count = 0u32;
|
||||||
|
for &byte in &mask[..bytes] {
|
||||||
|
count += byte.count_ones();
|
||||||
|
}
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derive physical core count from thread count and topology.
|
||||||
|
fn get_core_count(threads: u32) -> u32 {
|
||||||
|
let Some(smt_width) =
|
||||||
|
count_cpulist("/sys/devices/system/cpu/cpu0/topology/thread_siblings_list")
|
||||||
|
else {
|
||||||
|
return threads;
|
||||||
|
};
|
||||||
|
if smt_width == 0 {
|
||||||
|
return threads;
|
||||||
|
}
|
||||||
|
threads / smt_width
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detect P-core and E-core counts via sysfs PMU device files, which is done
|
||||||
|
/// by reading `/sys/devices/cpu_core/cpus` and `/sys/devices/cpu_atom/cpus`.
|
||||||
|
fn get_pe_cores() -> Option<(u32, u32)> {
|
||||||
|
let p = count_cpulist("/sys/devices/cpu_core/cpus")?;
|
||||||
|
let e = count_cpulist("/sys/devices/cpu_atom/cpus").unwrap_or(0);
|
||||||
|
if p > 0 || e > 0 { Some((p, e)) } else { None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a cpulist file and count listed CPUs.
|
||||||
|
fn count_cpulist(path: &str) -> Option<u32> {
|
||||||
|
let mut buf = [0u8; 64];
|
||||||
|
let n = read_file_fast(path, &mut buf).ok()?;
|
||||||
|
let data = &buf[..n];
|
||||||
|
|
||||||
|
let mut count = 0u32;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < data.len() {
|
||||||
|
// Parse start number
|
||||||
|
let start = parse_num(data, &mut i);
|
||||||
|
if i < data.len() && data[i] == b'-' {
|
||||||
|
i += 1;
|
||||||
|
let end = parse_num(data, &mut i);
|
||||||
|
// The Kernel always emits ascending ranges, so end is always >= start
|
||||||
|
// https://github.com/torvalds/linux/blob/v6.19/lib/vsprintf.c#L1276-L1303
|
||||||
|
count += end - start + 1;
|
||||||
|
} else {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
// Skip comma or newline
|
||||||
|
if i < data.len() && (data[i] == b',' || data[i] == b'\n') {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a decimal number from a byte slice, advancing the index.
|
||||||
|
fn parse_num(data: &[u8], i: &mut usize) -> u32 {
|
||||||
|
let mut n = 0u32;
|
||||||
|
while *i < data.len() && data[*i].is_ascii_digit() {
|
||||||
|
n = n * 10 + u32::from(data[*i] - b'0');
|
||||||
|
*i += 1;
|
||||||
|
}
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read CPU frequency in MHz. Tries sysfs first, then cpuinfo fields.
|
||||||
|
fn get_cpu_freq_mhz() -> Option<u32> {
|
||||||
|
// Try sysfs cpuinfo_max_freq (in kHz)
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
if let Ok(n) = read_file_fast(
|
||||||
|
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
|
||||||
|
&mut buf,
|
||||||
|
) {
|
||||||
|
let mut khz = 0u32;
|
||||||
|
for &b in &buf[..n] {
|
||||||
|
if b.is_ascii_digit() {
|
||||||
|
khz = khz * 10 + u32::from(b - b'0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if khz > 0 {
|
||||||
|
return Some(khz / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fall back to cpuinfo fields
|
||||||
|
let mut buf2 = [0u8; 2048];
|
||||||
|
let n = read_file_fast("/proc/cpuinfo", &mut buf2).ok()?;
|
||||||
|
let data = &buf2[..n];
|
||||||
|
for key in &[b"cpu MHz" as &[u8], b"cpu MHz dynamic", b"CPU MHz"] {
|
||||||
|
if let Some(val) = extract_field(data, key) {
|
||||||
|
// Parse integer part of the MHz value (e.g. "5200.00" -> 5200)
|
||||||
|
let mut mhz = 0u32;
|
||||||
|
for &b in val.as_bytes() {
|
||||||
|
if b == b'.' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if b.is_ascii_digit() {
|
||||||
|
mhz = mhz * 10 + u32::from(b - b'0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mhz > 0 {
|
||||||
|
return Some(mhz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse CPU model name from `/proc/cpuinfo` and append frequency.
|
||||||
|
fn get_model_name() -> Option<String> {
|
||||||
|
let mut buf = [0u8; 2048];
|
||||||
|
let n = read_file_fast("/proc/cpuinfo", &mut buf).ok()?;
|
||||||
|
let data = &buf[..n];
|
||||||
|
|
||||||
|
for key in &[
|
||||||
|
b"model name" as &[u8],
|
||||||
|
b"Model Name",
|
||||||
|
b"uarch",
|
||||||
|
b"isa",
|
||||||
|
b"cpu",
|
||||||
|
b"machine",
|
||||||
|
b"vendor_id",
|
||||||
|
] {
|
||||||
|
if let Some(val) = extract_field(data, key) {
|
||||||
|
let trimmed = trim(val);
|
||||||
|
if !trimmed.is_empty() {
|
||||||
|
let mut name = String::from(trimmed);
|
||||||
|
if let Some(mhz) = get_cpu_freq_mhz() {
|
||||||
|
name.push_str(" @ ");
|
||||||
|
// Round to nearest 0.01 GHz, then split so carries (e.g. 1999 MHz)
|
||||||
|
// roll into the integer part instead of overflowing the fraction.
|
||||||
|
let rounded_centesimal = (mhz + 5) / 10;
|
||||||
|
let ghz_int = rounded_centesimal / 100;
|
||||||
|
let ghz_frac = rounded_centesimal % 100;
|
||||||
|
write_u64(&mut name, u64::from(ghz_int));
|
||||||
|
name.push('.');
|
||||||
|
if ghz_frac < 10 {
|
||||||
|
name.push('0');
|
||||||
|
}
|
||||||
|
write_u64(&mut name, u64::from(ghz_frac));
|
||||||
|
name.push_str(" GHz");
|
||||||
|
}
|
||||||
|
return Some(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract value of first occurrence of `key` in cpuinfo.
|
||||||
|
fn extract_field<'a>(data: &'a [u8], key: &[u8]) -> Option<&'a str> {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < data.len() {
|
||||||
|
let remaining = &data[i..];
|
||||||
|
let eol = remaining
|
||||||
|
.iter()
|
||||||
|
.position(|&b| b == b'\n')
|
||||||
|
.unwrap_or(remaining.len());
|
||||||
|
let line = &remaining[..eol];
|
||||||
|
|
||||||
|
if line.starts_with(key) {
|
||||||
|
let mut p = key.len();
|
||||||
|
while p < line.len() && (line[p] == b'\t' || line[p] == b' ') {
|
||||||
|
p += 1;
|
||||||
|
}
|
||||||
|
if p < line.len() && line[p] == b':' {
|
||||||
|
p += 1;
|
||||||
|
while p < line.len() && line[p] == b' ' {
|
||||||
|
p += 1;
|
||||||
|
}
|
||||||
|
return core::str::from_utf8(&line[p..]).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += eol + 1;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Strip noise from model names.
|
||||||
|
fn trim(name: &str) -> &str {
|
||||||
|
let b = name.as_bytes();
|
||||||
|
let mut end = b.len();
|
||||||
|
|
||||||
|
while end > 0 && b[end - 1].is_ascii_whitespace() {
|
||||||
|
end -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if end >= 10 && &b[end - 10..end] == b" Processor" {
|
||||||
|
end -= 10;
|
||||||
|
} else if end >= 4 && &b[end - 4..end] == b" CPU" {
|
||||||
|
end -= 4;
|
||||||
|
}
|
||||||
|
while end > 0 && b[end - 1].is_ascii_whitespace() {
|
||||||
|
end -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if end >= 3 && &b[end - 3..end] == b"(R)" {
|
||||||
|
end -= 3;
|
||||||
|
} else if end >= 4
|
||||||
|
&& (&b[end - 4..end] == b"(TM)" || &b[end - 4..end] == b"(tm)")
|
||||||
|
{
|
||||||
|
end -= 4;
|
||||||
|
}
|
||||||
|
while end > 0 && b[end - 1].is_ascii_whitespace() {
|
||||||
|
end -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if end > 7 && &b[end - 5..end] == b"-Core" {
|
||||||
|
let mut p = end - 5;
|
||||||
|
while p > 0 && b[p - 1].is_ascii_digit() {
|
||||||
|
p -= 1;
|
||||||
|
}
|
||||||
|
if p > 0 && b[p - 1] == b' ' {
|
||||||
|
end = p - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while end > 0 && b[end - 1].is_ascii_whitespace() {
|
||||||
|
end -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut start = 0;
|
||||||
|
while start < end && b[start].is_ascii_whitespace() {
|
||||||
|
start += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&name[start..end]
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub mod colors;
|
pub mod colors;
|
||||||
|
pub mod cpu;
|
||||||
pub mod desktop;
|
pub mod desktop;
|
||||||
pub mod release;
|
pub mod release;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
|
|
@ -23,6 +24,7 @@ pub use microfetch_asm::{
|
||||||
sys_close,
|
sys_close,
|
||||||
sys_open,
|
sys_open,
|
||||||
sys_read,
|
sys_read,
|
||||||
|
sys_sched_getaffinity,
|
||||||
sys_statfs,
|
sys_statfs,
|
||||||
sys_sysinfo,
|
sys_sysinfo,
|
||||||
sys_uname,
|
sys_uname,
|
||||||
|
|
@ -281,6 +283,8 @@ struct Fields {
|
||||||
user_info: String,
|
user_info: String,
|
||||||
os_name: String,
|
os_name: String,
|
||||||
kernel_version: String,
|
kernel_version: String,
|
||||||
|
cpu_name: String,
|
||||||
|
cpu_cores: String,
|
||||||
shell: String,
|
shell: String,
|
||||||
uptime: String,
|
uptime: String,
|
||||||
desktop: String,
|
desktop: String,
|
||||||
|
|
@ -320,7 +324,7 @@ impl core::fmt::Write for StackWriter<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Custom logo art embedded at compile time via the `MICROFETCH_LOGO`
|
/// Custom logo art embedded at compile time via the `MICROFETCH_LOGO`
|
||||||
/// environment variable. Set it to 9 newline-separated lines of ASCII/Unicode
|
/// environment variable. Set it to 11 newline-separated lines of ASCII/Unicode
|
||||||
/// art when building to replace the default NixOS logo:
|
/// art when building to replace the default NixOS logo:
|
||||||
///
|
///
|
||||||
/// `MICROFETCH_LOGO="$(cat my_logo.txt)"` cargo build --release
|
/// `MICROFETCH_LOGO="$(cat my_logo.txt)"` cargo build --release
|
||||||
|
|
@ -338,6 +342,8 @@ fn print_system_info(fields: &Fields) -> Result<(), Error> {
|
||||||
user_info,
|
user_info,
|
||||||
os_name,
|
os_name,
|
||||||
kernel_version,
|
kernel_version,
|
||||||
|
cpu_name,
|
||||||
|
cpu_cores,
|
||||||
shell,
|
shell,
|
||||||
uptime,
|
uptime,
|
||||||
desktop,
|
desktop,
|
||||||
|
|
@ -349,7 +355,7 @@ fn print_system_info(fields: &Fields) -> Result<(), Error> {
|
||||||
let no_color = colors::is_no_color();
|
let no_color = colors::is_no_color();
|
||||||
let c = colors::Colors::new(no_color);
|
let c = colors::Colors::new(no_color);
|
||||||
|
|
||||||
let mut buf = [0u8; 2048];
|
let mut buf = [0u8; 2560];
|
||||||
let mut w = StackWriter::new(&mut buf);
|
let mut w = StackWriter::new(&mut buf);
|
||||||
|
|
||||||
if CUSTOM_LOGO.is_empty() {
|
if CUSTOM_LOGO.is_empty() {
|
||||||
|
|
@ -357,24 +363,25 @@ fn print_system_info(fields: &Fields) -> Result<(), Error> {
|
||||||
core::fmt::write(
|
core::fmt::write(
|
||||||
&mut w,
|
&mut w,
|
||||||
format_args!(
|
format_args!(
|
||||||
"\n {b} ▟█▖ {cy}▝█▙ ▗█▛ {user_info} ~{rs}\n {b} \
|
"\n {b}⠀⠀⠀⠀⠀⠀⢼⣿⣄⠀⠀⠀{cy}⠹⣿⣷⡀⠀⣠⣿⡧⠀⠀⠀⠀⠀⠀{rs} {user_info} ~{rs}\
|
||||||
▗▄▄▟██▄▄▄▄▄{cy}▝█▙█▛ {b}▖ {cy}\u{F313} {b}System{rs} \
|
\n {b}⠀⠀⠀⠀⠀⠀⠈⢿⣿⣆⠀⠀⠀{cy}⠘⣿⣿⣴⣿⡿⠁⠀⠀⠀⠀⠀⠀{rs} {cy}\u{F313} {b}System{rs} \u{E621} {os_name}\
|
||||||
{os_name}\n {b} ▀▀▀▀▀▀▀▀▀▀▀▘{cy}▝██ {b}▟█▖ {cy}\u{E712} \
|
\n {b}⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡜{cy}⢿⣿⣟⠀⠀⠀{b}⢀⡄⠀⠀⠀{rs} {cy}\u{E712} {b}Kernel{rs} \u{E621} {kernel_version}\
|
||||||
{b}Kernel{rs} {kernel_version}\n {cy} ▟█▛ \
|
\n {b}⠀⠀⠀⠉⠉⠉⠉{cy}⣩⣭⡭{b}⠉⠉⠉⠉⠉{cy}⠈⢿⣿⣆⠀{b}⢠⣿⣿⠂⠀⠀{rs} {cy}\u{F2DB} {b}CPU{rs} \u{E621} {cpu_name}\
|
||||||
{cy}▝█▘{b}▟█▛ {cy}\u{E795} {b}Shell{rs} {shell}\n \
|
\n {cy}⠀⠀⠀⠀⠀⠀⣼⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⢻⡟{b}⣡⣿⣿⠃⠀⠀⠀{rs} {cy}\u{F4BC} {b}Topology{rs} \u{E621} {cpu_cores}\
|
||||||
{cy}▟█████▛ {b}▟█████▛ {cy}\u{F017} {b}Uptime{rs} \
|
\n {cy}⢸⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀{b}⣰⣿⣿⣿⣿⣿⣿⡇{rs} {cy}\u{E795} {b}Shell{rs} \u{E621} {shell}\
|
||||||
{uptime}\n {cy} ▟█▛{b}▗█▖ {b}▟█▛ {cy}\u{F2D2} \
|
\n {cy}⠀⠀⠀⢠⣿⣿⢋{b}⣼⣧⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⡟⠀⠀⠀⠀⠀⠀{rs} {cy}\u{F017} {b}Uptime{rs} \u{E621} {uptime}\
|
||||||
{b}Desktop{rs} {desktop}\n {cy} ▝█▛ \
|
\n {cy}⠀⠀⠠⣿⣿⠃⠀{b}⠹⣿⣷⡀{cy}⣀⣀⣀⣀⣀{b}⣚⣛⣋{cy}⣀⣀⣀⣀⠀⠀⠀{rs} {cy}\u{F2D2} {b}Desktop{rs} \u{E621} {desktop}\
|
||||||
{b}██▖{cy}▗▄▄▄▄▄▄▄▄▄▄▄ {cy}\u{F035B} {b}Memory{rs} \
|
\n {cy}⠀⠀⠀⠘⠁⠀⠀⠀{b}⣽⣿⣷⡜{cy}⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀{rs} {cy}\u{F035B} {b}Memory{rs} \u{E621} {memory_usage}\
|
||||||
{memory_usage}\n {cy} ▝ {b}▟█▜█▖{cy}▀▀▀▀▀██▛▀▀▘ \
|
\n {b}⠀⠀⠀⠀⠀⠀⢀⣾⣿⠟⣿⣿⡄⠀⠀⠀{cy}⠹⣿⣷⡀⠀⠀⠀⠀⠀⠀{rs} {cy}\u{F194E} {b}Storage (/){rs} \u{E621} {storage}\
|
||||||
{cy}\u{F194E} {b}Storage (/){rs} {storage}\n {b} ▟█▘ ▜█▖ \
|
\n {b}⠀⠀⠀⠀⠀⠀⢺⣿⠋⠀⠈⢿⣿⣆⠀⠀⠀{cy}⠙⣿⡗⠀⠀⠀⠀⠀⠀{rs} {cy}\u{E22B} {b}Colors{rs} \u{E621} {colors}\n\n",
|
||||||
{cy}▝█▛ {cy}\u{E22B} {b}Colors{rs} {colors}\n\n",
|
|
||||||
b = c.blue,
|
b = c.blue,
|
||||||
cy = c.cyan,
|
cy = c.cyan,
|
||||||
rs = c.reset,
|
rs = c.reset,
|
||||||
user_info = user_info,
|
user_info = user_info,
|
||||||
os_name = os_name,
|
os_name = os_name,
|
||||||
kernel_version = kernel_version,
|
kernel_version = kernel_version,
|
||||||
|
cpu_name = cpu_name,
|
||||||
|
cpu_cores = cpu_cores,
|
||||||
shell = shell,
|
shell = shell,
|
||||||
uptime = uptime,
|
uptime = uptime,
|
||||||
desktop = desktop,
|
desktop = desktop,
|
||||||
|
|
@ -385,39 +392,41 @@ fn print_system_info(fields: &Fields) -> Result<(), Error> {
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
} else {
|
} else {
|
||||||
// Custom logo is 9 lines from MICROFETCH_LOGO env var, one per info row.
|
// Custom logo is 11 lines from MICROFETCH_LOGO env var, one per info row.
|
||||||
// Lines beyond 9 are ignored; missing lines render as empty.
|
// Lines beyond 11 are ignored; missing lines render as empty.
|
||||||
let mut lines = CUSTOM_LOGO.split('\n');
|
let mut lines = CUSTOM_LOGO.split('\n');
|
||||||
let logo_rows: [&str; 9] =
|
let logo_rows: [&str; 11] =
|
||||||
core::array::from_fn(|_| lines.next().unwrap_or(""));
|
core::array::from_fn(|_| lines.next().unwrap_or(""));
|
||||||
|
|
||||||
// Row format mirrors the default logo path exactly.
|
// Row format mirrors the default logo path exactly.
|
||||||
let rows: [(&str, &str, &str, &str, &str); 9] = [
|
let rows: [(&str, &str, &str, &str, &str); 11] = [
|
||||||
("", "", user_info.as_str(), " ", " ~"),
|
("", "", user_info.as_str(), " ", " ~"),
|
||||||
("\u{F313} ", "System", os_name.as_str(), " ", ""),
|
("\u{F313} ", "System", os_name.as_str(), " \u{E621} ", ""),
|
||||||
(
|
(
|
||||||
"\u{E712} ",
|
"\u{E712} ",
|
||||||
"Kernel",
|
"Kernel",
|
||||||
kernel_version.as_str(),
|
kernel_version.as_str(),
|
||||||
" ",
|
" \u{E621} ",
|
||||||
"",
|
"",
|
||||||
),
|
),
|
||||||
("\u{E795} ", "Shell", shell.as_str(), " ", ""),
|
("\u{F2DB} ", "CPU", cpu_name.as_str(), " \u{E621} ", ""),
|
||||||
("\u{F017} ", "Uptime", uptime.as_str(), " ", ""),
|
("\u{F4BC} ", "Topology", cpu_cores.as_str(), " \u{E621} ", ""),
|
||||||
("\u{F2D2} ", "Desktop", desktop.as_str(), " ", ""),
|
("\u{E795} ", "Shell", shell.as_str(), " \u{E621} ", ""),
|
||||||
|
("\u{F017} ", "Uptime", uptime.as_str(), " \u{E621} ", ""),
|
||||||
|
("\u{F2D2} ", "Desktop", desktop.as_str(), " \u{E621} ", ""),
|
||||||
(
|
(
|
||||||
"\u{F035B} ",
|
"\u{F035B} ",
|
||||||
"Memory",
|
"Memory",
|
||||||
memory_usage.as_str(),
|
memory_usage.as_str(),
|
||||||
" ",
|
" \u{E621} ",
|
||||||
"",
|
"",
|
||||||
),
|
),
|
||||||
("\u{F194E} ", "Storage (/)", storage.as_str(), " ", ""),
|
("\u{F194E} ", "Storage (/)", storage.as_str(), " \u{E621} ", ""),
|
||||||
("\u{E22B} ", "Colors", colors.as_str(), " ", ""),
|
("\u{E22B} ", "Colors", colors.as_str(), " \u{E621} ", ""),
|
||||||
];
|
];
|
||||||
|
|
||||||
core::fmt::write(&mut w, format_args!("\n")).ok();
|
core::fmt::write(&mut w, format_args!("\n")).ok();
|
||||||
for i in 0..9 {
|
for i in 0..11 {
|
||||||
let (icon, key, value, spacing, suffix) = rows[i];
|
let (icon, key, value, spacing, suffix) = rows[i];
|
||||||
if key.is_empty() {
|
if key.is_empty() {
|
||||||
// Row 1 has no icon/key, just logo + user_info
|
// Row 1 has no icon/key, just logo + user_info
|
||||||
|
|
@ -534,6 +543,8 @@ pub unsafe fn run(argc: i32, argv: *const *const u8) -> Result<(), Error> {
|
||||||
user_info: system::get_username_and_hostname(&utsname),
|
user_info: system::get_username_and_hostname(&utsname),
|
||||||
os_name: release::get_os_pretty_name()?,
|
os_name: release::get_os_pretty_name()?,
|
||||||
kernel_version: release::get_system_info(&utsname),
|
kernel_version: release::get_system_info(&utsname),
|
||||||
|
cpu_name: cpu::get_cpu_name(),
|
||||||
|
cpu_cores: cpu::get_cpu_cores()?,
|
||||||
shell: system::get_shell(),
|
shell: system::get_shell(),
|
||||||
desktop: desktop::get_desktop_info(),
|
desktop: desktop::get_desktop_info(),
|
||||||
uptime: uptime::get_current()?,
|
uptime: uptime::get_current()?,
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ fn round_f64(x: f64) -> f64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a u64 to string
|
/// Write a u64 to string
|
||||||
fn write_u64(s: &mut String, mut n: u64) {
|
pub fn write_u64(s: &mut String, mut n: u64) {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
s.push('0');
|
s.push('0');
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -328,7 +328,9 @@ person about current issues. To list a few, special thanks to:
|
||||||
- [@sioodmy](https://github.com/sioodmy) - Being cute
|
- [@sioodmy](https://github.com/sioodmy) - Being cute
|
||||||
- [@mewoocat](https://github.com/mewoocat) - The awesome NixOS logo ASCII used
|
- [@mewoocat](https://github.com/mewoocat) - The awesome NixOS logo ASCII used
|
||||||
in Microfetch
|
in Microfetch
|
||||||
- [@uzaaft](https://github.com/uzaaft) - Helping me going faster
|
- [@uzaaft](https://github.com/uzaaft) - Helping me go faster
|
||||||
|
- [@amaanq](https://github.com/amaanq) - Helping me go faster, smaller, and
|
||||||
|
prettier. Also the new logo and CPU field.
|
||||||
|
|
||||||
Additionally a big thank you to everyone who used, talked about or criticized
|
Additionally a big thank you to everyone who used, talked about or criticized
|
||||||
Microfetch. I might have missed your name here, but you have my thanks.
|
Microfetch. I might have missed your name here, but you have my thanks.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue