add jemalloc-ctl

This commit is contained in:
gnzlbg 2018-11-07 19:57:11 +01:00 committed by gnzlbg
parent 4d3701f58f
commit 0a711d8fc1
26 changed files with 2743 additions and 99 deletions

View File

@ -137,6 +137,7 @@ matrix:
if rustup component add clippy-preview; then
cargo clippy -p jemalloc-sys -- -D clippy::pedantic
cargo clippy -p jemallocator -- -D clippy::pedantic
cargo clippy -p jemalloc-ctl -- -D clippy::pedantic
fi
- name: "Shellcheck"
install: true

View File

@ -1,10 +1,16 @@
[package]
name = "jemallocator"
version = "0.2.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
version = "0.3.0"
authors = [
"Alex Crichton <alex@alexcrichton.com>",
"Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>",
"Simon Sapin <simon.sapin@exyr.org>",
"Steven Fackler <sfackler@gmail.com>"
]
license = "MIT/Apache-2.0"
readme = "README.md"
keywords = ["allocator"]
keywords = ["allocator", "jemalloc"]
categories = ["memory-management", "api-bindings"]
repository = "https://github.com/alexcrichton/jemallocator"
homepage = "https://github.com/alexcrichton/jemallocator"
documentation = "https://docs.rs/jemallocator"
@ -13,6 +19,14 @@ A Rust allocator backed by jemalloc
"""
edition = "2015"
[badges]
appveyor = { repository = "alexcrichton/jemallocator" }
travis-ci = { repository = "alexcrichton/jemallocator" }
codecov = { repository = "alexcrichton/jemallocator" }
is-it-maintained-issue-resolution = { repository = "alexcrichton/jemallocator" }
is-it-maintained-open-issues = { repository = "alexcrichton/jemallocator" }
maintenance = { status = "actively-developed" }
[lib]
test = false
bench = false
@ -21,11 +35,12 @@ bench = false
members = ["systest", "jemallocator-global" ]
[dependencies]
jemalloc-sys = { path = "jemalloc-sys", version = "0.2.0", default-features = false }
jemalloc-sys = { path = "jemalloc-sys", version = "0.3.0", default-features = false }
libc = { version = "^0.2.8", default-features = false }
[dev-dependencies]
paste = "0.1"
jemalloc-ctl = { path = "jemalloc-ctl", version = "0.3.0" }
[features]
default = ["background_threads_runtime_support"]

8
ci/dox.sh Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env sh
set -ex
export RUSTDOCFLAGS="--cfg jemallocator_docs"
cargo doc --features alloc_trait
cargo doc -p jemalloc-sys
cargo doc -p jemalloc-ctl

View File

@ -94,6 +94,10 @@ ${CARGO_CMD} test -vv --target "${TARGET}" --manifest-path jemalloc-sys/Cargo.to
${CARGO_CMD} test -vv --target "${TARGET}" \
--manifest-path jemalloc-sys/Cargo.toml \
--features unprefixed_malloc_on_supported_platforms
${CARGO_CMD} test -vv --target "${TARGET}" --manifest-path jemalloc-ctl/Cargo.toml \
--no-default-features
${CARGO_CMD} test -vv --target "${TARGET}" --manifest-path jemalloc-ctl \
--no-default-features --features use_std
${CARGO_CMD} test -vv --target "${TARGET}" -p systest
${CARGO_CMD} test -vv --target "${TARGET}" \
--manifest-path jemallocator-global/Cargo.toml

37
jemalloc-ctl/Cargo.toml Normal file
View File

@ -0,0 +1,37 @@
[package]
name = "jemalloc-ctl"
version = "0.3.0"
authors = ["Steven Fackler <sfackler@gmail.com>"]
license = "MIT/Apache-2.0"
readme = "README.md"
categories = ["memory-management", "api-bindings", "development-tools" ]
keywords = ["allocator", "jemalloc"]
repository = "https://github.com/alexcrichton/jemallocator"
homepage = "https://github.com/alexcrichton/jemallocator"
documentation = "https://docs.rs/jemalloc-ctl"
description = """
A safe wrapper over jemalloc's control and introspection APIs
"""
[badges]
appveyor = { repository = "alexcrichton/jemallocator" }
travis-ci = { repository = "alexcrichton/jemallocator" }
codecov = { repository = "alexcrichton/jemallocator" }
is-it-maintained-issue-resolution = { repository = "alexcrichton/jemallocator" }
is-it-maintained-open-issues = { repository = "alexcrichton/jemallocator" }
maintenance = { status = "actively-developed" }
[dependencies]
jemalloc-sys = { path = "../jemalloc-sys", version = "0.3.0" }
libc = { version = "0.2", default-features = false }
[dev-dependencies]
jemallocator = { path = "..", version = "0.3.0" }
[features]
default = []
use_std = [ "libc/use_std" ]
[package.metadata.docs.rs]
rustdoc-args = [ "--cfg jemallocator_docs" ]

202
jemalloc-ctl/LICENSE-APACHE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

19
jemalloc-ctl/LICENSE-MIT Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2017 Steven Fackler
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

67
jemalloc-ctl/README.md Normal file
View File

@ -0,0 +1,67 @@
# jemalloc-ctl
[![Travis-CI Status]][travis] [![Appveyor Status]][appveyor] [![Latest Version]][crates.io] [![docs]][docs.rs]
> A safe wrapper over `jemalloc`'s `mallctl*()` control and introspection APIs.
## Documentation
* [Latest release (docs.rs)][docs.rs]
* [master branch`][master_docs]
## Platform support
Supported on all platforms supported by the [`jemallocator`] crate.
## Example
```rust
extern crate jemallocator;
extern crate jemalloc_ctl;
use std::thread;
use std::time::Duration;
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
fn main() {
loop {
// many statistics are cached and only updated when the epoch is advanced.
jemalloc_ctl::epoch().unwrap();
let allocated = jemalloc_ctl::stats::allocated().unwrap();
let resident = jemalloc_ctl::stats::resident().unwrap();
println!("{} bytes allocated/{} bytes resident", allocated, resident);
thread::sleep(Duration::from_secs(10));
}
}
```
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in `jemalloc-ctl` by you, as defined in the Apache-2.0 license,
shall be dual licensed as above, without any additional terms or conditions.
[`jemallocator`]: https://github.com/alexcrichton/jemallocator
[travis]: https://travis-ci.org/alexcrichton/jemallocator
[Travis-CI Status]: https://travis-ci.org/alexcrichton/jemallocator.svg?branch=master
[appveyor]: https://ci.appveyor.com/project/alexcrichton/jemallocator/branch/master
[Appveyor Status]: https://ci.appveyor.com/api/projects/status/github/alexcrichton/jemallocator?branch=master&svg=true
[Latest Version]: https://img.shields.io/crates/v/jemalloc-ctl.svg
[crates.io]: https://crates.io/crates/jemalloc-ctl
[docs]: https://docs.rs/jemalloc-ctl/badge.svg
[docs.rs]: https://docs.rs/jemalloc-ctl/
[master_docs]: https://alexcrichton.github.io/jemallocator/jemalloc-ctl

View File

@ -0,0 +1,65 @@
//! Arena operations.
use error::Result;
use libc::c_uint;
use raw::{get, get_mib, name_to_mib};
const NARENAS: &[u8] = b"arenas.narenas\0";
/// Returns the current limit on the number of arenas.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!(
/// "number of arenas: {}",
/// jemalloc_ctl::arenas::narenas().unwrap()
/// );
/// }
/// ```
pub fn narenas() -> Result<c_uint> {
get(NARENAS)
}
/// 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([usize; 2]);
impl NArenas {
/// Returns a new `NArenas`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(NARENAS, &mut mib)?;
Ok(NArenas(mib))
}
/// Returns the maximum number of arenas.
pub fn get(self) -> Result<c_uint> {
get_mib(&self.0)
}
}

View File

@ -0,0 +1,183 @@
//! Background thread operations.
use error::Result;
use raw::{get, get_mib, name_to_mib, set, set_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> {
get(BACKGROUND_THREAD)
}
/// 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<()> {
set(BACKGROUND_THREAD, 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([usize; 1]);
impl BackgroundThread {
/// Returns a new `BackgroundThread`.
pub fn new() -> Result<Self> {
let mut mib = [0; 1];
name_to_mib(BACKGROUND_THREAD, &mut mib)?;
Ok(BackgroundThread(mib))
}
/// Returns the current background thread state.
pub fn get(self) -> Result<bool> {
get_mib(&self.0)
}
/// Sets the background thread state.
pub fn set(self, background_thread: bool) -> Result<()> {
set_mib(&self.0, 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> {
get(MAX_BACKGROUND_THREADS)
}
/// 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<()> {
set(MAX_BACKGROUND_THREADS, 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([usize; 1]);
impl MaxBackgroundThreads {
/// Returns a new `MaxBackgroundThreads`.
pub fn new() -> Result<Self> {
let mut mib = [0; 1];
name_to_mib(MAX_BACKGROUND_THREADS, &mut mib)?;
Ok(MaxBackgroundThreads(mib))
}
/// Returns the current background thread limit.
pub fn get(self) -> Result<usize> {
get_mib(&self.0)
}
/// Sets the background thread limit.
pub fn set(self, max_background_threads: usize) -> Result<()> {
set_mib(&self.0, max_background_threads)
}
}

View File

@ -0,0 +1,70 @@
//! Information about the jemalloc compile-time configuration
use error::Result;
use raw::{get_str, get_str_mib, name_to_mib};
const MALLOC_CONF: &[u8] = b"config.malloc_conf\0";
/// Returns the embeddec 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;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!(
/// "default malloc conf: {}",
/// jemalloc_ctl::config::malloc_conf().unwrap()
/// );
/// }
/// ```
pub fn malloc_conf() -> Result<&'static str> {
get_str(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([usize; 2]);
impl MallocConf {
/// Returns a new `MallocConf`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(MALLOC_CONF, &mut mib)?;
Ok(MallocConf(mib))
}
/// Returns the embedded configure-time-specified run-time options config.
pub fn get(self) -> Result<&'static str> {
get_str_mib(&self.0)
}
}

77
jemalloc-ctl/src/epoch.rs Normal file
View File

@ -0,0 +1,77 @@
//! Epoch access.
use error::Result;
use raw::{get_set, get_set_mib, name_to_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> {
get_set(EPOCH, 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([usize; 1]);
impl Epoch {
/// Returns a new `Epoch`.
pub fn new() -> Result<Self> {
let mut mib = [0; 1];
name_to_mib(EPOCH, &mut 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> {
get_set_mib(&self.0, 1)
}
}

92
jemalloc-ctl/src/error.rs Normal file
View File

@ -0,0 +1,92 @@
//! Error type
use libc::c_int;
use {fmt, result};
/// Error of the `jemalloc_sys::mallct`-family of functions.
///
/// The `jemalloc-sys` crate: `mallctl`, `mallctlnametomib`, and `mallctlbymib``
/// functions return `0` on success; otherwise they return an error value.
#[derive(Copy, Clone, PartialEq)]
#[repr(i32)]
pub enum Error {
/// Invalid argument.
///
/// `newp` is not `NULL`, and `newlen` is too large or too small.
/// Alternatively, `*oldlenp` is too large or too small; in this case as
/// much data as possible are read despite the error.
EINVAL = libc::EINVAL,
/// Unknown/invalid `name` or `mib`.
ENOENT = libc::ENOENT,
/// Invalid access.
///
/// Attempt to read or write `void` value, or attempt to write read-only
/// value.
EPERM = libc::EPERM,
/// Memory allocation failure.
EAGAIN = libc::EAGAIN,
/// Interface with side-effects failed in some way not directly related to
/// `mallctl*` read/write processing.
EFAULT = libc::EFAULT,
}
/// Result type
pub type Result<T> = result::Result<T, Error>;
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::EINVAL => write!(
f,
"`newp` is not `NULL`, and `newlen` is too large or too \
small. Alternatively, `*oldlenp` is too large or too \
small; in this case as much data as possible are read \
despite the error."
),
Error::ENOENT => write!(f, "`name` or `mib` specifies an unknown/invalid value."),
Error::EPERM => write!(
f,
"Attempt to read or write `void` value, or attempt to \
write read-only value."
),
Error::EAGAIN => write!(f, "A memory allocation failure occurred."),
Error::EFAULT => write!(
f,
"An interface with side effects failed in some way not \
directly related to `mallctl*()` read/write processing."
),
}
}
}
impl Error {
fn new(e: c_int) -> Self {
match e {
libc::EINVAL => Error::EINVAL,
libc::ENOENT => Error::ENOENT,
libc::EPERM => Error::EPERM,
libc::EAGAIN => Error::EAGAIN,
libc::EFAULT => Error::EFAULT,
v => panic!("unknown error code: {}", v),
}
}
}
pub(crate) fn cvt(ret: c_int) -> Result<()> {
match ret {
0 => Ok(()),
v => Err(Error::new(v)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn size_of_result_error() {
use mem::size_of;
assert_eq!(size_of::<Result<()>>(), size_of::<Error>());
assert_eq!(size_of::<Error>(), size_of::<libc::c_int>());
}
}

105
jemalloc-ctl/src/lib.rs Normal file
View File

@ -0,0 +1,105 @@
//! `jemalloc` control and introspection.
//!
//! `jemalloc` offers a powerful introspection and control interface through the `mallctl` function.
//! It can be used to tune the allocator, take heap dumps, and retrieve statistics. This crate
//! provides a typed API over that interface.
//!
//! While `mallctl` takes a string to specify an operation (e.g. `stats.allocated` or
//! `stats.arenas.15.muzzy_decay_ms`), the overhead of repeatedly parsing those strings is not
//! ideal. Fortunately, `jemalloc` offers the ability to translate the string ahead of time into a
//! "Management Information Base" (MIB) to speed up future lookups.
//!
//! This crate provides both a function and a type for each `mallctl` operation. While the
//! function is more convenient, the type will be more efficient if the operation will be repeatedly
//! performed. Its constructor performs the MIB lookup, so the struct should be saved if the same
//! operation is going to be repeatedly performed.
//!
//! # Examples
//!
//! Repeatedly printing allocation statistics:
//!
//! ```no_run
//! extern crate jemallocator;
//! extern crate jemalloc_ctl;
//!
//! use std::thread;
//! use std::time::Duration;
//!
//! #[global_allocator]
//! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
//!
//! fn main() {
//! loop {
//! // many statistics are cached and only updated when the epoch is advanced.
//! jemalloc_ctl::epoch().unwrap();
//!
//! let allocated = jemalloc_ctl::stats::allocated().unwrap();
//! let resident = jemalloc_ctl::stats::resident().unwrap();
//! println!("{} bytes allocated/{} bytes resident", allocated, resident);
//! thread::sleep(Duration::from_secs(10));
//! }
//! }
//! ```
//!
//! Doing the same with the MIB-based API:
//!
//! ```no_run
//! extern crate jemallocator;
//! extern crate jemalloc_ctl;
//!
//! use std::thread;
//! use std::time::Duration;
//!
//! #[global_allocator]
//! static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
//!
//! fn main() {
//! let epoch = jemalloc_ctl::Epoch::new().unwrap();
//! let allocated = jemalloc_ctl::stats::Allocated::new().unwrap();
//! let resident = jemalloc_ctl::stats::Resident::new().unwrap();
//! loop {
//! // many statistics are cached and only updated when the epoch is advanced.
//! epoch.advance().unwrap();
//!
//! let allocated = allocated.get().unwrap();
//! let resident = resident.get().unwrap();
//! println!("{} bytes allocated/{} bytes resident", allocated, resident);
//! thread::sleep(Duration::from_secs(10));
//! }
//! }
//! ```
#![deny(missing_docs)]
#![cfg_attr(not(feature = "use_std"), no_std)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::stutter))]
extern crate jemalloc_sys;
extern crate libc;
#[cfg(test)]
extern crate jemallocator;
#[cfg(test)]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[cfg(not(feature = "use_std"))]
use core as std;
use std::{fmt, mem, ptr, result, slice};
pub mod arenas;
mod background_threads;
pub mod config;
mod epoch;
mod error;
pub mod opt;
pub mod raw;
pub mod stats;
#[cfg(feature = "use_std")]
pub mod stats_print;
pub mod thread;
mod version;
pub use background_threads::*;
pub use epoch::*;
pub use error::{Error, Result};
pub use version::*;

531
jemalloc-ctl/src/opt.rs Normal file
View File

@ -0,0 +1,531 @@
//! Information about the run-time `jemalloc` configuration.
//!
//! These settings are controlled by the `MALLOC_CONF` environment variable.
use error::Result;
use libc::c_uint;
use raw::{get, get_mib, get_str, get_str_mib, name_to_mib};
const ABORT: &[u8] = b"opt.abort\0";
/// Determines if `jemalloc` will call `abort(3)` on most warnings.
///
/// This is disabled by default unless `--enable-debug` was specified during build configuration.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("abort on warning: {}", jemalloc_ctl::opt::abort().unwrap());
/// }
/// ```
pub fn abort() -> Result<bool> {
get(ABORT)
}
/// A type determining if `jemalloc` will call `abort(3)` on most warnings.
///
/// This is disabled by default unless `--enable-debug` was specified during build configuration.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Abort;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let abort = Abort::new().unwrap();
///
/// println!("abort on warning: {}", abort.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Abort([usize; 2]);
impl Abort {
/// Returns a new `Abort`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(ABORT, &mut mib)?;
Ok(Abort(mib))
}
/// Returns the abort-on-warning behavior.
pub fn get(self) -> Result<bool> {
get_mib(&self.0)
}
}
const DSS: &[u8] = b"opt.dss\0";
/// Returns the dss (`sbrk(2)`) allocation precedence as related to `mmap(2)` allocation.
///
/// The following settings are supported if `sbrk(2)` is supported by the operating system:
/// "disabled", "primary", and "secondary"; otherwise only "disabled" is supported. The default is
/// "secondary" if `sbrk(2)` is supported by the operating system; "disabled" otherwise.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("dss priority: {}", jemalloc_ctl::opt::dss().unwrap());
/// }
/// ```
pub fn dss() -> Result<&'static str> {
get_str(DSS)
}
/// A type providing access to the dss (`sbrk(2)`) allocation precedence as related to `mmap(2)`
/// allocation.
///
/// The following settings are supported if `sbrk(2)` is supported by the operating system:
/// "disabled", "primary", and "secondary"; otherwise only "disabled" is supported. The default is
/// "secondary" if `sbrk(2)` is supported by the operating system; "disabled" otherwise.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Dss;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let dss = Dss::new().unwrap();
///
/// println!("dss priority: {}", dss.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Dss([usize; 2]);
impl Dss {
/// Returns a new `Dss`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(DSS, &mut mib)?;
Ok(Dss(mib))
}
/// Returns the dss allocation precedence.
pub fn get(self) -> Result<&'static str> {
get_str_mib(&self.0)
}
}
const NARENAS: &[u8] = b"opt.narenas\0";
/// Returns the maximum number of arenas to use for automatic multiplexing of threads and arenas.
///
/// The default is four times the number of CPUs, or one if there is a single CPU.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("number of arenas: {}", jemalloc_ctl::opt::narenas().unwrap());
/// }
/// ```
pub fn narenas() -> Result<c_uint> {
get(NARENAS)
}
/// A type providing access to the maximum number of arenas to use for automatic multiplexing of
/// threads and arenas.
///
/// The default is four times the number of CPUs, or one if there is a single CPU.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::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([usize; 2]);
impl NArenas {
/// Returns a new `NArenas`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(NARENAS, &mut mib)?;
Ok(NArenas(mib))
}
/// Returns the maximum number of arenas.
pub fn get(self) -> Result<c_uint> {
get_mib(&self.0)
}
}
const JUNK: &[u8] = b"opt.junk\0";
/// Returns `jemalloc`'s junk filling mode.
///
/// Requires `--enable-fill` to have been specified during build configuration.
///
/// 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
/// and deallocated memory will be initialized, and if set to "false" junk filling will be disabled.
/// This is intended for debugging and will impact performance negatively.
///
/// The default is "false", unless `--enable-debug` was specified during build configuration, in
/// which case the default is "true".
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// println!("junk filling: {}", jemalloc_ctl::opt::junk().unwrap());
/// }
/// ```
pub fn junk() -> Result<&'static str> {
get_str(JUNK)
}
/// A type providing access to `jemalloc`'s junk filling mode.
///
/// Requires `--enable-fill` to have been specified during build configuration.
///
/// 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
/// and deallocated memory will be initialized, and if set to "false" junk filling will be disabled.
/// This is intended for debugging and will impact performance negatively.
///
/// The default is "false", unless `--enable-debug` was specified during build configuration, in
/// which case the default is "true".
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::opt::Junk;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let junk = Junk::new().unwrap();
///
/// println!("junk filling: {}", junk.get().unwrap());
/// }
/// ```
#[derive(Copy, Clone)]
pub struct Junk([usize; 2]);
impl Junk {
/// Returns a new `Junk`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(JUNK, &mut mib)?;
Ok(Junk(mib))
}
/// Returns jemalloc's junk filling mode.
pub fn get(self) -> Result<&'static str> {
get_str_mib(&self.0)
}
}
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> {
get(ZERO)
}
/// 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([usize; 2]);
impl Zero {
/// Returns a new `Zero`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(ZERO, &mut mib)?;
Ok(Zero(mib))
}
/// Returns the `jemalloc` zeroing behavior.
pub fn get(self) -> Result<bool> {
get_mib(&self.0)
}
}
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> {
get(TCACHE)
}
/// 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([usize; 2]);
impl Tcache {
/// Returns a new `Tcache`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(TCACHE, &mut mib)?;
Ok(Tcache(mib))
}
/// Returns the thread-local caching behavior.
pub fn get(self) -> Result<bool> {
get_mib(&self.0)
}
}
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> {
get(LG_TCACHE_MAX)
}
/// 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([usize; 2]);
impl LgTcacheMax {
/// Returns a new `LgTcacheMax`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(LG_TCACHE_MAX, &mut mib)?;
Ok(LgTcacheMax(mib))
}
/// Returns the maximum cached size class.
pub fn get(self) -> Result<usize> {
get_mib(&self.0)
}
}
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> {
get(BACKGROUND_THREAD)
}
/// 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([usize; 2]);
impl BackgroundThread {
/// Returns a new `BackgroundThread`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(BACKGROUND_THREAD, &mut mib)?;
Ok(BackgroundThread(mib))
}
/// Returns the background thread initialization behavior.
pub fn get(self) -> Result<bool> {
get_mib(&self.0)
}
}

244
jemalloc-ctl/src/raw.rs Normal file
View File

@ -0,0 +1,244 @@
//! Raw `malloctl` getter/setters
use error::{cvt, Error, Result};
use libc::c_char;
use {mem, ptr, slice};
/// Translates `name` to a `mib` (Management Information Base)
///
/// `mib`s are used to avoid repeated name lookups for applications that
/// repeatedly query the same portion of `jemalloc`s `mallctl` namespace.
///
/// On success, `mib` contains an array of integers. It is possible to pass
/// `mib` with a length smaller than the number of period-separated name
/// components. This results in a partial MIB that can be used as the basis for
/// constructing a complete MIB.
///
/// For name components that are integers (e.g. the `2` in `arenas.bin.2.size`),
/// the corresponding MIB component will always be that integer. Therefore, it
/// is legitimate to construct code like the following:
///
/// ```
/// extern crate libc;
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// use jemalloc_ctl::raw;
/// use libc::{c_uint, c_char};
/// unsafe {
/// let mut mib = [0; 4];
/// let nbins: c_uint = raw::get(b"arenas.nbins\0").unwrap();
/// raw::name_to_mib(b"arenas.bin.0.size\0", &mut mib).unwrap();
/// for i in 0..4 {
/// mib[2] = i;
/// let bin_size: usize = raw::get_mib(&mut mib).unwrap();
/// println!("arena bin {} has size {}", i, bin_size);
/// }
/// }
/// }
/// ```
pub fn name_to_mib(name: &[u8], mib: &mut [usize]) -> Result<()> {
unsafe {
validate_name(name)?;
let mut len = mib.len();
cvt(jemalloc_sys::mallctlnametomib(
name as *const _ as *const c_char,
mib.as_mut_ptr(),
&mut len,
))?;
assert_eq!(mib.len(), len);
Ok(())
}
}
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and reads its value.
///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
/// to a `mib` (Management Information Base).
pub fn get_mib<T: Copy>(mib: &[usize]) -> Result<T> {
unsafe {
let mut value = MaybeUninit { init: () };
let mut len = mem::size_of::<T>();
cvt(jemalloc_sys::mallctlbymib(
mib.as_ptr(),
mib.len(),
&mut value.init as *mut _ as *mut _,
&mut len,
ptr::null_mut(),
0,
))?;
assert_eq!(len, mem::size_of::<T>());
Ok(value.maybe_uninit)
}
}
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
/// reads its value.
pub fn get<T: Copy>(name: &[u8]) -> Result<T> {
unsafe {
validate_name(name)?;
let mut value = MaybeUninit { init: () };
let mut len = mem::size_of::<T>();
cvt(jemalloc_sys::mallctl(
name as *const _ as *const c_char,
&mut value.init as *mut _ as *mut _,
&mut len,
ptr::null_mut(),
0,
))?;
assert_eq!(len, mem::size_of::<T>());
Ok(value.maybe_uninit)
}
}
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`.
///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
/// to a `mib` (Management Information Base).
pub fn set_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
unsafe {
cvt(jemalloc_sys::mallctlbymib(
mib.as_ptr(),
mib.len(),
ptr::null_mut(),
ptr::null_mut(),
&mut value as *mut _ as *mut _,
mem::size_of::<T>(),
))
}
}
/// Uses the null-terminated string `name` as the key to the _MALLCTL NAMESPACE_
/// and sets it `value`
pub fn set<T>(name: &[u8], mut value: T) -> Result<()> {
unsafe {
validate_name(name)?;
cvt(jemalloc_sys::mallctl(
name as *const _ as *const c_char,
ptr::null_mut(),
ptr::null_mut(),
&mut value as *mut _ as *mut _,
mem::size_of::<T>(),
))
}
}
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and sets its `value`
/// returning its previous value.
///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
/// to a `mib` (Management Information Base).
pub fn get_set_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
unsafe {
let mut len = mem::size_of::<T>();
cvt(jemalloc_sys::mallctlbymib(
mib.as_ptr(),
mib.len(),
&mut value as *mut _ as *mut _,
&mut len,
&mut value as *mut _ as *mut _,
len,
))?;
assert_eq!(len, mem::size_of::<T>());
Ok(value)
}
}
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
/// sets its `value` returning its previous value.
pub fn get_set<T>(name: &[u8], mut value: T) -> Result<T> {
unsafe {
validate_name(name)?;
let mut len = mem::size_of::<T>();
cvt(jemalloc_sys::mallctl(
name as *const _ as *const c_char,
&mut value as *mut _ as *mut _,
&mut len,
&mut value as *mut _ as *mut _,
len,
))?;
assert_eq!(len, mem::size_of::<T>());
Ok(value)
}
}
/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and reads its value of
/// type `&str`
///
/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
/// to a `mib` (Management Information Base).
pub fn get_str_mib(mib: &[usize]) -> Result<&'static str> {
unsafe {
let ptr: *const c_char = get_mib(mib)?;
ptr2str(ptr)
}
}
/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
/// reads its value of type `&str`.
pub fn get_str(name: &[u8]) -> Result<&'static str> {
unsafe {
validate_name(name)?;
let ptr: *const c_char = get(name)?;
ptr2str(ptr)
}
}
unsafe fn ptr2str(ptr: *const c_char) -> Result<&'static str> {
let len = libc::strlen(ptr);
let byte_slice = slice::from_raw_parts(ptr as *const u8, (len + 1) as _);
core::str::from_utf8(byte_slice).map_err(|_| Error::EINVAL)
}
fn validate_name(name: &[u8]) -> Result<()> {
if name.is_empty() || *name.last().unwrap() != b'\0' {
Err(Error::EINVAL)
} else {
Ok(())
}
}
union MaybeUninit<T: Copy> {
init: (),
maybe_uninit: T,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ptr2str() {
unsafe {
//{ // This is undefined behavior:
// let cstr = b"";
// let rstr = ptr2str(cstr as *const _ as *const c_char);
// assert!(rstr.is_err());
// }
{
let cstr = b"\0";
let rstr = ptr2str(cstr as *const _ as *const c_char);
assert!(rstr.is_ok());
let rstr = rstr.unwrap();
assert!(rstr.len() == 1);
assert_eq!(rstr, "\0");
}
{
let cstr = b"foo baaar\0";
let rstr = ptr2str(cstr as *const _ as *const c_char);
assert!(rstr.is_ok());
let rstr = rstr.unwrap();
assert!(rstr.len() == "foo baaar\0".len());
assert_eq!(rstr, "foo baaar\0");
}
}
}
}

521
jemalloc-ctl/src/stats.rs Normal file
View File

@ -0,0 +1,521 @@
//! Global allocator statistics.
//!
//! `jemalloc` tracks a wide variety of statistics. Many of them are cached, and only refreshed when
//! the jemalloc "epoch" is advanced. See the [`Epoch`] type for more information.
//!
//! [`Epoch`]: ../struct.Epoch.html
use error::Result;
use raw::{get, get_mib, name_to_mib};
const ALLOCATED: &[u8] = b"stats.allocated\0";
/// Returns the total number of bytes allocated by the application.
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// function for more information.
///
/// This corresponds to `stats.allocated` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let a = jemalloc_ctl::stats::allocated().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// jemalloc_ctl::epoch().unwrap();
/// let b = jemalloc_ctl::stats::allocated().unwrap();
/// assert!(a < b);
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch().html
pub fn allocated() -> Result<usize> {
get(ALLOCATED)
}
/// A type providing access to the total number of bytes allocated by the application.
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.allocated` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Allocated;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let allocated = Allocated::new().unwrap();
///
/// let a = allocated.get().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// epoch.advance().unwrap();
/// let b = allocated.get().unwrap();
/// assert!(a < b);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
#[derive(Copy, Clone)]
pub struct Allocated([usize; 2]);
impl Allocated {
/// Returns a new `Allocated`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(ALLOCATED, &mut mib)?;
Ok(Allocated(mib))
}
/// Returns the total number of bytes allocated by the application.
pub fn get(self) -> Result<usize> {
get_mib(&self.0)
}
}
const ACTIVE: &[u8] = b"stats.active\0";
/// Returns the total number of bytes in active pages allocated by the application.
///
/// This is a multiple of the page size, and is greater than or equal to the value returned by
/// [`allocated`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// type for more information.
///
/// This corresponds to `stats.active` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let a = jemalloc_ctl::stats::active().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// jemalloc_ctl::epoch().unwrap();
/// let b = jemalloc_ctl::stats::active().unwrap();
/// assert!(a < b);
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch().html
/// [`allocated`]: fn.allocated.hml
pub fn active() -> Result<usize> {
get(ACTIVE)
}
/// A type providing access to the total number of bytes in active pages allocated by the
/// application.
///
/// This is a multiple of the page size, and greater than or equal to the value returned by
/// [`Allocated`].
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.active` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Active;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let active = Active::new().unwrap();
///
/// let a = active.get().unwrap();
/// let _buf = vec![0; 1024 * 1024];
/// epoch.advance().unwrap();
/// let b = active.get().unwrap();
/// assert!(a < b);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
/// [`Allocated`]: struct.Allocated.html
#[derive(Copy, Clone)]
pub struct Active([usize; 2]);
impl Active {
/// Returns a new `Allocated`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(ACTIVE, &mut mib)?;
Ok(Active(mib))
}
/// Returns the total number of bytes in active pages allocated by the application.
pub fn get(self) -> Result<usize> {
get_mib(&self.0)
}
}
const METADATA: &[u8] = b"stats.metadata\0";
/// Returns the total number of bytes dedicated to jemalloc metadata.
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`epoch`]
/// function for more information.
///
/// This corresponds to `stats.metadata` 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 jemalloc metadata", jemalloc_ctl::stats::metadata().unwrap());
/// }
/// ```
///
/// [`epoch`]: ../fn.epoch.html
pub fn metadata() -> Result<usize> {
get(METADATA)
}
/// A type providing access to the total number of bytes dedicated to jemalloc metadata.
///
/// This statistic is cached, and is only refreshed when the epoch is advanced. See the [`Epoch`]
/// type for more information.
///
/// This corresponds to `stats.metadata` in jemalloc's API.
///
/// # Examples
///
/// ```rust
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// use jemalloc_ctl::Epoch;
/// use jemalloc_ctl::stats::Metadata;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let epoch = Epoch::new().unwrap();
/// let metadata = Metadata::new().unwrap();
///
/// epoch.advance().unwrap();
/// let size = metadata.get().unwrap();
/// println!("{} bytes of jemalloc metadata", size);
/// }
/// ```
///
/// [`Epoch`]: ../struct.Epoch.html
#[derive(Copy, Clone)]
pub struct Metadata([usize; 2]);
impl Metadata {
/// Returns a new `Metadata`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(METADATA, &mut mib)?;
Ok(Metadata(mib))
}
/// Returns the total number of bytes dedicated to jemalloc metadata.
pub fn get(self) -> Result<usize> {
get_mib(&self.0)
}
}
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> {
get(RESIDENT)
}
/// 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([usize; 2]);
impl Resident {
/// Returns a new `Resident`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(RESIDENT, &mut 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> {
get_mib(&self.0)
}
}
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> {
get(MAPPED)
}
/// 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([usize; 2]);
impl Mapped {
/// Returns a new `Mapped`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(MAPPED, &mut mib)?;
Ok(Mapped(mib))
}
/// Returns the total number of bytes in active extents mapped by the allocator.
pub fn get(self) -> Result<usize> {
get_mib(&self.0)
}
}
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> {
get(RETAINED)
}
/// 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([usize; 2]);
impl Retained {
/// Returns a new `Retained`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(RETAINED, &mut mib)?;
Ok(Retained(mib))
}
/// Returns the total number of bytes in virtual memory mappings that were retained.
pub fn get(self) -> Result<usize> {
get_mib(&self.0)
}
}

View File

@ -0,0 +1,165 @@
//! Bulk statistics output.
extern crate std;
use libc::{c_char, c_void};
use std::any::Any;
use std::ffi::CStr;
use std::io::{self, Write};
use std::panic::{self, AssertUnwindSafe};
/// Statistics configuration.
///
/// All options default to `false`.
#[derive(Copy, Clone, Default)]
pub struct Options {
/// If set, the output will be JSON-formatted.
///
/// This corresponds to the `J` character.
pub json_format: bool,
/// If set, information that never changes during execution will be skipped.
///
/// This corresponds to the `g` character.
pub skip_constants: bool,
/// If set, merged information about arenas will be skipped.
///
/// This corresponds to the `m` character.
pub skip_merged_arenas: bool,
/// If set, information about individual arenas will be skipped.
///
/// This corresponds to the `a` character.
pub skip_per_arena: bool,
/// If set, information about individual size classes for bins will be skipped.
///
/// This corresponds to the `b` character.
pub skip_bin_size_classes: bool,
/// If set, information about individual size classes for large objects will be skipped.
///
/// This corresponds to the `l` character.
pub skip_large_size_classes: bool,
/// If set, mutex statistics will be skipped.
///
/// This corresponds to the `x` character.
pub skip_mutex_statistics: bool,
_p: (),
}
struct State<W> {
writer: W,
error: io::Result<()>,
panic: Result<(), Box<Any + Send>>,
}
extern "C" fn callback<W>(opaque: *mut c_void, buf: *const c_char)
where
W: Write,
{
unsafe {
let state = &mut *(opaque as *mut State<W>);
if state.error.is_err() || state.panic.is_err() {
return;
}
let buf = CStr::from_ptr(buf);
match panic::catch_unwind(AssertUnwindSafe(|| state.writer.write_all(buf.to_bytes()))) {
Ok(Ok(_)) => {}
Ok(Err(e)) => state.error = Err(e),
Err(e) => state.panic = Err(e),
}
}
}
/// Writes allocator statistics.
///
/// The information is the same that can be retrieved by the individual lookup methods in this
/// crate, but all done at once.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_possible_wrap))]
pub fn stats_print<W>(writer: W, options: Options) -> io::Result<()>
where
W: Write,
{
unsafe {
let mut state = State {
writer,
error: Ok(()),
panic: Ok(()),
};
let mut opts = [0; 8];
let mut i = 0;
if options.json_format {
opts[i] = b'J' as c_char;
i += 1;
}
if options.skip_constants {
opts[i] = b'g' as c_char;
i += 1;
}
if options.skip_merged_arenas {
opts[i] = b'm' as c_char;
i += 1;
}
if options.skip_per_arena {
opts[i] = b'a' as c_char;
i += 1;
}
if options.skip_bin_size_classes {
opts[i] = b'b' as c_char;
i += 1;
}
if options.skip_large_size_classes {
opts[i] = b'l' as c_char;
i += 1;
}
if options.skip_mutex_statistics {
opts[i] = b'x' as c_char;
i += 1;
}
opts[i] = 0;
jemalloc_sys::malloc_stats_print(
Some(callback::<W>),
&mut state as *mut _ as *mut c_void,
opts.as_ptr(),
);
if let Err(e) = state.panic {
panic::resume_unwind(e);
}
state.error
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn basic() {
let mut buf = vec![];
stats_print(&mut buf, Options::default()).unwrap();
println!("{}", String::from_utf8(buf).unwrap());
}
#[test]
fn all_options() {
let mut buf = vec![];
let options = Options {
json_format: true,
skip_constants: true,
skip_merged_arenas: true,
skip_per_arena: true,
skip_bin_size_classes: true,
skip_large_size_classes: true,
skip_mutex_statistics: true,
_p: (),
};
stats_print(&mut buf, options).unwrap();
println!("{}", String::from_utf8(buf).unwrap());
}
}

205
jemalloc-ctl/src/thread.rs Normal file
View File

@ -0,0 +1,205 @@
//! Thread specific operations.
use error::Result;
use raw::{get, get_mib, name_to_mib};
const ALLOCATEDP: &[u8] = b"thread.allocatedp\0";
/// Returns a thread-local pointer to the total number of bytes allocated by the current thread.
///
/// Unlike [`stats::allocated`], the value returned by this type is not the number of bytes
/// *currently* allocated, but rather the number of bytes that have *ever* been allocated by this
/// thread.
///
/// 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 `allocated` can be called on different threads and will
/// return the appropriate pointer for each of them.
///
/// This corresponds to `thread.allocatedp` in jemalloc's API.
///
/// # Examples
///
/// ```
/// extern crate jemallocator;
/// extern crate jemalloc_ctl;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let allocated = jemalloc_ctl::thread::allocatedp().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);
/// }
/// ```
pub fn allocatedp() -> Result<ThreadLocal<u64>> {
get(ALLOCATEDP).map(ThreadLocal)
}
/// A type providing access to the total number of bytes allocated by the current thread.
///
/// Unlike [`stats::Allocated`], the value returned by this type is not the number of bytes
/// *currently* allocated, but rather the number of bytes that have *ever* been allocated by this
/// 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>> {
get_mib(&self.0).map(ThreadLocal)
}
}
const DEALLOCATEDP: &[u8] = b"thread.deallocatedp\0";
/// Returns a pointer to the total number of bytes deallocated by the current thread.
///
/// 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>> {
get(DEALLOCATEDP).map(ThreadLocal)
}
/// A type providing access to the total number of bytes deallocated by the current 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 `DeallocatedP::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::DeallocatedP;
///
/// #[global_allocator]
/// static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
///
/// fn main() {
/// let deallocated = DeallocatedP::new().unwrap();
/// let deallocated = deallocated.get().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);
/// }
/// ```
#[derive(Copy, Clone)]
pub struct DeallocatedP([usize; 2]);
impl DeallocatedP {
/// Returns a new `Deallocated`.
pub fn new() -> Result<Self> {
let mut mib = [0; 2];
name_to_mib(DEALLOCATEDP, &mut mib)?;
Ok(DeallocatedP(mib))
}
/// Returns a thread-local pointer to the total number of bytes deallocated by this thread.
pub fn get(&self) -> Result<ThreadLocal<u64>> {
let ptr = get_mib::<*mut u64>(&self.0)?;
Ok(ThreadLocal(ptr))
}
}
/// A thread-local pointer.
///
/// It is neither `Sync` nor `Send`.
// NB we need *const here specifically since it's !Sync + !Send
#[derive(Copy, Clone)]
pub struct ThreadLocal<T>(*const T);
impl<T> ThreadLocal<T>
where
T: Copy,
{
/// Returns the current value at the pointer.
#[inline]
pub fn get(self) -> T {
unsafe { *self.0 }
}
}

View File

@ -0,0 +1,64 @@
//! Version operations.
use error::Result;
use raw::{get_str, get_str_mib, name_to_mib};
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> {
get_str(VERSION)
}
/// 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([usize; 1]);
impl Version {
/// Returns a new `Version`.
pub fn new() -> Result<Self> {
let mut mib = [0; 1];
name_to_mib(VERSION, &mut mib)?;
Ok(Version(mib))
}
/// Returns the jemalloc version string.
pub fn get(self) -> Result<&'static str> {
get_str_mib(&self.0)
}
}

View File

@ -1,20 +1,31 @@
[package]
name = "jemalloc-sys"
version = "0.2.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
version = "0.3.0"
authors = [
"Alex Crichton <alex@alexcrichton.com>",
"Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>",
]
build = "build.rs"
links = "jemalloc"
license = "MIT/Apache-2.0"
readme = "readme.md"
readme = "README.md"
repository = "https://github.com/alexcrichton/jemallocator"
homepage = "https://github.com/alexcrichton/jemallocator"
documentation = "https://docs.rs/jemallocator-sys"
keywords = ["allocator"]
keywords = ["allocator", "jemalloc"]
description = """
Rust FFI bindings to jemalloc
"""
edition = "2015"
[badges]
appveyor = { repository = "alexcrichton/jemallocator" }
travis-ci = { repository = "alexcrichton/jemallocator" }
codecov = { repository = "alexcrichton/jemallocator" }
is-it-maintained-issue-resolution = { repository = "alexcrichton/jemallocator" }
is-it-maintained-open-issues = { repository = "alexcrichton/jemallocator" }
maintenance = { status = "actively-developed" }
[lib]
test = false
bench = false

View File

@ -1,9 +1,17 @@
# jemalloc-sys - Rust bindings to the `jemalloc` C library
[![Travis-CI Status]][travis] [![Appveyor Status]][appveyor] [![Latest Version]][crates.io] [![docs]][docs.rs]
> Note: the Rust allocator API is implemented for `jemalloc` in the
> [`jemallocator`](https://crates.io/crates/jemallocator) crate.
## Documentation
* [Latest release (docs.rs)][docs.rs]
* [master branch`][master_docs]
`jemalloc` is a general purpose memory allocator, its documentation
can be found here:
* [API documentation][jemalloc_docs]
@ -14,15 +22,17 @@
**Current jemalloc version**: 5.1.
[jemalloc_docs]: http://jemalloc.net/jemalloc.3.html
[jemalloc_wiki]: https://github.com/jemalloc/jemalloc/wiki
## Platform support
# Features
See the platform support of the
[`jemallocator`](https://crates.io/crates/jemallocator) crate.
## Features
Most features correspond to `jemalloc` features - the reference is
[`jemalloc/INSTALL.md`][jemalloc_install].
## Cargo features
### Cargo features
This crate provides following cargo feature flags:
@ -67,7 +77,7 @@ This crate provides following cargo feature flags:
`jemalloc` as well. On some platforms prefixes are always used because
unprefixing is known to cause segfaults due to allocator mismatches.
## Environment variables
### Environment variables
`jemalloc` options taking values are passed via environment variables using the
schema `JEMALLOC_SYS_{KEY}=VALUE` where the `KEY` names correspond to the
@ -127,7 +137,7 @@ hyphens `-` are replaced with underscores `_`(see
[jemalloc_install]: https://github.com/jemalloc/jemalloc/blob/dev/INSTALL.md#advanced-configuration
# License
## License
This project is licensed under either of
@ -138,8 +148,18 @@ This project is licensed under either of
at your option.
### Contribution
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in `jemalloc-sys` by you, as defined in the Apache-2.0 license,
shall be dual licensed as above, without any additional terms or conditions.
[travis]: https://travis-ci.org/alexcrichton/jemallocator
[Travis-CI Status]: https://travis-ci.org/alexcrichton/jemallocator.svg?branch=master
[appveyor]: https://ci.appveyor.com/project/alexcrichton/jemallocator/branch/master
[Appveyor Status]: https://ci.appveyor.com/api/projects/status/github/alexcrichton/jemallocator?branch=master&svg=true
[Latest Version]: https://img.shields.io/crates/v/jemalloc-sys.svg
[crates.io]: https://crates.io/crates/jemalloc-ctl
[docs]: https://docs.rs/jemalloc-sys/badge.svg
[docs.rs]: https://docs.rs/jemalloc-sys/
[master_docs]: https://alexcrichton.github.io/jemallocator/jemalloc-sys

View File

@ -25,8 +25,6 @@ extern crate libc;
#[cfg(feature = "alloc_trait")]
use core::alloc::{Alloc, AllocErr, CannotReallocInPlace, Excess};
use core::alloc::{GlobalAlloc, Layout};
use core::mem;
use core::ptr;
#[cfg(feature = "alloc_trait")]
use core::ptr::NonNull;
@ -252,55 +250,7 @@ pub unsafe fn usable_size<T>(ptr: *const T) -> usize {
ffi::malloc_usable_size(ptr as *const c_void)
}
/// Fetch the value of options `name`.
///
/// Please note that if you want to fetch a string, use char* instead of &str or
/// cstring.
pub unsafe fn mallctl_fetch<T>(name: &[u8], t: &mut T) -> Result<(), libc::c_int> {
// make sure name is a valid c string.
if name.is_empty() || *name.last().unwrap() != 0 {
return Err(libc::EINVAL);
}
let mut t_size = mem::size_of::<T>();
let t_ptr = t as *mut T as *mut _;
let code = ffi::mallctl(
name.as_ptr() as *const _,
t_ptr,
&mut t_size,
ptr::null_mut(),
0,
);
if code != 0 {
return Err(code);
}
Ok(())
}
/// Set a value to option `name`.
///
/// Please note that if you want to set a string, use char* instead of &str or
/// cstring.
pub unsafe fn mallctl_set<T>(name: &[u8], mut t: T) -> Result<(), libc::c_int> {
// make sure name is a valid c string.
if name.is_empty() || *name.last().unwrap() != 0 {
return Err(libc::EINVAL);
}
let size = mem::size_of::<T>();
let code = ffi::mallctl(
name.as_ptr() as *const _,
ptr::null_mut(),
ptr::null_mut(),
&mut t as *mut T as *mut _,
size,
);
if code != 0 {
return Err(code);
}
Ok(())
}
/// Raw bindings to jemalloc
pub mod ffi {
mod ffi {
pub use jemalloc_sys::*;
}

View File

@ -1,4 +1,6 @@
//! Test background threads run-time default settings.
extern crate jemalloc_ctl;
extern crate jemallocator;
extern crate libc;
@ -9,11 +11,7 @@ static A: Jemalloc = Jemalloc;
// Returns true if background threads are enabled.
fn background_threads() -> bool {
unsafe {
let mut v: bool = false;
jemallocator::mallctl_fetch(b"opt.background_thread\0", &mut v).unwrap();
v
}
jemalloc_ctl::opt::background_thread().unwrap()
}
#[test]

View File

@ -4,6 +4,7 @@
#![cfg(not(feature = "unprefixed_malloc_on_supported_platforms"))]
#![cfg(not(target_env = "musl"))]
extern crate jemalloc_ctl;
extern crate jemallocator;
extern crate libc;
@ -28,17 +29,8 @@ pub static malloc_conf: Option<&'static libc::c_char> = Some(unsafe {
.y
});
// Returns true if background threads are enabled.
fn background_threads() -> bool {
unsafe {
let mut v: bool = false;
jemallocator::mallctl_fetch(b"opt.background_thread\0", &mut v).unwrap();
v
}
}
#[test]
fn background_threads_enabled() {
// Background threads are unconditionally enabled at run-time by default.
assert_eq!(background_threads(), true);
assert_eq!(jemalloc_ctl::opt::background_thread().unwrap(), true);
}

View File

@ -1,3 +1,4 @@
extern crate jemalloc_ctl;
extern crate jemallocator;
extern crate libc;
@ -18,24 +19,21 @@ fn smoke() {
}
#[test]
fn test_mallctl() {
let mut epoch: u64 = 0;
fn test_jemalloc_ctl_get_set() {
use jemalloc_ctl::{
raw::{get, set},
Error,
};
unsafe {
assert_eq!(
jemallocator::mallctl_fetch(b"", &mut epoch),
Err(libc::EINVAL)
);
assert_eq!(
jemallocator::mallctl_fetch(b"epoch", &mut epoch),
Err(libc::EINVAL)
);
jemallocator::mallctl_fetch(b"epoch\0", &mut epoch).unwrap();
assert!(epoch > 0);
assert_eq!(jemallocator::mallctl_set(b"", epoch), Err(libc::EINVAL));
assert_eq!(
jemallocator::mallctl_set(b"epoch", epoch),
Err(libc::EINVAL)
);
jemallocator::mallctl_set(b"epoch\0", epoch).unwrap();
{
// get}
assert_eq!(get::<u64>(b""), Err(Error::EINVAL));
assert_eq!(get::<u64>(b"epoch"), Err(Error::EINVAL));
let epoch = get::<u64>(b"epoch\0").unwrap();
assert!(epoch > 0);
assert_eq!(set(b"", epoch), Err(Error::EINVAL));
assert_eq!(set(b"epoch", epoch), Err(Error::EINVAL));
set(b"epoch\0", epoch).unwrap();
}
}
}