jemalloc_ctl/
lib.rs

1//! `jemalloc` control and introspection.
2//!
3//! `jemalloc` offers a powerful introspection and control interface through the `mallctl` function.
4//! It can be used to tune the allocator, take heap dumps, and retrieve statistics. This crate
5//! provides a typed API over that interface.
6//!
7//! While `mallctl` takes a string to specify an operation (e.g. `stats.allocated` or
8//! `stats.arenas.15.muzzy_decay_ms`), the overhead of repeatedly parsing those strings is not
9//! ideal. Fortunately, `jemalloc` offers the ability to translate the string ahead of time into a
10//! "Management Information Base" (MIB) to speed up future lookups.
11//!
12//! This crate provides a type for each `mallctl` operation. Calling
13//! `$op::{read(), write(x), update(x)}` on the type calls `mallctl` with the
14//! string-based API. If the operation will be repeatedly performed, a MIB for
15//! the operation can be obtained using `$op.mib()`.
16//!
17//! # Examples
18//!
19//! Repeatedly printing allocation statistics:
20//!
21//! ```no_run
22//! use std::thread;
23//! use std::time::Duration;
24//! use jemalloc_ctl::{stats, epoch};
25//!
26//! #[global_allocator]
27//! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
28//!
29//! fn main() {
30//!     loop {
31//!         // many statistics are cached and only updated when the epoch is advanced.
32//!         epoch::advance().unwrap();
33//!
34//!         let allocated = stats::allocated::read().unwrap();
35//!         let resident = stats::resident::read().unwrap();
36//!         println!("{} bytes allocated/{} bytes resident", allocated, resident);
37//!         thread::sleep(Duration::from_secs(10));
38//!     }
39//! }
40//! ```
41//!
42//! Doing the same with the MIB-based API:
43//!
44//! ```no_run
45//! use std::thread;
46//! use std::time::Duration;
47//! use jemalloc_ctl::{stats, epoch};
48//!
49//! #[global_allocator]
50//! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
51//!
52//! fn main() {
53//!     let e = epoch::mib().unwrap();
54//!     let allocated = stats::allocated::mib().unwrap();
55//!     let resident = stats::resident::mib().unwrap();
56//!     loop {
57//!         // many statistics are cached and only updated when the epoch is advanced.
58//!         e.advance().unwrap();
59//!
60//!         let allocated = allocated.read().unwrap();
61//!         let resident = resident.read().unwrap();
62//!         println!("{} bytes allocated/{} bytes resident", allocated, resident);
63//!         thread::sleep(Duration::from_secs(10));
64//!     }
65//! }
66//! ```
67// TODO: rename the following lint on next minor bump
68#![allow(renamed_and_removed_lints)]
69#![deny(missing_docs, broken_intra_doc_links)]
70#![cfg_attr(not(feature = "use_std"), no_std)]
71#![cfg_attr(feature = "cargo-clippy", allow(clippy::module_name_repetitions))]
72
73#[cfg(test)]
74#[global_allocator]
75static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
76
77use crate::std::{fmt, mem, num, ops, ptr, result, slice, str};
78#[cfg(not(feature = "use_std"))]
79use core as std;
80#[cfg(feature = "use_std")]
81use std;
82
83#[macro_use]
84mod macros;
85
86pub mod arenas;
87pub mod config;
88mod error;
89mod keys;
90pub mod opt;
91pub mod raw;
92pub mod stats;
93#[cfg(feature = "use_std")]
94pub mod stats_print;
95pub mod thread;
96
97pub use error::{Error, Result};
98pub use keys::{Access, AsName, Mib, MibStr, Name};
99
100option! {
101    version[ str: b"version\0", str: 1 ] => &'static str |
102    ops: r |
103    docs:
104    /// `jemalloc` version string.
105    ///
106    /// # Example
107    ///
108    /// ```
109    /// # #[global_allocator]
110    /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
111    /// #
112    /// # fn main() {
113    /// use jemalloc_ctl::version;
114    /// println!("jemalloc version {}", version::read().unwrap());
115    /// let version_mib = version::mib().unwrap();
116    /// println!("jemalloc version {}", version_mib.read().unwrap());
117    /// # }
118    /// ```
119    mib_docs: /// See [`version`].
120}
121
122option! {
123    background_thread[ str: b"background_thread\0", non_str: 1 ] => bool |
124    ops: r,w,u |
125    docs:
126    /// State of internal background worker threads.
127    ///
128    /// When enabled, background threads are created on demand (the number of
129    /// background threads will be no more than the number of CPUs or active
130    /// arenas). Threads run periodically and handle purging asynchronously.
131    ///
132    /// ```
133    /// # #[global_allocator]
134    /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
135    /// #
136    /// # fn main() {
137    /// # #[cfg(not(target_os = "macos"))] {
138    /// #
139    /// use jemalloc_ctl::background_thread;
140    /// let bg = background_thread::mib().unwrap();
141    /// let s = bg.read().unwrap();
142    /// println!("background_threads enabled: {}", s);
143    /// let p = background_thread::update(!s).unwrap();
144    /// println!("background_threads enabled: {} => {}", p, bg.read().unwrap());
145    /// assert_eq!(p, s);
146    /// background_thread::write(s).unwrap();
147    /// println!("background_threads enabled: {}", bg.read().unwrap());
148    /// assert_eq!(p, s);
149    /// #
150    /// # } // #[cfg(..)]
151    /// # }
152    /// ```
153    mib_docs: /// See [`background_thread`].
154}
155
156option! {
157    max_background_threads[ str: b"max_background_threads\0", non_str: 1 ] => libc::size_t |
158    ops: r, w, u |
159    docs:
160    /// Maximum number of background threads that will be created.
161    ///
162    /// ```
163    /// # #[global_allocator]
164    /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
165    /// #
166    /// # fn main() {
167    /// # #[cfg(not(target_os = "macos"))] {
168    /// #
169    /// use jemalloc_ctl::max_background_threads;
170    /// let m = max_background_threads::mib().unwrap();
171    /// println!("max_background_threads: {}", m.read().unwrap());
172    /// m.write(2).unwrap();
173    /// assert_eq!(m.read().unwrap(), 2);
174    /// #
175    /// # } // #[cfg(..)]
176    /// # }
177    /// ```
178    mib_docs: /// See [`max_background_threads`].
179}
180
181option! {
182    epoch[ str: b"epoch\0", non_str: 1 ] => u64 |
183    ops: r, w, u |
184    docs:
185    /// `jemalloc` epoch.
186    ///
187    /// Many of the statistics tracked by `jemalloc` are cached. The epoch
188    /// controls when they are refreshed.
189    ///
190    /// # Example
191    ///
192    /// Advancing the epoch:
193    ///
194    /// ```
195    /// # #[global_allocator]
196    /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
197    /// #
198    /// # fn main() {
199    /// #
200    /// use jemalloc_ctl::epoch;
201    /// let e = epoch::mib().unwrap();
202    /// let a = e.advance().unwrap();
203    /// let b = e.advance().unwrap();
204    /// assert_eq!(a + 1, b);
205    ///
206    /// let o = e.update(0).unwrap();
207    /// assert_eq!(o, e.read().unwrap());
208    /// # }
209    mib_docs: /// See [`epoch`].
210}
211
212impl epoch {
213    /// Advances the epoch returning its old value - see [`epoch`].
214    pub fn advance() -> crate::error::Result<u64> {
215        Self::update(1)
216    }
217}
218
219impl epoch_mib {
220    /// Advances the epoch returning its old value - see [`epoch`].
221    pub fn advance(self) -> crate::error::Result<u64> {
222        self.0.update(1)
223    }
224}