tikv-jemalloc-ctl-0.5.4/.cargo_vcs_info.json0000644000000001520000000000100143610ustar { "git": { "sha1": "0f8983f71813c6e052c6ab445e965c4b0a7e251e" }, "path_in_vcs": "jemalloc-ctl" }tikv-jemalloc-ctl-0.5.4/Cargo.toml0000644000000034440000000000100123660ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "tikv-jemalloc-ctl" version = "0.5.4" authors = [ "Steven Fackler ", "Gonzalo Brito Gadeschi ", "The TiKV Project Developers", ] description = """ A safe wrapper over jemalloc's control and introspection APIs """ homepage = "https://github.com/tikv/jemallocator" documentation = "https://docs.rs/tikv-jemalloc-ctl" readme = "README.md" keywords = [ "allocator", "jemalloc", ] categories = [ "memory-management", "api-bindings", "development-tools", ] license = "MIT/Apache-2.0" repository = "https://github.com/tikv/jemallocator" [package.metadata.docs.rs] rustdoc-args = [ "--cfg", "jemallocator_docs", ] [dependencies.libc] version = "0.2" default-features = false [dependencies.paste] version = "1" [dependencies.tikv-jemalloc-sys] version = "0.5.0" [dev-dependencies.tikv-jemallocator] version = "0.5.0" [features] default = [] disable_initial_exec_tls = ["tikv-jemalloc-sys/disable_initial_exec_tls"] use_std = ["libc/use_std"] [badges.codecov] repository = "tikv/jemallocator" [badges.is-it-maintained-issue-resolution] repository = "tikv/jemallocator" [badges.is-it-maintained-open-issues] repository = "tikv/jemallocator" [badges.maintenance] status = "actively-developed" [badges.travis-ci] repository = "tikv/jemallocator" tikv-jemalloc-ctl-0.5.4/Cargo.toml.orig000064400000000000000000000025061046102023000160450ustar 00000000000000[package] name = "tikv-jemalloc-ctl" version = "0.5.4" authors = [ "Steven Fackler ", "Gonzalo Brito Gadeschi ", "The TiKV Project Developers", ] license = "MIT/Apache-2.0" readme = "README.md" categories = ["memory-management", "api-bindings", "development-tools" ] keywords = ["allocator", "jemalloc"] repository = "https://github.com/tikv/jemallocator" homepage = "https://github.com/tikv/jemallocator" documentation = "https://docs.rs/tikv-jemalloc-ctl" description = """ A safe wrapper over jemalloc's control and introspection APIs """ edition = "2018" [badges] travis-ci = { repository = "tikv/jemallocator" } codecov = { repository = "tikv/jemallocator" } is-it-maintained-issue-resolution = { repository = "tikv/jemallocator" } is-it-maintained-open-issues = { repository = "tikv/jemallocator" } maintenance = { status = "actively-developed" } [dependencies] tikv-jemalloc-sys = { path = "../jemalloc-sys", version = "0.5.0" } libc = { version = "0.2", default-features = false } paste = "1" [dev-dependencies] tikv-jemallocator = { path = "../jemallocator", version = "0.5.0" } [features] default = [] use_std = [ "libc/use_std" ] disable_initial_exec_tls = ["tikv-jemalloc-sys/disable_initial_exec_tls"] [package.metadata.docs.rs] rustdoc-args = [ "--cfg", "jemallocator_docs" ] tikv-jemalloc-ctl-0.5.4/LICENSE-APACHE000064400000000000000000000261361046102023000151070ustar 00000000000000 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. tikv-jemalloc-ctl-0.5.4/LICENSE-MIT000064400000000000000000000020421046102023000146050ustar 00000000000000Copyright (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. tikv-jemalloc-ctl-0.5.4/README.md000064400000000000000000000041411046102023000144320ustar 00000000000000# jemalloc-ctl [![Travis-CI Status]][travis] [![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] ## Platform support Supported on all platforms supported by the [`tikv-jemallocator`] crate. ## Example ```no_run use std::thread; use std::time::Duration; use tikv_jemalloc_ctl::{stats, epoch}; #[global_allocator] static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; fn main() { // Obtain a MIB for the `epoch`, `stats.allocated`, and // `atats.resident` keys: let e = epoch::mib().unwrap(); let allocated = stats::allocated::mib().unwrap(); let resident = stats::resident::mib().unwrap(); loop { // Many statistics are cached and only updated // when the epoch is advanced: e.advance().unwrap(); // Read statistics using MIB key: let allocated = allocated.read().unwrap(); let resident = resident.read().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. [`tikv-jemallocator`]: https://github.com/tikv/jemallocator [travis]: https://travis-ci.com/tikv/jemallocator [Travis-CI Status]: https://travis-ci.com/tikv/jemallocator.svg?branch=master [Latest Version]: https://img.shields.io/crates/v/tikv-jemallocator.svg [crates.io]: https://crates.io/crates/tikv-jemallocator [docs]: https://docs.rs/tikv-jemallocator/badge.svg [docs.rs]: https://docs.rs/tikv-jemallocator/ tikv-jemalloc-ctl-0.5.4/rustfmt.toml000064400000000000000000000000161046102023000155510ustar 00000000000000max_width = 79tikv-jemalloc-ctl-0.5.4/src/arenas.rs000064400000000000000000000012571046102023000155660ustar 00000000000000//! Arena operations. option! { narenas[ str: b"arenas.narenas\0", non_str: 2 ] => libc::c_uint | ops: r | docs: /// Current limit on the number of arenas. /// /// # Examples /// /// ``` /// # /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::arenas; /// println!("number of arenas: {}", arenas::narenas::read().unwrap()); /// /// let arenas_mib = arenas::narenas::mib().unwrap(); /// println!("number of arenas: {}", arenas_mib.read().unwrap()); /// # } /// ``` mib_docs: /// See [`narenas`]. } tikv-jemalloc-ctl-0.5.4/src/config.rs000064400000000000000000000014231046102023000155550ustar 00000000000000//! `jemalloc`'s build-time configuration. option! { malloc_conf[ str: b"config.malloc_conf\0", str: 2 ] => &'static str | ops: r | docs: /// Default run-time options specified during `jemalloc`'s build configuration. /// /// The string will be empty unless `--with-malloc-conf` was specified /// during build configuration. /// /// # Examples /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::config; /// let malloc_conf = config::malloc_conf::mib().unwrap(); /// println!("default malloc conf: {}", malloc_conf.read().unwrap()); /// # } /// ``` mib_docs: /// See [`malloc_conf`]. } tikv-jemalloc-ctl-0.5.4/src/error.rs000064400000000000000000000055501046102023000154460ustar 00000000000000//! Error type #![cfg_attr( feature = "cargo-clippy", allow(clippy::cast_sign_loss, clippy::cast_possible_wrap) )] use crate::{fmt, num, result}; use libc::c_int; pub trait NonZeroT { type T; } impl NonZeroT for i32 { type T = num::NonZeroU32; } impl NonZeroT for i64 { type T = num::NonZeroU64; } pub type NonZeroCInt = ::T; /// Errors of the `tikv_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. #[repr(transparent)] #[derive(Copy, Clone, PartialEq)] pub struct Error(NonZeroCInt); /// Result type pub type Result = result::Result; impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let code = self.0.get() as c_int; match description(code) { Some(m) => write!(f, "{m}"), None => write!(f, "Unknown error code: \"{code}\"."), } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ::fmt(self, f) } } #[cfg(feature = "use_std")] use std::error::Error as StdError; #[cfg(feature = "use_std")] impl StdError for Error { fn description(&self) -> &str { match description(self.0.get() as c_int) { Some(m) => m, None => "Unknown error", } } fn cause(&self) -> Option<&dyn StdError> { None } fn source(&self) -> Option<&(dyn StdError + 'static)> { None } } fn description(code: c_int) -> Option<&'static str> { match code { libc::EINVAL => Some( "`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.", ), libc::ENOENT => { Some("`name` or `mib` specifies an unknown/invalid value.") } libc::EPERM => Some( "Attempt to read or write `void` value, or attempt to \ write read-only value.", ), libc::EAGAIN => Some("A memory allocation failure occurred."), libc::EFAULT => Some( "An interface with side effects failed in some way not \ directly related to `mallctl*()` read/write processing.", ), _ => None, } } pub(crate) fn cvt(ret: c_int) -> Result<()> { match ret { 0 => Ok(()), v => Err(Error(unsafe { NonZeroCInt::new_unchecked(v as _) })), } } #[cfg(test)] mod tests { use super::*; #[test] fn size_of_result_error() { use crate::mem::size_of; assert_eq!(size_of::>(), size_of::()); assert_eq!(size_of::(), size_of::()); } } tikv-jemalloc-ctl-0.5.4/src/keys.rs000064400000000000000000000316501046102023000152700ustar 00000000000000//! Key types to index the _MALLCTL NAMESPACE_. //! //! The [`Name`] and [`Mib`]/[`MibStr`] types are provided as safe indices into //! the _MALLCTL NAMESPACE_. These are constructed from null-terminated strings //! via the [`AsName`] trait. The [`Access`] trait provides provides safe access //! into the `_MALLCTL NAMESPACE_`. //! //! # Example //! //! ``` //! #[global_allocator] //! static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; //! //! fn main() { //! use tikv_jemalloc_ctl::{Access, AsName, Name, Mib}; //! use libc::{c_uint, c_char}; //! let name = b"arenas.nbins\0".name(); //! let nbins: c_uint = name.read().unwrap(); //! let mut mib: Mib<[usize; 4]> = b"arenas.bin.0.size\0".name().mib().unwrap(); //! for i in 0..4 { //! mib[2] = i; //! let bin_size: usize = mib.read().unwrap(); //! println!("arena bin {} has size {}", i, bin_size); //! } //! } //! ``` #![allow(clippy::uninlined_format_args)] use crate::error::Result; use crate::std::str; use crate::{fmt, ops, raw}; /// A `Name` in the _MALLCTL NAMESPACE_. #[repr(transparent)] #[derive(PartialEq, Eq)] pub struct Name([u8]); /// Converts a null-terminated byte-string into a [`Name`]. pub trait AsName { /// Converts a null-terminated byte-string into a [`Name`]. fn name(&self) -> &Name; } impl AsName for [u8] { fn name(&self) -> &Name { assert!( !self.is_empty(), "cannot create Name from empty byte-string" ); assert_eq!( *self.last().unwrap(), b'\0', "cannot create Name from non-null-terminated byte-string \"{}\"", str::from_utf8(self).unwrap() ); unsafe { &*(self as *const Self as *const Name) } } } impl AsName for str { fn name(&self) -> &Name { self.as_bytes().name() } } impl Name { /// Returns the [`Mib`] of `self`. pub fn mib(&self) -> Result> { let mut mib: Mib = Mib::default(); raw::name_to_mib(&self.0, mib.0.as_mut())?; Ok(mib) } /// Returns the [`MibStr`] of `self` which is a key whose value is a string. pub fn mib_str(&self) -> Result> { assert!( self.value_type_str(), "key \"{}\" does not refer to a string", self ); let mut mib: MibStr = MibStr::default(); raw::name_to_mib(&self.0, mib.0.as_mut())?; Ok(mib) } /// Returns `true` if `self` is a key in the _MALLCTL NAMESPCE_ referring to /// a null-terminated string. pub fn value_type_str(&self) -> bool { // remove the null-terminator: let name = self.0.split_at(self.0.len() - 1).0; if name.is_empty() { return false; } debug_assert_ne!(*name.last().unwrap(), b'\0'); match name { b"version" | b"config.malloc_conf" | b"opt.metadata_thp" | b"opt.dss" | b"opt.percpu_arena" | b"opt.stats_print_opts" | b"opt.junk" | b"opt.thp" | b"opt.prof_prefix" | b"thread.prof.name" | b"prof.dump" => true, v if v.starts_with(b"arena.") && v.ends_with(b".dss") => true, v if v.starts_with(b"stats.arenas.") && v.ends_with(b".dss") => { true } _ => 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", str::from_utf8(&self.0).unwrap()) } } impl fmt::Display for Name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", str::from_utf8(&self.0).unwrap()) } } /// Management Information Base of a non-string value. #[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] pub struct Mib(T); /// Management Information Base of a string value. #[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] pub struct MibStr(T); impl AsRef<[usize]> for Mib { fn as_ref(&self) -> &[usize] { self.0.as_ref() } } impl AsMut<[usize]> for Mib { fn as_mut(&mut self) -> &mut [usize] { self.0.as_mut() } } impl ops::Index for Mib { type Output = usize; fn index(&self, idx: usize) -> &Self::Output { &self.0.as_ref()[idx] } } impl ops::IndexMut for Mib { fn index_mut(&mut self, idx: usize) -> &mut Self::Output { &mut self.0.as_mut()[idx] } } impl ops::Index for MibStr { type Output = usize; fn index(&self, idx: usize) -> &Self::Output { &self.0.as_ref()[idx] } } impl ops::IndexMut for MibStr { fn index_mut(&mut self, idx: usize) -> &mut Self::Output { &mut self.0.as_mut()[idx] } } /// Safe read access to the _MALLCTL NAMESPACE_. pub trait Access { /// Read the key at `self`. fn read(&self) -> Result; /// Write `value` at the key `self`. fn write(&self, value: T) -> Result<()>; /// Write `value` at the key `self` returning its previous value. fn update(&self, value: T) -> Result; } macro_rules! impl_access { ($id:ty) => { impl Access<$id> for Mib { fn read(&self) -> Result<$id> { unsafe { raw::read_mib(self.0.as_ref()) } } fn write(&self, value: $id) -> Result<()> { unsafe { raw::write_mib(self.0.as_ref(), value) } } fn update(&self, value: $id) -> Result<$id> { unsafe { raw::update_mib(self.0.as_ref(), value) } } } impl Access<$id> for Name { fn read(&self) -> Result<$id> { unsafe { raw::read(&self.0) } } fn write(&self, value: $id) -> Result<()> { unsafe { raw::write(&self.0, value) } } fn update(&self, value: $id) -> Result<$id> { unsafe { raw::update(&self.0, value) } } } }; } impl_access!(u32); impl_access!(u64); impl_access!(isize); impl_access!(usize); impl Access for Mib { fn read(&self) -> Result { unsafe { let v: u8 = raw::read_mib(self.0.as_ref())?; assert!(v == 0 || v == 1); Ok(v == 1) } } fn write(&self, value: bool) -> Result<()> { unsafe { raw::write_mib(self.0.as_ref(), value) } } fn update(&self, value: bool) -> Result { unsafe { let v: u8 = raw::update_mib(self.0.as_ref(), value as u8)?; Ok(v == 1) } } } impl Access for Name { fn read(&self) -> Result { unsafe { let v: u8 = raw::read(&self.0)?; assert!(v == 0 || v == 1); Ok(v == 1) } } fn write(&self, value: bool) -> Result<()> { unsafe { raw::write(&self.0, value) } } fn update(&self, value: bool) -> Result { unsafe { let v: u8 = raw::update(&self.0, value as u8)?; Ok(v == 1) } } } impl Access<&'static [u8]> for MibStr { fn read(&self) -> Result<&'static [u8]> { // this is safe because the only safe way to construct a `MibStr` is by // validating that the key refers to a byte-string value unsafe { raw::read_str_mib(self.0.as_ref()) } } fn write(&self, value: &'static [u8]) -> Result<()> { raw::write_str_mib(self.0.as_ref(), value) } fn update(&self, value: &'static [u8]) -> Result<&'static [u8]> { // this is safe because the only safe way to construct a `MibStr` is by // validating that the key refers to a byte-string value unsafe { raw::update_str_mib(self.0.as_ref(), value) } } } impl Access<&'static [u8]> for Name { fn read(&self) -> Result<&'static [u8]> { assert!( self.value_type_str(), "the name \"{:?}\" does not refer to a byte string", self ); // this is safe because the key refers to a byte string: unsafe { raw::read_str(&self.0) } } fn write(&self, value: &'static [u8]) -> Result<()> { assert!( self.value_type_str(), "the name \"{:?}\" does not refer to a byte string", self ); raw::write_str(&self.0, value) } fn update(&self, value: &'static [u8]) -> Result<&'static [u8]> { assert!( self.value_type_str(), "the name \"{:?}\" does not refer to a byte string", self ); // this is safe because the key refers to a byte string: unsafe { raw::update_str(&self.0, value) } } } impl Access<&'static str> for MibStr { fn read(&self) -> Result<&'static str> { // this is safe because the only safe way to construct a `MibStr` is by // validating that the key refers to a byte-string value let s = unsafe { raw::read_str_mib(self.0.as_ref())? }; Ok(str::from_utf8(s).unwrap()) } fn write(&self, value: &'static str) -> Result<()> { raw::write_str_mib(self.0.as_ref(), value.as_bytes()) } fn update(&self, value: &'static str) -> Result<&'static str> { // this is safe because the only safe way to construct a `MibStr` is by // validating that the key refers to a byte-string value let s = unsafe { raw::update_str_mib(self.0.as_ref(), value.as_bytes())? }; Ok(str::from_utf8(s).unwrap()) } } impl Access<&'static str> for Name { fn read(&self) -> Result<&'static str> { assert!( self.value_type_str(), "the name \"{:?}\" does not refer to a byte string", self ); // this is safe because the key refers to a byte string: let s = unsafe { raw::read_str(&self.0)? }; Ok(str::from_utf8(s).unwrap()) } fn write(&self, value: &'static str) -> Result<()> { assert!( self.value_type_str(), "the name \"{:?}\" does not refer to a byte string", self ); raw::write_str(&self.0, value.as_bytes()) } fn update(&self, value: &'static str) -> Result<&'static str> { assert!( self.value_type_str(), "the name \"{:?}\" does not refer to a byte string", self ); // this is safe because the key refers to a byte string: let s = unsafe { raw::update_str(&self.0, value.as_bytes())? }; Ok(str::from_utf8(s).unwrap()) } } #[cfg(test)] mod tests { use super::{Access, AsName, Mib, MibStr}; #[test] fn bool_rw() { let name = b"thread.tcache.enabled\0".name(); let tcache: bool = name.read().unwrap(); let new_tcache = !tcache; name.write(new_tcache).unwrap(); let mib: Mib<[usize; 3]> = name.mib().unwrap(); let r: bool = mib.read().unwrap(); assert_eq!(r, new_tcache); } #[test] fn u32_r() { let name = b"arenas.bin.0.nregs\0".name(); let v: u32 = name.read().unwrap(); let mib: Mib<[usize; 4]> = name.mib().unwrap(); let r: u32 = mib.read().unwrap(); assert_eq!(r, v); } #[test] fn size_t_r() { let name = b"arenas.lextent.0.size\0".name(); let v: libc::size_t = name.read().unwrap(); let mib: Mib<[usize; 4]> = name.mib().unwrap(); let r: libc::size_t = mib.read().unwrap(); assert_eq!(r, v); } #[test] fn ssize_t_rw() { let name = b"arenas.dirty_decay_ms\0".name(); let v: libc::ssize_t = name.read().unwrap(); name.write(v).unwrap(); let mib: Mib<[usize; 2]> = name.mib().unwrap(); let r: libc::ssize_t = mib.read().unwrap(); assert_eq!(r, v); } #[test] fn u64_rw() { let name = b"epoch\0".name(); let epoch: u64 = name.read().unwrap(); name.write(epoch).unwrap(); let mib: Mib<[usize; 1]> = name.mib().unwrap(); let epoch: u64 = mib.read().unwrap(); mib.write(epoch).unwrap(); } #[test] fn str_rw() { let name = b"arena.0.dss\0".name(); let dss: &'static [u8] = name.read().unwrap(); name.write(dss).unwrap(); let mib: MibStr<[usize; 3]> = name.mib_str().unwrap(); let dss2: &'static [u8] = mib.read().unwrap(); mib.write(dss2).unwrap(); assert_eq!(dss, dss2); } } pub trait MibArg: Copy + Clone + PartialEq + Default + fmt::Debug + AsRef<[usize]> + AsMut<[usize]> { } impl MibArg for T where T: Copy + Clone + PartialEq + Default + fmt::Debug + AsRef<[usize]> + AsMut<[usize]> { } tikv-jemalloc-ctl-0.5.4/src/lib.rs000064400000000000000000000156141046102023000150650ustar 00000000000000//! `jemalloc` control and introspection. //! //! `jemalloc` offers a powerful introspection and control interface through the `mallctl` function. //! It can be used to tune the allocator, take heap dumps, and retrieve statistics. This crate //! provides a typed API over that interface. //! //! While `mallctl` takes a string to specify an operation (e.g. `stats.allocated` or //! `stats.arenas.15.muzzy_decay_ms`), the overhead of repeatedly parsing those strings is not //! ideal. Fortunately, `jemalloc` offers the ability to translate the string ahead of time into a //! "Management Information Base" (MIB) to speed up future lookups. //! //! This crate provides a type for each `mallctl` operation. Calling //! `$op::{read(), write(x), update(x)}` on the type calls `mallctl` with the //! string-based API. If the operation will be repeatedly performed, a MIB for //! the operation can be obtained using `$op.mib()`. //! //! # Examples //! //! Repeatedly printing allocation statistics: //! //! ```no_run //! use std::thread; //! use std::time::Duration; //! use tikv_jemalloc_ctl::{stats, epoch}; //! //! #[global_allocator] //! static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; //! //! fn main() { //! loop { //! // many statistics are cached and only updated when the epoch is advanced. //! epoch::advance().unwrap(); //! //! let allocated = stats::allocated::read().unwrap(); //! let resident = stats::resident::read().unwrap(); //! println!("{} bytes allocated/{} bytes resident", allocated, resident); //! thread::sleep(Duration::from_secs(10)); //! } //! } //! ``` //! //! Doing the same with the MIB-based API: //! //! ```no_run //! use std::thread; //! use std::time::Duration; //! use tikv_jemalloc_ctl::{stats, epoch}; //! //! #[global_allocator] //! static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; //! //! fn main() { //! let e = epoch::mib().unwrap(); //! let allocated = stats::allocated::mib().unwrap(); //! let resident = stats::resident::mib().unwrap(); //! loop { //! // many statistics are cached and only updated when the epoch is advanced. //! e.advance().unwrap(); //! //! let allocated = allocated.read().unwrap(); //! let resident = resident.read().unwrap(); //! println!("{} bytes allocated/{} bytes resident", allocated, resident); //! thread::sleep(Duration::from_secs(10)); //! } //! } //! ``` // TODO: rename the following lint on next minor bump #![allow(renamed_and_removed_lints)] #![deny(missing_docs, broken_intra_doc_links)] #![cfg_attr(not(feature = "use_std"), no_std)] #![cfg_attr(feature = "cargo-clippy", allow(clippy::module_name_repetitions))] #[cfg(test)] #[global_allocator] static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; use crate::std::{fmt, mem, num, ops, ptr, result, slice, str}; #[cfg(not(feature = "use_std"))] use core as std; #[cfg(feature = "use_std")] use std; #[macro_use] mod macros; pub mod arenas; pub mod config; mod error; mod keys; pub mod opt; pub mod raw; pub mod stats; #[cfg(feature = "use_std")] pub mod stats_print; pub mod thread; pub use error::{Error, Result}; pub use keys::{Access, AsName, Mib, MibStr, Name}; option! { version[ str: b"version\0", str: 1 ] => &'static str | ops: r | docs: /// `jemalloc` version string. /// /// # Example /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::version; /// println!("jemalloc version {}", version::read().unwrap()); /// let version_mib = version::mib().unwrap(); /// println!("jemalloc version {}", version_mib.read().unwrap()); /// # } /// ``` mib_docs: /// See [`version`]. } option! { background_thread[ str: b"background_thread\0", non_str: 1 ] => bool | ops: r,w,u | docs: /// State of internal background worker threads. /// /// When enabled, background threads are created on demand (the number of /// background threads will be no more than the number of CPUs or active /// arenas). Threads run periodically and handle purging asynchronously. /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// # #[cfg(not(target_os = "macos"))] { /// # /// use tikv_jemalloc_ctl::background_thread; /// let bg = background_thread::mib().unwrap(); /// let s = bg.read().unwrap(); /// println!("background_threads enabled: {}", s); /// let p = background_thread::update(!s).unwrap(); /// println!("background_threads enabled: {} => {}", p, bg.read().unwrap()); /// assert_eq!(p, s); /// background_thread::write(s).unwrap(); /// println!("background_threads enabled: {}", bg.read().unwrap()); /// assert_eq!(p, s); /// # /// # } // #[cfg(..)] /// # } /// ``` mib_docs: /// See [`background_thread`]. } option! { max_background_threads[ str: b"max_background_threads\0", non_str: 1 ] => libc::size_t | ops: r, w, u | docs: /// Maximum number of background threads that will be created. /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// # #[cfg(not(target_os = "macos"))] { /// # /// use tikv_jemalloc_ctl::max_background_threads; /// let m = max_background_threads::mib().unwrap(); /// println!("max_background_threads: {}", m.read().unwrap()); /// m.write(2).unwrap(); /// assert_eq!(m.read().unwrap(), 2); /// # /// # } // #[cfg(..)] /// # } /// ``` mib_docs: /// See [`max_background_threads`]. } option! { epoch[ str: b"epoch\0", non_str: 1 ] => u64 | ops: r, w, u | docs: /// `jemalloc` epoch. /// /// Many of the statistics tracked by `jemalloc` are cached. The epoch /// controls when they are refreshed. /// /// # Example /// /// Advancing the epoch: /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// # /// use tikv_jemalloc_ctl::epoch; /// let e = epoch::mib().unwrap(); /// let a = e.advance().unwrap(); /// let b = e.advance().unwrap(); /// assert_eq!(a + 1, b); /// /// let o = e.update(0).unwrap(); /// assert_eq!(o, e.read().unwrap()); /// # } mib_docs: /// See [`epoch`]. } impl epoch { /// Advances the epoch returning its old value - see [`epoch`]. pub fn advance() -> crate::error::Result { Self::update(1) } } impl epoch_mib { /// Advances the epoch returning its old value - see [`epoch`]. pub fn advance(self) -> crate::error::Result { self.0.update(1) } } tikv-jemalloc-ctl-0.5.4/src/macros.rs000064400000000000000000000157541046102023000156100ustar 00000000000000//! 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::paste! { $(#[$doc])* #[allow(non_camel_case_types)] pub struct $id; impl $id { const NAME: &'static crate::keys::Name = { union U<'a> { bytes: &'a [u8], name: &'a crate::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() -> crate::error::Result<[<$id _mib>]> { Ok([<$id _mib>](Self::NAME.$name_to_mib()?)) } /// Key [`crate::keys::Name`]. pub fn name() -> &'static crate::keys::Name { Self::NAME } } $(#[$doc_mib])* #[repr(transparent)] #[derive(Copy, Clone)] #[allow(non_camel_case_types)] pub struct [<$id _mib>](pub crate::keys::$mib); } }; } /// Read macro_rules! r { ($id:ident => $ret_ty:ty) => { paste::paste! { impl $id { /// Reads value using string API. pub fn read() -> crate::error::Result<$ret_ty> { use crate::keys::Access; Self::NAME.read() } } impl [<$id _mib>] { /// Reads value using MIB API. pub fn read(self) -> crate::error::Result<$ret_ty> { use crate::keys::Access; self.0.read() } } #[cfg(test)] #[test] #[cfg(not(target_arch = "mips64el"))] #[allow(unused)] fn [<$id _read_test>]() { match stringify!($id) { "background_thread" | "max_background_threads" if cfg!(target_os = "macos") => return, _ => (), } let a = $id::read().unwrap(); let mib = $id::mib().unwrap(); let b = mib.read().unwrap(); #[cfg(feature = "use_std")] println!( concat!( stringify!($id), " (read): \"{}\" - \"{}\""), a, b ); } } }; } /// Write macro_rules! w { ($id:ident => $ret_ty:ty) => { paste::paste! { impl $id { /// Writes `value` using string API. pub fn write(value: $ret_ty) -> crate::error::Result<()> { use crate::keys::Access; Self::NAME.write(value) } } impl [<$id _mib>] { /// Writes `value` using MIB API. pub fn write(self, value: $ret_ty) -> crate::error::Result<()> { use crate::keys::Access; self.0.write(value) } } #[cfg(test)] #[test] #[cfg(not(target_arch = "mips64el"))] 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(); #[cfg(feature = "use_std")] println!( concat!( stringify!($id), " (write): \"{}\""), $ret_ty::default() ); } } }; } /// Update macro_rules! u { ($id:ident => $ret_ty:ty) => { paste::paste! { impl $id { /// Updates key to `value` returning its old value using string API. pub fn update(value: $ret_ty) -> crate::error::Result<$ret_ty> { use crate::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) -> crate::error::Result<$ret_ty> { use crate::keys::Access; self.0.update(value) } } #[cfg(test)] #[test] #[cfg(not(target_arch = "mips64el"))] #[allow(unused)] fn [<$id _update_test>]() { match stringify!($id) { "background_thread" | "max_background_threads" if cfg!(target_os = "macos") => return, _ => (), } let a = $id::update($ret_ty::default()).unwrap(); let mib = $id::mib().unwrap(); let b = mib.update($ret_ty::default()).unwrap(); #[cfg(feature = "use_std")] println!( concat!( stringify!($id), " (update): (\"{}\", \"{}\") - \"{}\""), a, b, $ret_ty::default() ); } } }; } /// 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])* } }; } tikv-jemalloc-ctl-0.5.4/src/opt.rs000064400000000000000000000151711046102023000151170ustar 00000000000000//! `jemalloc`'s run-time configuration. //! //! These settings are controlled by the `MALLOC_CONF` environment variable. option! { abort[ str: b"opt.abort\0", non_str: 2 ] => bool | ops: r | docs: /// Whether `jemalloc` calls `abort(3)` on most warnings. /// /// This is disabled by default unless `--enable-debug` was specified during /// build configuration. /// /// # Examples /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::opt; /// let abort = opt::abort::mib().unwrap(); /// println!("abort on warning: {}", abort.read().unwrap()); /// # } /// ``` mib_docs: /// See [`abort`]. } option! { dss[ str: b"opt.dss\0", str: 2 ] => &'static str | ops: r | docs: /// 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 /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::opt; /// let dss = opt::dss::read().unwrap(); /// println!("dss priority: {}", dss); /// # } /// ``` mib_docs: /// See [`dss`]. } option! { narenas[ str: b"opt.narenas\0", non_str: 2 ] => libc::c_uint | ops: r | docs: /// 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 /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::opt; /// let narenas = opt::narenas::read().unwrap(); /// println!("number of arenas: {}", narenas); /// # } /// ``` mib_docs: /// See [`narenas`]. } option! { junk[ str: b"opt.junk\0", str: 2 ] => &'static str | ops: r | docs: /// `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 /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::opt; /// let junk = opt::junk::read().unwrap(); /// println!("junk filling: {}", junk); /// # } /// ``` mib_docs: /// See [`junk`]. } option! { zero[ str: b"opt.zero\0", non_str: 2 ] => bool | ops: r | docs: /// `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 /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::opt; /// let zero = opt::zero::read().unwrap(); /// println!("zeroing: {}", zero); /// # } /// ``` mib_docs: /// See [`zero`]. } option! { tcache[ str: b"opt.tcache\0", non_str: 2 ] => bool | ops: r | docs: /// 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 /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::opt; /// let tcache = opt::tcache::read().unwrap(); /// println!("thread-local caching: {}", tcache); /// # } /// ``` mib_docs: /// See [`tcache`]. } option! { tcache_max[ str: b"opt.tcache_max\0", non_str: 2 ] => libc::size_t | ops: r | docs: /// 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 /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::opt; /// let tcache_max = opt::tcache_max::read().unwrap(); /// println!("max cached allocation size: {}", tcache_max); /// # } /// ``` mib_docs: /// See [`tcache_max`]. } option! { background_thread[ str: b"opt.background_thread\0", non_str: 2 ] => bool | ops: r | docs: /// `jemalloc`'s default initialization behavior for background threads. /// /// `jemalloc` automatically spawns background worker threads on /// initialization (first `jemalloc` call) if this option is enabled. By /// default this option is disabled - `malloc_conf=background_thread:true` /// changes its default. /// /// # Examples /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::opt; /// let background_thread = opt::background_thread::read().unwrap(); /// println!("background threads since initialization: {}", background_thread); /// # } /// ``` mib_docs: /// See [`background_thread`]. } tikv-jemalloc-ctl-0.5.4/src/raw.rs000064400000000000000000000355301046102023000151070ustar 00000000000000//! Raw `unsafe` access to the `malloctl` API. use crate::error::{cvt, Result}; use crate::{mem, ptr, slice}; use libc::c_char; /// 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: /// /// ``` /// #[global_allocator] /// static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// /// fn main() { /// use tikv_jemalloc_ctl::raw; /// use libc::{c_uint, c_char}; /// unsafe { /// let mut mib = [0; 4]; /// let nbins: c_uint = raw::read(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::read_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(tikv_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). /// /// # Safety /// /// This function is `unsafe` because it is possible to use it to construct an /// 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 /// `u8` can. pub unsafe fn read_mib(mib: &[usize]) -> Result { let mut value = MaybeUninit { init: () }; let mut len = mem::size_of::(); cvt(tikv_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::()); Ok(value.maybe_uninit) } /// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and /// reads its value. /// /// # Safety /// /// This function is `unsafe` because it is possible to use it to construct an /// 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 /// `u8` can. pub unsafe fn read(name: &[u8]) -> Result { validate_name(name); let mut value = MaybeUninit { init: () }; let mut len = mem::size_of::(); cvt(tikv_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::()); Ok(value.maybe_uninit) } /// 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`) /// to a `mib` (Management Information Base). /// /// # Safety /// /// This function is `unsafe` because it is possible to use it to construct an /// 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 /// `u8` can. pub unsafe fn write_mib(mib: &[usize], mut value: T) -> Result<()> { cvt(tikv_jemalloc_sys::mallctlbymib( mib.as_ptr(), mib.len(), ptr::null_mut(), ptr::null_mut(), &mut value as *mut _ as *mut _, mem::size_of::(), )) } /// Uses the null-terminated string `name` as the key to the _MALLCTL NAMESPACE_ /// and writes it `value` /// /// # Safety /// /// This function is `unsafe` because it is possible to use it to construct an /// 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 /// `u8` can. pub unsafe fn write(name: &[u8], mut value: T) -> Result<()> { validate_name(name); cvt(tikv_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::(), )) } /// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes 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). /// /// # Safety /// /// This function is `unsafe` because it is possible to use it to construct an /// 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 /// `u8` can. pub unsafe fn update_mib(mib: &[usize], mut value: T) -> Result { let mut len = mem::size_of::(); cvt(tikv_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::()); Ok(value) } /// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and /// writes its `value` returning its previous value. /// /// # Safety /// /// This function is `unsafe` because it is possible to use it to construct an /// 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 /// `u8` can. pub unsafe fn update(name: &[u8], mut value: T) -> Result { validate_name(name); let mut len = mem::size_of::(); cvt(tikv_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::()); Ok(value) } /// 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). /// /// # Safety /// /// This function is unsafe because if the key does not return a pointer to a /// null-terminated string the behavior is undefined. /// /// For example, a key for a `u64` value can be used to read a pointer on 64-bit /// platform, where this pointer will point to the address denoted by the `u64`s /// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer /// that will not point to a null-terminated string. /// /// This function needs to compute the length of the string by looking for the /// null-terminator: `\0`. This requires reading the memory behind the pointer. /// /// If the pointer is invalid (e.g. because it was converted from a `u64` that /// does not represent a valid address), reading the string to look for `\0` /// will dereference a non-dereferenceable pointer, which is undefined behavior. /// /// 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, /// which is undefined behavior. pub unsafe fn read_str_mib(mib: &[usize]) -> Result<&'static [u8]> { let ptr: *const c_char = read_mib(mib)?; Ok(ptr2str(ptr)) } /// 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`) /// to a `mib` (Management Information Base). /// /// # Panics /// /// If `value` is not a non-empty null-terminated string. pub fn write_str_mib(mib: &[usize], value: &'static [u8]) -> Result<()> { assert!(!value.is_empty(), "value cannot be empty"); assert_eq!(*value.last().unwrap(), b'\0'); // This is safe because `value` will always point to a null-terminated // string, which makes it safe for all key value types: pointers to // null-terminated strings, pointers, pointer-sized integers, etc. unsafe { write_mib(mib, value.as_ptr() as *const c_char) } } /// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes 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). /// /// # Safety /// /// This function is unsafe because if the key does not return a pointer to a /// null-terminated string the behavior is undefined. /// /// For example, a key for a `u64` value can be used to read a pointer on 64-bit /// platform, where this pointer will point to the address denoted by the `u64`s /// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer /// that will not point to a null-terminated string. /// /// This function needs to compute the length of the string by looking for the /// null-terminator: `\0`. This requires reading the memory behind the pointer. /// /// If the pointer is invalid (e.g. because it was converted from a `u64` that /// does not represent a valid address), reading the string to look for `\0` /// will dereference a non-dereferenceable pointer, which is undefined behavior. /// /// 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, /// which is undefined behavior. pub unsafe fn update_str_mib( mib: &[usize], value: &'static [u8], ) -> Result<&'static [u8]> { let ptr: *const c_char = update_mib(mib, value.as_ptr() as *const c_char)?; Ok(ptr2str(ptr)) } /// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and /// reads its value. /// /// # Safety /// /// This function is unsafe because if the key does not return a pointer to a /// null-terminated string the behavior is undefined. /// /// For example, a key for a `u64` value can be used to read a pointer on 64-bit /// platform, where this pointer will point to the address denoted by the `u64`s /// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer /// that will not point to a null-terminated string. /// /// This function needs to compute the length of the string by looking for the /// null-terminator: `\0`. This requires reading the memory behind the pointer. /// /// If the pointer is invalid (e.g. because it was converted from a `u64` that /// does not represent a valid address), reading the string to look for `\0` /// will dereference a non-dereferenceable pointer, which is undefined behavior. /// /// 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, /// which is undefined behavior. pub unsafe fn read_str(name: &[u8]) -> Result<&'static [u8]> { let ptr: *const c_char = read(name)?; Ok(ptr2str(ptr)) } /// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and /// writes its `value`. pub fn write_str(name: &[u8], value: &'static [u8]) -> Result<()> { assert!(!value.is_empty(), "value cannot be empty"); assert_eq!(*value.last().unwrap(), b'\0'); // This is safe because `value` will always point to a null-terminated // string, which makes it safe for all key value types: pointers to // null-terminated strings, pointers, pointer-sized integers, etc. unsafe { write(name, value.as_ptr() as *const c_char) } } /// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and /// writes its `value` returning its previous value. /// /// # Safety /// /// This function is unsafe because if the key does not return a pointer to a /// null-terminated string the behavior is undefined. /// /// For example, a key for a `u64` value can be used to read a pointer on 64-bit /// platform, where this pointer will point to the address denoted by the `u64`s /// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer /// that will not point to a null-terminated string. /// /// This function needs to compute the length of the string by looking for the /// null-terminator: `\0`. This requires reading the memory behind the pointer. /// /// If the pointer is invalid (e.g. because it was converted from a `u64` that /// does not represent a valid address), reading the string to look for `\0` /// will dereference a non-dereferenceable pointer, which is undefined behavior. /// /// 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, /// which is undefined behavior. pub unsafe fn update_str( name: &[u8], value: &'static [u8], ) -> Result<&'static [u8]> { let ptr: *const c_char = update(name, value.as_ptr() as *const c_char)?; Ok(ptr2str(ptr)) } /// Converts a non-empty null-terminated character string at `ptr` into a valid /// null-terminated UTF-8 string. /// /// # Panics /// /// If `ptr.is_null()`. /// /// # Safety /// /// If `ptr` does not point to a null-terminated character string the behavior /// is undefined. unsafe fn ptr2str(ptr: *const c_char) -> &'static [u8] { assert!( !ptr.is_null(), "attempt to convert a null-ptr to a UTF-8 string" ); let len = libc::strlen(ptr); slice::from_raw_parts(ptr as *const u8, len + 1) } fn validate_name(name: &[u8]) { assert!(!name.is_empty(), "empty byte string"); assert_eq!( *name.last().unwrap(), b'\0', "non-null terminated byte string" ); } union MaybeUninit { init: (), maybe_uninit: T, } #[cfg(test)] mod tests { use super::*; #[test] #[cfg(not(target_arch = "mips64el"))] // FIXME: SIGFPE 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_eq!(rstr.len(), 1); assert_eq!(rstr, b"\0"); } { let cstr = b"foo baaar\0"; let rstr = ptr2str(cstr as *const _ as *const c_char); assert_eq!(rstr.len(), b"foo baaar\0".len()); assert_eq!(rstr, b"foo baaar\0"); } } } } tikv-jemalloc-ctl-0.5.4/src/stats.rs000064400000000000000000000157451046102023000154620ustar 00000000000000//! 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 [`crate::epoch`] type //! for more information. option! { allocated[ str: b"stats.allocated\0", non_str: 2 ] => libc::size_t | ops: r | docs: /// Total number of bytes allocated by the application. /// /// This statistic is cached, and is only refreshed when the epoch is /// advanced. See the [`crate::epoch`] type for more information. /// /// This corresponds to `stats.allocated` in jemalloc's API. /// /// # Examples /// /// ```rust /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::{epoch, stats}; /// let e = epoch::mib().unwrap(); /// let allocated = stats::allocated::mib().unwrap(); /// /// let a = allocated.read().unwrap(); /// let _buf = vec![0; 1024 * 1024]; /// e.advance().unwrap(); /// let b = allocated.read().unwrap(); /// assert!(a < b); /// # } /// ``` mib_docs: /// See [`allocated`]. } option! { active[ str: b"stats.active\0", non_str: 2 ] => libc::size_t | ops: r | docs: /// 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 [`crate::epoch`] type for more information. /// /// This corresponds to `stats.active` in jemalloc's API. /// /// # Examples /// /// ```rust /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::{epoch, stats}; /// let e = epoch::mib().unwrap(); /// let active = stats::active::mib().unwrap(); /// /// let a = active.read().unwrap(); /// let _buf = vec![0; 1024 * 1024]; /// e.advance().unwrap(); /// let b = active.read().unwrap(); /// assert!(a < b); /// # } /// ``` mib_docs: /// See [`active`]. } option! { metadata[ str: b"stats.metadata\0", non_str: 2 ] => libc::size_t | ops: r | docs: /// Total number of bytes dedicated to `jemalloc` metadata. /// /// This statistic is cached, and is only refreshed when the epoch is /// advanced. See the [`crate::epoch`] type for more information. /// /// This corresponds to `stats.metadata` in jemalloc's API. /// /// # Examples /// /// ```rust /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::{epoch, stats}; /// let e = epoch::mib().unwrap(); /// let metadata = stats::metadata::mib().unwrap(); /// /// e.advance().unwrap(); /// let size = metadata.read().unwrap(); /// println!("{} bytes of jemalloc metadata", size); /// # } /// ``` mib_docs: /// See [`metadata`]. } option! { resident[ str: b"stats.resident\0", non_str: 2 ] => libc::size_t | ops: r | docs: /// 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 [`crate::epoch`] type for more information. /// /// This corresponds to `stats.resident` in jemalloc's API. /// /// # Examples /// /// ```rust /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::{epoch, stats}; /// let e = epoch::mib().unwrap(); /// let resident = stats::resident::mib().unwrap(); /// /// e.advance().unwrap(); /// let size = resident.read().unwrap(); /// println!("{} bytes of total resident data", size); /// # } /// ``` mib_docs: /// See [`resident`]. } option! { mapped[ str: b"stats.mapped\0", non_str: 2 ] => libc::size_t | ops: r | docs: /// 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 [`crate::epoch`] type for more information. /// /// This corresponds to `stats.mapped` in jemalloc's API. /// /// # Examples /// /// ```rust /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_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`]. } option! { retained[ str: b"stats.retained\0", non_str: 2 ] => libc::size_t | ops: r | docs: /// 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 [`crate::epoch`] type for more information. /// /// This corresponds to `stats.retained` in jemalloc's API. /// /// # Examples /// /// ```rust /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::{epoch, stats}; /// let e = epoch::mib().unwrap(); /// let retained = stats::retained::mib().unwrap(); /// /// e.advance().unwrap(); /// let size = retained.read().unwrap(); /// println!("{} bytes of total retained data", size); /// # } /// ``` mib_docs: /// See [`retained`]. } tikv-jemalloc-ctl-0.5.4/src/stats_print.rs000064400000000000000000000104241046102023000166630ustar 00000000000000//! Bulk statistics output. 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 { writer: W, error: io::Result<()>, panic: Result<(), Box>, } extern "C" fn callback(opaque: *mut c_void, buf: *const c_char) where W: Write, { unsafe { let state = &mut *(opaque as *mut State); 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(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; tikv_jemalloc_sys::malloc_stats_print( Some(callback::), &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()); } } tikv-jemalloc-ctl-0.5.4/src/thread.rs000064400000000000000000000074261046102023000155700ustar 00000000000000//! Thread specific operations. use crate::error::Result; use crate::raw::{read, read_mib}; option! { allocatedp[ str: b"thread.allocatedp\0", non_str: 2 ] => *mut u64 | ops: | docs: /// Access to the total number of bytes allocated by the current thread. /// /// Unlike [`crate::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 `read` 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::read` can be called on different threads /// and will return the appropriate pointer for each of them. /// /// # Example /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::thread; /// let allocated = thread::allocatedp::mib().unwrap(); /// let allocated = allocated.read().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); /// # } /// ``` mib_docs: /// See [`allocatedp`]. } impl allocatedp { /// Reads value using string API. pub fn read() -> Result> { unsafe { read(Self::name().as_bytes()).map(ThreadLocal) } } } impl allocatedp_mib { /// Reads value using MIB API. pub fn read(&self) -> Result> { unsafe { read_mib(self.0.as_ref()).map(ThreadLocal) } } } option! { deallocatedp[ str: b"thread.deallocatedp\0", non_str: 2 ] => *mut u64 | ops: | docs: /// Access to the total number of bytes deallocated by the current thread. /// /// The `read` 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::read`] can be called on different /// threads and will return the appropriate pointer for each of them. /// /// # Example /// /// ``` /// # #[global_allocator] /// # static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; /// # /// # fn main() { /// use tikv_jemalloc_ctl::thread; /// let deallocated = thread::deallocatedp::mib().unwrap(); /// let deallocated = deallocated.read().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); /// # } /// ``` mib_docs: /// See [`deallocatedp`]. } impl deallocatedp { /// Reads value using string API. pub fn read() -> Result> { unsafe { read(Self::name().as_bytes()).map(ThreadLocal) } } } impl deallocatedp_mib { /// Reads value using MIB API. pub fn read(&self) -> Result> { unsafe { read_mib(self.0.as_ref()).map(ThreadLocal) } } } /// A thread-local pointer. /// /// It is neither `Sync` nor `Send`. // NB we need *const here specifically since it's !Sync + !Send #[repr(transparent)] #[derive(Copy, Clone)] pub struct ThreadLocal(*const T); impl ThreadLocal where T: Copy, { /// Returns the current value at the pointer. #[inline] pub fn get(self) -> T { unsafe { *self.0 } } }