blob: a5aef998ad9cab798eb6f7b5b34c0e1720c2b1cb [file] [log] [blame] [edit]
//! `jemalloc` control and introspection.
//!
//! `jemalloc` offers a powerful introspection and control interface through the `mallctl` function.
//! It can be used to tune the allocator, take heap dumps, and retrieve statistics. This crate
//! provides a typed API over that interface.
//!
//! While `mallctl` takes a string to specify an operation (e.g. `stats.allocated` or
//! `stats.arenas.15.muzzy_decay_ms`), the overhead of repeatedly parsing those strings is not
//! ideal. Fortunately, `jemalloc` offers the ability to translate the string ahead of time into a
//! "Management Information Base" (MIB) to speed up future lookups.
//!
//! This crate provides a type for each `mallctl` operation. Calling
//! `$op::{read(), write(x), update(x)}` on the type calls `mallctl` with the
//! string-based API. If the operation will be repeatedly performed, a MIB for
//! the operation can be obtained using `$op.mib()`.
//!
//! # Examples
//!
//! Repeatedly printing allocation statistics:
//!
//! ```no_run
//! use std::thread;
//! use std::time::Duration;
//! use tikv_jemalloc_ctl::{stats, epoch};
//!
//! #[global_allocator]
//! static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
//!
//! fn main() {
//! loop {
//! // many statistics are cached and only updated when the epoch is advanced.
//! epoch::advance().unwrap();
//!
//! let allocated = stats::allocated::read().unwrap();
//! let resident = stats::resident::read().unwrap();
//! println!("{} bytes allocated/{} bytes resident", allocated, resident);
//! thread::sleep(Duration::from_secs(10));
//! }
//! }
//! ```
//!
//! Doing the same with the MIB-based API:
//!
//! ```no_run
//! use std::thread;
//! use std::time::Duration;
//! use tikv_jemalloc_ctl::{stats, epoch};
//!
//! #[global_allocator]
//! static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
//!
//! fn main() {
//! let e = epoch::mib().unwrap();
//! let allocated = stats::allocated::mib().unwrap();
//! let resident = stats::resident::mib().unwrap();
//! loop {
//! // many statistics are cached and only updated when the epoch is advanced.
//! e.advance().unwrap();
//!
//! let allocated = allocated.read().unwrap();
//! let resident = resident.read().unwrap();
//! println!("{} bytes allocated/{} bytes resident", allocated, resident);
//! thread::sleep(Duration::from_secs(10));
//! }
//! }
//! ```
// TODO: rename the following lint on next minor bump
#![allow(renamed_and_removed_lints)]
#![deny(missing_docs, broken_intra_doc_links)]
#![cfg_attr(not(feature = "use_std"), no_std)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::module_name_repetitions))]
#[cfg(test)]
#[global_allocator]
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
use crate::std::{fmt, mem, num, ops, ptr, result, slice, str};
#[cfg(not(feature = "use_std"))]
use core as std;
#[cfg(feature = "use_std")]
use std;
#[macro_use]
mod macros;
pub mod arenas;
pub mod config;
mod error;
mod keys;
pub mod opt;
pub mod raw;
pub mod stats;
#[cfg(feature = "use_std")]
pub mod stats_print;
pub mod thread;
pub use error::{Error, Result};
pub use keys::{Access, AsName, Mib, MibStr, Name};
option! {
version[ str: b"version\0", str: 1 ] => &'static str |
ops: r |
docs:
/// `jemalloc` version string.
///
/// # Example
///
/// ```
/// # #[global_allocator]
/// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use tikv_jemalloc_ctl::version;
/// println!("jemalloc version {}", version::read().unwrap());
/// let version_mib = version::mib().unwrap();
/// println!("jemalloc version {}", version_mib.read().unwrap());
/// # }
/// ```
mib_docs: /// See [`version`].
}
option! {
background_thread[ str: b"background_thread\0", non_str: 1 ] => bool |
ops: r,w,u |
docs:
/// State of internal background worker threads.
///
/// When enabled, background threads are created on demand (the number of
/// background threads will be no more than the number of CPUs or active
/// arenas). Threads run periodically and handle purging asynchronously.
///
/// ```
/// # #[global_allocator]
/// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
/// #
/// # fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// #
/// use tikv_jemalloc_ctl::background_thread;
/// let bg = background_thread::mib().unwrap();
/// let s = bg.read().unwrap();
/// println!("background_threads enabled: {}", s);
/// let p = background_thread::update(!s).unwrap();
/// println!("background_threads enabled: {} => {}", p, bg.read().unwrap());
/// assert_eq!(p, s);
/// background_thread::write(s).unwrap();
/// println!("background_threads enabled: {}", bg.read().unwrap());
/// assert_eq!(p, s);
/// #
/// # } // #[cfg(..)]
/// # }
/// ```
mib_docs: /// See [`background_thread`].
}
option! {
max_background_threads[ str: b"max_background_threads\0", non_str: 1 ] => libc::size_t |
ops: r, w, u |
docs:
/// Maximum number of background threads that will be created.
///
/// ```
/// # #[global_allocator]
/// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
/// #
/// # fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// #
/// use tikv_jemalloc_ctl::max_background_threads;
/// let m = max_background_threads::mib().unwrap();
/// println!("max_background_threads: {}", m.read().unwrap());
/// m.write(2).unwrap();
/// assert_eq!(m.read().unwrap(), 2);
/// #
/// # } // #[cfg(..)]
/// # }
/// ```
mib_docs: /// See [`max_background_threads`].
}
option! {
epoch[ str: b"epoch\0", non_str: 1 ] => u64 |
ops: r, w, u |
docs:
/// `jemalloc` epoch.
///
/// Many of the statistics tracked by `jemalloc` are cached. The epoch
/// controls when they are refreshed.
///
/// # Example
///
/// Advancing the epoch:
///
/// ```
/// # #[global_allocator]
/// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
/// #
/// # fn main() {
/// #
/// use tikv_jemalloc_ctl::epoch;
/// let e = epoch::mib().unwrap();
/// let a = e.advance().unwrap();
/// let b = e.advance().unwrap();
/// assert_eq!(a + 1, b);
///
/// let o = e.update(0).unwrap();
/// assert_eq!(o, e.read().unwrap());
/// # }
mib_docs: /// See [`epoch`].
}
impl epoch {
/// Advances the epoch returning its old value - see [`epoch`].
pub fn advance() -> crate::error::Result<u64> {
Self::update(1)
}
}
impl epoch_mib {
/// Advances the epoch returning its old value - see [`epoch`].
pub fn advance(self) -> crate::error::Result<u64> {
self.0.update(1)
}
}