malloc_size_of-0.1.1/.cargo_vcs_info.json0000644000000001540000000000100140200ustar { "git": { "sha1": "376d170293fb28dd3e5950e2dabd0c39e55db410" }, "path_in_vcs": "malloc_size_of" }malloc_size_of-0.1.1/Cargo.lock0000644000000005660000000000100120020ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "malloc_size_of" version = "0.1.1" dependencies = [ "void", ] [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" malloc_size_of-0.1.1/Cargo.toml0000644000000020230000000000100120130ustar # 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 = "2021" rust-version = "1.56" name = "malloc_size_of" version = "0.1.1" authors = ["The Servo Project Developers"] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A an allocator-agnostic crate for measuring the heap size of a value" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/servo/malloc_size_of" [features] default = ["std"] std = [] [lib] name = "malloc_size_of" path = "src/lib.rs" [dependencies.void] version = "1.0.2" optional = true malloc_size_of-0.1.1/Cargo.toml.orig000064400000000000000000000006251046102023000155020ustar 00000000000000[package] name = "malloc_size_of" description = "A an allocator-agnostic crate for measuring the heap size of a value" version = "0.1.1" authors = ["The Servo Project Developers"] license = "MIT OR Apache-2.0" repository = "https://github.com/servo/malloc_size_of" edition = "2021" rust-version = "1.56" [features] default = ["std"] std = [] [dependencies] void = { version = "1.0.2", optional = true } malloc_size_of-0.1.1/LICENSE-APACHE000064400000000000000000000251371046102023000145440ustar 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. malloc_size_of-0.1.1/LICENSE-MIT000064400000000000000000000017771046102023000142600ustar 00000000000000Permission 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. malloc_size_of-0.1.1/README.md000064400000000000000000000024601046102023000140710ustar 00000000000000# MallocSizeOf [![Build Status](https://github.com/servo/malloc_size_of/actions/workflows/main.yml/badge.svg)](https://github.com/servo/malloc_size_of/actions) [![Crates.io](https://img.shields.io/crates/v/malloc_size_of.svg)](https://crates.io/crates/malloc_size_of) [![Docs](https://docs.rs/malloc_size_of/badge.svg)](https://docs.rs/malloc_size_of) ![Crates.io License](https://img.shields.io/crates/l/malloc_size_of) [![dependency status](https://deps.rs/repo/github/servo/malloc_size_of/status.svg)](https://deps.rs/repo/github/servo/malloc_size_of) A an allocator-agnostic crate for measuring the runtime size of a value including the size of any heap allocations that are owned by that value. This crate is used by both Servo and Firefox for memory usage calculation. ## Features - It isn't bound to a particular heap allocator. - It provides traits for both "shallow" and "deep" measurement, which gives flexibility in the cases where the traits can't be used. - It allows for measuring blocks even when only an interior pointer can be obtained for heap allocations, e.g. `HashSet` and `HashMap`. (This relies on the heap allocator having suitable support, which `jemalloc` has.) - It allows handling of types like `Rc` and `Arc` by providing traits that are different to the ones for non-graph structures. malloc_size_of-0.1.1/src/impls.rs000064400000000000000000000272501046102023000150770ustar 00000000000000// Copyright 2016-2017 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps}; use core::cell::{Cell, RefCell}; use core::hash::Hash; use core::marker::PhantomData; use core::mem::size_of; use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; use core::ops::{Range, RangeFrom, RangeInclusive, RangeTo}; use core::sync::atomic::AtomicBool; use core::sync::atomic::{AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize}; use core::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize}; use alloc::borrow::{Cow, ToOwned}; use alloc::boxed::Box; use alloc::collections::{BTreeMap, VecDeque}; use alloc::string::String; use alloc::vec::Vec; #[cfg(feature = "std")] use std::{ collections::{HashMap, HashSet}, hash::BuildHasher, sync::Mutex, }; // Our one exception to being completely dependency-free. The void crate is tiny, unlikely to ever // do another release, and likely to go away if/when the never type stabilises. #[cfg(feature = "void")] impl MallocSizeOf for void::Void { #[inline] fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { void::unreachable(*self) } } /// For use on types where size_of() returns 0. #[macro_export] macro_rules! malloc_size_of_is_0( ($($ty:ty),+) => ( $( impl $crate::MallocSizeOf for $ty { #[inline(always)] fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize { 0 } } )+ ); ($($ty:ident<$($gen:ident),+>),+) => ( $( impl<$($gen: $crate::MallocSizeOf),+> $crate::MallocSizeOf for $ty<$($gen),+> { #[inline(always)] fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize { 0 } } )+ ); ); malloc_size_of_is_0!((), bool, char, str); malloc_size_of_is_0!(u8, u16, u32, u64, u128, usize); malloc_size_of_is_0!(i8, i16, i32, i64, i128, isize); malloc_size_of_is_0!(f32, f64); malloc_size_of_is_0!(AtomicBool); malloc_size_of_is_0!(AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize); malloc_size_of_is_0!(AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize); malloc_size_of_is_0!( NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize, NonZeroU128 ); malloc_size_of_is_0!( NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroIsize, NonZeroI128 ); impl MallocSizeOf for &'_ T { fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { // Zero makes sense for a non-owning reference. 0 } } impl MallocSizeOf for &'_ mut T { fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { // Zero makes sense for a non-owning reference. 0 } } // PhantomData is always 0. impl MallocSizeOf for PhantomData { fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { 0 } } impl MallocSizeOf for [T; N] { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.iter().fold(0, |acc, item| acc + item.size_of(ops)) } } impl MallocSizeOf for (T1, T2) where T1: MallocSizeOf, T2: MallocSizeOf, { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.0.size_of(ops) + self.1.size_of(ops) } } impl MallocSizeOf for (T1, T2, T3) where T1: MallocSizeOf, T2: MallocSizeOf, T3: MallocSizeOf, { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.0.size_of(ops) + self.1.size_of(ops) + self.2.size_of(ops) } } impl MallocSizeOf for (T1, T2, T3, T4) where T1: MallocSizeOf, T2: MallocSizeOf, T3: MallocSizeOf, T4: MallocSizeOf, { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.0.size_of(ops) + self.1.size_of(ops) + self.2.size_of(ops) + self.3.size_of(ops) } } impl MallocSizeOf for [T] { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let mut n = 0; for elem in self.iter() { n += elem.size_of(ops); } n } } impl MallocSizeOf for Range { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.start.size_of(ops) + self.end.size_of(ops) } } impl MallocSizeOf for RangeInclusive { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.start().size_of(ops) + self.end().size_of(ops) } } impl MallocSizeOf for RangeTo { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.end.size_of(ops) } } impl MallocSizeOf for RangeFrom { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.start.size_of(ops) } } impl MallocSizeOf for Option { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { match self { Some(val) => val.size_of(ops), None => 0, } } } impl MallocSizeOf for Result { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { match *self { Ok(ref x) => x.size_of(ops), Err(ref e) => e.size_of(ops), } } } impl MallocSizeOf for Cell { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.get().size_of(ops) } } impl MallocSizeOf for RefCell { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.borrow().size_of(ops) } } impl MallocSizeOf for Cow<'_, B> where B::Owned: MallocSizeOf, { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { match *self { Cow::Borrowed(_) => 0, Cow::Owned(ref b) => b.size_of(ops), } } } impl MallocSizeOf for String { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { unsafe { ops.malloc_size_of(self.as_ptr()) } } } impl MallocShallowSizeOf for Box { fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { unsafe { ops.malloc_size_of(&**self) } } } impl MallocSizeOf for Box { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.shallow_size_of(ops) + (**self).size_of(ops) } } impl MallocShallowSizeOf for Vec { fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { unsafe { ops.malloc_size_of(self.as_ptr()) } } } impl MallocSizeOf for Vec { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let mut n = self.shallow_size_of(ops); for elem in self.iter() { n += elem.size_of(ops); } n } } impl MallocShallowSizeOf for VecDeque { fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { if ops.has_malloc_enclosing_size_of() { if let Some(front) = self.front() { // The front element is an interior pointer. unsafe { ops.malloc_enclosing_size_of(front) } } else { // This assumes that no memory is allocated when the VecDeque is empty. 0 } } else { // An estimate. self.capacity() * size_of::() } } } impl MallocSizeOf for VecDeque { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let mut n = self.shallow_size_of(ops); for elem in self.iter() { n += elem.size_of(ops); } n } } impl MallocShallowSizeOf for BTreeMap where K: Eq + Hash, { fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { if ops.has_malloc_enclosing_size_of() { self.values() .next() .map_or(0, |v| unsafe { ops.malloc_enclosing_size_of(v) }) } else { self.len() * (size_of::() + size_of::() + size_of::()) } } } impl MallocSizeOf for BTreeMap where K: Eq + Hash + MallocSizeOf, V: MallocSizeOf, { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let mut n = self.shallow_size_of(ops); for (k, v) in self.iter() { n += k.size_of(ops); n += v.size_of(ops); } n } } #[cfg(feature = "std")] impl MallocShallowSizeOf for HashSet where T: Eq + Hash, S: BuildHasher, { fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { if ops.has_malloc_enclosing_size_of() { // The first value from the iterator gives us an interior pointer. // `ops.malloc_enclosing_size_of()` then gives us the storage size. // This assumes that the `HashSet`'s contents (values and hashes) // are all stored in a single contiguous heap allocation. self.iter() .next() .map_or(0, |t| unsafe { ops.malloc_enclosing_size_of(t) }) } else { // An estimate. self.capacity() * (size_of::() + size_of::()) } } } #[cfg(feature = "std")] impl MallocSizeOf for HashSet where T: Eq + Hash + MallocSizeOf, S: BuildHasher, { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let mut n = self.shallow_size_of(ops); for t in self.iter() { n += t.size_of(ops); } n } } #[cfg(feature = "std")] impl MallocShallowSizeOf for HashMap where K: Eq + Hash, S: BuildHasher, { fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { // See the implementation for HashSet for details. if ops.has_malloc_enclosing_size_of() { self.values() .next() .map_or(0, |v| unsafe { ops.malloc_enclosing_size_of(v) }) } else { self.capacity() * (size_of::() + size_of::() + size_of::()) } } } #[cfg(feature = "std")] impl MallocSizeOf for HashMap where K: Eq + Hash + MallocSizeOf, V: MallocSizeOf, S: BuildHasher, { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let mut n = self.shallow_size_of(ops); for (k, v) in self.iter() { n += k.size_of(ops); n += v.size_of(ops); } n } } /// If a mutex is stored directly as a member of a data type that is being measured, /// it is the unique owner of its contents and deserves to be measured. /// /// If a mutex is stored inside of an Arc value as a member of a data type that is being measured, /// the Arc will not be automatically measured so there is no risk of overcounting the mutex's /// contents. #[cfg(feature = "std")] impl MallocSizeOf for Mutex { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { (*self.lock().unwrap()).size_of(ops) } } // XXX: we don't want MallocSizeOf to be defined for Rc and Arc. If negative // trait bounds are ever allowed, this code should be uncommented. // (We do have a compile-fail test for this: rc_arc_must_not_derive_malloc_size_of.rs) //impl !MallocSizeOf for Arc { } //impl !MallocShallowSizeOf for Arc { } malloc_size_of-0.1.1/src/lib.rs000064400000000000000000000221621046102023000145160ustar 00000000000000// Copyright 2016-2017 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! A an allocator-agnostic crate for measuring the runtime size of a value //! including the size of any heap allocations that are owned by that value. //! //! - The core abstraction is provided by the [`MallocSizeOf`] trait which should be implemented for all //! types whose size you wish to measure. //! - A derive macro for implementing this trait on structs is provided by the [malloc_size_of_derive](https://docs.rs/malloc_size_of_derive) crate //! - Additionally there are [`MallocUnconditionalSizeOf`], [`MallocConditionalSizeOf`] traits for measuring //! types where ownership is shared (such as `Arc` and `Rc`). //! - Each of these traits also has a "shallow" variant ([`MallocShallowSizeOf`], [`MallocUnconditionalShallowSizeOf`], and [`MallocConditionalShallowSizeOf`]) which only measure the heap size of the value passed //! and not any nested allocations. //! //! All of these traits rely on being provided with an instance of [`MallocSizeOfOps`] which allows size computations //! to call into the allocator to ask it for the underlyinhg size of the allocations backing data structures. //! //! This crate is used by both Servo and Firefox for memory usage calculation. //! //! ## Features //! //! - It isn't bound to a particular heap allocator. //! - It provides traits for both "shallow" and "deep" measurement, which gives //! flexibility in the cases where the traits can't be used. //! - It allows for measuring blocks even when only an interior pointer can be //! obtained for heap allocations, e.g. `HashSet` and `HashMap`. (This relies //! on the heap allocator having suitable support, which `jemalloc` has.) //! - It allows handling of types like `Rc` and `Arc` by providing traits that //! are different to the ones for non-graph structures. //! //! ## Suggested usage //! //! - When possible, use the `MallocSizeOf` trait. (Deriving support is //! provided by the `malloc_size_of_derive` crate.) //! - If you need an additional synchronization argument, provide a function //! that is like the standard trait method, but with the extra argument. //! - If you need multiple measurements for a type, provide a function named //! `add_size_of` that takes a mutable reference to a struct that contains //! the multiple measurement fields. //! - When deep measurement (via `MallocSizeOf`) cannot be implemented for a //! type, shallow measurement (via `MallocShallowSizeOf`) in combination with //! iteration can be a useful substitute. //! - `Rc` and `Arc` are always tricky, which is why `MallocSizeOf` is not (and //! should not be) implemented for them. //! - If an `Rc` or `Arc` is known to be a "primary" reference and can always //! be measured, it should be measured via the `MallocUnconditionalSizeOf` //! trait. //! - If an `Rc` or `Arc` should be measured only if it hasn't been seen //! before, it should be measured via the `MallocConditionalSizeOf` trait. //! - Using universal function call syntax is a good idea when measuring boxed //! fields in structs, because it makes it clear that the Box is being //! measured as well as the thing it points to. E.g. //! ` as MallocSizeOf>::size_of(field, ops)`. #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; mod impls; use alloc::boxed::Box; use core::ffi::c_void; /// Trait for measuring the "deep" heap usage of a data structure. This is the /// most commonly-used of the traits. pub trait MallocSizeOf { /// Measure the heap usage of all descendant heap-allocated structures, but /// not the space taken up by the value itself. fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize; } /// Trait for measuring the "shallow" heap usage of a container. pub trait MallocShallowSizeOf { /// Measure the heap usage of immediate heap-allocated descendant /// structures, but not the space taken up by the value itself. Anything /// beyond the immediate descendants must be measured separately, using /// iteration. fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; } /// Like `MallocSizeOf`, but with a different name so it cannot be used /// accidentally with derive(MallocSizeOf). For use with types like `Rc` and /// `Arc` when appropriate (e.g. when measuring a "primary" reference). pub trait MallocUnconditionalSizeOf { /// Measure the heap usage of all heap-allocated descendant structures, but /// not the space taken up by the value itself. fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; } /// `MallocUnconditionalSizeOf` combined with `MallocShallowSizeOf`. pub trait MallocUnconditionalShallowSizeOf { /// `unconditional_size_of` combined with `shallow_size_of`. fn unconditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; } /// Like `MallocSizeOf`, but only measures if the value hasn't already been /// measured. For use with types like `Rc` and `Arc` when appropriate (e.g. /// when there is no "primary" reference). pub trait MallocConditionalSizeOf { /// Measure the heap usage of all heap-allocated descendant structures, but /// not the space taken up by the value itself, and only if that heap usage /// hasn't already been measured. fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; } /// `MallocConditionalSizeOf` combined with `MallocShallowSizeOf`. pub trait MallocConditionalShallowSizeOf { /// `conditional_size_of` combined with `shallow_size_of`. fn conditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; } /// A C function that takes a pointer to a heap allocation and returns its size. type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize; /// A closure implementing a stateful predicate on pointers. type VoidPtrToBoolFnMut = dyn FnMut(*const c_void) -> bool; /// Operations used when measuring heap usage of data structures. pub struct MallocSizeOfOps { /// A function that returns the size of a heap allocation. size_of_op: VoidPtrToSizeFn, /// Like `size_of_op`, but can take an interior pointer. Optional because /// not all allocators support this operation. If it's not provided, some /// memory measurements will actually be computed estimates rather than /// real and accurate measurements. enclosing_size_of_op: Option, /// Check if a pointer has been seen before, and remember it for next time. /// Useful when measuring `Rc`s and `Arc`s. Optional, because many places /// don't need it. have_seen_ptr_op: Option>, } impl MallocSizeOfOps { pub fn new( size_of: VoidPtrToSizeFn, malloc_enclosing_size_of: Option, have_seen_ptr: Option>, ) -> Self { MallocSizeOfOps { size_of_op: size_of, enclosing_size_of_op: malloc_enclosing_size_of, have_seen_ptr_op: have_seen_ptr, } } /// Check if an allocation is empty. This relies on knowledge of how Rust /// handles empty allocations, which may change in the future. fn is_empty(ptr: *const T) -> bool { // The correct condition is this: // `ptr as usize <= ::std::mem::align_of::()` // But we can't call align_of() on a ?Sized T. So we approximate it // with the following. 256 is large enough that it should always be // larger than the required alignment, but small enough that it is // always in the first page of memory and therefore not a legitimate // address. ptr as *const usize as usize <= 256 } /// Call `size_of_op` on `ptr`, first checking that the allocation isn't /// empty, because some types (such as `Vec`) utilize empty allocations. #[allow(clippy::missing_safety_doc)] pub unsafe fn malloc_size_of(&self, ptr: *const T) -> usize { if MallocSizeOfOps::is_empty(ptr) { 0 } else { (self.size_of_op)(ptr as *const c_void) } } /// Is an `enclosing_size_of_op` available? pub fn has_malloc_enclosing_size_of(&self) -> bool { self.enclosing_size_of_op.is_some() } /// Call `enclosing_size_of_op`, which must be available, on `ptr`, which /// must not be empty. #[allow(clippy::missing_safety_doc)] pub unsafe fn malloc_enclosing_size_of(&self, ptr: *const T) -> usize { assert!(!MallocSizeOfOps::is_empty(ptr)); (self.enclosing_size_of_op.unwrap())(ptr as *const c_void) } /// Call `have_seen_ptr_op` on `ptr`. pub fn have_seen_ptr(&mut self, ptr: *const T) -> bool { let have_seen_ptr_op = self .have_seen_ptr_op .as_mut() .expect("missing have_seen_ptr_op"); have_seen_ptr_op(ptr as *const c_void) } }