]> git.lizzy.rs Git - rust.git/blobdiff - crates/profile/src/memory_usage.rs
Add support for mallinfo2 on glibc Linux
[rust.git] / crates / profile / src / memory_usage.rs
index 6ef58c9c14e9b05096980a165b35c13664961e52..0b0a196aeb7265a7c7d44b56dadd1a532dcb8850 100644 (file)
@@ -32,9 +32,7 @@ pub fn now() -> MemoryUsage {
                     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.
@@ -58,6 +56,37 @@ pub fn now() -> MemoryUsage {
     }
 }
 
+#[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);