diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs index d8a74758247..182acecf885 100644 --- a/components/servo_arc/lib.rs +++ b/components/servo_arc/lib.rs @@ -41,6 +41,7 @@ use std::hash::{Hash, Hasher}; use std::iter::{ExactSizeIterator, Iterator}; use std::mem; use std::ops::{Deref, DerefMut}; +use std::os::raw::c_void; use std::process; use std::ptr; use std::slice; @@ -200,6 +201,7 @@ impl Arc { pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> { ArcBorrow(&**self) } + /// Temporarily converts |self| into a bonafide RawOffsetArc and exposes it to the /// provided callback. The refcount is not modified. #[inline(always)] @@ -218,6 +220,12 @@ impl Arc { // Forward the result. result } + + /// Returns the address on the heap of the Arc itself -- not the T within it -- for memory + /// reporting. + pub fn heap_ptr(&self) -> *const c_void { + self.p.ptr() as *const ArcInner as *const c_void + } } impl Arc { diff --git a/components/style/data.rs b/components/style/data.rs index bce4d4502f5..8e63fb52501 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -15,6 +15,8 @@ use servo_arc::Arc; use shared_lock::StylesheetGuards; use std::fmt; use std::ops::{Deref, DerefMut}; +#[cfg(feature = "gecko")] +use stylesheets::{MallocSizeOfWithRepeats, SizeOfState}; bitflags! { flags RestyleFlags: u8 { @@ -242,6 +244,20 @@ impl fmt::Debug for ElementStyles { } } +#[cfg(feature = "gecko")] +impl MallocSizeOfWithRepeats for ElementStyles { + fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize { + let mut n = 0; + if let Some(ref primary) = self.primary { + n += primary.malloc_size_of_children(state) + }; + + // We may measure more fields in the future if DMD says it's worth it. + + n + } +} + /// Style system data associated with an Element. /// /// In Gecko, this hangs directly off the Element. Servo, this is embedded @@ -392,3 +408,14 @@ impl ElementData { self.restyle.clear_flags_and_damage(); } } + +#[cfg(feature = "gecko")] +impl MallocSizeOfWithRepeats for ElementData { + fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize { + let n = self.styles.malloc_size_of_children(state); + + // We may measure more fields in the future if DMD says it's worth it. + + n + } +} diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index 3cb2183f051..5a11e75582a 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -14,6 +14,7 @@ use gecko_bindings::structs::mozilla::css::ImageValue; use gecko_bindings::structs::mozilla::css::URLValue; use gecko_bindings::structs::mozilla::css::URLValueData; use gecko_bindings::structs::mozilla::MallocSizeOf; +use gecko_bindings::structs::mozilla::SeenPtrs; use gecko_bindings::structs::mozilla::Side; use gecko_bindings::structs::nsIContent; use gecko_bindings::structs::nsIDocument; @@ -1081,6 +1082,10 @@ extern "C" { extern "C" { pub fn Gecko_DropElementSnapshot(snapshot: ServoElementSnapshotOwned); } +extern "C" { + pub fn Gecko_HaveSeenPtr(table: *mut SeenPtrs, ptr: *const ::std::os::raw::c_void) + -> bool; +} extern "C" { pub fn Gecko_ResizeTArrayForStrings(array: *mut nsTArray, length: u32); @@ -1878,6 +1883,11 @@ extern "C" { extern "C" { pub fn Servo_Element_ClearData(node: RawGeckoElementBorrowed); } +extern "C" { + pub fn Servo_Element_SizeOfExcludingThis(malloc_size_of: MallocSizeOf, + seen_ptrs: *mut SeenPtrs, + node: RawGeckoElementBorrowed) -> usize; +} extern "C" { pub fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader, gecko_stylesheet: diff --git a/components/style/gecko/generated/structs_debug.rs b/components/style/gecko/generated/structs_debug.rs index ac9ad2686ce..7d6eed0e140 100644 --- a/components/style/gecko/generated/structs_debug.rs +++ b/components/style/gecko/generated/structs_debug.rs @@ -6024,6 +6024,21 @@ pub mod root { MAX = 29, } #[repr(C)] + #[derive(Debug, Copy)] + pub struct SeenPtrs { + pub _bindgen_opaque_blob: [u64; 6usize], + } + #[test] + fn bindgen_test_layout_SeenPtrs() { + assert_eq!(::std::mem::size_of::() , 48usize , concat ! + ( "Size of: " , stringify ! ( SeenPtrs ) )); + assert_eq! (::std::mem::align_of::() , 8usize , concat ! + ( "Alignment of " , stringify ! ( SeenPtrs ) )); + } + impl Clone for SeenPtrs { + fn clone(&self) -> Self { *self } + } + #[repr(C)] #[derive(Debug)] pub struct URLExtraData { pub mRefCnt: root::mozilla::ThreadSafeAutoRefCnt, diff --git a/components/style/gecko/generated/structs_release.rs b/components/style/gecko/generated/structs_release.rs index ad3b9843db6..7ab31bfc5d0 100644 --- a/components/style/gecko/generated/structs_release.rs +++ b/components/style/gecko/generated/structs_release.rs @@ -5906,6 +5906,21 @@ pub mod root { MAX = 29, } #[repr(C)] + #[derive(Debug, Copy)] + pub struct SeenPtrs { + pub _bindgen_opaque_blob: [u64; 5usize], + } + #[test] + fn bindgen_test_layout_SeenPtrs() { + assert_eq!(::std::mem::size_of::() , 40usize , concat ! + ( "Size of: " , stringify ! ( SeenPtrs ) )); + assert_eq! (::std::mem::align_of::() , 8usize , concat ! + ( "Alignment of " , stringify ! ( SeenPtrs ) )); + } + impl Clone for SeenPtrs { + fn clone(&self) -> Self { *self } + } + #[repr(C)] #[derive(Debug)] pub struct URLExtraData { pub mRefCnt: root::mozilla::ThreadSafeAutoRefCnt, diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index d35b6bf4b10..7db095ef64d 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -60,6 +60,7 @@ use selector_parser::PseudoElement; use servo_arc::{Arc, RawOffsetArc}; use std::mem::{forget, uninitialized, transmute, zeroed}; use std::{cmp, ops, ptr}; +use stylesheets::{MallocSizeOfWithRepeats, SizeOfState}; use values::{self, Auto, CustomIdent, Either, KeyframesName}; use values::computed::ToComputedValue; use values::computed::effects::{BoxShadow, Filter, SimpleShadow}; @@ -308,6 +309,11 @@ impl ComputedValuesInner { self.visited_style.as_ref().map(|x| &**x) } + /// Gets the raw visited style. Useful for memory reporting. + pub fn get_raw_visited_style(&self) -> &Option> { + &self.visited_style + } + /// Gets a reference to the visited style. Panic if no visited style exists. pub fn visited_style(&self) -> &ComputedValues { self.get_visited_style().unwrap() @@ -364,6 +370,18 @@ impl ComputedValuesInner { } } +impl MallocSizeOfWithRepeats for ComputedValues { + fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize { + let mut n = 0; + if let Some(ref raw_offset_arc) = *self.get_raw_visited_style() { + n += raw_offset_arc.with_arc(|a: &Arc| { + a.malloc_size_of_children(state) + }) + } + n + } +} + <%def name="declare_style_struct(style_struct)"> pub use ::gecko_bindings::structs::mozilla::Gecko${style_struct.gecko_name} as ${style_struct.gecko_struct_name}; impl ${style_struct.gecko_struct_name} { diff --git a/components/style/stylesheets/memory.rs b/components/style/stylesheets/memory.rs index 44a2e429d5b..39338502a25 100644 --- a/components/style/stylesheets/memory.rs +++ b/components/style/stylesheets/memory.rs @@ -4,6 +4,12 @@ //! Memory reporting for the style system when running inside of Gecko. +#[cfg(feature = "gecko")] +use gecko_bindings::bindings::Gecko_HaveSeenPtr; +#[cfg(feature = "gecko")] +use gecko_bindings::structs::SeenPtrs; +#[cfg(feature = "gecko")] +use servo_arc::Arc; use shared_lock::SharedRwLockReadGuard; use std::os::raw::c_void; @@ -14,11 +20,24 @@ use std::os::raw::c_void; /// do_malloc_size_of(), rather than directly. pub type MallocSizeOfFn = unsafe extern "C" fn(ptr: *const c_void) -> usize; +/// Servo-side counterpart to mozilla::SizeOfState. The only difference is that +/// this struct doesn't contain the SeenPtrs table, just a pointer to it. +#[cfg(feature = "gecko")] +pub struct SizeOfState { + /// Function that measures the size of heap blocks. + pub malloc_size_of: MallocSizeOfFn, + /// Table recording heap blocks that have already been measured. + pub seen_ptrs: *mut SeenPtrs, +} + +/// Call malloc_size_of on ptr, first checking that the allocation isn't empty. +pub unsafe fn is_empty(ptr: *const T) -> bool { + return ptr as usize <= ::std::mem::align_of::(); +} + /// Call malloc_size_of on ptr, first checking that the allocation isn't empty. pub unsafe fn do_malloc_size_of(malloc_size_of: MallocSizeOfFn, ptr: *const T) -> usize { - use std::mem::align_of; - - if ptr as usize <= align_of::() { + if is_empty(ptr) { 0 } else { malloc_size_of(ptr as *const c_void) @@ -32,6 +51,15 @@ pub trait MallocSizeOf { fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize; } +/// Like MallocSizeOf, but takes a SizeOfState which allows it to measure +/// graph-like structures such as those containing Arcs. +#[cfg(feature = "gecko")] +pub trait MallocSizeOfWithRepeats { + /// Measure the size of any heap-allocated structures that hang off this + /// value, but not the space taken up by the value itself. + fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize; +} + /// Like MallocSizeOf, but operates with the global SharedRwLockReadGuard /// locked. pub trait MallocSizeOfWithGuard { @@ -58,6 +86,19 @@ impl MallocSizeOf for Vec { } } +#[cfg(feature = "gecko")] +impl MallocSizeOfWithRepeats for Arc { + fn malloc_size_of_children(&self, state: &mut SizeOfState) -> usize { + let mut n = 0; + let heap_ptr = self.heap_ptr(); + if unsafe { !is_empty(heap_ptr) && !Gecko_HaveSeenPtr(state.seen_ptrs, heap_ptr) } { + n += unsafe { (state.malloc_size_of)(heap_ptr) }; + n += (**self).malloc_size_of_children(state); + } + n + } +} + impl MallocSizeOfWithGuard for Vec { fn malloc_size_of_children( &self, diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index 480ccf65400..9a0271c2f54 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -40,6 +40,8 @@ pub use self::keyframes_rule::KeyframesRule; pub use self::loader::StylesheetLoader; pub use self::media_rule::MediaRule; pub use self::memory::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard}; +#[cfg(feature = "gecko")] +pub use self::memory::{MallocSizeOfWithRepeats, SizeOfState}; pub use self::namespace_rule::NamespaceRule; pub use self::page_rule::PageRule; pub use self::rule_parser::{State, TopLevelRuleParser}; diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 7c9eea0cd93..a63c3627f63 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -74,6 +74,7 @@ use style::gecko_bindings::structs::IterationCompositeOperation; use style::gecko_bindings::structs::MallocSizeOf; use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4; use style::gecko_bindings::structs::RawGeckoPresContextOwned; +use style::gecko_bindings::structs::SeenPtrs; use style::gecko_bindings::structs::ServoElementSnapshotTable; use style::gecko_bindings::structs::ServoTraversalFlags; use style::gecko_bindings::structs::StyleRuleInclusion; @@ -107,8 +108,9 @@ use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard use style::string_cache::Atom; use style::style_adjuster::StyleAdjuster; use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule}; -use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MallocSizeOfWithGuard, MediaRule}; -use style::stylesheets::{NamespaceRule, Origin, PageRule, StyleRule, SupportsRule}; +use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MallocSizeOfWithGuard}; +use style::stylesheets::{MallocSizeOfWithRepeats, MediaRule}; +use style::stylesheets::{NamespaceRule, Origin, PageRule, SizeOfState, StyleRule, SupportsRule}; use style::stylesheets::StylesheetContents; use style::stylesheets::StylesheetLoader as StyleStylesheetLoader; use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue}; @@ -744,6 +746,24 @@ pub extern "C" fn Servo_Element_ClearData(element: RawGeckoElementBorrowed) { unsafe { GeckoElement(element).clear_data() }; } +#[no_mangle] +pub extern "C" fn Servo_Element_SizeOfExcludingThis(malloc_size_of: MallocSizeOf, + seen_ptrs: *mut SeenPtrs, + element: RawGeckoElementBorrowed) -> usize { + let malloc_size_of = malloc_size_of.unwrap(); + let element = GeckoElement(element); + let borrow = element.borrow_data(); + if let Some(data) = borrow { + let mut state = SizeOfState { + malloc_size_of: malloc_size_of, + seen_ptrs: seen_ptrs, + }; + (*data).malloc_size_of_children(&mut state) + } else { + 0 + } +} + #[no_mangle] pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyleSheetContentsStrong { let global_style_data = &*GLOBAL_STYLE_DATA;