├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── appveyor.yml ├── build.rs ├── derive ├── Cargo.toml ├── lib.rs └── test.rs ├── src └── lib.rs └── tests └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *.swp 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - 1.11.0 4 | - nightly 5 | - beta 6 | - stable 7 | 8 | os: 9 | - linux 10 | - osx 11 | 12 | notifications: 13 | webhooks: http://build.servo.org:54856/travis 14 | 15 | script: 16 | - cargo test 17 | - "[ $TRAVIS_RUST_VERSION != nightly ] || cargo test --features unstable" 18 | - "[[ $TRAVIS_RUST_VERSION != nightly && $TRAVIS_RUST_VERSION != beta ]] || cargo test --manifest-path derive/Cargo.toml" 19 | 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "heapsize" 3 | version = "0.4.2" 4 | authors = [ "The Servo Project Developers" ] 5 | description = "Infrastructure for measuring the total runtime size of an object on the heap" 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/servo/heapsize" 8 | build = "build.rs" 9 | 10 | [target.'cfg(windows)'.dependencies] 11 | winapi = { version = "0.3.4", features = ["std", "heapapi"] } 12 | 13 | [features] 14 | unstable = [] 15 | 16 | # https://github.com/servo/heapsize/issues/74 17 | flexible-tests = [] 18 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 The Servo Developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # heapsize 2 | 3 | In support of measuring heap allocations in Rust programs. 4 | 5 | [API Documentation](https://docs.rs/heapsize/) 6 | 7 | ## Status 8 | 9 | This crate is not maintained and is no longer used by Servo. At the time of 10 | writing, Servo uses internal 11 | [`malloc_size_of`](https://github.com/servo/servo/tree/faf3a183f3755a9986ec4379abadf3523bd8b3c0/components/malloc_size_of) 12 | instead. 13 | 14 | ## License 15 | 16 | Licensed under either of 17 | 18 | * Apache License, Version 2.0 19 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 20 | * MIT license 21 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 22 | 23 | at your option. 24 | 25 | ### Contribution 26 | 27 | Unless you explicitly state otherwise, any contribution intentionally submitted 28 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 29 | dual licensed as above, without any additional terms or conditions. 30 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - FEATURES: "" 4 | - FEATURES: "unstable" 5 | 6 | platform: 7 | - i686-pc-windows-gnu 8 | - i686-pc-windows-msvc 9 | - x86_64-pc-windows-gnu 10 | - x86_64-pc-windows-msvc 11 | 12 | install: 13 | - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:PLATFORM}.exe" 14 | - rust-nightly-%PLATFORM%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" 15 | - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin 16 | - rustc -V 17 | - cargo -V 18 | 19 | build_script: 20 | - cargo build --verbose --features "%FEATURES%" 21 | 22 | test_script: 23 | - cargo test --verbose --features "%FEATURES%" 24 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env::var; 2 | use std::process::Command; 3 | use std::str; 4 | 5 | fn main() { 6 | let verbose = Command::new(var("RUSTC").unwrap_or("rustc".into())) 7 | .arg("--version") 8 | .arg("--verbose") 9 | .output() 10 | .unwrap() 11 | .stdout; 12 | let verbose = str::from_utf8(&verbose).unwrap(); 13 | let mut commit_date = None; 14 | let mut release = None; 15 | for line in verbose.lines() { 16 | let mut parts = line.split(':'); 17 | match parts.next().unwrap().trim() { 18 | "commit-date" => commit_date = Some(parts.next().unwrap().trim()), 19 | "release" => release = Some(parts.next().unwrap().trim()), 20 | _ => {} 21 | } 22 | } 23 | let version = release.unwrap().split('-').next().unwrap();; 24 | let mut version_components = version.split('.').map(|s| s.parse::().unwrap()); 25 | let version = ( 26 | version_components.next().unwrap(), 27 | version_components.next().unwrap(), 28 | version_components.next().unwrap(), 29 | // "unknown" sorts after "2016-02-14", which is what we want to defaut to unprefixed 30 | // https://github.com/servo/heapsize/pull/44#issuecomment-187935883 31 | commit_date.unwrap() 32 | ); 33 | assert_eq!(version_components.next(), None); 34 | if version < (1, 8, 0, "2016-02-14") { 35 | println!("cargo:rustc-cfg=prefixed_jemalloc"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "heapsize_derive" 3 | version = "0.1.4" 4 | authors = ["The Servo Project Developers"] 5 | description = "Automatically generating infrastructure for measuring the total runtime size of an object on the heap" 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/servo/heapsize" 8 | 9 | [lib] 10 | path = "lib.rs" 11 | proc-macro = true 12 | 13 | [[test]] 14 | name = "test" 15 | path = "test.rs" 16 | 17 | [dependencies] 18 | syn = "0.11" 19 | quote = "0.3" 20 | synstructure = "0.5" 21 | -------------------------------------------------------------------------------- /derive/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(test))] extern crate proc_macro; 2 | #[macro_use] extern crate quote; 3 | extern crate syn; 4 | extern crate synstructure; 5 | 6 | #[cfg(not(test))] 7 | #[proc_macro_derive(HeapSizeOf, attributes(ignore_heap_size_of))] 8 | pub fn expand_token_stream(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 9 | expand_string(&input.to_string()).parse().unwrap() 10 | } 11 | 12 | fn expand_string(input: &str) -> String { 13 | let mut type_ = syn::parse_macro_input(input).unwrap(); 14 | 15 | let style = synstructure::BindStyle::Ref.into(); 16 | let match_body = synstructure::each_field(&mut type_, &style, |binding| { 17 | let ignore = binding.field.attrs.iter().any(|attr| match attr.value { 18 | syn::MetaItem::Word(ref ident) | 19 | syn::MetaItem::List(ref ident, _) if ident == "ignore_heap_size_of" => { 20 | panic!("#[ignore_heap_size_of] should have an explanation, \ 21 | e.g. #[ignore_heap_size_of = \"because reasons\"]"); 22 | } 23 | syn::MetaItem::NameValue(ref ident, _) if ident == "ignore_heap_size_of" => { 24 | true 25 | } 26 | _ => false, 27 | }); 28 | if ignore { 29 | None 30 | } else if let syn::Ty::Array(..) = binding.field.ty { 31 | Some(quote! { 32 | for item in #binding.iter() { 33 | sum += ::heapsize::HeapSizeOf::heap_size_of_children(item); 34 | } 35 | }) 36 | } else { 37 | Some(quote! { 38 | sum += ::heapsize::HeapSizeOf::heap_size_of_children(#binding); 39 | }) 40 | } 41 | }); 42 | 43 | let name = &type_.ident; 44 | let (impl_generics, ty_generics, where_clause) = type_.generics.split_for_impl(); 45 | let mut where_clause = where_clause.clone(); 46 | for param in &type_.generics.ty_params { 47 | where_clause.predicates.push(syn::WherePredicate::BoundPredicate(syn::WhereBoundPredicate { 48 | bound_lifetimes: Vec::new(), 49 | bounded_ty: syn::Ty::Path(None, param.ident.clone().into()), 50 | bounds: vec![syn::TyParamBound::Trait( 51 | syn::PolyTraitRef { 52 | bound_lifetimes: Vec::new(), 53 | trait_ref: syn::parse_path("::heapsize::HeapSizeOf").unwrap(), 54 | }, 55 | syn::TraitBoundModifier::None 56 | )], 57 | })) 58 | } 59 | 60 | let tokens = quote! { 61 | impl #impl_generics ::heapsize::HeapSizeOf for #name #ty_generics #where_clause { 62 | #[inline] 63 | #[allow(unused_variables, unused_mut, unreachable_code)] 64 | fn heap_size_of_children(&self) -> usize { 65 | let mut sum = 0; 66 | match *self { 67 | #match_body 68 | } 69 | sum 70 | } 71 | } 72 | }; 73 | 74 | tokens.to_string() 75 | } 76 | 77 | #[test] 78 | fn test_struct() { 79 | let mut source = "struct Foo { bar: Bar, baz: T, #[ignore_heap_size_of = \"\"] z: Arc }"; 80 | let mut expanded = expand_string(source); 81 | let mut no_space = expanded.replace(" ", ""); 82 | macro_rules! match_count { 83 | ($e: expr, $count: expr) => { 84 | assert_eq!(no_space.matches(&$e.replace(" ", "")).count(), $count, 85 | "counting occurences of {:?} in {:?} (whitespace-insensitive)", 86 | $e, expanded) 87 | } 88 | } 89 | match_count!("struct", 0); 90 | match_count!("ignore_heap_size_of", 0); 91 | match_count!("impl ::heapsize::HeapSizeOf for Foo where T: ::heapsize::HeapSizeOf {", 1); 92 | match_count!("sum += ::heapsize::HeapSizeOf::heap_size_of_children(", 2); 93 | 94 | source = "struct Bar([Baz; 3]);"; 95 | expanded = expand_string(source); 96 | no_space = expanded.replace(" ", ""); 97 | match_count!("for item in", 1); 98 | } 99 | 100 | #[should_panic(expected = "should have an explanation")] 101 | #[test] 102 | fn test_no_reason() { 103 | expand_string("struct A { #[ignore_heap_size_of] b: C }"); 104 | } 105 | -------------------------------------------------------------------------------- /derive/test.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate heapsize_derive; 2 | 3 | mod heapsize { 4 | pub trait HeapSizeOf { 5 | fn heap_size_of_children(&self) -> usize; 6 | } 7 | 8 | impl HeapSizeOf for Box { 9 | fn heap_size_of_children(&self) -> usize { 10 | ::std::mem::size_of::() 11 | } 12 | } 13 | } 14 | 15 | 16 | #[derive(HeapSizeOf)] 17 | struct Foo([Box; 2], Box); 18 | 19 | #[test] 20 | fn test() { 21 | use heapsize::HeapSizeOf; 22 | assert_eq!(Foo([Box::new(1), Box::new(2)], Box::new(3)).heap_size_of_children(), 9); 23 | } 24 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Data structure measurement. 2 | 3 | #[cfg(target_os = "windows")] 4 | extern crate winapi; 5 | 6 | #[cfg(target_os = "windows")] 7 | use winapi::um::heapapi::{GetProcessHeap, HeapSize, HeapValidate}; 8 | use std::borrow::Cow; 9 | use std::cell::{Cell, RefCell}; 10 | use std::collections::{BTreeMap, HashSet, HashMap, LinkedList, VecDeque}; 11 | use std::hash::BuildHasher; 12 | use std::hash::Hash; 13 | use std::marker::PhantomData; 14 | use std::mem::{size_of, align_of}; 15 | use std::net::{Ipv4Addr, Ipv6Addr}; 16 | use std::ops::{Range, RangeFrom, RangeFull, RangeTo}; 17 | use std::os::raw::c_void; 18 | use std::sync::Arc; 19 | use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize}; 20 | use std::rc::Rc; 21 | 22 | /// Get the size of a heap block. 23 | /// 24 | /// Ideally Rust would expose a function like this in std::rt::heap. 25 | /// 26 | /// `unsafe` because the caller must ensure that the pointer is from jemalloc. 27 | /// FIXME: This probably interacts badly with custom allocators: 28 | /// https://doc.rust-lang.org/book/custom-allocators.html 29 | pub unsafe fn heap_size_of(ptr: *const T) -> usize { 30 | if ptr as usize <= align_of::() { 31 | 0 32 | } else { 33 | heap_size_of_impl(ptr as *const c_void) 34 | } 35 | } 36 | 37 | #[cfg(not(target_os = "windows"))] 38 | unsafe fn heap_size_of_impl(ptr: *const c_void) -> usize { 39 | // The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some 40 | // platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice 41 | // this function doesn't modify the contents of the block that `ptr` points to, so we use 42 | // `*const c_void` here. 43 | extern "C" { 44 | #[cfg_attr(any(prefixed_jemalloc, target_os = "macos", target_os = "ios", target_os = "android"), link_name = "je_malloc_usable_size")] 45 | fn malloc_usable_size(ptr: *const c_void) -> usize; 46 | } 47 | malloc_usable_size(ptr) 48 | } 49 | 50 | #[cfg(target_os = "windows")] 51 | unsafe fn heap_size_of_impl(mut ptr: *const c_void) -> usize { 52 | let heap = GetProcessHeap(); 53 | 54 | if HeapValidate(heap, 0, ptr) == 0 { 55 | ptr = *(ptr as *const *const c_void).offset(-1); 56 | } 57 | 58 | HeapSize(heap, 0, ptr) as usize 59 | } 60 | 61 | // The simplest trait for measuring the size of heap data structures. More complex traits that 62 | // return multiple measurements -- e.g. measure text separately from images -- are also possible, 63 | // and should be used when appropriate. 64 | // 65 | pub trait HeapSizeOf { 66 | /// Measure the size of any heap-allocated structures that hang off this value, but not the 67 | /// space taken up by the value itself (i.e. what size_of:: measures, more or less); that 68 | /// space is handled by the implementation of HeapSizeOf for Box below. 69 | fn heap_size_of_children(&self) -> usize; 70 | } 71 | 72 | // There are two possible ways to measure the size of `self` when it's on the heap: compute it 73 | // (with `::std::rt::heap::usable_size(::std::mem::size_of::(), 0)`) or measure it directly 74 | // using the heap allocator (with `heap_size_of`). We do the latter, for the following reasons. 75 | // 76 | // * The heap allocator is the true authority for the sizes of heap blocks; its measurement is 77 | // guaranteed to be correct. In comparison, size computations are error-prone. (For example, the 78 | // `rt::heap::usable_size` function used in some of Rust's non-default allocator implementations 79 | // underestimate the true usable size of heap blocks, which is safe in general but would cause 80 | // under-measurement here.) 81 | // 82 | // * If we measure something that isn't a heap block, we'll get a crash. This keeps us honest, 83 | // which is important because unsafe code is involved and this can be gotten wrong. 84 | // 85 | // However, in the best case, the two approaches should give the same results. 86 | // 87 | impl HeapSizeOf for Box { 88 | fn heap_size_of_children(&self) -> usize { 89 | // Measure size of `self`. 90 | unsafe { 91 | heap_size_of(&**self as *const T as *const c_void) + (**self).heap_size_of_children() 92 | } 93 | } 94 | } 95 | 96 | impl HeapSizeOf for [T] { 97 | fn heap_size_of_children(&self) -> usize { 98 | self.iter().fold(0, |size, item| size + item.heap_size_of_children()) 99 | } 100 | } 101 | 102 | impl HeapSizeOf for String { 103 | fn heap_size_of_children(&self) -> usize { 104 | unsafe { 105 | heap_size_of(self.as_ptr()) 106 | } 107 | } 108 | } 109 | 110 | impl<'a, T: ?Sized> HeapSizeOf for &'a T { 111 | fn heap_size_of_children(&self) -> usize { 112 | 0 113 | } 114 | } 115 | 116 | // The implementations for *mut T and *const T are designed for use cases like LinkedHashMap where 117 | // you have a data structure which internally maintains an e.g. HashMap parameterized with raw 118 | // pointers. We want to be able to rely on the standard HeapSizeOf implementation for `HashMap`, 119 | // and can handle the contribution of the raw pointers manually. 120 | // 121 | // These have to return 0 since we don't know if the pointer is pointing to a heap allocation or 122 | // even valid memory. 123 | impl HeapSizeOf for *mut T { 124 | fn heap_size_of_children(&self) -> usize { 125 | 0 126 | } 127 | } 128 | 129 | impl HeapSizeOf for *const T { 130 | fn heap_size_of_children(&self) -> usize { 131 | 0 132 | } 133 | } 134 | 135 | impl HeapSizeOf for Option { 136 | fn heap_size_of_children(&self) -> usize { 137 | match *self { 138 | None => 0, 139 | Some(ref x) => x.heap_size_of_children() 140 | } 141 | } 142 | } 143 | 144 | impl HeapSizeOf for Result { 145 | fn heap_size_of_children(&self) -> usize { 146 | match *self { 147 | Ok(ref x) => x.heap_size_of_children(), 148 | Err(ref e) => e.heap_size_of_children(), 149 | } 150 | } 151 | } 152 | 153 | impl<'a, B: ?Sized + ToOwned> HeapSizeOf for Cow<'a, B> where B::Owned: HeapSizeOf { 154 | fn heap_size_of_children(&self) -> usize { 155 | match *self { 156 | Cow::Borrowed(_) => 0, 157 | Cow::Owned(ref b) => b.heap_size_of_children(), 158 | } 159 | } 160 | } 161 | 162 | impl HeapSizeOf for () { 163 | fn heap_size_of_children(&self) -> usize { 164 | 0 165 | } 166 | } 167 | 168 | impl HeapSizeOf for (T1, T2) 169 | where T1: HeapSizeOf, T2 :HeapSizeOf 170 | { 171 | fn heap_size_of_children(&self) -> usize { 172 | self.0.heap_size_of_children() + 173 | self.1.heap_size_of_children() 174 | } 175 | } 176 | 177 | impl HeapSizeOf for (T1, T2, T3) 178 | where T1: HeapSizeOf, T2 :HeapSizeOf, T3: HeapSizeOf 179 | { 180 | fn heap_size_of_children(&self) -> usize { 181 | self.0.heap_size_of_children() + 182 | self.1.heap_size_of_children() + 183 | self.2.heap_size_of_children() 184 | } 185 | } 186 | 187 | impl HeapSizeOf for (T1, T2, T3, T4) 188 | where T1: HeapSizeOf, T2 :HeapSizeOf, T3: HeapSizeOf, T4: HeapSizeOf 189 | { 190 | fn heap_size_of_children(&self) -> usize { 191 | self.0.heap_size_of_children() + 192 | self.1.heap_size_of_children() + 193 | self.2.heap_size_of_children() + 194 | self.3.heap_size_of_children() 195 | } 196 | } 197 | 198 | impl HeapSizeOf for (T1, T2, T3, T4, T5) 199 | where T1: HeapSizeOf, T2 :HeapSizeOf, T3: HeapSizeOf, T4: HeapSizeOf, T5: HeapSizeOf 200 | { 201 | fn heap_size_of_children(&self) -> usize { 202 | self.0.heap_size_of_children() + 203 | self.1.heap_size_of_children() + 204 | self.2.heap_size_of_children() + 205 | self.3.heap_size_of_children() + 206 | self.4.heap_size_of_children() 207 | } 208 | } 209 | 210 | impl HeapSizeOf for Arc { 211 | fn heap_size_of_children(&self) -> usize { 212 | (**self).heap_size_of_children() 213 | } 214 | } 215 | 216 | impl HeapSizeOf for RefCell { 217 | fn heap_size_of_children(&self) -> usize { 218 | self.borrow().heap_size_of_children() 219 | } 220 | } 221 | 222 | impl HeapSizeOf for Cell { 223 | fn heap_size_of_children(&self) -> usize { 224 | self.get().heap_size_of_children() 225 | } 226 | } 227 | 228 | impl HeapSizeOf for Vec { 229 | fn heap_size_of_children(&self) -> usize { 230 | self.iter().fold( 231 | unsafe { heap_size_of(self.as_ptr()) }, 232 | |n, elem| n + elem.heap_size_of_children()) 233 | } 234 | } 235 | 236 | impl HeapSizeOf for VecDeque { 237 | fn heap_size_of_children(&self) -> usize { 238 | self.iter().fold( 239 | // FIXME: get the buffer pointer for heap_size_of(), capacity() is a lower bound: 240 | self.capacity() * size_of::(), 241 | |n, elem| n + elem.heap_size_of_children()) 242 | } 243 | } 244 | 245 | impl HeapSizeOf for Vec> { 246 | fn heap_size_of_children(&self) -> usize { 247 | // The fate of measuring Rc is still undecided, but we still want to measure 248 | // the space used for storing them. 249 | unsafe { 250 | heap_size_of(self.as_ptr()) 251 | } 252 | } 253 | } 254 | 255 | impl HeapSizeOf for HashSet 256 | where T: Eq + Hash, S: BuildHasher { 257 | fn heap_size_of_children(&self) -> usize { 258 | //TODO(#6908) measure actual bucket memory usage instead of approximating 259 | let size = self.capacity() * (size_of::() + size_of::()); 260 | self.iter().fold(size, |n, value| { 261 | n + value.heap_size_of_children() 262 | }) 263 | } 264 | } 265 | 266 | impl HeapSizeOf for HashMap 267 | where K: Eq + Hash, S: BuildHasher { 268 | fn heap_size_of_children(&self) -> usize { 269 | //TODO(#6908) measure actual bucket memory usage instead of approximating 270 | let size = self.capacity() * (size_of::() + size_of::() + size_of::()); 271 | self.iter().fold(size, |n, (key, value)| { 272 | n + key.heap_size_of_children() + value.heap_size_of_children() 273 | }) 274 | } 275 | } 276 | 277 | // PhantomData is always 0. 278 | impl HeapSizeOf for PhantomData { 279 | fn heap_size_of_children(&self) -> usize { 280 | 0 281 | } 282 | } 283 | 284 | // A linked list has an overhead of two words per item. 285 | impl HeapSizeOf for LinkedList { 286 | fn heap_size_of_children(&self) -> usize { 287 | let mut size = 0; 288 | for item in self { 289 | size += 2 * size_of::() + size_of::() + item.heap_size_of_children(); 290 | } 291 | size 292 | } 293 | } 294 | 295 | // FIXME: Overhead for the BTreeMap nodes is not accounted for. 296 | impl HeapSizeOf for BTreeMap { 297 | fn heap_size_of_children(&self) -> usize { 298 | let mut size = 0; 299 | for (key, value) in self.iter() { 300 | size += size_of::<(K, V)>() + 301 | key.heap_size_of_children() + 302 | value.heap_size_of_children(); 303 | } 304 | size 305 | } 306 | } 307 | 308 | impl HeapSizeOf for Range 309 | where 310 | T: HeapSizeOf, 311 | { 312 | fn heap_size_of_children(&self) -> usize { 313 | self.start.heap_size_of_children() + self.end.heap_size_of_children() 314 | } 315 | } 316 | 317 | impl HeapSizeOf for RangeFrom 318 | where 319 | T: HeapSizeOf, 320 | { 321 | fn heap_size_of_children(&self) -> usize { 322 | self.start.heap_size_of_children() 323 | } 324 | } 325 | 326 | impl HeapSizeOf for RangeTo 327 | where 328 | T: HeapSizeOf, 329 | { 330 | fn heap_size_of_children(&self) -> usize { 331 | self.end.heap_size_of_children() 332 | } 333 | } 334 | 335 | /// For use on types defined in external crates 336 | /// with known heap sizes. 337 | #[macro_export] 338 | macro_rules! known_heap_size( 339 | ($size:expr, $($ty:ty),+) => ( 340 | $( 341 | impl $crate::HeapSizeOf for $ty { 342 | #[inline(always)] 343 | fn heap_size_of_children(&self) -> usize { 344 | $size 345 | } 346 | } 347 | )+ 348 | ); 349 | ($size: expr, $($ty:ident<$($gen:ident),+>),+) => ( 350 | $( 351 | impl<$($gen: $crate::HeapSizeOf),+> $crate::HeapSizeOf for $ty<$($gen),+> { 352 | #[inline(always)] 353 | fn heap_size_of_children(&self) -> usize { 354 | $size 355 | } 356 | } 357 | )+ 358 | ); 359 | ); 360 | 361 | known_heap_size!(0, char, str); 362 | known_heap_size!(0, u8, u16, u32, u64, usize); 363 | known_heap_size!(0, i8, i16, i32, i64, isize); 364 | known_heap_size!(0, bool, f32, f64); 365 | known_heap_size!(0, AtomicBool, AtomicIsize, AtomicUsize); 366 | known_heap_size!(0, Ipv4Addr, Ipv6Addr, RangeFull); 367 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature= "unstable", feature(allocator_api, repr_simd))] 2 | 3 | extern crate heapsize; 4 | 5 | use heapsize::{HeapSizeOf, heap_size_of}; 6 | 7 | /// https://github.com/servo/heapsize/issues/74 8 | #[cfg(feature = "flexible-tests")] 9 | macro_rules! assert_size { 10 | ($actual: expr, $expected: expr) => { 11 | { 12 | let actual = $actual; 13 | let expected = $expected; 14 | assert!(actual >= expected, "expected {:?} >= {:?}", actual, expected) 15 | } 16 | } 17 | } 18 | 19 | #[cfg(not(feature = "flexible-tests"))] 20 | macro_rules! assert_size { 21 | ($actual: expr, $expected: expr) => { 22 | assert_eq!($actual, $expected) 23 | } 24 | } 25 | 26 | #[cfg(feature = "unstable")] 27 | mod unstable { 28 | use heapsize::heap_size_of; 29 | use std::os::raw::c_void; 30 | use std::heap::{Heap, Alloc, Layout}; 31 | 32 | unsafe fn allocate(size: usize, align: usize) -> *mut u8 { 33 | Heap.alloc(Layout::from_size_align(size, align).unwrap()).unwrap() 34 | } 35 | 36 | unsafe fn deallocate(ptr: *mut u8, size: usize, align: usize) { 37 | Heap.dealloc(ptr, Layout::from_size_align(size, align).unwrap()) 38 | } 39 | 40 | #[repr(simd)] 41 | struct OverAligned(u64, u64, u64, u64); 42 | 43 | #[cfg(not(target_os = "windows"))] 44 | #[test] 45 | fn test_alloc() { 46 | unsafe { 47 | // A 64 byte request is allocated exactly. 48 | let x = allocate(64, 1); 49 | assert_size!(heap_size_of(x as *const c_void), 64); 50 | deallocate(x, 64, 1); 51 | 52 | // A 255 byte request is rounded up to 256 bytes. 53 | let x = allocate(255, 1); 54 | assert_size!(heap_size_of(x as *const c_void), 256); 55 | deallocate(x, 255, 1); 56 | 57 | // A 1MiB request is allocated exactly. 58 | let x = allocate(1024 * 1024, 1); 59 | assert_size!(heap_size_of(x as *const c_void), 1024 * 1024); 60 | deallocate(x, 1024 * 1024, 1); 61 | 62 | // An overaligned 1MiB request is allocated exactly. 63 | let x = allocate(1024 * 1024, 32); 64 | assert_size!(heap_size_of(x as *const c_void), 1024 * 1024); 65 | deallocate(x, 1024 * 1024, 32); 66 | } 67 | } 68 | 69 | #[cfg(target_os = "windows")] 70 | #[test] 71 | fn test_alloc() { 72 | unsafe { 73 | // A 64 byte request is allocated exactly. 74 | let x = allocate(64, 1); 75 | assert_size!(heap_size_of(x as *const c_void), 64); 76 | deallocate(x, 64, 1); 77 | 78 | // A 255 byte request is allocated exactly. 79 | let x = allocate(255, 1); 80 | assert_size!(heap_size_of(x as *const c_void), 255); 81 | deallocate(x, 255, 1); 82 | 83 | // A 1MiB request is allocated exactly. 84 | let x = allocate(1024 * 1024, 1); 85 | assert_size!(heap_size_of(x as *const c_void), 1024 * 1024); 86 | deallocate(x, 1024 * 1024, 1); 87 | 88 | // An overaligned 1MiB request is over-allocated. 89 | let x = allocate(1024 * 1024, 32); 90 | assert_size!(heap_size_of(x as *const c_void), 1024 * 1024 + 32); 91 | deallocate(x, 1024 * 1024, 32); 92 | } 93 | } 94 | 95 | #[cfg(not(target_os = "windows"))] 96 | #[test] 97 | fn test_simd() { 98 | let x = Box::new(OverAligned(0, 0, 0, 0)); 99 | assert_size!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32); 100 | } 101 | 102 | #[cfg(target_os = "windows")] 103 | #[test] 104 | fn test_simd() { 105 | let x = Box::new(OverAligned(0, 0, 0, 0)); 106 | assert_size!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32 + 32); 107 | } 108 | } 109 | 110 | #[test] 111 | fn test_boxed_str() { 112 | let x = "raclette".to_owned().into_boxed_str(); 113 | assert_size!(x.heap_size_of_children(), 8); 114 | } 115 | 116 | #[test] 117 | fn test_heap_size() { 118 | 119 | // Note: jemalloc often rounds up request sizes. However, it does not round up for request 120 | // sizes of 8 and higher that are powers of two. We take advantage of knowledge here to make 121 | // the sizes of various heap-allocated blocks predictable. 122 | 123 | //----------------------------------------------------------------------- 124 | // Start with basic heap block measurement. 125 | 126 | unsafe { 127 | // EMPTY is the special non-null address used to represent zero-size allocations. 128 | assert_size!(heap_size_of::<[u64; 0]>(&*Box::new([42_u64; 0])), 0); 129 | assert_size!(heap_size_of::<[u8; 0]>(&*Box::new([42_u8; 0])), 0); 130 | } 131 | 132 | //----------------------------------------------------------------------- 133 | // Test HeapSizeOf implementations for various built-in types. 134 | 135 | // Not on the heap; 0 bytes. 136 | let x = 0i64; 137 | assert_size!(x.heap_size_of_children(), 0); 138 | 139 | // An i64 is 8 bytes. 140 | let x = Box::new(0i64); 141 | assert_size!(x.heap_size_of_children(), 8); 142 | 143 | // An ascii string with 16 chars is 16 bytes in UTF-8. 144 | let string = String::from("0123456789abcdef"); 145 | assert_size!(string.heap_size_of_children(), 16); 146 | 147 | let string_ref: (&String, ()) = (&string, ()); 148 | assert_size!(string_ref.heap_size_of_children(), 0); 149 | 150 | let slice: &str = &*string; 151 | assert_size!(slice.heap_size_of_children(), 0); 152 | 153 | // Not on the heap. 154 | let x: Option = None; 155 | assert_size!(x.heap_size_of_children(), 0); 156 | 157 | // Not on the heap. 158 | let x = Some(0i64); 159 | assert_size!(x.heap_size_of_children(), 0); 160 | 161 | // The `Some` is not on the heap, but the Box is. 162 | let x = Some(Box::new(0i64)); 163 | assert_size!(x.heap_size_of_children(), 8); 164 | 165 | // Not on the heap. 166 | let x = ::std::sync::Arc::new(0i64); 167 | assert_size!(x.heap_size_of_children(), 0); 168 | 169 | // The `Arc` is not on the heap, but the Box is. 170 | let x = ::std::sync::Arc::new(Box::new(0i64)); 171 | assert_size!(x.heap_size_of_children(), 8); 172 | 173 | // Zero elements, no heap storage. 174 | let x: Vec = vec![]; 175 | assert_size!(x.heap_size_of_children(), 0); 176 | 177 | // Four elements, 8 bytes per element. 178 | let x = vec![0i64, 1i64, 2i64, 3i64]; 179 | assert_size!(x.heap_size_of_children(), 32); 180 | } 181 | 182 | #[test] 183 | fn test_boxed_slice() { 184 | let x = vec![1i64, 2i64].into_boxed_slice(); 185 | assert_size!(x.heap_size_of_children(), 16) 186 | } 187 | --------------------------------------------------------------------------------