malloc_info/lib.rs
1//! This crate provides safe access to glibc's `malloc_info` function. See the
2//! [malloc_info(3)](https://man7.org/linux/man-pages/man3/malloc_info.3.html) page for details on
3//! that function.
4//!
5//! # Example
6//! ```rust
7//! # use malloc_info::malloc_info;
8//! let info = malloc_info().expect("malloc_info");
9//! println!("{:#?}", info);
10//! ```
11//!
12//! # Caveats
13//! `malloc_info` is a glibc-specific function and is not available on all platforms. This crate
14//! will not work on platforms where `malloc_info` is not available.
15//!
16//! `malloc_info` will only report heap statistics for the glibc heap. If your program uses a
17//! different heap implementation, for example by `#[global_allocator]` or by using a different
18//! libc, `malloc_info` will not report statistics for that heap.
19
20use errno::Errno;
21use thiserror::Error;
22
23pub mod info;
24mod memstream;
25
26use memstream::MemStream;
27
28/// Internal representation for errors occurring during the [`malloc_info`] call. This is private so
29/// we can modify it without breaking the public API.
30#[derive(Debug, Error)]
31enum ErrorRepr {
32 /// An error occurred when interfacing with libc
33 #[error("libc error: {0}")]
34 LibC(#[from] Errno),
35
36 /// An internal error occurred when interfacing with the memstream module
37 #[error(transparent)]
38 Memstream(#[from] memstream::Error),
39
40 /// An error occurred when parsing the XML output of `malloc_info`
41 #[error("failed to parse malloc_info XML output: {0}")]
42 Xml(#[from] quick_xml::DeError),
43}
44
45/// Custom error type for errors occurring during the [`malloc_info`] call
46#[derive(Debug, Error)]
47#[error(transparent)]
48pub struct Error(#[from] ErrorRepr);
49
50/// Safely get information from [`libc::malloc_info`]. See library-level documentation for more
51/// information.
52pub fn malloc_info() -> Result<info::Malloc, Error> {
53 fn malloc_info() -> Result<info::Malloc, ErrorRepr> {
54 let mem_stream = MemStream::new()?;
55 let mut cursor = std::io::Cursor::new(mem_stream);
56
57 // SAFETY: `libc::malloc_info` is marked unsafe because it is in the libc crate and it deals
58 // with raw pointers. Being in the libc crate is not inherently unsafe. The raw pointer it
59 // deals with is a pointer to a FILE struct, taken from the mem_stream object, which we control
60 // and have exclusive, mutable access to in this function, ensuring no other code can access
61 // it.
62 //
63 // The same logic applies to `libc::fflush`.
64 unsafe {
65 if libc::malloc_info(0, cursor.get_mut().fp) != 0 {
66 return Err(errno::errno().into());
67 }
68
69 if libc::fflush(cursor.get_mut().fp) != 0 {
70 return Err(errno::errno().into());
71 }
72 }
73
74 Ok(quick_xml::de::from_reader(&mut cursor)?)
75 }
76 malloc_info().map_err(Error::from)
77}
78
79#[cfg(test)]
80mod test {
81 use super::*;
82
83 #[tokio::test]
84 async fn call_from_async() {
85 let _ = tokio::task::spawn(async { malloc_info().expect("malloc_info") }).await;
86 }
87}