diff --git a/Cargo.toml b/Cargo.toml index 6c12c3205..c2511404c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,5 @@ members = [ "primitive-types", "ethereum-types", "ethbloom", + "parity-util-mem/derive" ] diff --git a/parity-util-mem/Cargo.toml b/parity-util-mem/Cargo.toml index a89122723..10a90c3cc 100644 --- a/parity-util-mem/Cargo.toml +++ b/parity-util-mem/Cargo.toml @@ -9,7 +9,6 @@ edition = "2018" [dependencies] cfg-if = "0.1.10" -malloc_size_of_derive = "0.1.1" dlmalloc = { version = "0.1.3", features = ["global"], optional = true } wee_alloc = { version = "0.4.5", optional = true } # from https://github.com/microsoft/mimalloc: @@ -19,6 +18,7 @@ wee_alloc = { version = "0.4.5", optional = true } # The performance penalty is only around 3% on average over our benchmarks. mimallocator = { version = "0.1.3", features = ["secure"], optional = true } mimalloc-sys = { version = "0.1.6", optional = true } +parity-util-mem-derive = { path = "derive", version = "0.1" } smallvec = { version = "1.0.0", optional = true } ethereum-types = { version = "0.8.0", optional = true, path = "../ethereum-types" } diff --git a/parity-util-mem/derive/Cargo.toml b/parity-util-mem/derive/Cargo.toml new file mode 100644 index 000000000..f37d38013 --- /dev/null +++ b/parity-util-mem/derive/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "parity-util-mem-derive" +version = "0.1.0" +authors = ["Parity Technologies"] +license = "MIT" +description = "Crate for memory reporting" +repository = "https://github.com/paritytech/pariry-common/parity-util-mem/derive" + +[lib] +path = "lib.rs" +proc-macro = true + +[dependencies] +proc-macro2 = "1" +syn = { version = "1", features = ["full"] } +synstructure = "0.12" diff --git a/parity-util-mem/derive/lib.rs b/parity-util-mem/derive/lib.rs new file mode 100644 index 000000000..c1c1e504e --- /dev/null +++ b/parity-util-mem/derive/lib.rs @@ -0,0 +1,84 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! A crate for deriving the MallocSizeOf trait. +//! +//! This is a copy of Servo malloc_size_of_derive code, modified to work with +//! our `parity_util_mem` library + +extern crate proc_macro2; +#[macro_use] +extern crate syn; +#[macro_use] +extern crate synstructure; + +#[cfg(not(test))] +decl_derive!([MallocSizeOf, attributes(ignore_malloc_size_of)] => malloc_size_of_derive); + +fn malloc_size_of_derive(s: synstructure::Structure) -> proc_macro2::TokenStream { + let match_body = s.each(|binding| { + let ignore = binding.ast().attrs.iter().any(|attr| match attr.parse_meta().unwrap() { + syn::Meta::Path(ref path) | syn::Meta::List(syn::MetaList { ref path, .. }) + if path.is_ident("ignore_malloc_size_of") => + { + panic!( + "#[ignore_malloc_size_of] should have an explanation, \ + e.g. #[ignore_malloc_size_of = \"because reasons\"]" + ); + } + syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) if path.is_ident("ignore_malloc_size_of") => true, + _ => false, + }); + if ignore { + None + } else if let syn::Type::Array(..) = binding.ast().ty { + Some(quote! { + for item in #binding.iter() { + sum += parity_util_mem::MallocSizeOf::size_of(item, ops); + } + }) + } else { + Some(quote! { + sum += parity_util_mem::MallocSizeOf::size_of(#binding, ops); + }) + } + }); + + let ast = s.ast(); + let name = &ast.ident; + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + let mut where_clause = where_clause.unwrap_or(&parse_quote!(where)).clone(); + for param in ast.generics.type_params() { + let ident = ¶m.ident; + where_clause.predicates.push(parse_quote!(#ident: parity_util_mem::MallocSizeOf)); + } + + let tokens = quote! { + impl #impl_generics parity_util_mem::MallocSizeOf for #name #ty_generics #where_clause { + #[inline] + #[allow(unused_variables, unused_mut, unreachable_code)] + fn size_of(&self, ops: &mut parity_util_mem::MallocSizeOfOps) -> usize { + let mut sum = 0; + match *self { + #match_body + } + sum + } + } + }; + + tokens +} diff --git a/parity-util-mem/src/impls.rs b/parity-util-mem/src/impls.rs index 322375873..4124b132a 100644 --- a/parity-util-mem/src/impls.rs +++ b/parity-util-mem/src/impls.rs @@ -32,6 +32,12 @@ malloc_size_of_is_0!(std::time::Duration); malloc_size_of_is_0!(U64, U128, U256, U512, H32, H64, H128, H160, H256, H264, H512, H520, Bloom); +malloc_size_of_is_0!( + [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], [u8; 9], [u8; 10], [u8; 11], [u8; 12], + [u8; 13], [u8; 14], [u8; 15], [u8; 16], [u8; 17], [u8; 18], [u8; 19], [u8; 20], [u8; 21], [u8; 22], [u8; 23], + [u8; 24], [u8; 25], [u8; 26], [u8; 27], [u8; 28], [u8; 29], [u8; 30], [u8; 31], [u8; 32] +); + macro_rules! impl_smallvec { ($size: expr) => { impl MallocSizeOf for SmallVec<[T; $size]> diff --git a/parity-util-mem/src/lib.rs b/parity-util-mem/src/lib.rs index 43fbc4ab6..23c07e27b 100644 --- a/parity-util-mem/src/lib.rs +++ b/parity-util-mem/src/lib.rs @@ -23,8 +23,6 @@ #[cfg(not(feature = "std"))] extern crate alloc; -use malloc_size_of_derive as malloc_size_derive; - cfg_if::cfg_if! { if #[cfg(all( feature = "jemalloc-global", @@ -72,7 +70,8 @@ pub mod impls; pub use allocators::MallocSizeOfExt; pub use malloc_size::{MallocSizeOf, MallocSizeOfOps}; -pub use malloc_size_derive::*; + +pub use parity_util_mem_derive::*; #[cfg(feature = "std")] #[cfg(test)] diff --git a/parity-util-mem/src/malloc_size.rs b/parity-util-mem/src/malloc_size.rs index a748eec82..a86823db5 100644 --- a/parity-util-mem/src/malloc_size.rs +++ b/parity-util-mem/src/malloc_size.rs @@ -43,8 +43,8 @@ //! measured as well as the thing it points to. E.g. //! ` as MallocSizeOf>::size_of(field, ops)`. -//! This is an extended (for own internal needs) version of the Servo internal malloc_size crate. -//! We should occasionally track the upstream changes/fixes and reintroduce them here, be they applicable. +//! This is an extended version of the Servo internal malloc_size crate. +//! We should occasionally track the upstream changes/fixes and reintroduce them here, whenever applicable. #[cfg(not(feature = "std"))] use alloc::vec::Vec; @@ -426,11 +426,7 @@ where } #[cfg(feature = "std")] -impl MallocShallowSizeOf for std::collections::HashMap -where - K: Eq + Hash, - S: BuildHasher, -{ +impl MallocShallowSizeOf for std::collections::HashMap { fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { // See the implementation for std::collections::HashSet for details. if ops.has_malloc_enclosing_size_of() { @@ -444,9 +440,8 @@ where #[cfg(feature = "std")] impl MallocSizeOf for std::collections::HashMap where - K: Eq + Hash + MallocSizeOf, + K: MallocSizeOf, V: MallocSizeOf, - S: BuildHasher, { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let mut n = self.shallow_size_of(ops); @@ -458,10 +453,7 @@ where } } -impl MallocShallowSizeOf for rstd::collections::BTreeMap -where - K: Eq + Hash, -{ +impl MallocShallowSizeOf for rstd::collections::BTreeMap { 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) }) @@ -473,7 +465,7 @@ where impl MallocSizeOf for rstd::collections::BTreeMap where - K: Eq + Hash + MallocSizeOf, + K: MallocSizeOf, V: MallocSizeOf, { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { diff --git a/parity-util-mem/tests/derive.rs b/parity-util-mem/tests/derive.rs new file mode 100644 index 000000000..10dc6975d --- /dev/null +++ b/parity-util-mem/tests/derive.rs @@ -0,0 +1,63 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use parity_util_mem::{MallocSizeOf, MallocSizeOfExt}; + +#[test] +#[cfg(feature = "std")] +fn derive_vec() { + #[derive(MallocSizeOf)] + struct Trivia { + v: Vec, + } + + let t = Trivia { v: vec![0u8; 1024] }; + + assert!(t.malloc_size_of() > 1000); +} + +#[test] +#[cfg(feature = "std")] +fn derive_hashmap() { + #[derive(MallocSizeOf, Default)] + struct Trivia { + hm: std::collections::HashMap>, + } + + let mut t = Trivia::default(); + + t.hm.insert(1, vec![0u8; 2048]); + + assert!(t.malloc_size_of() > 2000); +} + +#[test] +#[cfg(feature = "std")] +fn derive_ignore() { + #[derive(MallocSizeOf, Default)] + struct Trivia { + hm: std::collections::HashMap>, + #[ignore_malloc_size_of = "I don't like vectors"] + v: Vec, + } + + let mut t = Trivia::default(); + + t.hm.insert(1, vec![0u8; 2048]); + t.v = vec![0u8; 1024]; + + assert!(t.malloc_size_of() < 3000); +}