pprof-alloc 0.2.0

Allocation profiling and Linux memory telemetry for Rust services.
Documentation
#[cfg(not(all(
	feature = "frame-pointer",
	target_os = "linux",
	any(target_arch = "x86_64", target_arch = "aarch64")
)))]
use backtrace::trace;
#[cfg(all(
	feature = "frame-pointer",
	target_os = "linux",
	any(target_arch = "x86_64", target_arch = "aarch64")
))]
use frame_pointer::trace;

use itertools::Itertools;
use serde::Serialize;
use smallvec::SmallVec;
use std::fmt;
use std::hash::{Hash, Hasher};

#[cfg(not(all(
	feature = "frame-pointer",
	target_os = "linux",
	any(target_arch = "x86_64", target_arch = "aarch64")
)))]
mod backtrace;
#[cfg(all(
	feature = "frame-pointer",
	target_os = "linux",
	any(target_arch = "x86_64", target_arch = "aarch64")
))]
mod frame_pointer;

const SOFT_MAX_DEPTH: usize = 128;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
pub enum CaptureMode {
	FramePointer,
	Backtrace,
}

#[cfg(all(
	feature = "frame-pointer",
	target_os = "linux",
	any(target_arch = "x86_64", target_arch = "aarch64")
))]
pub const fn capture_mode() -> CaptureMode {
	CaptureMode::FramePointer
}

#[cfg(not(all(
	feature = "frame-pointer",
	target_os = "linux",
	any(target_arch = "x86_64", target_arch = "aarch64")
)))]
pub const fn capture_mode() -> CaptureMode {
	CaptureMode::Backtrace
}

#[derive(Clone)]
struct UnresolvedFrames(SmallVec<[u64; SOFT_MAX_DEPTH]>);

impl From<SmallVec<[u64; SOFT_MAX_DEPTH]>> for UnresolvedFrames {
	fn from(x: SmallVec<[u64; SOFT_MAX_DEPTH]>) -> Self {
		Self(x)
	}
}

#[derive(Clone)]
pub struct HashedBacktrace {
	inner: UnresolvedFrames,
	hash: u64,
}

impl fmt::Debug for HashedBacktrace {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		let address = self.inner.0.iter().map(|x| format!("{:#x}", x)).join(" ");
		f.write_str(&address)
	}
}

impl HashedBacktrace {
	pub fn capture() -> Self {
		let bt = trace();
		let mut hasher = ahash::AHasher::default();
		bt.0.iter().for_each(|x| hasher.write_u64(*x));
		let hash = hasher.finish();
		Self { inner: bt, hash }
	}
	pub fn addrs(&self) -> Vec<u64> {
		self.inner.0.iter().copied().collect_vec()
	}
}

impl PartialEq for HashedBacktrace {
	fn eq(&self, other: &Self) -> bool {
		self.hash == other.hash && self.inner.0 == other.inner.0
	}
}

impl Eq for HashedBacktrace {}

impl Hash for HashedBacktrace {
	fn hash<H: Hasher>(&self, state: &mut H) {
		self.inner.0.iter().for_each(|x| state.write_u64(*x));
	}
}