servo/components/util/memory.rs
Nicholas Nethercote 52447ccd9b Move profiler code from util into a new crate profile.
- Most of util::memory has been moved into profile::mem, though the
  `SizeOf` trait and related things remain in util::memory. The
  `SystemMemoryReporter` code is now in a submodule
  profile::mem::system_reporter.

- util::time has been moved entirely into profile::time.
2015-03-24 02:09:31 -07:00

161 lines
5.8 KiB
Rust

/* 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 SizeOf {
/// 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 SizeOf for Box<T> below.
fn size_of_excluding_self(&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: SizeOf> SizeOf for Box<T> {
fn size_of_excluding_self(&self) -> usize {
// Measure size of `self`.
heap_size_of(&**self as *const T as *const c_void) + (**self).size_of_excluding_self()
}
}
impl SizeOf for String {
fn size_of_excluding_self(&self) -> usize {
heap_size_of(self.as_ptr() as *const c_void)
}
}
impl<T: SizeOf> SizeOf for Option<T> {
fn size_of_excluding_self(&self) -> usize {
match *self {
None => 0,
Some(ref x) => x.size_of_excluding_self()
}
}
}
impl<T: SizeOf> SizeOf for Arc<T> {
fn size_of_excluding_self(&self) -> usize {
(**self).size_of_excluding_self()
}
}
impl<T: SizeOf> SizeOf for Vec<T> {
fn size_of_excluding_self(&self) -> usize {
heap_size_of(self.as_ptr() as *const c_void) +
self.iter().fold(0, |n, elem| n + elem.size_of_excluding_self())
}
}
// FIXME(njn): We can't implement SizeOf accurately for LinkedList because it requires access to the
// private Node type. Eventually we'll want to add SizeOf (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: SizeOf> SizeOf for LinkedList<T> {
fn size_of_excluding_self(&self) -> usize {
let list2: &LinkedList2<T> = unsafe { transmute(self) };
list2.size_of_excluding_self()
}
}
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: SizeOf> SizeOf for Node<T> {
// Unlike most size_of_excluding_self() functions, this one does *not* measure descendents.
// Instead, LinkedList2<T>::size_of_excluding_self() handles that, so that it can use iteration
// instead of recursion, which avoids potentially blowing the stack.
fn size_of_excluding_self(&self) -> usize {
self.value.size_of_excluding_self()
}
}
impl<T: SizeOf> SizeOf for LinkedList2<T> {
fn size_of_excluding_self(&self) -> usize {
let mut size = 0;
let mut curr: &Link<T> = &self.list_head;
while curr.is_some() {
size += (*curr).size_of_excluding_self();
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) {}
}