allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize),
}
} else if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
- // Note: This is incredibly slow.
- let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
- MemoryUsage { allocated: Bytes(alloc) }
+ memusage_linux()
} else if #[cfg(windows)] {
// There doesn't seem to be an API for determining heap usage, so we try to
// approximate that by using the Commit Charge value.
}
}
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+fn memusage_linux() -> MemoryUsage {
+ // Linux/glibc has 2 APIs for allocator introspection that we can use: mallinfo and mallinfo2.
+ // mallinfo uses `int` fields and cannot handle memory usage exceeding 2 GB.
+ // mallinfo2 is very recent, so its presence needs to be detected at runtime.
+ // Both are abysmally slow.
+
+ use std::ffi::CStr;
+ use std::sync::atomic::{AtomicUsize, Ordering};
+
+ static MALLINFO2: AtomicUsize = AtomicUsize::new(1);
+
+ let mut mallinfo2 = MALLINFO2.load(Ordering::Relaxed);
+ if mallinfo2 == 1 {
+ let cstr = CStr::from_bytes_with_nul(b"mallinfo2\0").unwrap();
+ mallinfo2 = unsafe { libc::dlsym(libc::RTLD_DEFAULT, cstr.as_ptr()) } as usize;
+ // NB: races don't matter here, since they'll always store the same value
+ MALLINFO2.store(mallinfo2, Ordering::Relaxed);
+ }
+
+ if mallinfo2 == 0 {
+ // mallinfo2 does not exist, use mallinfo.
+ let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
+ MemoryUsage { allocated: Bytes(alloc) }
+ } else {
+ let mallinfo2: fn() -> libc::mallinfo2 = unsafe { std::mem::transmute(mallinfo2) };
+ let alloc = mallinfo2().uordblks as isize;
+ MemoryUsage { allocated: Bytes(alloc) }
+ }
+}
+
#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct Bytes(isize);