From da7a09c358a041d8a922d41c2c926569edc18879 Mon Sep 17 00:00:00 2001 From: James Brown Date: Wed, 29 Apr 2026 12:33:16 -0700 Subject: [PATCH] feat: expose mi_stats_get_json and a safe wrapper around it --- libmimalloc-sys/src/extended.rs | 8 +++++++- src/extended.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/libmimalloc-sys/src/extended.rs b/libmimalloc-sys/src/extended.rs index d37adba..2d60c48 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. @@ -1092,7 +1098,7 @@ mod tests { #[test] fn it_calculates_usable_size() { - let ptr = unsafe { mi_malloc(32) } as *mut u8; + let ptr = unsafe { crate::mi_malloc(32) } as *mut u8; let usable_size = unsafe { mi_usable_size(ptr as *mut c_void) }; assert!( usable_size >= 32, diff --git a/src/extended.rs b/src/extended.rs index be9614c..6861dde 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,29 @@ impl MiMalloc { pub unsafe fn usable_size(&self, ptr: *const u8) -> usize { ffi::mi_usable_size(ptr as *const c_void) } + + /// Call the given function with a string version of the JSON stats for the whole process + /// + /// Allocates (using mimalloc itself) to store the JSON structure. + #[cfg(not(feature = "v2"))] + pub fn with_stats_json(f: F) -> Result + where + F: FnOnce(&str) -> O, + { + unsafe { + let buf = ffi::mi_stats_get_json(0, core::ptr::null::() as *mut _); + if buf.is_null() { + return Err("failed to call mi_stats_get_json"); + } + let cstr = CStr::from_ptr(buf); + let slice = cstr + .to_str() + .map_err(|_| "mi_stats_get_json contained invalid UTF-8")?; + let o = f(slice); + ffi::mi_free(buf as _); + Ok(o) + } + } } #[cfg(test)] @@ -43,4 +68,12 @@ mod test { assert!(usable_size >= 8); } } + + #[test] + #[cfg(not(feature = "v2"))] + fn test_with_stats_json() { + let (first_char, len) = MiMalloc::with_stats_json(|f| (f.chars().next(), f.len())).unwrap(); + assert_eq!(first_char, Some('{')); + assert!(len > 1); + } }