stylo: Measure Elements and ComputedValues.

The patch provides FFI access to Gecko's SeenPtrs type from Rust, in
order to record what has already been measured when measuring Arcs. (The
SeenPtrs must be initialized on the Gecko side because the same table is
reused for measuring all Elements within a window, because Elements can
share ComputedValues.) I have confirmed with DMD that this is working
correctly.

The patch also introduces MallocSizeOfRepeats, which is like
MallocSizeOf but takes a SizeOfState, which holds a SeenPtrs table.
This commit is contained in:
Nicholas Nethercote 2017-08-02 16:42:09 +10:00
parent e07beacd4d
commit 526e9691f8
9 changed files with 161 additions and 5 deletions

View file

@ -41,6 +41,7 @@ use std::hash::{Hash, Hasher};
use std::iter::{ExactSizeIterator, Iterator}; use std::iter::{ExactSizeIterator, Iterator};
use std::mem; use std::mem;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::os::raw::c_void;
use std::process; use std::process;
use std::ptr; use std::ptr;
use std::slice; use std::slice;
@ -200,6 +201,7 @@ impl<T> Arc<T> {
pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> { pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> {
ArcBorrow(&**self) ArcBorrow(&**self)
} }
/// Temporarily converts |self| into a bonafide RawOffsetArc and exposes it to the /// Temporarily converts |self| into a bonafide RawOffsetArc and exposes it to the
/// provided callback. The refcount is not modified. /// provided callback. The refcount is not modified.
#[inline(always)] #[inline(always)]
@ -218,6 +220,12 @@ impl<T> Arc<T> {
// Forward the result. // Forward the result.
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<T> as *const c_void
}
} }
impl<T: ?Sized> Arc<T> { impl<T: ?Sized> Arc<T> {

View file

@ -15,6 +15,8 @@ use servo_arc::Arc;
use shared_lock::StylesheetGuards; use shared_lock::StylesheetGuards;
use std::fmt; use std::fmt;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
#[cfg(feature = "gecko")]
use stylesheets::{MallocSizeOfWithRepeats, SizeOfState};
bitflags! { bitflags! {
flags RestyleFlags: u8 { 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. /// Style system data associated with an Element.
/// ///
/// In Gecko, this hangs directly off the Element. Servo, this is embedded /// In Gecko, this hangs directly off the Element. Servo, this is embedded
@ -392,3 +408,14 @@ impl ElementData {
self.restyle.clear_flags_and_damage(); 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
}
}

View file

@ -14,6 +14,7 @@ use gecko_bindings::structs::mozilla::css::ImageValue;
use gecko_bindings::structs::mozilla::css::URLValue; use gecko_bindings::structs::mozilla::css::URLValue;
use gecko_bindings::structs::mozilla::css::URLValueData; use gecko_bindings::structs::mozilla::css::URLValueData;
use gecko_bindings::structs::mozilla::MallocSizeOf; use gecko_bindings::structs::mozilla::MallocSizeOf;
use gecko_bindings::structs::mozilla::SeenPtrs;
use gecko_bindings::structs::mozilla::Side; use gecko_bindings::structs::mozilla::Side;
use gecko_bindings::structs::nsIContent; use gecko_bindings::structs::nsIContent;
use gecko_bindings::structs::nsIDocument; use gecko_bindings::structs::nsIDocument;
@ -1081,6 +1082,10 @@ extern "C" {
extern "C" { extern "C" {
pub fn Gecko_DropElementSnapshot(snapshot: ServoElementSnapshotOwned); 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" { extern "C" {
pub fn Gecko_ResizeTArrayForStrings(array: *mut nsTArray<nsStringRepr>, pub fn Gecko_ResizeTArrayForStrings(array: *mut nsTArray<nsStringRepr>,
length: u32); length: u32);
@ -1878,6 +1883,11 @@ extern "C" {
extern "C" { extern "C" {
pub fn Servo_Element_ClearData(node: RawGeckoElementBorrowed); 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" { extern "C" {
pub fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader, pub fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader,
gecko_stylesheet: gecko_stylesheet:

View file

@ -6024,6 +6024,21 @@ pub mod root {
MAX = 29, MAX = 29,
} }
#[repr(C)] #[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::<SeenPtrs>() , 48usize , concat !
( "Size of: " , stringify ! ( SeenPtrs ) ));
assert_eq! (::std::mem::align_of::<SeenPtrs>() , 8usize , concat !
( "Alignment of " , stringify ! ( SeenPtrs ) ));
}
impl Clone for SeenPtrs {
fn clone(&self) -> Self { *self }
}
#[repr(C)]
#[derive(Debug)] #[derive(Debug)]
pub struct URLExtraData { pub struct URLExtraData {
pub mRefCnt: root::mozilla::ThreadSafeAutoRefCnt, pub mRefCnt: root::mozilla::ThreadSafeAutoRefCnt,

View file

@ -5906,6 +5906,21 @@ pub mod root {
MAX = 29, MAX = 29,
} }
#[repr(C)] #[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::<SeenPtrs>() , 40usize , concat !
( "Size of: " , stringify ! ( SeenPtrs ) ));
assert_eq! (::std::mem::align_of::<SeenPtrs>() , 8usize , concat !
( "Alignment of " , stringify ! ( SeenPtrs ) ));
}
impl Clone for SeenPtrs {
fn clone(&self) -> Self { *self }
}
#[repr(C)]
#[derive(Debug)] #[derive(Debug)]
pub struct URLExtraData { pub struct URLExtraData {
pub mRefCnt: root::mozilla::ThreadSafeAutoRefCnt, pub mRefCnt: root::mozilla::ThreadSafeAutoRefCnt,

View file

@ -60,6 +60,7 @@ use selector_parser::PseudoElement;
use servo_arc::{Arc, RawOffsetArc}; use servo_arc::{Arc, RawOffsetArc};
use std::mem::{forget, uninitialized, transmute, zeroed}; use std::mem::{forget, uninitialized, transmute, zeroed};
use std::{cmp, ops, ptr}; use std::{cmp, ops, ptr};
use stylesheets::{MallocSizeOfWithRepeats, SizeOfState};
use values::{self, Auto, CustomIdent, Either, KeyframesName}; use values::{self, Auto, CustomIdent, Either, KeyframesName};
use values::computed::ToComputedValue; use values::computed::ToComputedValue;
use values::computed::effects::{BoxShadow, Filter, SimpleShadow}; use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
@ -308,6 +309,11 @@ impl ComputedValuesInner {
self.visited_style.as_ref().map(|x| &**x) 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<RawOffsetArc<ComputedValues>> {
&self.visited_style
}
/// Gets a reference to the visited style. Panic if no visited style exists. /// Gets a reference to the visited style. Panic if no visited style exists.
pub fn visited_style(&self) -> &ComputedValues { pub fn visited_style(&self) -> &ComputedValues {
self.get_visited_style().unwrap() 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<ComputedValues>| {
a.malloc_size_of_children(state)
})
}
n
}
}
<%def name="declare_style_struct(style_struct)"> <%def name="declare_style_struct(style_struct)">
pub use ::gecko_bindings::structs::mozilla::Gecko${style_struct.gecko_name} as ${style_struct.gecko_struct_name}; pub use ::gecko_bindings::structs::mozilla::Gecko${style_struct.gecko_name} as ${style_struct.gecko_struct_name};
impl ${style_struct.gecko_struct_name} { impl ${style_struct.gecko_struct_name} {

View file

@ -4,6 +4,12 @@
//! Memory reporting for the style system when running inside of Gecko. //! 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 shared_lock::SharedRwLockReadGuard;
use std::os::raw::c_void; use std::os::raw::c_void;
@ -14,11 +20,24 @@ use std::os::raw::c_void;
/// do_malloc_size_of(), rather than directly. /// do_malloc_size_of(), rather than directly.
pub type MallocSizeOfFn = unsafe extern "C" fn(ptr: *const c_void) -> usize; 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<T>(ptr: *const T) -> bool {
return ptr as usize <= ::std::mem::align_of::<T>();
}
/// Call malloc_size_of on ptr, first checking that the allocation isn't empty. /// Call malloc_size_of on ptr, first checking that the allocation isn't empty.
pub unsafe fn do_malloc_size_of<T>(malloc_size_of: MallocSizeOfFn, ptr: *const T) -> usize { pub unsafe fn do_malloc_size_of<T>(malloc_size_of: MallocSizeOfFn, ptr: *const T) -> usize {
use std::mem::align_of; if is_empty(ptr) {
if ptr as usize <= align_of::<T>() {
0 0
} else { } else {
malloc_size_of(ptr as *const c_void) 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; 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 /// Like MallocSizeOf, but operates with the global SharedRwLockReadGuard
/// locked. /// locked.
pub trait MallocSizeOfWithGuard { pub trait MallocSizeOfWithGuard {
@ -58,6 +86,19 @@ impl<T: MallocSizeOf> MallocSizeOf for Vec<T> {
} }
} }
#[cfg(feature = "gecko")]
impl<T: MallocSizeOfWithRepeats> MallocSizeOfWithRepeats for Arc<T> {
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<T: MallocSizeOfWithGuard> MallocSizeOfWithGuard for Vec<T> { impl<T: MallocSizeOfWithGuard> MallocSizeOfWithGuard for Vec<T> {
fn malloc_size_of_children( fn malloc_size_of_children(
&self, &self,

View file

@ -40,6 +40,8 @@ pub use self::keyframes_rule::KeyframesRule;
pub use self::loader::StylesheetLoader; pub use self::loader::StylesheetLoader;
pub use self::media_rule::MediaRule; pub use self::media_rule::MediaRule;
pub use self::memory::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard}; 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::namespace_rule::NamespaceRule;
pub use self::page_rule::PageRule; pub use self::page_rule::PageRule;
pub use self::rule_parser::{State, TopLevelRuleParser}; pub use self::rule_parser::{State, TopLevelRuleParser};

View file

@ -74,6 +74,7 @@ use style::gecko_bindings::structs::IterationCompositeOperation;
use style::gecko_bindings::structs::MallocSizeOf; use style::gecko_bindings::structs::MallocSizeOf;
use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4; use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4;
use style::gecko_bindings::structs::RawGeckoPresContextOwned; use style::gecko_bindings::structs::RawGeckoPresContextOwned;
use style::gecko_bindings::structs::SeenPtrs;
use style::gecko_bindings::structs::ServoElementSnapshotTable; use style::gecko_bindings::structs::ServoElementSnapshotTable;
use style::gecko_bindings::structs::ServoTraversalFlags; use style::gecko_bindings::structs::ServoTraversalFlags;
use style::gecko_bindings::structs::StyleRuleInclusion; use style::gecko_bindings::structs::StyleRuleInclusion;
@ -107,8 +108,9 @@ use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard
use style::string_cache::Atom; use style::string_cache::Atom;
use style::style_adjuster::StyleAdjuster; use style::style_adjuster::StyleAdjuster;
use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule}; use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MallocSizeOfWithGuard, MediaRule}; use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MallocSizeOfWithGuard};
use style::stylesheets::{NamespaceRule, Origin, PageRule, StyleRule, SupportsRule}; use style::stylesheets::{MallocSizeOfWithRepeats, MediaRule};
use style::stylesheets::{NamespaceRule, Origin, PageRule, SizeOfState, StyleRule, SupportsRule};
use style::stylesheets::StylesheetContents; use style::stylesheets::StylesheetContents;
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader; use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue}; 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() }; 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] #[no_mangle]
pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyleSheetContentsStrong { pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyleSheetContentsStrong {
let global_style_data = &*GLOBAL_STYLE_DATA; let global_style_data = &*GLOBAL_STYLE_DATA;