separate safe/unsafe ctl apis, test everything

This commit is contained in:
gnzlbg 2018-11-09 10:25:44 +01:00 committed by gnzlbg
parent 7fb8e425e5
commit 1abc6d7ef8
17 changed files with 1056 additions and 1724 deletions

View File

@ -24,6 +24,7 @@ maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
jemalloc-sys = { path = "../jemalloc-sys", version = "0.3.0" } jemalloc-sys = { path = "../jemalloc-sys", version = "0.3.0" }
libc = { version = "0.2", default-features = false } libc = { version = "0.2", default-features = false }
paste = { version = "0.1" }
[dev-dependencies] [dev-dependencies]
jemallocator = { path = "..", version = "0.3.0" } jemallocator = { path = "..", version = "0.3.0" }

View File

@ -1,64 +1,27 @@
//! Arena operations. //! Arena operations.
use error::Result; option! {
use keys::{Access, AsName, Mib}; narenas[ str: b"arenas.narenas\0", non_str: 2 ] => libc::c_uint |
use libc::c_uint; ops: r |
docs:
const NARENAS: &[u8] = b"arenas.narenas\0"; /// Current limit on the number of arenas.
///
/// Returns the current limit on the number of arenas. /// # Examples
/// ///
/// # Examples /// ```
/// /// # extern crate jemallocator;
/// ``` /// # extern crate jemalloc_ctl;
/// extern crate jemallocator; /// #
/// extern crate jemalloc_ctl; /// # #[global_allocator]
/// /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #[global_allocator] /// #
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// # fn main() {
/// /// use jemalloc_ctl::arenas;
/// fn main() { /// println!("number of arenas: {}", arenas::narenas::read().unwrap());
/// println!( ///
/// "number of arenas: {}", /// let arenas_mib = arenas::narenas::mib().unwrap();
/// jemalloc_ctl::arenas::narenas().unwrap() /// println!("number of arenas: {}", arenas_mib.read().unwrap());
/// ); /// # }
/// } /// ```
/// ``` mib_docs: /// See [`narenas`].
pub fn narenas() -> Result<c_uint> {
NARENAS.name().read()
}
/// A type providing access to the current limit on the number of arenas.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::arenas::NArenas;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let narenas = NArenas::new().unwrap();
///
/// println!("number of arenas: {}", narenas.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct NArenas(Mib<[usize; 2]>);
impl NArenas {
/// Returns a new `NArenas`.
pub fn new() -> Result<Self> {
let mib = NARENAS.name().mib()?;
Ok(NArenas(mib))
}
/// Returns the maximum number of arenas.
pub fn get(self) -> Result<c_uint> {
self.0.read()
}
} }

View File

@ -1,181 +0,0 @@
//! Background thread operations.
use error::Result;
use keys::{Access, AsName, Mib};
const BACKGROUND_THREAD: &[u8] = b"background_thread\0";
/// Returns the 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.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))]
/// println!(
/// "background_thread: {}",
/// jemalloc_ctl::background_thread().unwrap()
/// );
/// }
/// ```
pub fn background_thread() -> Result<bool> {
BACKGROUND_THREAD.name().read()
}
/// Enables or disables 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.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// jemalloc_ctl::set_background_thread(true).unwrap();
/// assert!(jemalloc_ctl::background_thread().unwrap());
/// # }
/// }
/// ```
pub fn set_background_thread(background_thread: bool) -> Result<()> {
BACKGROUND_THREAD.name().write(background_thread)
}
/// A type providing access to the 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.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// let mut background_thread
/// = jemalloc_ctl::BackgroundThread::new().unwrap();
/// background_thread.set(true).unwrap();
/// assert!(background_thread.get().unwrap());
/// # }
/// }
/// ```
#[derive(Copy, Clone)]
pub struct BackgroundThread(Mib<[usize; 1]>);
impl BackgroundThread {
/// Returns a new `BackgroundThread`.
pub fn new() -> Result<Self> {
let mib = BACKGROUND_THREAD.name().mib()?;
Ok(BackgroundThread(mib))
}
/// Returns the current background thread state.
pub fn get(self) -> Result<bool> {
self.0.read()
}
/// Sets the background thread state.
pub fn set(self, background_thread: bool) -> Result<()> {
self.0.write(background_thread)
}
}
const MAX_BACKGROUND_THREADS: &[u8] = b"max_background_threads\0";
/// Returns the maximum number of background threads that will be created.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))]
/// println!(
/// "max_background_threads: {}",
/// jemalloc_ctl::max_background_threads().unwrap()
/// );
/// }
/// ```
pub fn max_background_threads() -> Result<usize> {
MAX_BACKGROUND_THREADS.name().read()
}
/// Sets the maximum number of background threads that will be created.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// jemalloc_ctl::set_max_background_threads(1).unwrap();
/// assert_eq!(jemalloc_ctl::max_background_threads().unwrap(), 1);
/// # }
/// }
/// ```
pub fn set_max_background_threads(max_background_threads: usize) -> Result<()> {
MAX_BACKGROUND_THREADS.name().write(max_background_threads)
}
/// A type providing access to the maximum number of background threads that
/// will be created.
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// let mut max_background_threads
/// = jemalloc_ctl::MaxBackgroundThreads::new().unwrap();
/// max_background_threads.set(1).unwrap();
/// assert_eq!(max_background_threads.get().unwrap(), 1);
/// # }
/// }
/// ```
#[derive(Copy, Clone)]
pub struct MaxBackgroundThreads(Mib<[usize; 1]>);
impl MaxBackgroundThreads {
/// Returns a new `MaxBackgroundThreads`.
pub fn new() -> Result<Self> {
let mib = MAX_BACKGROUND_THREADS.name().mib()?;
Ok(MaxBackgroundThreads(mib))
}
/// Returns the current background thread limit.
pub fn get(self) -> Result<usize> {
self.0.read()
}
/// Sets the background thread limit.
pub fn set(self, max_background_threads: usize) -> Result<()> {
self.0.write(max_background_threads)
}
}

View File

@ -1,69 +1,28 @@
//! Information about the jemalloc compile-time configuration //! `jemalloc`'s build-time configuration.
use error::Result;
use keys::{Access, AsName, MibStr};
const MALLOC_CONF: &[u8] = b"config.malloc_conf\0"; option! {
malloc_conf[ str: b"config.malloc_conf\0", str: 2 ] => &'static str |
/// Returns the embeddec configure-time-specified run-time options config. ops: r |
/// docs:
/// The string will be empty unless `--with-malloc-conf` was specified during /// Default run-time options specified during `jemalloc`'s build configuration.
/// build configuration. ///
/// /// The string will be empty unless `--with-malloc-conf` was specified
/// # Examples /// during build configuration.
/// ///
/// ``` /// # Examples
/// extern crate jemallocator; ///
/// extern crate jemalloc_ctl; /// ```
/// /// # extern crate jemallocator;
/// #[global_allocator] /// # extern crate jemalloc_ctl;
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// #
/// /// # #[global_allocator]
/// fn main() { /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// println!( /// #
/// "default malloc conf: {}", /// # fn main() {
/// jemalloc_ctl::config::malloc_conf().unwrap() /// use jemalloc_ctl::config;
/// ); /// let malloc_conf = config::malloc_conf::mib().unwrap();
/// } /// println!("default malloc conf: {}", malloc_conf.read().unwrap());
/// ``` /// # }
pub fn malloc_conf() -> Result<&'static str> { /// ```
MALLOC_CONF.name().read() mib_docs: /// See [`malloc_conf`].
}
/// A type providing access to the embedded configure-time-specified run-time
/// options config.
///
/// The string will be empty unless `--with-malloc-conf` was specified during
/// build configuration.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::config::MallocConf;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let malloc_conf = MallocConf::new().unwrap();
///
/// println!("default malloc conf: {}", malloc_conf.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct MallocConf(MibStr<[usize; 2]>);
impl MallocConf {
/// Returns a new `MallocConf`.
pub fn new() -> Result<Self> {
let mib = MALLOC_CONF.name().mib_str()?;
Ok(MallocConf(mib))
}
/// Returns the embedded configure-time-specified run-time options config.
pub fn get(self) -> Result<&'static str> {
self.0.read()
}
} }

View File

@ -1,76 +0,0 @@
//! Epoch access.
use error::Result;
use keys::{Access, AsName, Mib};
const EPOCH: &[u8] = b"epoch\0";
/// Advances the jemalloc epoch, returning it.
///
/// Many of the statistics tracked by jemalloc are cached. The epoch controls
/// when they are refreshed.
///
/// # Example
///
/// Advancing the epoch:
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let a = jemalloc_ctl::epoch().unwrap();
/// let b = jemalloc_ctl::epoch().unwrap();
/// assert_eq!(a + 1, b);
/// }
/// ```
pub fn epoch() -> Result<u64> {
EPOCH.name().read_write(1)
}
/// A type providing access to the jemalloc epoch.
///
/// Many of the statistics tracked by jemalloc are cached. The epoch controls
/// when they are refreshed.
///
/// # Example
///
/// Advancing the epoch:
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
///
/// let a = epoch.advance().unwrap();
/// let b = epoch.advance().unwrap();
/// assert_eq!(a + 1, b);
/// }
#[derive(Copy, Clone)]
pub struct Epoch(Mib<[usize; 1]>);
impl Epoch {
/// Returns a new `Epoch`.
pub fn new() -> Result<Self> {
let mib = EPOCH.name().mib()?;
Ok(Epoch(mib))
}
/// Advances the epoch, returning it.
///
/// The epoch advances by 1 every time it is advanced, so the value can be
/// used to determine if another thread triggered a referesh.
pub fn advance(self) -> Result<u64> {
self.0.read_write(1)
}
}

View File

@ -19,7 +19,7 @@ impl NonZeroT for i64 {
pub type NonZeroCInt = <c_int as NonZeroT>::T; pub type NonZeroCInt = <c_int as NonZeroT>::T;
/// Error of the `jemalloc_sys::mallct`-family of functions. /// Errors of the `jemalloc_sys::mallct`-family of functions.
/// ///
/// The `jemalloc-sys` crate: `mallctl`, `mallctlnametomib`, and `mallctlbymib`` /// The `jemalloc-sys` crate: `mallctl`, `mallctlnametomib`, and `mallctlbymib``
/// functions return `0` on success; otherwise they return an error value. /// functions return `0` on success; otherwise they return an error value.

View File

@ -1,8 +1,8 @@
//! Key types to index the _MALLCTL NAMESPACE_. //! Key types to index the _MALLCTL NAMESPACE_.
//! //!
//! The [`Name`] and [`Mib`] types are provided as safe indices into the //! The [`Name`] and [`Mib`]/[`MibStr`] types are provided as safe indices into
//! _MALLCTL NAMESPACE_. These are constructed from slices via the [`AsName`] //! the _MALLCTL NAMESPACE_. These are constructed from null-terminated strings
//! and [`IntoMib`] traits. The [`Access`] trait provides provides safe access //! via the [`AsName`] trait. The [`Access`] trait provides provides safe access
//! into the `_MALLCTL NAMESPACE_`. //! into the `_MALLCTL NAMESPACE_`.
//! //!
//! # Example //! # Example
@ -57,7 +57,13 @@ impl AsName for [u8] {
"cannot create Name from non-null-terminated byte-string \"{}\"", "cannot create Name from non-null-terminated byte-string \"{}\"",
str::from_utf8(self).unwrap() str::from_utf8(self).unwrap()
); );
unsafe { &*(self as *const [u8] as *const Name) } unsafe { &*(self as *const Self as *const Name) }
}
}
impl AsName for str {
fn name(&self) -> &Name {
self.as_bytes().name()
} }
} }
@ -108,6 +114,11 @@ impl Name {
_ => false, _ => false,
} }
} }
/// Returns the name as null-terminated byte-string.
pub fn as_bytes(&self) -> &'static [u8] {
unsafe { &*(self as *const Self as *const [u8]) }
}
} }
impl fmt::Debug for Name { impl fmt::Debug for Name {
@ -124,16 +135,28 @@ impl fmt::Display for Name {
} }
} }
/// Management Information Base. /// Management Information Base of a non-string value.
#[repr(transparent)] #[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Debug, Default)] #[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct Mib<T: MibArg>(T); pub struct Mib<T: MibArg>(T);
/// Management Information Base refering to a string value. /// Management Information Base of a string value.
#[repr(transparent)] #[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Debug, Default)] #[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct MibStr<T: MibArg>(T); pub struct MibStr<T: MibArg>(T);
impl<T: MibArg> AsRef<[usize]> for Mib<T> {
fn as_ref(&self) -> &[usize] {
self.0.as_ref()
}
}
impl<T: MibArg> AsMut<[usize]> for Mib<T> {
fn as_mut(&mut self) -> &mut [usize] {
self.0.as_mut()
}
}
impl<T: MibArg> ops::Index<usize> for Mib<T> { impl<T: MibArg> ops::Index<usize> for Mib<T> {
type Output = usize; type Output = usize;
fn index(&self, idx: usize) -> &Self::Output { fn index(&self, idx: usize) -> &Self::Output {
@ -167,31 +190,31 @@ pub trait Access<T> {
/// Write `value` at the key `self`. /// Write `value` at the key `self`.
fn write(&self, value: T) -> Result<()>; fn write(&self, value: T) -> Result<()>;
/// Write `value` at the key `self` returning its previous value. /// Write `value` at the key `self` returning its previous value.
fn read_write(&self, value: T) -> Result<T>; fn update(&self, value: T) -> Result<T>;
} }
macro_rules! impl_access { macro_rules! impl_access {
($id:ty) => { ($id:ty) => {
impl<T: MibArg> Access<$id> for Mib<T> { impl<T: MibArg> Access<$id> for Mib<T> {
fn read(&self) -> Result<$id> { fn read(&self) -> Result<$id> {
unsafe { raw::get_mib(self.0.as_ref()) } unsafe { raw::read_mib(self.0.as_ref()) }
} }
fn write(&self, value: $id) -> Result<()> { fn write(&self, value: $id) -> Result<()> {
unsafe { raw::set_mib(self.0.as_ref(), value) } unsafe { raw::write_mib(self.0.as_ref(), value) }
} }
fn read_write(&self, value: $id) -> Result<$id> { fn update(&self, value: $id) -> Result<$id> {
unsafe { raw::get_set_mib(self.0.as_ref(), value) } unsafe { raw::update_mib(self.0.as_ref(), value) }
} }
} }
impl Access<$id> for Name { impl Access<$id> for Name {
fn read(&self) -> Result<$id> { fn read(&self) -> Result<$id> {
unsafe { raw::get(&self.0) } unsafe { raw::read(&self.0) }
} }
fn write(&self, value: $id) -> Result<()> { fn write(&self, value: $id) -> Result<()> {
unsafe { raw::set(&self.0, value) } unsafe { raw::write(&self.0, value) }
} }
fn read_write(&self, value: $id) -> Result<$id> { fn update(&self, value: $id) -> Result<$id> {
unsafe { raw::get_set(&self.0, value) } unsafe { raw::update(&self.0, value) }
} }
} }
}; };
@ -205,17 +228,17 @@ impl_access!(usize);
impl<T: MibArg> Access<bool> for Mib<T> { impl<T: MibArg> Access<bool> for Mib<T> {
fn read(&self) -> Result<bool> { fn read(&self) -> Result<bool> {
unsafe { unsafe {
let v: u8 = raw::get_mib(self.0.as_ref())?; let v: u8 = raw::read_mib(self.0.as_ref())?;
assert!(v == 0 || v == 1); assert!(v == 0 || v == 1);
Ok(v == 1) Ok(v == 1)
} }
} }
fn write(&self, value: bool) -> Result<()> { fn write(&self, value: bool) -> Result<()> {
unsafe { raw::set_mib(self.0.as_ref(), value) } unsafe { raw::write_mib(self.0.as_ref(), value) }
} }
fn read_write(&self, value: bool) -> Result<bool> { fn update(&self, value: bool) -> Result<bool> {
unsafe { unsafe {
let v: u8 = raw::get_set_mib(self.0.as_ref(), value as u8)?; let v: u8 = raw::update_mib(self.0.as_ref(), value as u8)?;
Ok(v == 1) Ok(v == 1)
} }
} }
@ -224,17 +247,17 @@ impl<T: MibArg> Access<bool> for Mib<T> {
impl Access<bool> for Name { impl Access<bool> for Name {
fn read(&self) -> Result<bool> { fn read(&self) -> Result<bool> {
unsafe { unsafe {
let v: u8 = raw::get(&self.0)?; let v: u8 = raw::read(&self.0)?;
assert!(v == 0 || v == 1); assert!(v == 0 || v == 1);
Ok(v == 1) Ok(v == 1)
} }
} }
fn write(&self, value: bool) -> Result<()> { fn write(&self, value: bool) -> Result<()> {
unsafe { raw::set(&self.0, value) } unsafe { raw::write(&self.0, value) }
} }
fn read_write(&self, value: bool) -> Result<bool> { fn update(&self, value: bool) -> Result<bool> {
unsafe { unsafe {
let v: u8 = raw::get_set(&self.0, value as u8)?; let v: u8 = raw::update(&self.0, value as u8)?;
Ok(v == 1) Ok(v == 1)
} }
} }
@ -244,15 +267,15 @@ impl<T: MibArg> Access<&'static [u8]> for MibStr<T> {
fn read(&self) -> Result<&'static [u8]> { fn read(&self) -> Result<&'static [u8]> {
// this is safe because the only safe way to construct a `MibStr` is by // this is safe because the only safe way to construct a `MibStr` is by
// validating that the key refers to a byte-string value // validating that the key refers to a byte-string value
unsafe { raw::get_str_mib(self.0.as_ref()) } unsafe { raw::read_str_mib(self.0.as_ref()) }
} }
fn write(&self, value: &'static [u8]) -> Result<()> { fn write(&self, value: &'static [u8]) -> Result<()> {
raw::set_str_mib(self.0.as_ref(), value) raw::write_str_mib(self.0.as_ref(), value)
} }
fn read_write(&self, value: &'static [u8]) -> Result<&'static [u8]> { fn update(&self, value: &'static [u8]) -> Result<&'static [u8]> {
// this is safe because the only safe way to construct a `MibStr` is by // this is safe because the only safe way to construct a `MibStr` is by
// validating that the key refers to a byte-string value // validating that the key refers to a byte-string value
unsafe { raw::get_set_str_mib(self.0.as_ref(), value) } unsafe { raw::update_str_mib(self.0.as_ref(), value) }
} }
} }
@ -264,7 +287,7 @@ impl Access<&'static [u8]> for Name {
self self
); );
// this is safe because the key refers to a byte string: // this is safe because the key refers to a byte string:
unsafe { raw::get_str(&self.0) } unsafe { raw::read_str(&self.0) }
} }
fn write(&self, value: &'static [u8]) -> Result<()> { fn write(&self, value: &'static [u8]) -> Result<()> {
assert!( assert!(
@ -272,16 +295,16 @@ impl Access<&'static [u8]> for Name {
"the name \"{:?}\" does not refer to a byte string", "the name \"{:?}\" does not refer to a byte string",
self self
); );
raw::set_str(&self.0, value) raw::write_str(&self.0, value)
} }
fn read_write(&self, value: &'static [u8]) -> Result<&'static [u8]> { fn update(&self, value: &'static [u8]) -> Result<&'static [u8]> {
assert!( assert!(
self.value_type_str(), self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string", "the name \"{:?}\" does not refer to a byte string",
self self
); );
// this is safe because the key refers to a byte string: // this is safe because the key refers to a byte string:
unsafe { raw::get_set_str(&self.0, value) } unsafe { raw::update_str(&self.0, value) }
} }
} }
@ -289,16 +312,16 @@ impl<T: MibArg> Access<&'static str> for MibStr<T> {
fn read(&self) -> Result<&'static str> { fn read(&self) -> Result<&'static str> {
// this is safe because the only safe way to construct a `MibStr` is by // this is safe because the only safe way to construct a `MibStr` is by
// validating that the key refers to a byte-string value // validating that the key refers to a byte-string value
let s = unsafe { raw::get_str_mib(self.0.as_ref())? }; let s = unsafe { raw::read_str_mib(self.0.as_ref())? };
Ok(str::from_utf8(s).unwrap()) Ok(str::from_utf8(s).unwrap())
} }
fn write(&self, value: &'static str) -> Result<()> { fn write(&self, value: &'static str) -> Result<()> {
raw::set_str_mib(self.0.as_ref(), value.as_bytes()) raw::write_str_mib(self.0.as_ref(), value.as_bytes())
} }
fn read_write(&self, value: &'static str) -> Result<&'static str> { fn update(&self, value: &'static str) -> Result<&'static str> {
// this is safe because the only safe way to construct a `MibStr` is by // this is safe because the only safe way to construct a `MibStr` is by
// validating that the key refers to a byte-string value // validating that the key refers to a byte-string value
let s = unsafe { raw::get_set_str_mib(self.0.as_ref(), value.as_bytes())? }; let s = unsafe { raw::update_str_mib(self.0.as_ref(), value.as_bytes())? };
Ok(str::from_utf8(s).unwrap()) Ok(str::from_utf8(s).unwrap())
} }
} }
@ -311,7 +334,7 @@ impl Access<&'static str> for Name {
self self
); );
// this is safe because the key refers to a byte string: // this is safe because the key refers to a byte string:
let s = unsafe { raw::get_str(&self.0)? }; let s = unsafe { raw::read_str(&self.0)? };
Ok(str::from_utf8(s).unwrap()) Ok(str::from_utf8(s).unwrap())
} }
fn write(&self, value: &'static str) -> Result<()> { fn write(&self, value: &'static str) -> Result<()> {
@ -320,16 +343,16 @@ impl Access<&'static str> for Name {
"the name \"{:?}\" does not refer to a byte string", "the name \"{:?}\" does not refer to a byte string",
self self
); );
raw::set_str(&self.0, value.as_bytes()) raw::write_str(&self.0, value.as_bytes())
} }
fn read_write(&self, value: &'static str) -> Result<&'static str> { fn update(&self, value: &'static str) -> Result<&'static str> {
assert!( assert!(
self.value_type_str(), self.value_type_str(),
"the name \"{:?}\" does not refer to a byte string", "the name \"{:?}\" does not refer to a byte string",
self self
); );
// this is safe because the key refers to a byte string: // this is safe because the key refers to a byte string:
let s = unsafe { raw::get_set_str(&self.0, value.as_bytes())? }; let s = unsafe { raw::update_str(&self.0, value.as_bytes())? };
Ok(str::from_utf8(s).unwrap()) Ok(str::from_utf8(s).unwrap())
} }
} }

View File

@ -9,10 +9,10 @@
//! ideal. Fortunately, `jemalloc` offers the ability to translate the string ahead of time into a //! ideal. Fortunately, `jemalloc` offers the ability to translate the string ahead of time into a
//! "Management Information Base" (MIB) to speed up future lookups. //! "Management Information Base" (MIB) to speed up future lookups.
//! //!
//! This crate provides both a function and a type for each `mallctl` operation. While the //! This crate provides a type for each `mallctl` operation. Calling
//! function is more convenient, the type will be more efficient if the operation will be repeatedly //! `$op::{read(), write(x), update(x)}` on the type calls `mallctl` with the
//! performed. Its constructor performs the MIB lookup, so the struct should be saved if the same //! string-based API. If the operation will be repeatedly performed, a MIB for
//! operation is going to be repeatedly performed. //! the operation can be obtained using `$op.mib()`.
//! //!
//! # Examples //! # Examples
//! //!
@ -24,6 +24,7 @@
//! //!
//! use std::thread; //! use std::thread;
//! use std::time::Duration; //! use std::time::Duration;
//! use jemalloc_ctl::{stats, epoch};
//! //!
//! #[global_allocator] //! #[global_allocator]
//! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; //! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
@ -31,10 +32,10 @@
//! fn main() { //! fn main() {
//! loop { //! loop {
//! // many statistics are cached and only updated when the epoch is advanced. //! // many statistics are cached and only updated when the epoch is advanced.
//! jemalloc_ctl::epoch().unwrap(); //! epoch::advance().unwrap();
//! //!
//! let allocated = jemalloc_ctl::stats::allocated().unwrap(); //! let allocated = stats::allocated::read().unwrap();
//! let resident = jemalloc_ctl::stats::resident().unwrap(); //! let resident = stats::resident::read().unwrap();
//! println!("{} bytes allocated/{} bytes resident", allocated, resident); //! println!("{} bytes allocated/{} bytes resident", allocated, resident);
//! thread::sleep(Duration::from_secs(10)); //! thread::sleep(Duration::from_secs(10));
//! } //! }
@ -49,20 +50,21 @@
//! //!
//! use std::thread; //! use std::thread;
//! use std::time::Duration; //! use std::time::Duration;
//! use jemalloc_ctl::{stats, epoch};
//! //!
//! #[global_allocator] //! #[global_allocator]
//! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; //! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
//! //!
//! fn main() { //! fn main() {
//! let epoch = jemalloc_ctl::Epoch::new().unwrap(); //! let e = epoch::mib().unwrap();
//! let allocated = jemalloc_ctl::stats::Allocated::new().unwrap(); //! let allocated = stats::allocated::mib().unwrap();
//! let resident = jemalloc_ctl::stats::Resident::new().unwrap(); //! let resident = stats::resident::mib().unwrap();
//! loop { //! loop {
//! // many statistics are cached and only updated when the epoch is advanced. //! // many statistics are cached and only updated when the epoch is advanced.
//! epoch.advance().unwrap(); //! e.advance().unwrap();
//! //!
//! let allocated = allocated.get().unwrap(); //! let allocated = allocated.read().unwrap();
//! let resident = resident.get().unwrap(); //! let resident = resident.read().unwrap();
//! println!("{} bytes allocated/{} bytes resident", allocated, resident); //! println!("{} bytes allocated/{} bytes resident", allocated, resident);
//! thread::sleep(Duration::from_secs(10)); //! thread::sleep(Duration::from_secs(10));
//! } //! }
@ -75,6 +77,7 @@
extern crate jemalloc_sys; extern crate jemalloc_sys;
extern crate libc; extern crate libc;
extern crate paste;
#[cfg(test)] #[cfg(test)]
extern crate jemallocator; extern crate jemallocator;
@ -87,10 +90,11 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
use core as std; use core as std;
use std::{fmt, mem, num, ops, ptr, result, slice, str}; use std::{fmt, mem, num, ops, ptr, result, slice, str};
#[macro_use]
mod macros;
pub mod arenas; pub mod arenas;
mod background_threads;
pub mod config; pub mod config;
mod epoch;
mod error; mod error;
mod keys; mod keys;
pub mod opt; pub mod opt;
@ -99,10 +103,143 @@ pub mod stats;
#[cfg(feature = "use_std")] #[cfg(feature = "use_std")]
pub mod stats_print; pub mod stats_print;
pub mod thread; pub mod thread;
mod version;
pub use background_threads::*;
pub use epoch::*;
pub use error::{Error, Result}; pub use error::{Error, Result};
pub use keys::{Access, AsName, Mib, MibStr, Name}; pub use keys::{Access, AsName, Mib, MibStr, Name};
pub use version::*;
option! {
version[ str: b"version\0", str: 1 ] => &'static str |
ops: r |
docs:
/// `jemalloc` version string.
///
/// # Example
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use 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.
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// #
/// use jemalloc_ctl::background_thread;
/// let s = background_thread::read().unwrap();
/// println!("background_threads enabled: {}", s);
/// let p = background_thread::update(!s).unwrap();
/// assert_eq!(p, s);
/// let s = background_thread::read().unwrap();
/// assert_ne!(p, s);
/// background_thread::write(!s).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.
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// # #[cfg(not(target_os = "macos"))] {
/// #
/// use jemalloc_ctl::max_background_threads;
/// let n = max_background_threads::read().unwrap();
/// println!("max_background_threads: {}", n);
/// max_background_threads::write(n + 1).unwrap();
/// assert_eq!(max_background_threads::read().unwrap(), n + 1);
/// #
/// # } // #[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:
///
/// ```
/// # extern crate jemallocator;
/// # extern crate jemalloc_ctl;
/// #
/// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// #
/// use 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() -> ::error::Result<u64> {
Self::update(1)
}
}
impl epoch_mib {
/// Advances the epoch returning its old value - see [`epoch`].
pub fn advance(self) -> ::error::Result<u64> {
self.0.update(1)
}
}

210
jemalloc-ctl/src/macros.rs Normal file
View File

@ -0,0 +1,210 @@
//! Utility macros
macro_rules! types {
($id:ident[ str: $byte_string:expr, $mib:ty, $name_to_mib:ident ] |
docs: $(#[$doc:meta])*
mib_docs: $(#[$doc_mib:meta])*
) => {
paste::item! {
$(#[$doc])*
#[allow(non_camel_case_types)]
pub struct $id;
impl $id {
const NAME: &'static ::keys::Name = {
union U<'a> {
bytes: &'a [u8],
name: &'a ::keys::Name
}
unsafe { U { bytes: $byte_string }.name }
};
/// Returns Management Information Base (MIB)
///
/// This value can be used to access the key without doing string lookup.
pub fn mib() -> ::error::Result<[<$id _mib>]> {
Ok([<$id _mib>](Self::NAME.$name_to_mib()?))
}
/// Key [`Name`].
pub fn name() -> &'static ::keys::Name {
Self::NAME
}
}
$(#[$doc_mib])*
#[doc(hidden)]
#[repr(transparent)]
#[derive(Copy, Clone)]
#[allow(non_camel_case_types)]
pub struct [<$id _mib>](pub ::keys::$mib);
}
};
}
/// Read
macro_rules! r {
($id:ident => $ret_ty:ty) => {
paste::item! {
impl $id {
/// Reads value using string API.
pub fn read() -> ::error::Result<$ret_ty> {
use ::keys::Access;
Self::NAME.read()
}
}
impl [<$id _mib>] {
/// Reads value using MIB API.
pub fn read(self) -> ::error::Result<$ret_ty> {
use ::keys::Access;
self.0.read()
}
}
#[cfg(test)]
#[test]
fn [<$id _read_test>]() {
match stringify!($id) {
"background_thread" |
"max_background_threads"
if cfg!(target_os = "macos") => return,
_ => (),
}
let _ = $id::read().unwrap();
let mib = $id::mib().unwrap();
let _ = mib.read().unwrap();
}
}
};
}
/// Write
macro_rules! w {
($id:ident => $ret_ty:ty) => {
paste::item! {
impl $id {
/// Writes `value` using string API.
pub fn write(value: $ret_ty) -> ::error::Result<()> {
use ::keys::Access;
Self::NAME.write(value)
}
}
impl [<$id _mib>] {
/// Writes `value` using MIB API.
pub fn write(self, value: $ret_ty) -> ::error::Result<()> {
use ::keys::Access;
self.0.write(value)
}
}
#[cfg(test)]
#[test]
fn [<$id _write_test>]() {
match stringify!($id) {
"background_thread" |
"max_background_threads"
if cfg!(target_os = "macos") => return,
_ => (),
}
let _ = $id::write($ret_ty::default()).unwrap();
let mib = $id::mib().unwrap();
let _ = mib.write($ret_ty::default()).unwrap();
}
}
};
}
/// Update
macro_rules! u {
($id:ident => $ret_ty:ty) => {
paste::item! {
impl $id {
/// Updates key to `value` returning its old value using string API.
pub fn update(value: $ret_ty) -> ::error::Result<$ret_ty> {
use ::keys::Access;
Self::NAME.update(value)
}
}
impl [<$id _mib>] {
/// Updates key to `value` returning its old value using MIB API.
pub fn update(self, value: $ret_ty) -> ::error::Result<$ret_ty> {
use ::keys::Access;
self.0.update(value)
}
}
#[cfg(test)]
#[test]
fn [<$id _update_test>]() {
match stringify!($id) {
"background_thread" |
"max_background_threads"
if cfg!(target_os = "macos") => return,
_ => (),
}
let _ = $id::update($ret_ty::default()).unwrap();
let mib = $id::mib().unwrap();
let _ = mib.update($ret_ty::default()).unwrap();
}
}
};
}
/// Creates a new option
macro_rules! option {
($id:ident[ str: $byte_string:expr, $mib:ty, $name_to_mib:ident ] => $ret_ty:ty |
ops: $($ops:ident),* |
docs:
$(#[$doc:meta])*
mib_docs:
$(#[$doc_mib:meta])*
) => {
types! {
$id[ str: $byte_string, $mib, $name_to_mib ] |
docs: $(#[$doc])*
mib_docs: $(#[$doc_mib])*
}
$(
$ops!($id => $ret_ty);
)*
};
// Non-string option:
($id:ident[ str: $byte_string:expr, non_str: $mib_len:expr ] => $ret_ty:ty |
ops: $($ops:ident),* |
docs:
$(#[$doc:meta])*
mib_docs:
$(#[$doc_mib:meta])*
) => {
option! {
$id[ str: $byte_string, Mib<[usize; $mib_len]>, mib ] => $ret_ty |
ops: $($ops),* |
docs: $(#[$doc])*
mib_docs: $(#[$doc_mib])*
}
};
// String option:
($id:ident[ str: $byte_string:expr, str: $mib_len:expr ] => $ret_ty:ty |
ops: $($ops:ident),* |
docs:
$(#[$doc:meta])*
mib_docs:
$(#[$doc_mib:meta])*
) => {
option! {
$id[ str: $byte_string, MibStr<[usize; $mib_len]>, mib_str ] => $ret_ty |
ops: $($ops),* |
docs: $(#[$doc])*
mib_docs: $(#[$doc_mib])*
}
};
}

View File

@ -1,523 +1,241 @@
//! Information about the run-time `jemalloc` configuration. //! `jemalloc`'s run-time configuration.
//! //!
//! These settings are controlled by the `MALLOC_CONF` environment variable. //! These settings are controlled by the `MALLOC_CONF` environment variable.
use error::Result;
use keys::{Access, AsName, Mib, MibStr};
use libc::c_uint;
const ABORT: &[u8] = b"opt.abort\0"; option! {
abort[ str: b"opt.abort\0", non_str: 2 ] => bool |
/// Determines if `jemalloc` will call `abort(3)` on most warnings. ops: r |
/// docs:
/// This is disabled by default unless `--enable-debug` was specified during build configuration. /// Whether `jemalloc` calls `abort(3)` on most warnings.
/// ///
/// # Examples /// This is disabled by default unless `--enable-debug` was specified during
/// /// build configuration.
/// ``` ///
/// extern crate jemallocator; /// # Examples
/// extern crate jemalloc_ctl; ///
/// /// ```
/// #[global_allocator] /// # extern crate jemallocator;
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// # extern crate jemalloc_ctl;
/// /// #
/// fn main() { /// # #[global_allocator]
/// println!("abort on warning: {}", jemalloc_ctl::opt::abort().unwrap()); /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// } /// #
/// ``` /// # fn main() {
pub fn abort() -> Result<bool> { /// use jemalloc_ctl::opt;
ABORT.name().read() /// let abort = opt::abort::mib().unwrap();
/// println!("abort on warning: {}", abort.read().unwrap());
/// # }
/// ```
mib_docs: /// See [`abort`].
} }
/// A type determining if `jemalloc` will call `abort(3)` on most warnings. option! {
/// dss[ str: b"opt.dss\0", str: 2 ] => &'static str |
/// This is disabled by default unless `--enable-debug` was specified during build configuration. ops: r |
/// docs:
/// # Examples /// The `dss` (`sbrk(2)`) allocation precedence as related to `mmap(2)`
/// /// allocation.
/// ``` ///
/// extern crate jemallocator; /// The following settings are supported if `sbrk(2)` is supported by the
/// extern crate jemalloc_ctl; /// operating system: "disabled", "primary", and "secondary"; otherwise only
/// /// "disabled" is supported. The default is "secondary" if `sbrk(2)` is
/// use jemalloc_ctl::opt::Abort; /// supported by the operating system; "disabled" otherwise.
/// ///
/// #[global_allocator] /// # Examples
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; ///
/// /// ```
/// fn main() { /// # extern crate jemallocator;
/// let abort = Abort::new().unwrap(); /// # extern crate jemalloc_ctl;
/// /// #
/// println!("abort on warning: {}", abort.get().unwrap()); /// # #[global_allocator]
/// } /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// ``` /// #
#[derive(Copy, Clone)] /// # fn main() {
pub struct Abort(Mib<[usize; 2]>); /// use jemalloc_ctl::opt;
/// let dss = opt::dss::read().unwrap();
impl Abort { /// println!("dss priority: {}", dss);
/// Returns a new `Abort`. /// # }
pub fn new() -> Result<Self> { /// ```
let mib = ABORT.name().mib()?; mib_docs: /// See [`dss`].
Ok(Abort(mib))
}
/// Returns the abort-on-warning behavior.
pub fn get(self) -> Result<bool> {
self.0.read()
}
} }
const DSS: &[u8] = b"opt.dss\0"; option! {
narenas[ str: b"opt.narenas\0", non_str: 2 ] => libc::c_uint |
/// Returns the dss (`sbrk(2)`) allocation precedence as related to `mmap(2)` allocation. ops: r |
/// docs:
/// The following settings are supported if `sbrk(2)` is supported by the operating system: /// Maximum number of arenas to use for automatic multiplexing of threads
/// "disabled", "primary", and "secondary"; otherwise only "disabled" is supported. The default is /// and arenas.
/// "secondary" if `sbrk(2)` is supported by the operating system; "disabled" otherwise. ///
/// /// The default is four times the number of CPUs, or one if there is a
/// # Examples /// single CPU.
/// ///
/// ``` /// # Examples
/// extern crate jemallocator; ///
/// extern crate jemalloc_ctl; /// ```
/// /// # extern crate jemallocator;
/// #[global_allocator] /// # extern crate jemalloc_ctl;
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// #
/// /// # #[global_allocator]
/// fn main() { /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// println!("dss priority: {}", jemalloc_ctl::opt::dss().unwrap()); /// #
/// } /// # fn main() {
/// ``` /// use jemalloc_ctl::opt;
pub fn dss() -> Result<&'static str> { /// let narenas = opt::narenas::read().unwrap();
DSS.name().read() /// println!("number of arenas: {}", narenas);
/// # }
/// ```
mib_docs: /// See [`narenas`].
} }
/// A type providing access to the dss (`sbrk(2)`) allocation precedence as related to `mmap(2)` option! {
/// allocation. junk[ str: b"opt.junk\0", str: 2 ] => &'static str |
/// ops: r |
/// The following settings are supported if `sbrk(2)` is supported by the operating system: docs:
/// "disabled", "primary", and "secondary"; otherwise only "disabled" is supported. The default is /// `jemalloc`'s junk filling mode.
/// "secondary" if `sbrk(2)` is supported by the operating system; "disabled" otherwise. ///
/// /// Requires `--enable-fill` to have been specified during build
/// # Examples /// configuration.
/// ///
/// ``` /// If set to "alloc", each byte of uninitialized allocated memory will be
/// extern crate jemallocator; /// set to `0x5a`. If set to "free", each byte of deallocated memory will be set
/// extern crate jemalloc_ctl; /// to `0x5a`. If set to "true", both allocated and deallocated memory will be
/// /// initialized, and if set to "false" junk filling will be disabled. This is
/// use jemalloc_ctl::opt::Dss; /// intended for debugging and will impact performance negatively.
/// ///
/// #[global_allocator] /// The default is "false", unless `--enable-debug` was specified during
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// build configuration, in
/// /// which case the default is "true".
/// fn main() { ///
/// let dss = Dss::new().unwrap(); /// # Examples
/// ///
/// println!("dss priority: {}", dss.get().unwrap()); /// ```
/// } /// # extern crate jemallocator;
/// ``` /// # extern crate jemalloc_ctl;
#[derive(Copy, Clone)] /// #
pub struct Dss(MibStr<[usize; 2]>); /// # #[global_allocator]
/// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
impl Dss { /// #
/// Returns a new `Dss`. /// # fn main() {
pub fn new() -> Result<Self> { /// use jemalloc_ctl::opt;
let mib = DSS.name().mib_str()?; /// let junk = opt::junk::read().unwrap();
Ok(Dss(mib)) /// println!("junk filling: {}", junk);
} /// # }
/// ```
/// Returns the dss allocation precedence. mib_docs: /// See [`junk`].
pub fn get(self) -> Result<&'static str> {
self.0.read()
}
} }
const NARENAS: &[u8] = b"opt.narenas\0"; option! {
zero[ str: b"opt.zero\0", non_str: 2 ] => bool |
/// Returns the maximum number of arenas to use for automatic multiplexing of threads and arenas. ops: r |
/// docs:
/// The default is four times the number of CPUs, or one if there is a single CPU. /// `jemalloc`'s zeroing behavior.
/// ///
/// # Examples /// Requires `--enable-fill` to have been specified during build
/// /// configuration.
/// ``` ///
/// extern crate jemallocator; /// If enabled, `jemalloc` will initialize each byte of uninitialized
/// extern crate jemalloc_ctl; /// allocated memory to 0. This is intended for debugging and will impact
/// /// performance negatively. It is disabled by default.
/// #[global_allocator] ///
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// # Examples
/// ///
/// fn main() { /// ```
/// println!("number of arenas: {}", jemalloc_ctl::opt::narenas().unwrap()); /// # extern crate jemallocator;
/// } /// # extern crate jemalloc_ctl;
/// ``` /// #
pub fn narenas() -> Result<c_uint> { /// # #[global_allocator]
NARENAS.name().read() /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// #
/// # fn main() {
/// use jemalloc_ctl::opt;
/// let zero = opt::zero::read().unwrap();
/// println!("zeroing: {}", zero);
/// # }
/// ```
mib_docs: /// See [`zero`].
} }
/// A type providing access to the maximum number of arenas to use for automatic multiplexing of option! {
/// threads and arenas. tcache[ str: b"opt.tcache\0", non_str: 2 ] => bool |
/// ops: r |
/// The default is four times the number of CPUs, or one if there is a single CPU. docs:
/// /// Thread-local allocation caching behavior.
/// # Examples ///
/// /// Thread-specific caching allows many allocations to be satisfied without
/// ``` /// performing any thread synchronization, at the cost of increased memory
/// extern crate jemallocator; /// use. This is enabled by default.
/// extern crate jemalloc_ctl; ///
/// /// # Examples
/// use jemalloc_ctl::opt::NArenas; ///
/// /// ```
/// #[global_allocator] /// # extern crate jemallocator;
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// # extern crate jemalloc_ctl;
/// /// #
/// fn main() { /// # #[global_allocator]
/// let narenas = NArenas::new().unwrap(); /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// /// #
/// println!("number of arenas: {}", narenas.get().unwrap()); /// # fn main() {
/// } /// use jemalloc_ctl::opt;
/// ``` /// let tcache = opt::tcache::read().unwrap();
#[derive(Copy, Clone)] /// println!("thread-local caching: {}", tcache);
pub struct NArenas(Mib<[usize; 2]>); /// # }
/// ```
impl NArenas { mib_docs: /// See [`tcache`].
/// Returns a new `NArenas`.
pub fn new() -> Result<Self> {
let mib = NARENAS.name().mib()?;
Ok(NArenas(mib))
}
/// Returns the maximum number of arenas.
pub fn get(self) -> Result<c_uint> {
self.0.read()
}
} }
const JUNK: &[u8] = b"opt.junk\0"; option! {
lg_tcache_max[ str: b"opt.lg_tcache_max\0", non_str: 2 ] => libc::size_t |
/// Returns `jemalloc`'s junk filling mode. ops: r |
/// docs:
/// Requires `--enable-fill` to have been specified during build configuration. /// Maximum size class (log base 2) to cache in the thread-specific cache
/// /// (`tcache`).
/// If set to "alloc", each byte of uninitialized allocated memory will be set to `0x5a`. If set to ///
/// "free", each byte of deallocated memory will be set to `0x5a`. If set to "true", both allocated /// At a minimum, all small size classes are cached, and at a maximum all
/// and deallocated memory will be initialized, and if set to "false" junk filling will be disabled. /// large size classes are cached. The default maximum is 32 KiB (2^15).
/// This is intended for debugging and will impact performance negatively. ///
/// /// # Examples
/// The default is "false", unless `--enable-debug` was specified during build configuration, in ///
/// which case the default is "true". /// ```
/// /// # extern crate jemallocator;
/// # Examples /// # extern crate jemalloc_ctl;
/// /// #
/// ``` /// # #[global_allocator]
/// extern crate jemallocator; /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// extern crate jemalloc_ctl; /// #
/// /// # fn main() {
/// #[global_allocator] /// use jemalloc_ctl::opt;
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// let lg_tcache_max = opt::lg_tcache_max::read().unwrap();
/// /// println!("max cached allocation size: {}", 1 << lg_tcache_max);
/// fn main() { /// # }
/// println!("junk filling: {}", jemalloc_ctl::opt::junk().unwrap()); /// ```
/// } mib_docs: /// See [`lg_tcache_max`].
/// ```
pub fn junk() -> Result<&'static str> {
JUNK.name().read()
} }
/// A type providing access to `jemalloc`'s junk filling mode. option! {
/// background_thread[ str: b"opt.background_thread\0", non_str: 2 ] => bool |
/// Requires `--enable-fill` to have been specified during build configuration. ops: r |
/// docs:
/// If set to "alloc", each byte of uninitialized allocated memory will be set to `0x5a`. If set to /// `jemalloc`'s default initialization behavior for background threads.
/// "free", each byte of deallocated memory will be set to `0x5a`. If set to "true", both allocated ///
/// and deallocated memory will be initialized, and if set to "false" junk filling will be disabled. /// `jemalloc` automatically spawns background worker threads on
/// This is intended for debugging and will impact performance negatively. /// initialization (first `jemalloc` call) if this option is enabled. By
/// /// default this option is disabled - `malloc_conf=background_thread:true`
/// The default is "false", unless `--enable-debug` was specified during build configuration, in /// changes its default.
/// which case the default is "true". ///
/// /// # Examples
/// # Examples ///
/// /// ```
/// ``` /// # extern crate jemallocator;
/// extern crate jemallocator; /// # extern crate jemalloc_ctl;
/// extern crate jemalloc_ctl; /// #
/// /// # #[global_allocator]
/// use jemalloc_ctl::opt::Junk; /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// /// #
/// #[global_allocator] /// # fn main() {
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// use jemalloc_ctl::opt;
/// /// let background_thread = opt::background_thread::read().unwrap();
/// fn main() { /// println!("background threads since initialization: {}", background_thread);
/// let junk = Junk::new().unwrap(); /// # }
/// /// ```
/// println!("junk filling: {}", junk.get().unwrap()); mib_docs: /// See [`background_thread`].
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Junk(MibStr<[usize; 2]>);
impl Junk {
/// Returns a new `Junk`.
pub fn new() -> Result<Self> {
let mib = JUNK.name().mib_str()?;
Ok(Junk(mib))
}
/// Returns jemalloc's junk filling mode.
pub fn get(self) -> Result<&'static str> {
self.0.read()
}
}
const ZERO: &[u8] = b"opt.zero\0";
/// Returns jemalloc's zeroing behavior.
///
/// Requires `--enable-fill` to have been specified during build configuration.
///
/// If enabled, `jemalloc` will initialize each byte of uninitialized allocated memory to 0. This is
/// intended for debugging and will impact performance negatively. It is disabled by default.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("zeroing: {}", jemalloc_ctl::opt::zero().unwrap());
/// }
/// ```
pub fn zero() -> Result<bool> {
ZERO.name().read()
}
/// A type providing access to jemalloc's zeroing behavior.
///
/// Requires `--enable-fill` to have been specified during build configuration.
///
/// If enabled, `jemalloc` will initialize each byte of uninitialized allocated memory to 0. This is
/// intended for debugging and will impact performance negatively. It is disabled by default.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Zero;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let zero = Zero::new().unwrap();
///
/// println!("zeroing: {}", zero.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Zero(Mib<[usize; 2]>);
impl Zero {
/// Returns a new `Zero`.
pub fn new() -> Result<Self> {
let mib = ZERO.name().mib()?;
Ok(Zero(mib))
}
/// Returns the `jemalloc` zeroing behavior.
pub fn get(self) -> Result<bool> {
self.0.read()
}
}
const TCACHE: &[u8] = b"opt.tcache\0";
/// Determines if thread-local allocation caching is enabled.
///
/// Thread-specific caching allows many allocations to be satisfied without performing any thread
/// synchronization, at the cost of increased memory use. This is enabled by default.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("thread-local caching: {}", jemalloc_ctl::opt::tcache().unwrap());
/// }
/// ```
pub fn tcache() -> Result<bool> {
TCACHE.name().read()
}
/// A type providing access to thread-local allocation caching behavior.
///
/// Thread-specific caching allows many allocations to be satisfied without performing any thread
/// synchronization, at the cost of increased memory use. This is enabled by default.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Tcache;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let tcache = Tcache::new().unwrap();
///
/// println!("thread-local caching: {}", tcache.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Tcache(Mib<[usize; 2]>);
impl Tcache {
/// Returns a new `Tcache`.
pub fn new() -> Result<Self> {
let mib = TCACHE.name().mib()?;
Ok(Tcache(mib))
}
/// Returns the thread-local caching behavior.
pub fn get(self) -> Result<bool> {
self.0.read()
}
}
const LG_TCACHE_MAX: &[u8] = b"opt.lg_tcache_max\0";
/// Returns the maximum size class (log base 2) to cache in the thread-specific cache (tcache).
///
/// At a minimum, all small size classes are cached, and at a maximum all large size classes are
/// cached. The default maximum is 32 KiB (2^15).
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("max cached allocation size: {}", 1 << jemalloc_ctl::opt::lg_tcache_max().unwrap());
/// }
/// ```
pub fn lg_tcache_max() -> Result<usize> {
LG_TCACHE_MAX.name().read()
}
/// A type providing access to the maximum size class (log base 2) to cache in the thread-specific
/// cache (tcache).
///
/// At a minimum, all small size classes are cached, and at a maximum all large size classes are
/// cached. The default maximum is 32 KiB (2^15).
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::LgTcacheMax;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let lg_tcache_max = LgTcacheMax::new().unwrap();
///
/// println!("max cached allocation size: {}", 1 << lg_tcache_max.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct LgTcacheMax(Mib<[usize; 2]>);
impl LgTcacheMax {
/// Returns a new `LgTcacheMax`.
pub fn new() -> Result<Self> {
let mib = LG_TCACHE_MAX.name().mib()?;
Ok(LgTcacheMax(mib))
}
/// Returns the maximum cached size class.
pub fn get(self) -> Result<usize> {
self.0.read()
}
}
const BACKGROUND_THREAD: &[u8] = b"opt.background_thread\0";
/// Returns whether `jemalloc` is initialized with background worker threads
/// enabled.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!(
/// "initialized with background threads enabled: {}",
/// jemalloc_ctl::opt::background_thread().unwrap()
/// );
/// }
/// ```
pub fn background_thread() -> Result<bool> {
BACKGROUND_THREAD.name().read()
}
/// A type determining if `jemalloc` will be initialized with background worker
/// threads enabled.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::BackgroundThread;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let background_thread = BackgroundThread::new().unwrap();
///
/// println!(
/// "initialized with background threads enabled: {}",
/// background_thread.get().unwrap()
/// );
/// }
/// ```
#[derive(Copy, Clone)]
pub struct BackgroundThread(Mib<[usize; 2]>);
impl BackgroundThread {
/// Returns a new `BackgroundThread`.
pub fn new() -> Result<Self> {
let mib = BACKGROUND_THREAD.name().mib()?;
Ok(BackgroundThread(mib))
}
/// Returns the background thread initialization behavior.
pub fn get(self) -> Result<bool> {
self.0.read()
}
} }

View File

@ -1,4 +1,4 @@
//! Raw `malloctl` getter/setters //! Raw `unsafe` access to the `malloctl` API.
use error::{cvt, Result}; use error::{cvt, Result};
use libc::c_char; use libc::c_char;
@ -31,11 +31,11 @@ use {mem, ptr, slice};
/// use libc::{c_uint, c_char}; /// use libc::{c_uint, c_char};
/// unsafe { /// unsafe {
/// let mut mib = [0; 4]; /// let mut mib = [0; 4];
/// let nbins: c_uint = raw::get(b"arenas.nbins\0").unwrap(); /// let nbins: c_uint = raw::read(b"arenas.nbins\0").unwrap();
/// raw::name_to_mib(b"arenas.bin.0.size\0", &mut mib).unwrap(); /// raw::name_to_mib(b"arenas.bin.0.size\0", &mut mib).unwrap();
/// for i in 0..4 { /// for i in 0..4 {
/// mib[2] = i; /// mib[2] = i;
/// let bin_size: usize = raw::get_mib(&mut mib).unwrap(); /// let bin_size: usize = raw::read_mib(&mut mib).unwrap();
/// println!("arena bin {} has size {}", i, bin_size); /// println!("arena bin {} has size {}", i, bin_size);
/// } /// }
/// } /// }
@ -67,7 +67,7 @@ pub fn name_to_mib(name: &[u8], mib: &mut [usize]) -> Result<()> {
/// invalid `T`, for example, by passing `T=bool` for a key returning `u8`. The /// invalid `T`, for example, by passing `T=bool` for a key returning `u8`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that /// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can. /// `u8` can.
pub unsafe fn get_mib<T: Copy>(mib: &[usize]) -> Result<T> { pub unsafe fn read_mib<T: Copy>(mib: &[usize]) -> Result<T> {
let mut value = MaybeUninit { init: () }; let mut value = MaybeUninit { init: () };
let mut len = mem::size_of::<T>(); let mut len = mem::size_of::<T>();
cvt(jemalloc_sys::mallctlbymib( cvt(jemalloc_sys::mallctlbymib(
@ -91,7 +91,7 @@ pub unsafe fn get_mib<T: Copy>(mib: &[usize]) -> Result<T> {
/// invalid `T`, for example, by passing `T=bool` for a key returning `u8`. The /// invalid `T`, for example, by passing `T=bool` for a key returning `u8`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that /// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can. /// `u8` can.
pub unsafe fn get<T: Copy>(name: &[u8]) -> Result<T> { pub unsafe fn read<T: Copy>(name: &[u8]) -> Result<T> {
validate_name(name); validate_name(name);
let mut value = MaybeUninit { init: () }; let mut value = MaybeUninit { init: () };
@ -107,7 +107,7 @@ pub unsafe fn get<T: Copy>(name: &[u8]) -> Result<T> {
Ok(value.maybe_uninit) Ok(value.maybe_uninit)
} }
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`. /// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`.
/// ///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`) /// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
/// to a `mib` (Management Information Base). /// to a `mib` (Management Information Base).
@ -118,7 +118,7 @@ pub unsafe fn get<T: Copy>(name: &[u8]) -> Result<T> {
/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The /// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that /// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can. /// `u8` can.
pub unsafe fn set_mib<T>(mib: &[usize], mut value: T) -> Result<()> { pub unsafe fn write_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
cvt(jemalloc_sys::mallctlbymib( cvt(jemalloc_sys::mallctlbymib(
mib.as_ptr(), mib.as_ptr(),
mib.len(), mib.len(),
@ -130,7 +130,7 @@ pub unsafe fn set_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
} }
/// Uses the null-terminated string `name` as the key to the _MALLCTL NAMESPACE_ /// Uses the null-terminated string `name` as the key to the _MALLCTL NAMESPACE_
/// and sets it `value` /// and writes it `value`
/// ///
/// # Safety /// # Safety
/// ///
@ -138,7 +138,7 @@ pub unsafe fn set_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The /// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that /// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can. /// `u8` can.
pub unsafe fn set<T>(name: &[u8], mut value: T) -> Result<()> { pub unsafe fn write<T>(name: &[u8], mut value: T) -> Result<()> {
validate_name(name); validate_name(name);
cvt(jemalloc_sys::mallctl( cvt(jemalloc_sys::mallctl(
@ -150,7 +150,7 @@ pub unsafe fn set<T>(name: &[u8], mut value: T) -> Result<()> {
)) ))
} }
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value` /// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`
/// returning its previous value. /// returning its previous value.
/// ///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`) /// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
@ -162,7 +162,7 @@ pub unsafe fn set<T>(name: &[u8], mut value: T) -> Result<()> {
/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The /// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that /// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can. /// `u8` can.
pub unsafe fn get_set_mib<T>(mib: &[usize], mut value: T) -> Result<T> { pub unsafe fn update_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
let mut len = mem::size_of::<T>(); let mut len = mem::size_of::<T>();
cvt(jemalloc_sys::mallctlbymib( cvt(jemalloc_sys::mallctlbymib(
mib.as_ptr(), mib.as_ptr(),
@ -177,7 +177,7 @@ pub unsafe fn get_set_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
} }
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and /// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
/// sets its `value` returning its previous value. /// writes its `value` returning its previous value.
/// ///
/// # Safety /// # Safety
/// ///
@ -185,7 +185,7 @@ pub unsafe fn get_set_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The /// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that /// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
/// `u8` can. /// `u8` can.
pub unsafe fn get_set<T>(name: &[u8], mut value: T) -> Result<T> { pub unsafe fn update<T>(name: &[u8], mut value: T) -> Result<T> {
validate_name(name); validate_name(name);
let mut len = mem::size_of::<T>(); let mut len = mem::size_of::<T>();
@ -225,12 +225,12 @@ pub unsafe fn get_set<T>(name: &[u8], mut value: T) -> Result<T> {
/// If the pointer is valid but it does not point to a null-terminated string, /// If the pointer is valid but it does not point to a null-terminated string,
/// looking for `\0` will read garbage and might end up reading out-of-bounds, /// looking for `\0` will read garbage and might end up reading out-of-bounds,
/// which is undefined behavior. /// which is undefined behavior.
pub unsafe fn get_str_mib(mib: &[usize]) -> Result<&'static [u8]> { pub unsafe fn read_str_mib(mib: &[usize]) -> Result<&'static [u8]> {
let ptr: *const c_char = get_mib(mib)?; let ptr: *const c_char = read_mib(mib)?;
ptr2str(ptr) ptr2str(ptr)
} }
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`. /// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`.
/// ///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`) /// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
/// to a `mib` (Management Information Base). /// to a `mib` (Management Information Base).
@ -238,16 +238,16 @@ pub unsafe fn get_str_mib(mib: &[usize]) -> Result<&'static [u8]> {
/// # Panics /// # Panics
/// ///
/// If `value` is not a non-empty null-terminated string. /// If `value` is not a non-empty null-terminated string.
pub fn set_str_mib(mib: &[usize], value: &'static [u8]) -> Result<()> { pub fn write_str_mib(mib: &[usize], value: &'static [u8]) -> Result<()> {
assert!(!value.is_empty(), "value cannot be empty"); assert!(!value.is_empty(), "value cannot be empty");
assert_eq!(*value.last().unwrap(), b'\0'); assert_eq!(*value.last().unwrap(), b'\0');
// This is safe because `value` will always point to a null-terminated // This is safe because `value` will always point to a null-terminated
// string, which makes it safe for all key value types: pointers to // string, which makes it safe for all key value types: pointers to
// null-terminated strings, pointers, pointer-sized integers, etc. // null-terminated strings, pointers, pointer-sized integers, etc.
unsafe { set_mib(mib, value.as_ptr() as *const c_char) } unsafe { write_mib(mib, value.as_ptr() as *const c_char) }
} }
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value` /// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`
/// returning its previous value. /// returning its previous value.
/// ///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`) /// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
@ -273,8 +273,8 @@ pub fn set_str_mib(mib: &[usize], value: &'static [u8]) -> Result<()> {
/// If the pointer is valid but it does not point to a null-terminated string, /// If the pointer is valid but it does not point to a null-terminated string,
/// looking for `\0` will read garbage and might end up reading out-of-bounds, /// looking for `\0` will read garbage and might end up reading out-of-bounds,
/// which is undefined behavior. /// which is undefined behavior.
pub unsafe fn get_set_str_mib(mib: &[usize], value: &'static [u8]) -> Result<&'static [u8]> { pub unsafe fn update_str_mib(mib: &[usize], value: &'static [u8]) -> Result<&'static [u8]> {
let ptr: *const c_char = get_set_mib(mib, value.as_ptr() as *const c_char)?; let ptr: *const c_char = update_mib(mib, value.as_ptr() as *const c_char)?;
ptr2str(ptr) ptr2str(ptr)
} }
@ -301,24 +301,24 @@ pub unsafe fn get_set_str_mib(mib: &[usize], value: &'static [u8]) -> Result<&'s
/// If the pointer is valid but it does not point to a null-terminated string, /// If the pointer is valid but it does not point to a null-terminated string,
/// looking for `\0` will read garbage and might end up reading out-of-bounds, /// looking for `\0` will read garbage and might end up reading out-of-bounds,
/// which is undefined behavior. /// which is undefined behavior.
pub unsafe fn get_str(name: &[u8]) -> Result<&'static [u8]> { pub unsafe fn read_str(name: &[u8]) -> Result<&'static [u8]> {
let ptr: *const c_char = get(name)?; let ptr: *const c_char = read(name)?;
ptr2str(ptr) ptr2str(ptr)
} }
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and /// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
/// sets its `value`. /// writes its `value`.
pub fn set_str(name: &[u8], value: &'static [u8]) -> Result<()> { pub fn write_str(name: &[u8], value: &'static [u8]) -> Result<()> {
assert!(!value.is_empty(), "value cannot be empty"); assert!(!value.is_empty(), "value cannot be empty");
assert_eq!(*value.last().unwrap(), b'\0'); assert_eq!(*value.last().unwrap(), b'\0');
// This is safe because `value` will always point to a null-terminated // This is safe because `value` will always point to a null-terminated
// string, which makes it safe for all key value types: pointers to // string, which makes it safe for all key value types: pointers to
// null-terminated strings, pointers, pointer-sized integers, etc. // null-terminated strings, pointers, pointer-sized integers, etc.
unsafe { set(name, value.as_ptr() as *const c_char) } unsafe { write(name, value.as_ptr() as *const c_char) }
} }
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and /// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
/// sets its `value` returning its previous value. /// writes its `value` returning its previous value.
/// ///
/// # Safety /// # Safety
/// ///
@ -340,8 +340,8 @@ pub fn set_str(name: &[u8], value: &'static [u8]) -> Result<()> {
/// If the pointer is valid but it does not point to a null-terminated string, /// If the pointer is valid but it does not point to a null-terminated string,
/// looking for `\0` will read garbage and might end up reading out-of-bounds, /// looking for `\0` will read garbage and might end up reading out-of-bounds,
/// which is undefined behavior. /// which is undefined behavior.
pub unsafe fn get_set_str(name: &[u8], value: &'static [u8]) -> Result<&'static [u8]> { pub unsafe fn update_str(name: &[u8], value: &'static [u8]) -> Result<&'static [u8]> {
let ptr: *const c_char = get_set(name, value.as_ptr() as *const c_char)?; let ptr: *const c_char = update(name, value.as_ptr() as *const c_char)?;
ptr2str(ptr) ptr2str(ptr)
} }

View File

@ -1,515 +1,228 @@
//! Global allocator statistics. //! Global allocator statistics.
//! //!
//! `jemalloc` tracks a wide variety of statistics. Many of them are cached, and only refreshed when //! `jemalloc` tracks a wide variety of statistics. Many of them are cached, and
//! the jemalloc "epoch" is advanced. See the [`Epoch`] type for more information. //! only refreshed when the `jemalloc` "epoch" is advanced. See the [`::epoch`] type
//! //! for more information.
//! [`Epoch`]: ../struct.Epoch.html
use error::Result; option! {
use keys::{Access, AsName, Mib}; allocated[ str: b"stats.allocated\0", non_str: 2 ] => libc::size_t |
ops: r |
const ALLOCATED: &[u8] = b"stats.allocated\0"; docs:
/// Total number of bytes allocated by the application.
/// Returns the total number of bytes allocated by the application. ///
/// /// This statistic is cached, and is only refreshed when the epoch is
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`] /// advanced. See the [`::epoch`] type for more information.
/// function for more information. ///
/// /// This corresponds to `stats.allocated` in jemalloc's API.
/// This corresponds to `stats.allocated` in jemalloc's API. ///
/// /// # Examples
/// # Examples ///
/// /// ```rust
/// ``` /// # extern crate jemallocator;
/// extern crate jemallocator; /// # extern crate jemalloc_ctl;
/// extern crate jemalloc_ctl; /// #
/// /// # #[global_allocator]
/// #[global_allocator] /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// #
/// /// # fn main() {
/// fn main() { /// use jemalloc_ctl::{epoch, stats};
/// let a = jemalloc_ctl::stats::allocated().unwrap(); /// let e = epoch::mib().unwrap();
/// let _buf = vec![0; 1024 * 1024]; /// let allocated = stats::allocated::mib().unwrap();
/// jemalloc_ctl::epoch().unwrap(); ///
/// let b = jemalloc_ctl::stats::allocated().unwrap(); /// let a = allocated.read().unwrap();
/// assert!(a < b); /// let _buf = vec![0; 1024 * 1024];
/// } /// e.advance().unwrap();
/// ``` /// let b = allocated.read().unwrap();
/// /// assert!(a < b);
/// [`epoch`]: ../fn.epoch().html /// # }
pub fn allocated() -> Result<usize> { /// ```
ALLOCATED.name().read() mib_docs: /// See [`allocated`].
} }
/// A type providing access to the total number of bytes allocated by the application. option! {
/// active[ str: b"stats.active\0", non_str: 2 ] => libc::size_t |
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`] ops: r |
/// type for more information. docs:
/// /// Total number of bytes in active pages allocated by the application.
/// This corresponds to `stats.allocated` in jemalloc's API. ///
/// /// This is a multiple of the page size, and greater than or equal to the
/// # Examples /// value returned by [`allocated`].
/// ///
/// ```rust /// This statistic is cached, and is only refreshed when the epoch is
/// extern crate jemallocator; /// advanced. See the [`::epoch`] type for more information.
/// extern crate jemalloc_ctl; ///
/// /// This corresponds to `stats.active` in jemalloc's API.
/// use jemalloc_ctl::Epoch; ///
/// use jemalloc_ctl::stats::Allocated; /// # Examples
/// ///
/// #[global_allocator] /// ```rust
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// # extern crate jemallocator;
/// /// # extern crate jemalloc_ctl;
/// fn main() { /// #
/// let epoch = Epoch::new().unwrap(); /// # #[global_allocator]
/// let allocated = Allocated::new().unwrap(); /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// /// #
/// let a = allocated.get().unwrap(); /// # fn main() {
/// let _buf = vec![0; 1024 * 1024]; /// use jemalloc_ctl::{epoch, stats};
/// epoch.advance().unwrap(); /// let e = epoch::mib().unwrap();
/// let b = allocated.get().unwrap(); /// let active = stats::active::mib().unwrap();
/// assert!(a < b); ///
/// } /// let a = active.read().unwrap();
/// ``` /// let _buf = vec![0; 1024 * 1024];
/// /// e.advance().unwrap();
/// [`Epoch`]: ../struct.Epoch.html /// let b = active.read().unwrap();
#[derive(Copy, Clone)] /// assert!(a < b);
pub struct Allocated(Mib<[usize; 2]>); /// # }
/// ```
impl Allocated { mib_docs: /// See [`active`].
/// Returns a new `Allocated`.
pub fn new() -> Result<Self> {
let mib = ALLOCATED.name().mib()?;
Ok(Allocated(mib))
}
/// Returns the total number of bytes allocated by the application.
pub fn get(self) -> Result<usize> {
self.0.read()
}
} }
const ACTIVE: &[u8] = b"stats.active\0"; option! {
metadata[ str: b"stats.metadata\0", non_str: 2 ] => libc::size_t |
/// Returns the total number of bytes in active pages allocated by the application. ops: r |
/// docs:
/// This is a multiple of the page size, and is greater than or equal to the value returned by /// Total number of bytes dedicated to `jemalloc` metadata.
/// [`allocated`]. ///
/// /// This statistic is cached, and is only refreshed when the epoch is
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`] /// advanced. See the [`::epoch`] type for more information.
/// type for more information. ///
/// /// This corresponds to `stats.metadata` in jemalloc's API.
/// This corresponds to `stats.active` in jemalloc's API. ///
/// /// # Examples
/// # Examples ///
/// /// ```rust
/// ``` /// # extern crate jemallocator;
/// extern crate jemallocator; /// # extern crate jemalloc_ctl;
/// extern crate jemalloc_ctl; /// #
/// /// # #[global_allocator]
/// #[global_allocator] /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// #
/// /// # fn main() {
/// fn main() { /// use jemalloc_ctl::{epoch, stats};
/// let a = jemalloc_ctl::stats::active().unwrap(); /// let e = epoch::mib().unwrap();
/// let _buf = vec![0; 1024 * 1024]; /// let metadata = stats::metadata::mib().unwrap();
/// jemalloc_ctl::epoch().unwrap(); ///
/// let b = jemalloc_ctl::stats::active().unwrap(); /// e.advance().unwrap();
/// assert!(a < b); /// let size = metadata.read().unwrap();
/// } /// println!("{} bytes of jemalloc metadata", size);
/// ``` /// # }
/// /// ```
/// [`epoch`]: ../fn.epoch().html mib_docs: /// See [`metadata`].
/// [`allocated`]: fn.allocated.hml
pub fn active() -> Result<usize> {
ACTIVE.name().read()
} }
/// A type providing access to the total number of bytes in active pages allocated by the option! {
/// application. resident[ str: b"stats.resident\0", non_str: 2 ] => libc::size_t |
/// ops: r |
/// This is a multiple of the page size, and greater than or equal to the value returned by docs:
/// [`Allocated`]. /// Total number of bytes in physically resident data pages mapped by the
/// /// allocator.
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`] ///
/// type for more information. /// This consists of all pages dedicated to allocator metadata, pages
/// /// backing active allocations, and unused dirty pages. It may overestimate
/// This corresponds to `stats.active` in jemalloc's API. /// the true value because pages may not actually be physically resident if
/// /// they correspond to demand-zeroed virtual memory that has not yet been
/// # Examples /// touched. This is a multiple of the page size, and is larger than the
/// /// value returned by [`active`].
/// ```rust ///
/// extern crate jemallocator; /// This statistic is cached, and is only refreshed when the epoch is
/// extern crate jemalloc_ctl; /// advanced. See the [`::epoch`] type for more information.
/// ///
/// use jemalloc_ctl::Epoch; /// This corresponds to `stats.resident` in jemalloc's API.
/// use jemalloc_ctl::stats::Active; ///
/// /// # Examples
/// #[global_allocator] ///
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// ```rust
/// /// # extern crate jemallocator;
/// fn main() { /// # extern crate jemalloc_ctl;
/// let epoch = Epoch::new().unwrap(); /// #
/// let active = Active::new().unwrap(); /// # #[global_allocator]
/// /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// let a = active.get().unwrap(); /// #
/// let _buf = vec![0; 1024 * 1024]; /// # fn main() {
/// epoch.advance().unwrap(); /// use jemalloc_ctl::{epoch, stats};
/// let b = active.get().unwrap(); /// let e = epoch::mib().unwrap();
/// assert!(a < b); /// let resident = stats::resident::mib().unwrap();
/// } ///
/// ``` /// e.advance().unwrap();
/// /// let size = resident.read().unwrap();
/// [`Epoch`]: ../struct.Epoch.html /// println!("{} bytes of total resident data", size);
/// [`Allocated`]: struct.Allocated.html /// # }
#[derive(Copy, Clone)] /// ```
pub struct Active(Mib<[usize; 2]>); mib_docs: /// See [`resident`].
impl Active {
/// Returns a new `Allocated`.
pub fn new() -> Result<Self> {
let mib = ACTIVE.name().mib()?;
Ok(Active(mib))
}
/// Returns the total number of bytes in active pages allocated by the application.
pub fn get(self) -> Result<usize> {
self.0.read()
}
} }
const METADATA: &[u8] = b"stats.metadata\0"; option! {
mapped[ str: b"stats.mapped\0", non_str: 2 ] => libc::size_t |
/// Returns the total number of bytes dedicated to jemalloc metadata. ops: r |
/// docs:
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`] /// Total number of bytes in active extents mapped by the allocator.
/// function for more information. ///
/// /// This does not include inactive extents, even those that contain unused
/// This corresponds to `stats.metadata` in jemalloc's API. /// dirty pages, so there is no strict ordering between this and the value
/// /// returned by [`resident`]. This is a multiple of the page size, and is
/// # Examples /// larger than the value returned by [`active`].
/// ///
/// ``` /// This statistic is cached, and is only refreshed when the epoch is
/// extern crate jemallocator; /// advanced. See the [`::epoch`] type for more information.
/// extern crate jemalloc_ctl; ///
/// /// This corresponds to `stats.mapped` in jemalloc's API.
/// #[global_allocator] ///
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// # Examples
/// ///
/// fn main() { /// ```rust
/// jemalloc_ctl::epoch().unwrap(); /// # extern crate jemallocator;
/// println!("{} bytes of jemalloc metadata", jemalloc_ctl::stats::metadata().unwrap()); /// # extern crate jemalloc_ctl;
/// } /// #
/// ``` /// # #[global_allocator]
/// /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// [`epoch`]: ../fn.epoch.html /// #
pub fn metadata() -> Result<usize> { /// # fn main() {
METADATA.name().read() /// use jemalloc_ctl::{epoch, stats};
/// let e = epoch::mib().unwrap();
/// let mapped = stats::mapped::mib().unwrap();
///
/// e.advance().unwrap();
/// let size = mapped.read().unwrap();
/// println!("{} bytes of total mapped data", size);
/// # }
/// ```
mib_docs: /// See [`mapped`].
} }
/// A type providing access to the total number of bytes dedicated to jemalloc metadata. option! {
/// retained[ str: b"stats.retained\0", non_str: 2 ] => libc::size_t |
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`] ops: r |
/// type for more information. docs:
/// /// Total number of bytes in virtual memory mappings that were retained
/// This corresponds to `stats.metadata` in jemalloc's API. /// rather than being returned to the operating system via e.g. `munmap(2)`.
/// ///
/// # Examples /// Retained virtual memory is typically untouched, decommitted, or purged,
/// /// so it has no strongly associated physical memory. Retained memory is
/// ```rust /// excluded from mapped memory statistics, e.g. [`mapped`].
/// extern crate jemallocator; ///
/// extern crate jemalloc_ctl; /// This statistic is cached, and is only refreshed when the epoch is
/// /// advanced. See the [`::epoch`] type for more information.
/// use jemalloc_ctl::Epoch; ///
/// use jemalloc_ctl::stats::Metadata; /// This corresponds to `stats.retained` in jemalloc's API.
/// ///
/// #[global_allocator] /// # Examples
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; ///
/// /// ```rust
/// fn main() { /// # extern crate jemallocator;
/// let epoch = Epoch::new().unwrap(); /// # extern crate jemalloc_ctl;
/// let metadata = Metadata::new().unwrap(); /// #
/// /// # #[global_allocator]
/// epoch.advance().unwrap(); /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// let size = metadata.get().unwrap(); /// #
/// println!("{} bytes of jemalloc metadata", size); /// # fn main() {
/// } /// use jemalloc_ctl::{epoch, stats};
/// ``` /// let e = epoch::mib().unwrap();
/// /// let retained = stats::retained::mib().unwrap();
/// [`Epoch`]: ../struct.Epoch.html ///
#[derive(Copy, Clone)] /// e.advance().unwrap();
pub struct Metadata(Mib<[usize; 2]>); /// let size = retained.read().unwrap();
/// println!("{} bytes of total retained data", size);
impl Metadata { /// # }
/// Returns a new `Metadata`. /// ```
pub fn new() -> Result<Self> { mib_docs: /// See [`retained`].
let mib = METADATA.name().mib()?;
Ok(Metadata(mib))
}
/// Returns the total number of bytes dedicated to jemalloc metadata.
pub fn get(self) -> Result<usize> {
self.0.read()
}
}
const RESIDENT: &[u8] = b"stats.resident\0";
/// Returns the total number of bytes in physically resident data pages mapped by the allocator.
///
/// This consists of all pages dedicated to allocator metadata, pages backing active allocations,
/// and unused dirty pages. It may overestimate the true value because pages may not actually be
/// physically resident if they correspond to demand-zeroed virtual memory that has not yet been
/// touched. This is a multiple of the page size, and is larger than the value returned by
/// [`active`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// function for more information.
///
/// This corresponds to `stats.resident` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// jemalloc_ctl::epoch().unwrap();
/// println!("{} bytes of total resident data", jemalloc_ctl::stats::resident().unwrap());
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch.html
/// [`active`]: fn.active.html
pub fn resident() -> Result<usize> {
RESIDENT.name().read()
}
/// A type providing access to the total number of bytes in physically resident data pages mapped
/// by the allocator.
///
/// This consists of all pages dedicated to allocator metadata, pages backing active allocations,
/// and unused dirty pages. It may overestimate the true value because pages may not actually be
/// physically resident if they correspond to demand-zeroed virtual memory that has not yet been
/// touched. This is a multiple of the page size, and is larger than the value returned by
/// [`Active`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.resident` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Resident;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let resident = Resident::new().unwrap();
///
/// epoch.advance().unwrap();
/// let size = resident.get().unwrap();
/// println!("{} bytes of total resident data", size);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
/// [`Active`]: struct.Active.html
#[derive(Copy, Clone)]
pub struct Resident(Mib<[usize; 2]>);
impl Resident {
/// Returns a new `Resident`.
pub fn new() -> Result<Self> {
let mib = RESIDENT.name().mib()?;
Ok(Resident(mib))
}
/// Returns the total number of bytes in physically resident data pages mapped by the allocator.
pub fn get(self) -> Result<usize> {
self.0.read()
}
}
const MAPPED: &[u8] = b"stats.mapped\0";
/// Returns the total number of bytes in active extents mapped by the allocator.
///
/// This does not include inactive extents, even those taht contain unused dirty pages, so there
/// is no strict ordering between this and the value returned by [`resident`]. This is a
/// multiple of the page size, and is larger than the value returned by [`active`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// type for more information.
///
/// This corresponds to `stats.mapped` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// jemalloc_ctl::epoch().unwrap();
/// println!("{} bytes of total mapped data", jemalloc_ctl::stats::mapped().unwrap());
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch.html
/// [`resident`]: fn.resident.html
/// [`active`]: fn.active.html
pub fn mapped() -> Result<usize> {
MAPPED.name().read()
}
/// A type providing access to the total number of bytes in active extents mapped by the allocator.
///
/// This does not include inactive extents, even those that contain unused dirty pages, so there
/// is no strict ordering between this and the value returned by [`Resident`]. This is a
/// multiple of the page size, and is larger than the value returned by [`Active`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.mapped` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Mapped;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let mapped = Mapped::new().unwrap();
///
/// epoch.advance().unwrap();
/// let size = mapped.get().unwrap();
/// println!("{} bytes of total mapped data", size);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
/// [`Resident`]: struct.Resident.html
/// [`Active`]: struct.Active.html
#[derive(Copy, Clone)]
pub struct Mapped(Mib<[usize; 2]>);
impl Mapped {
/// Returns a new `Mapped`.
pub fn new() -> Result<Self> {
let mib = MAPPED.name().mib()?;
Ok(Mapped(mib))
}
/// Returns the total number of bytes in active extents mapped by the allocator.
pub fn get(self) -> Result<usize> {
self.0.read()
}
}
const RETAINED: &[u8] = b"stats.retained\0";
/// Returns the total number of bytes in virtual memory mappings that were retained rather than being returned to the
/// operating system via e.g. `munmap(2)`.
///
/// Retained virtual memory is typically untouched, decommitted, or purged, so it has no strongly associated physical
/// memory. Retained memory is excluded from mapped memory statistics, e.g. [`mapped`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// type for more information.
///
/// This corresponds to `stats.retained` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// jemalloc_ctl::epoch().unwrap();
/// println!("{} bytes of total retained data", jemalloc_ctl::stats::retained().unwrap());
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch.html
/// [`mapped`]: fn.mapped.html
pub fn retained() -> Result<usize> {
RETAINED.name().read()
}
/// A type providing access to the total number of bytes in virtual memory mappings that were retained rather than being
/// returned to the operating system via e.g. `munmap(2)`.
///
/// Retained virtual memory is typically untouched, decommitted, or purged, so it has no strongly associated physical
/// memory. Retained memory is excluded from mapped memory statistics, e.g. [`Mapped`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.retained` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Retained;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let retained = Retained::new().unwrap();
///
/// epoch.advance().unwrap();
/// let size = retained.get().unwrap();
/// println!("{} bytes of total retained data", size);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
/// [`Mapped`]: struct.Mapped.html
#[derive(Copy, Clone)]
pub struct Retained(Mib<[usize; 2]>);
impl Retained {
/// Returns a new `Retained`.
pub fn new() -> Result<Self> {
let mib = RETAINED.name().mib()?;
Ok(Retained(mib))
}
/// Returns the total number of bytes in virtual memory mappings that were retained.
pub fn get(self) -> Result<usize> {
self.0.read()
}
} }

View File

@ -1,188 +1,115 @@
//! Thread specific operations. //! Thread specific operations.
use error::Result; use error::Result;
use raw::{get, get_mib, name_to_mib}; use raw::{read, read_mib};
const ALLOCATEDP: &[u8] = b"thread.allocatedp\0"; option! {
allocatedp[ str: b"thread.allocatedp\0", non_str: 2 ] => *mut u64 |
/// Returns a thread-local pointer to the total number of bytes allocated by the current thread. ops: |
/// docs:
/// Unlike [`stats::allocated`], the value returned by this type is not the number of bytes /// Access to the total number of bytes allocated by the current thread.
/// *currently* allocated, but rather the number of bytes that have *ever* been allocated by this ///
/// thread. /// Unlike [`::stats::allocated`], the value returned by this type is not the
/// /// number of bytes *currently* allocated, but rather the number of bytes
/// This function doesn't return the value directly, but actually a pointer to the value. This /// that have *ever* been allocated by this thread.
/// allows for very fast repeated lookup, since there is no function call overhead. The pointer type ///
/// cannot be sent to other threads, but `allocated` can be called on different threads and will /// The `read` method doesn't return the value directly, but actually a
/// return the appropriate pointer for each of them. /// pointer to the value. This allows for very fast repeated lookup, since
/// /// there is no function call overhead. The pointer type cannot be sent to
/// This corresponds to `thread.allocatedp` in jemalloc's API. /// other threads, but `allocated::read` can be called on different threads
/// /// and will return the appropriate pointer for each of them.
/// # Examples ///
/// /// # Example
/// ``` ///
/// extern crate jemallocator; /// ```
/// extern crate jemalloc_ctl; /// # extern crate jemallocator;
/// /// # extern crate jemalloc_ctl;
/// #[global_allocator] /// #
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// # #[global_allocator]
/// /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// fn main() { /// #
/// let allocated = jemalloc_ctl::thread::allocatedp().unwrap(); /// # fn main() {
/// /// use jemalloc_ctl::thread;
/// let a = allocated.get(); /// let allocated = thread::allocatedp::mib().unwrap();
/// let buf = vec![0; 1024 * 1024]; /// let allocated = allocated.read().unwrap();
/// let b = allocated.get(); ///
/// drop(buf); /// let a = allocated.get();
/// let c = allocated.get(); /// let buf = vec![0; 1024 * 1024];
/// /// let b = allocated.get();
/// assert!(a < b); /// drop( buf);
/// assert_eq!(b, c); /// let c = allocated.get();
/// } ///
/// ``` /// assert!(a < b);
pub fn allocatedp() -> Result<ThreadLocal<u64>> { /// assert_eq!(b, c);
unsafe { get(ALLOCATEDP).map(ThreadLocal) } /// # }
/// ```
mib_docs: /// See [`allocatedp`].
} }
/// A type providing access to the total number of bytes allocated by the current thread. impl allocatedp {
/// /// Reads value using string API.
/// Unlike [`stats::Allocated`], the value returned by this type is not the number of bytes pub fn read() -> Result<ThreadLocal<u64>> {
/// *currently* allocated, but rather the number of bytes that have *ever* been allocated by this unsafe { read(Self::name().as_bytes()).map(ThreadLocal) }
/// thread.
///
/// The `get` method doesn't return the value directly, but actually a pointer to the value. This
/// allows for very fast repeated lookup, since there is no function call overhead. The pointer type
/// cannot be sent to other threads, but `Allocated::get` can be called on different threads and
/// will return the appropriate pointer for each of them.
///
/// # Example
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::thread::AllocatedP;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let allocated = AllocatedP::new().unwrap();
/// let allocated = allocated.get().unwrap();
///
/// let a = allocated.get();
/// let buf = vec![0; 1024 * 1024];
/// let b = allocated.get();
/// drop(buf);
/// let c = allocated.get();
///
/// assert!(a < b);
/// assert_eq!(b, c);
/// }
/// ```
///
/// [`stats::Allocated`]: ../stats/struct.Allocated.html
#[derive(Copy, Clone)]
pub struct AllocatedP([usize; 2]);
impl AllocatedP {
/// Returns a new `Allocated`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(ALLOCATEDP, &mut mib)?;
Ok(AllocatedP(mib))
}
/// Returns a thread-local pointer to the total number of bytes allocated by this thread.
pub fn get(&self) -> Result<ThreadLocal<u64>> {
unsafe { get_mib(&self.0).map(ThreadLocal) }
} }
} }
const DEALLOCATEDP: &[u8] = b"thread.deallocatedp\0"; impl allocatedp_mib {
/// Reads value using MIB API.
/// Returns a pointer to the total number of bytes deallocated by the current thread. pub fn read(&self) -> Result<ThreadLocal<u64>> {
/// unsafe { read_mib(self.0.as_ref()).map(ThreadLocal) }
/// This function doesn't return the value directly, but actually a pointer to the value. This }
/// allows for very fast repeated lookup, since there is no function call overhead. The pointer type
/// cannot be sent to other threads, but `deallocatedp` can be called on different threads and will
/// return the appropriate pointer for each of them.
///
/// This corresponds to `thread.deallocatedp` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let deallocated = jemalloc_ctl::thread::deallocatedp().unwrap();
///
/// let a = deallocated.get();
/// let buf = vec![0; 1024 * 1024];
/// let b = deallocated.get();
/// drop(buf);
/// let c = deallocated.get();
///
/// assert_eq!(a, b);
/// assert!(b < c);
/// }
/// ```
pub fn deallocatedp() -> Result<ThreadLocal<u64>> {
unsafe { get(DEALLOCATEDP).map(ThreadLocal) }
} }
/// A type providing access to the total number of bytes deallocated by the current thread. option! {
/// deallocatedp[ str: b"thread.deallocatedp\0", non_str: 2 ] => *mut u64 |
/// The `get` method doesn't return the value directly, but actually a pointer to the value. This ops: |
/// allows for very fast repeated lookup, since there is no function call overhead. The pointer type docs:
/// cannot be sent to other threads, but `DeallocatedP::get` can be called on different threads and /// Access to the total number of bytes deallocated by the current thread.
/// will return the appropriate pointer for each of them. ///
/// /// The `read` method doesn't return the value directly, but actually a
/// # Example /// pointer to the value. This allows for very fast repeated lookup, since
/// /// there is no function call overhead. The pointer type cannot be sent to
/// ``` /// other threads, but [`deallocatedp::read`] can be called on different
/// extern crate jemallocator; /// threads and will return the appropriate pointer for each of them.
/// extern crate jemalloc_ctl; ///
/// /// # Example
/// use jemalloc_ctl::thread::DeallocatedP; ///
/// /// ```
/// #[global_allocator] /// # extern crate jemallocator;
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; /// # extern crate jemalloc_ctl;
/// /// #
/// fn main() { /// # #[global_allocator]
/// let deallocated = DeallocatedP::new().unwrap(); /// # static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// let deallocated = deallocated.get().unwrap(); /// #
/// /// # fn main() {
/// let a = deallocated.get(); /// use jemalloc_ctl::thread;
/// let buf = vec![0; 1024 * 1024]; /// let deallocated = thread::deallocatedp::mib().unwrap();
/// let b = deallocated.get(); /// let deallocated = deallocated.read().unwrap();
/// drop(buf); ///
/// let c = deallocated.get(); /// let a = deallocated.get();
/// /// let buf = vec![0; 1024 * 1024];
/// assert_eq!(a, b); /// let b = deallocated.get();
/// assert!(b < c); /// drop(buf);
/// } /// let c = deallocated.get();
/// ``` ///
#[derive(Copy, Clone)] /// assert_eq!(a, b);
pub struct DeallocatedP([usize; 2]); /// assert!(b < c);
/// # }
/// ```
mib_docs: /// See [`deallocatedp`].
}
impl DeallocatedP { impl deallocatedp {
/// Returns a new `Deallocated`. /// Reads value using string API.
pub fn new() -> Result<Self> { pub fn read() -> Result<ThreadLocal<u64>> {
let mut mib = [0; 2]; unsafe { read(Self::name().as_bytes()).map(ThreadLocal) }
name_to_mib(DEALLOCATEDP, &mut mib)?;
Ok(DeallocatedP(mib))
} }
}
/// Returns a thread-local pointer to the total number of bytes deallocated by this thread. impl deallocatedp_mib {
pub fn get(&self) -> Result<ThreadLocal<u64>> { /// Reads value using MIB API.
let ptr = unsafe { get_mib::<*mut u64>(&self.0)? }; pub fn read(&self) -> Result<ThreadLocal<u64>> {
Ok(ThreadLocal(ptr)) unsafe { read_mib(self.0.as_ref()).map(ThreadLocal) }
} }
} }
@ -190,6 +117,7 @@ impl DeallocatedP {
/// ///
/// It is neither `Sync` nor `Send`. /// It is neither `Sync` nor `Send`.
// NB we need *const here specifically since it's !Sync + !Send // NB we need *const here specifically since it's !Sync + !Send
#[repr(transparent)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct ThreadLocal<T>(*const T); pub struct ThreadLocal<T>(*const T);

View File

@ -1,63 +0,0 @@
//! Version operations.
use error::Result;
use keys::{Access, AsName, MibStr};
const VERSION: &[u8] = b"version\0";
/// Returns the jemalloc version string.
///
/// # Note
///
/// The version of jemalloc currently shipped with the Rust distribution has a bogus version string.
///
/// # Example
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("jemalloc version {}", jemalloc_ctl::version().unwrap());
/// }
/// ```
pub fn version() -> Result<&'static str> {
VERSION.name().read()
}
/// A type providing access to the jemalloc version string.
///
/// # Example
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Version;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let version = Version::new().unwrap();
///
/// println!("jemalloc version {}", version.get().unwrap());
/// }
#[derive(Copy, Clone)]
pub struct Version(MibStr<[usize; 1]>);
impl Version {
/// Returns a new `Version`.
pub fn new() -> Result<Self> {
let mib = VERSION.name().mib_str()?;
Ok(Version(mib))
}
/// Returns the jemalloc version string.
pub fn get(self) -> Result<&'static str> {
self.0.read()
}
}

View File

@ -11,7 +11,7 @@ static A: Jemalloc = Jemalloc;
// Returns true if background threads are enabled. // Returns true if background threads are enabled.
fn background_threads() -> bool { fn background_threads() -> bool {
jemalloc_ctl::opt::background_thread().unwrap() jemalloc_ctl::opt::background_thread::read().unwrap()
} }
#[test] #[test]

View File

@ -32,5 +32,5 @@ pub static malloc_conf: Option<&'static libc::c_char> = Some(unsafe {
#[test] #[test]
fn background_threads_enabled() { fn background_threads_enabled() {
// Background threads are unconditionally enabled at run-time by default. // Background threads are unconditionally enabled at run-time by default.
assert_eq!(jemalloc_ctl::opt::background_thread().unwrap(), true); assert_eq!(jemalloc_ctl::opt::background_thread::read().unwrap(), true);
} }

View File

@ -2,7 +2,7 @@ extern crate jemalloc_ctl;
extern crate jemallocator; extern crate jemallocator;
extern crate libc; extern crate libc;
use jemalloc_ctl::{AsName, Access}; use jemalloc_ctl::{Access, AsName};
use jemallocator::Jemalloc; use jemallocator::Jemalloc;
use std::alloc::{GlobalAlloc, Layout}; use std::alloc::{GlobalAlloc, Layout};
@ -21,33 +21,33 @@ fn smoke() {
#[test] #[test]
fn ctl_get_set() { fn ctl_get_set() {
let epoch: u64 = b"epoch\0".name().read().unwrap(); let epoch: u64 = "epoch\0".name().read().unwrap();
assert!(epoch > 0); assert!(epoch > 0);
b"epoch\0".name().write(epoch).unwrap(); "epoch\0".name().write(epoch).unwrap();
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn ctl_panic_empty_get() { fn ctl_panic_empty_get() {
let _ : u64 = b"".name().read().unwrap(); let _: u64 = "".name().read().unwrap();
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn ctl_panic_empty_set() { fn ctl_panic_empty_set() {
let epoch: u64 = b"epoch\0".name().read().unwrap(); let epoch: u64 = "epoch\0".name().read().unwrap();
b"".name().write(epoch).unwrap(); "".name().write(epoch).unwrap();
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn ctl_panic_non_null_terminated_get() { fn ctl_panic_non_null_terminated_get() {
let _: u64 = b"epoch".name().read().unwrap(); let _: u64 = "epoch".name().read().unwrap();
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn ctl_panic_non_null_terminated_set() { fn ctl_panic_non_null_terminated_set() {
let epoch: u64 = b"epoch\0".name().read().unwrap(); let epoch: u64 = "epoch\0".name().read().unwrap();
b"epoch".name().write(epoch).unwrap(); "epoch".name().write(epoch).unwrap();
} }