#[must_use]
pub fn get_memory_mb() -> usize {
#[cfg(target_os = "linux")]
{
if let Ok(status) = std::fs::read_to_string("/proc/self/status") {
for line in status.lines() {
if line.starts_with("VmRSS:") {
if let Some(kb_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = kb_str.parse::<usize>() {
return kb / 1024;
}
}
}
}
}
}
0
}
#[must_use]
pub fn get_process_memory_kb() -> Option<usize> {
#[cfg(target_os = "linux")]
{
std::fs::read_to_string("/proc/self/status")
.ok()?
.lines()
.find(|line| line.starts_with("VmRSS:"))
.and_then(|line| {
line.split_whitespace()
.nth(1)
.and_then(|s| s.parse::<usize>().ok())
})
}
#[cfg(target_os = "macos")]
{
use std::process::Command;
if let Ok(output) = Command::new("ps")
.args(&["-o", "rss=", "-p", &std::process::id().to_string()])
.output()
{
if let Ok(s) = String::from_utf8(output.stdout) {
if let Ok(kb) = s.trim().parse::<usize>() {
return Some(kb);
}
}
}
None
}
#[cfg(target_os = "windows")]
{
use std::mem;
use winapi::um::processthreadsapi::GetCurrentProcess;
use winapi::um::psapi::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS};
unsafe {
let h_process = GetCurrentProcess();
let mut pmc: PROCESS_MEMORY_COUNTERS = mem::zeroed();
pmc.cb = mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32;
if GetProcessMemoryInfo(h_process, &mut pmc as *mut _ as *mut _, pmc.cb) != 0 {
Some((pmc.WorkingSetSize / 1024) as usize)
} else {
None
}
}
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
{
None
}
}
thread_local! {
static MEMORY_LOG: std::cell::RefCell<Vec<(String, usize)>> = const { std::cell::RefCell::new(Vec::new()) };
}
#[must_use]
pub fn track_memory(label: &str) -> usize {
let mb = get_memory_mb();
MEMORY_LOG.with(|log| {
let mut log = log.borrow_mut();
let delta = if let Some((_, last_mb)) = log.last() {
let diff = (mb as i32) - (*last_mb as i32);
if diff != 0 {
format!(" ({diff:+} MB)")
} else {
String::new()
}
} else {
String::new()
};
log.push((label.to_string(), mb));
if log.len() > 30 {
log.remove(0);
}
tracing::info!("MEMORY[{}]: {} MB{}", label, mb, delta);
});
mb
}
#[must_use]
pub fn get_memory_history() -> Vec<(String, usize)> {
MEMORY_LOG.with(|log| log.borrow().clone())
}
#[must_use]
pub fn format_memory_history() -> String {
MEMORY_LOG.with(|log| {
let log = log.borrow();
let mut output = String::from("Memory History:\n");
for (i, (label, mb)) in log.iter().enumerate() {
let delta = if i > 0 {
let prev_mb = log[i - 1].1;
let diff = (*mb as i32) - (prev_mb as i32);
if diff != 0 {
format!(" ({diff:+} MB)")
} else {
String::new()
}
} else {
String::new()
};
output.push_str(&format!(" {label}: {mb} MB{delta}\n"));
}
output
})
}