diff --git a/libmimalloc-sys/src/extended.rs b/libmimalloc-sys/src/extended.rs index d37adba..52e4770 100644 --- a/libmimalloc-sys/src/extended.rs +++ b/libmimalloc-sys/src/extended.rs @@ -447,6 +447,12 @@ extern "C" { /// /// Note: This function is thread safe. pub fn mi_register_error(out: mi_error_fun, arg: *mut c_void); + + /// Get the statistics for the current subprocess aggregated over all its heaps as JSON. + /// + /// Returns pointer to the buffer or NULL on failure. Use mi_free() to free the buffer if the buf parameter was NULL. + #[cfg(not(feature = "v2"))] + pub fn mi_stats_get_json(buf_size: usize, buf: *mut c_char) -> *mut c_char; } /// An output callback. Must be thread-safe. @@ -1088,6 +1094,7 @@ extern "C" { #[cfg(test)] mod tests { + use super::super::mi_malloc; use super::*; #[test] diff --git a/libmimalloc-sys/sys-test/build.rs b/libmimalloc-sys/sys-test/build.rs index 27ab233..5a49144 100644 --- a/libmimalloc-sys/sys-test/build.rs +++ b/libmimalloc-sys/sys-test/build.rs @@ -55,5 +55,11 @@ fn main() { } }); + if version == "v3" { + cfg.header("mimalloc-stats.h").include(format!( + "{cargo_manifest_dir}/../c_src/mimalloc/{version}/include" + )); + } + cfg.generate("../src/lib.rs", "all.rs"); } diff --git a/src/extended.rs b/src/extended.rs index be9614c..acf0c14 100644 --- a/src/extended.rs +++ b/src/extended.rs @@ -1,5 +1,7 @@ use crate::MiMalloc; use core::ffi::c_void; +#[cfg(not(feature = "v2"))] +use core::ffi::{c_char, CStr}; impl MiMalloc { /// Get the mimalloc version. @@ -17,6 +19,48 @@ impl MiMalloc { pub unsafe fn usable_size(&self, ptr: *const u8) -> usize { ffi::mi_usable_size(ptr as *const c_void) } + + /// Extract a string containing the JSON statistics for the whole process + /// + /// Allocates (using mimalloc itself) to store the JSON structure. + #[cfg(not(feature = "v2"))] + pub fn stats_json() -> Result { + unsafe { + let buf = ffi::mi_stats_get_json(0, core::ptr::null::() as *mut _); + if let Some(inner) = core::ptr::NonNull::new(buf) { + Ok(StatsJson { inner }) + } else { + Err("failed to call mi_stats_get_json") + } + } + } +} + +#[cfg(not(feature = "v2"))] +/// Wrapper around the output of `MiMalloc::stats_json()` +/// +/// Derefs to a CStr +pub struct StatsJson { + inner: core::ptr::NonNull, +} + +#[cfg(not(feature = "v2"))] +impl core::ops::Deref for StatsJson { + type Target = CStr; + + fn deref(&self) -> &Self::Target { + unsafe { + let cstr = CStr::from_ptr(self.inner.as_ptr()); + &cstr + } + } +} + +#[cfg(not(feature = "v2"))] +impl Drop for StatsJson { + fn drop(&mut self) { + unsafe { ffi::mi_free(self.inner.as_ptr() as _) } + } } #[cfg(test)] @@ -43,4 +87,13 @@ mod test { assert!(usable_size >= 8); } } + + #[test] + #[cfg(not(feature = "v2"))] + fn test_stats_json() { + let stats = MiMalloc::stats_json().expect("should get stats"); + let slice = stats.to_str().expect("should be valid UTF-8"); + assert_eq!(slice.chars().next(), Some('{')); + assert!(stats.count_bytes() > 1); + } } diff --git a/src/lib.rs b/src/lib.rs index 4288b2f..f54f1da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ //! own benchmarks. //! //! To enable secure mode, put in `Cargo.toml`: -//! ```rust,ignore +//! ```toml //! [dependencies] //! mimalloc = { version = "*", features = ["secure"] } //! ```