Skip to content

Commit 409f5fb

Browse files
Merge #9204
9204: feat: more accurate memory usage info on glibc Linux r=jonas-schievink a=jonas-schievink This adds support for the new `mallinfo2` API added in glibc 2.33. It addresses a shortcoming in the `mallinfo` API where it was unable to handle memory usage of more than 2 GB, which we sometimes exceed. Blocked on rust-lang/libc#2228 Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
2 parents 72ea028 + 74dc9bb commit 409f5fb

File tree

2 files changed

+34
-5
lines changed

2 files changed

+34
-5
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/profile/src/memory_usage.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ impl MemoryUsage {
3232
allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize),
3333
}
3434
} else if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
35-
// Note: This is incredibly slow.
36-
let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
37-
MemoryUsage { allocated: Bytes(alloc) }
35+
memusage_linux()
3836
} else if #[cfg(windows)] {
3937
// There doesn't seem to be an API for determining heap usage, so we try to
4038
// approximate that by using the Commit Charge value.
@@ -58,6 +56,37 @@ impl MemoryUsage {
5856
}
5957
}
6058

59+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
60+
fn memusage_linux() -> MemoryUsage {
61+
// Linux/glibc has 2 APIs for allocator introspection that we can use: mallinfo and mallinfo2.
62+
// mallinfo uses `int` fields and cannot handle memory usage exceeding 2 GB.
63+
// mallinfo2 is very recent, so its presence needs to be detected at runtime.
64+
// Both are abysmally slow.
65+
66+
use std::ffi::CStr;
67+
use std::sync::atomic::{AtomicUsize, Ordering};
68+
69+
static MALLINFO2: AtomicUsize = AtomicUsize::new(1);
70+
71+
let mut mallinfo2 = MALLINFO2.load(Ordering::Relaxed);
72+
if mallinfo2 == 1 {
73+
let cstr = CStr::from_bytes_with_nul(b"mallinfo2\0").unwrap();
74+
mallinfo2 = unsafe { libc::dlsym(libc::RTLD_DEFAULT, cstr.as_ptr()) } as usize;
75+
// NB: races don't matter here, since they'll always store the same value
76+
MALLINFO2.store(mallinfo2, Ordering::Relaxed);
77+
}
78+
79+
if mallinfo2 == 0 {
80+
// mallinfo2 does not exist, use mallinfo.
81+
let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
82+
MemoryUsage { allocated: Bytes(alloc) }
83+
} else {
84+
let mallinfo2: fn() -> libc::mallinfo2 = unsafe { std::mem::transmute(mallinfo2) };
85+
let alloc = mallinfo2().uordblks as isize;
86+
MemoryUsage { allocated: Bytes(alloc) }
87+
}
88+
}
89+
6190
#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
6291
pub struct Bytes(isize);
6392

0 commit comments

Comments
 (0)