mod collections;
mod path;
pub use path::{Path, PathGuard};
pub trait LogicalMemoryProfile {
fn visit_logical_memory<V: LogicalMemoryVisitor>(&self, path: &mut Path, visitor: &mut V);
}
pub trait LogicalMemoryVisitor {
fn visit_leaf(&mut self, path: &Path, bytes: usize);
}
#[derive(Debug, Default)]
pub struct FoldedCollector {
aggregated: std::collections::HashMap<String, usize>,
}
impl FoldedCollector {
pub fn new() -> Self {
Self::default()
}
pub fn finish(self) -> String {
let mut lines: Vec<_> = self
.aggregated
.into_iter()
.map(|(path, bytes)| format!("{path} {bytes}"))
.collect();
lines.sort();
lines.join("\n")
}
}
impl LogicalMemoryVisitor for FoldedCollector {
fn visit_leaf(&mut self, path: &Path, bytes: usize) {
if bytes == 0 {
return;
}
let frames = path.as_slice().join(";");
*self.aggregated.entry(frames).or_insert(0) += bytes;
}
}
pub fn logical_memory_to_folded<T: LogicalMemoryProfile>(value: &T) -> String {
let mut path = Path::new();
let mut collector = FoldedCollector::new();
value.visit_logical_memory(&mut path, &mut collector);
collector.finish()
}
pub fn logical_total_bytes<T: LogicalMemoryProfile>(value: &T) -> usize {
struct Sum(usize);
impl LogicalMemoryVisitor for Sum {
fn visit_leaf(&mut self, _path: &Path, bytes: usize) {
self.0 += bytes;
}
}
let mut path = Path::new();
let mut sum = Sum(0);
value.visit_logical_memory(&mut path, &mut sum);
sum.0
}
#[macro_export]
macro_rules! impl_logical_memory_profile {
($type_path:path as $type_name:literal { $($field:ident),* $(,)? }) => {
impl $crate::logical_memory::LogicalMemoryProfile for $type_path {
fn visit_logical_memory<V: $crate::logical_memory::LogicalMemoryVisitor>(
&self,
path: &mut $crate::logical_memory::Path,
visitor: &mut V,
) {
$(
self.$field.visit_logical_memory(
path.with(concat!($type_name, ".", stringify!($field))).as_mut(),
visitor,
);
)*
}
}
};
($type_name:ident { $($field:ident),* $(,)? }) => {
impl $crate::logical_memory::LogicalMemoryProfile for $type_name {
fn visit_logical_memory<V: $crate::logical_memory::LogicalMemoryVisitor>(
&self,
path: &mut $crate::logical_memory::Path,
visitor: &mut V,
) {
$(
self.$field.visit_logical_memory(
path.with(concat!(stringify!($type_name), ".", stringify!($field))).as_mut(),
visitor,
);
)*
}
}
};
}
macro_rules! impl_logical_memory_profile_for_primitive {
($($ty:ty),*) => {
$(
impl LogicalMemoryProfile for $ty {
fn visit_logical_memory<V: LogicalMemoryVisitor>(&self, path: &mut Path, visitor: &mut V) {
use std::mem::size_of;
visitor.visit_leaf(path, size_of::<$ty>());
}
}
)*
};
}
impl_logical_memory_profile_for_primitive!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64
);
#[cfg(test)]
mod tests;