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}