diff options
author | Nicholas Nethercote <nnethercote@mozilla.com> | 2015-03-22 15:20:31 -0700 |
---|---|---|
committer | Nicholas Nethercote <nnethercote@mozilla.com> | 2015-03-25 16:00:23 -0700 |
commit | ce36e574f4f404d7b0c8d15d3d55ea62fca8deb6 (patch) | |
tree | f414f6c9095ccebaa241bac7a030cc41f8bd5b52 /components/util/mem.rs | |
parent | 7f587f6cb56b1dae1a56dec36754007ef4d376ac (diff) | |
download | servo-ce36e574f4f404d7b0c8d15d3d55ea62fca8deb6.tar.gz servo-ce36e574f4f404d7b0c8d15d3d55ea62fca8deb6.zip |
Rename lots of profiling-related things.
------------------------------------------------------------------------
BEFORE AFTER
------------------------------------------------------------------------
util::memory util::mem
- heap_size_of - heap_size_of (unchanged)
- SizeOf - HeapSizeOf
- size_of_excluding_self - heap_size_of_children
prof::mem prof::mem
- MemoryProfilerChan - ProfilerChan
- MemoryReport - Report
- MemoryReportsChan - ReportsChan
- MemoryReporter - Reporter
- MemoryProfilerMsg - ProfilerMsg
- {R,UnR}egisterMemoryReporter - {R,UnR}egisterReporter
- MemoryProfiler - Prof
- ReportsForest - ReportsForest (unchanged)
- ReportsTree - ReportsTree (unchanged)
- SystemMemoryReporter - SystemReporter
prof::time prof::time
- TimeProfilerChan - ProfilerChan
- TimerMetadata - TimerMetadata (unchanged)
- Formatable - Formattable [spelling!]
- TimeProfilerMsg - ProfilerMsg
- TimeProfilerCategory - ProfilerCategory
- TimeProfilerBuckets - ProfilerBuckets
- TimeProfiler - Profiler
- TimerMetadataFrameType - TimerMetadataFrameType (unchanged)
- TimerMetadataReflowType - TimerMetadataReflowType (unchanged)
- ProfilerMetadata - ProfilerMetadata (unchanged)
In a few places both prof::time and prof::mem are used, and so
module-qualification is needed to avoid overlap, e.g. time::Profiler and
mem::Profiler. Likewise with std::mem and prof::mem. This is not a big
deal.
Diffstat (limited to 'components/util/mem.rs')
-rw-r--r-- | components/util/mem.rs | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/components/util/mem.rs b/components/util/mem.rs new file mode 100644 index 00000000000..ad7cf6108a2 --- /dev/null +++ b/components/util/mem.rs @@ -0,0 +1,161 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Data structure measurement. + +use libc::{c_void, size_t}; +use std::collections::LinkedList; +use std::mem::transmute; +use std::sync::Arc; + +extern { + // Get the size of a heap block. + // + // Ideally Rust would expose a function like this in std::rt::heap, which would avoid the + // jemalloc dependence. + // + // The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some + // platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice + // this function doesn't modify the contents of the block that `ptr` points to, so we use + // `*const c_void` here. + fn je_malloc_usable_size(ptr: *const c_void) -> size_t; +} + +// A wrapper for je_malloc_usable_size that handles `EMPTY` and returns `usize`. +pub fn heap_size_of(ptr: *const c_void) -> usize { + if ptr == ::std::rt::heap::EMPTY as *const c_void { + 0 + } else { + unsafe { je_malloc_usable_size(ptr) as usize } + } +} + +// The simplest trait for measuring the size of heap data structures. More complex traits that +// return multiple measurements -- e.g. measure text separately from images -- are also possible, +// and should be used when appropriate. +// +// FIXME(njn): it would be nice to be able to derive this trait automatically, given that +// implementations are mostly repetitive and mechanical. +// +pub trait HeapSizeOf { + /// Measure the size of any heap-allocated structures that hang off this value, but not the + /// space taken up by the value itself (i.e. what size_of::<T> measures, more or less); that + /// space is handled by the implementation of HeapSizeOf for Box<T> below. + fn heap_size_of_children(&self) -> usize; +} + +// There are two possible ways to measure the size of `self` when it's on the heap: compute it +// (with `::std::rt::heap::usable_size(::std::mem::size_of::<T>(), 0)`) or measure it directly +// using the heap allocator (with `heap_size_of`). We do the latter, for the following reasons. +// +// * The heap allocator is the true authority for the sizes of heap blocks; its measurement is +// guaranteed to be correct. In comparison, size computations are error-prone. (For example, the +// `rt::heap::usable_size` function used in some of Rust's non-default allocator implementations +// underestimate the true usable size of heap blocks, which is safe in general but would cause +// under-measurement here.) +// +// * If we measure something that isn't a heap block, we'll get a crash. This keeps us honest, +// which is important because unsafe code is involved and this can be gotten wrong. +// +// However, in the best case, the two approaches should give the same results. +// +impl<T: HeapSizeOf> HeapSizeOf for Box<T> { + fn heap_size_of_children(&self) -> usize { + // Measure size of `self`. + heap_size_of(&**self as *const T as *const c_void) + (**self).heap_size_of_children() + } +} + +impl HeapSizeOf for String { + fn heap_size_of_children(&self) -> usize { + heap_size_of(self.as_ptr() as *const c_void) + } +} + +impl<T: HeapSizeOf> HeapSizeOf for Option<T> { + fn heap_size_of_children(&self) -> usize { + match *self { + None => 0, + Some(ref x) => x.heap_size_of_children() + } + } +} + +impl<T: HeapSizeOf> HeapSizeOf for Arc<T> { + fn heap_size_of_children(&self) -> usize { + (**self).heap_size_of_children() + } +} + +impl<T: HeapSizeOf> HeapSizeOf for Vec<T> { + fn heap_size_of_children(&self) -> usize { + heap_size_of(self.as_ptr() as *const c_void) + + self.iter().fold(0, |n, elem| n + elem.heap_size_of_children()) + } +} + +// FIXME(njn): We can't implement HeapSizeOf accurately for LinkedList because it requires access +// to the private Node type. Eventually we'll want to add HeapSizeOf (or equivalent) to Rust +// itself. In the meantime, we use the dirty hack of transmuting LinkedList into an identical type +// (LinkedList2) and measuring that. +impl<T: HeapSizeOf> HeapSizeOf for LinkedList<T> { + fn heap_size_of_children(&self) -> usize { + let list2: &LinkedList2<T> = unsafe { transmute(self) }; + list2.heap_size_of_children() + } +} + +struct LinkedList2<T> { + _length: usize, + list_head: Link<T>, + _list_tail: Rawlink<Node<T>>, +} + +type Link<T> = Option<Box<Node<T>>>; + +struct Rawlink<T> { + _p: *mut T, +} + +struct Node<T> { + next: Link<T>, + _prev: Rawlink<Node<T>>, + value: T, +} + +impl<T: HeapSizeOf> HeapSizeOf for Node<T> { + // Unlike most heap_size_of_children() functions, this one does *not* measure descendents. + // Instead, LinkedList2<T>::heap_size_of_children() handles that, so that it can use iteration + // instead of recursion, which avoids potentially blowing the stack. + fn heap_size_of_children(&self) -> usize { + self.value.heap_size_of_children() + } +} + +impl<T: HeapSizeOf> HeapSizeOf for LinkedList2<T> { + fn heap_size_of_children(&self) -> usize { + let mut size = 0; + let mut curr: &Link<T> = &self.list_head; + while curr.is_some() { + size += (*curr).heap_size_of_children(); + curr = &curr.as_ref().unwrap().next; + } + size + } +} + +// This is a basic sanity check. If the representation of LinkedList changes such that it becomes a +// different size to LinkedList2, this will fail at compile-time. +#[allow(dead_code)] +unsafe fn linked_list2_check() { + transmute::<LinkedList<i32>, LinkedList2<i32>>(panic!()); +} + +// Currently, types that implement the Drop type are larger than those that don't. Because +// LinkedList implements Drop, LinkedList2 must also so that linked_list2_check() doesn't fail. +#[unsafe_destructor] +impl<T> Drop for LinkedList2<T> { + fn drop(&mut self) {} +} + |