зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #5193 - Add memory reporting infrastructure and use it to measure the display list (from nnethercote:measure-display-list); r=jdm
These changeset implements the beginnings of fine-grained measurement of Servo's data structures. @pcwalton, @jdm: are you likely reviewers for this? Source-Repo: https://github.com/servo/servo Source-Revision: f093620922621e1877393b03968ed0ce767fdf12
This commit is contained in:
Родитель
0f0d8d9c81
Коммит
97460a4cdd
|
@ -1348,12 +1348,8 @@ impl<Window> CompositorEventListener for IOCompositor<Window> where Window: Wind
|
||||||
while self.port.try_recv_compositor_msg().is_some() {}
|
while self.port.try_recv_compositor_msg().is_some() {}
|
||||||
|
|
||||||
// Tell the profiler, memory profiler, and scrolling timer to shut down.
|
// Tell the profiler, memory profiler, and scrolling timer to shut down.
|
||||||
let TimeProfilerChan(ref time_profiler_chan) = self.time_profiler_chan;
|
self.time_profiler_chan.send(time::TimeProfilerMsg::Exit);
|
||||||
time_profiler_chan.send(time::TimeProfilerMsg::Exit).unwrap();
|
self.memory_profiler_chan.send(memory::MemoryProfilerMsg::Exit);
|
||||||
|
|
||||||
let MemoryProfilerChan(ref memory_profiler_chan) = self.memory_profiler_chan;
|
|
||||||
memory_profiler_chan.send(memory::MemoryProfilerMsg::Exit).unwrap();
|
|
||||||
|
|
||||||
self.scrolling_timer.shutdown();
|
self.scrolling_timer.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ use net::resource_task;
|
||||||
use net::storage_task::{StorageTask, StorageTaskMsg};
|
use net::storage_task::{StorageTask, StorageTaskMsg};
|
||||||
use util::cursor::Cursor;
|
use util::cursor::Cursor;
|
||||||
use util::geometry::PagePx;
|
use util::geometry::PagePx;
|
||||||
|
use util::memory::MemoryProfilerChan;
|
||||||
use util::opts;
|
use util::opts;
|
||||||
use util::task::spawn_named;
|
use util::task::spawn_named;
|
||||||
use util::time::TimeProfilerChan;
|
use util::time::TimeProfilerChan;
|
||||||
|
@ -91,6 +92,9 @@ pub struct Constellation<LTF, STF> {
|
||||||
/// A channel through which messages can be sent to the time profiler.
|
/// A channel through which messages can be sent to the time profiler.
|
||||||
pub time_profiler_chan: TimeProfilerChan,
|
pub time_profiler_chan: TimeProfilerChan,
|
||||||
|
|
||||||
|
/// A channel through which messages can be sent to the memory profiler.
|
||||||
|
pub memory_profiler_chan: MemoryProfilerChan,
|
||||||
|
|
||||||
pub window_size: WindowSizeData,
|
pub window_size: WindowSizeData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,6 +171,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
font_cache_task: FontCacheTask,
|
font_cache_task: FontCacheTask,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
|
memory_profiler_chan: MemoryProfilerChan,
|
||||||
devtools_chan: Option<DevtoolsControlChan>,
|
devtools_chan: Option<DevtoolsControlChan>,
|
||||||
storage_task: StorageTask)
|
storage_task: StorageTask)
|
||||||
-> ConstellationChan {
|
-> ConstellationChan {
|
||||||
|
@ -191,6 +196,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
||||||
root_frame_id: None,
|
root_frame_id: None,
|
||||||
next_frame_id: FrameId(0),
|
next_frame_id: FrameId(0),
|
||||||
time_profiler_chan: time_profiler_chan,
|
time_profiler_chan: time_profiler_chan,
|
||||||
|
memory_profiler_chan: memory_profiler_chan,
|
||||||
window_size: WindowSizeData {
|
window_size: WindowSizeData {
|
||||||
visible_viewport: opts::get().initial_window_size.as_f32() * ScaleFactor(1.0),
|
visible_viewport: opts::get().initial_window_size.as_f32() * ScaleFactor(1.0),
|
||||||
initial_viewport: opts::get().initial_window_size.as_f32() * ScaleFactor(1.0),
|
initial_viewport: opts::get().initial_window_size.as_f32() * ScaleFactor(1.0),
|
||||||
|
@ -240,6 +246,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
||||||
self.resource_task.clone(),
|
self.resource_task.clone(),
|
||||||
self.storage_task.clone(),
|
self.storage_task.clone(),
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
|
self.memory_profiler_chan.clone(),
|
||||||
window_size,
|
window_size,
|
||||||
script_channel,
|
script_channel,
|
||||||
load_data);
|
load_data);
|
||||||
|
|
|
@ -19,6 +19,7 @@ use net::resource_task::ResourceTask;
|
||||||
use net::storage_task::StorageTask;
|
use net::storage_task::StorageTask;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::geometry::{PagePx};
|
use util::geometry::{PagePx};
|
||||||
|
use util::memory::MemoryProfilerChan;
|
||||||
use util::time::TimeProfilerChan;
|
use util::time::TimeProfilerChan;
|
||||||
use std::sync::mpsc::{Receiver, channel};
|
use std::sync::mpsc::{Receiver, channel};
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ impl Pipeline {
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
storage_task: StorageTask,
|
storage_task: StorageTask,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
|
memory_profiler_chan: MemoryProfilerChan,
|
||||||
window_size: Option<WindowSizeData>,
|
window_size: Option<WindowSizeData>,
|
||||||
script_chan: Option<ScriptControlChan>,
|
script_chan: Option<ScriptControlChan>,
|
||||||
load_data: LoadData)
|
load_data: LoadData)
|
||||||
|
@ -123,6 +125,7 @@ impl Pipeline {
|
||||||
|
|
||||||
LayoutTaskFactory::create(None::<&mut LTF>,
|
LayoutTaskFactory::create(None::<&mut LTF>,
|
||||||
id,
|
id,
|
||||||
|
load_data.url.clone(),
|
||||||
layout_pair,
|
layout_pair,
|
||||||
pipeline_port,
|
pipeline_port,
|
||||||
constellation_chan,
|
constellation_chan,
|
||||||
|
@ -133,6 +136,7 @@ impl Pipeline {
|
||||||
image_cache_task,
|
image_cache_task,
|
||||||
font_cache_task,
|
font_cache_task,
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
|
memory_profiler_chan,
|
||||||
layout_shutdown_chan);
|
layout_shutdown_chan);
|
||||||
|
|
||||||
Pipeline::new(id,
|
Pipeline::new(id,
|
||||||
|
|
|
@ -37,6 +37,7 @@ use net::image::base::Image;
|
||||||
use util::cursor::Cursor;
|
use util::cursor::Cursor;
|
||||||
use util::dlist as servo_dlist;
|
use util::dlist as servo_dlist;
|
||||||
use util::geometry::{self, Au, MAX_RECT, ZERO_RECT};
|
use util::geometry::{self, Au, MAX_RECT, ZERO_RECT};
|
||||||
|
use util::memory::SizeOf;
|
||||||
use util::range::Range;
|
use util::range::Range;
|
||||||
use util::smallvec::{SmallVec, SmallVec8};
|
use util::smallvec::{SmallVec, SmallVec8};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -198,6 +199,17 @@ impl DisplayList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for DisplayList {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.background_and_borders.size_of_excluding_self() +
|
||||||
|
self.block_backgrounds_and_borders.size_of_excluding_self() +
|
||||||
|
self.floats.size_of_excluding_self() +
|
||||||
|
self.content.size_of_excluding_self() +
|
||||||
|
self.outlines.size_of_excluding_self() +
|
||||||
|
self.children.size_of_excluding_self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents one CSS stacking context, which may or may not have a hardware layer.
|
/// Represents one CSS stacking context, which may or may not have a hardware layer.
|
||||||
pub struct StackingContext {
|
pub struct StackingContext {
|
||||||
/// The display items that make up this stacking context.
|
/// The display items that make up this stacking context.
|
||||||
|
@ -501,6 +513,14 @@ impl StackingContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for StackingContext {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.display_list.size_of_excluding_self()
|
||||||
|
|
||||||
|
// FIXME(njn): other fields may be measured later, esp. `layer`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the stacking context in the given tree of stacking contexts with a specific layer ID.
|
/// Returns the stacking context in the given tree of stacking contexts with a specific layer ID.
|
||||||
pub fn find_stacking_context_with_layer_id(this: &Arc<StackingContext>, layer_id: LayerId)
|
pub fn find_stacking_context_with_layer_id(this: &Arc<StackingContext>, layer_id: LayerId)
|
||||||
-> Option<Arc<StackingContext>> {
|
-> Option<Arc<StackingContext>> {
|
||||||
|
@ -556,6 +576,13 @@ impl BaseDisplayItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for BaseDisplayItem {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.metadata.size_of_excluding_self() +
|
||||||
|
self.clip.size_of_excluding_self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A clipping region for a display item. Currently, this can describe rectangles, rounded
|
/// A clipping region for a display item. Currently, this can describe rectangles, rounded
|
||||||
/// rectangles (for `border-radius`), or arbitrary intersections of the two. Arbitrary transforms
|
/// rectangles (for `border-radius`), or arbitrary intersections of the two. Arbitrary transforms
|
||||||
/// are not supported because those are handled by the higher-level `StackingContext` abstraction.
|
/// are not supported because those are handled by the higher-level `StackingContext` abstraction.
|
||||||
|
@ -681,6 +708,18 @@ impl ClippingRegion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for ClippingRegion {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.complex.size_of_excluding_self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SizeOf for ComplexClippingRegion {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Metadata attached to each display item. This is useful for performing auxiliary tasks with
|
/// Metadata attached to each display item. This is useful for performing auxiliary tasks with
|
||||||
/// the display list involving hit testing: finding the originating DOM node and determining the
|
/// the display list involving hit testing: finding the originating DOM node and determining the
|
||||||
/// cursor to use when the element is hovered over.
|
/// cursor to use when the element is hovered over.
|
||||||
|
@ -712,6 +751,12 @@ impl DisplayItemMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for DisplayItemMetadata {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Paints a solid color.
|
/// Paints a solid color.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SolidColorDisplayItem {
|
pub struct SolidColorDisplayItem {
|
||||||
|
@ -722,6 +767,12 @@ pub struct SolidColorDisplayItem {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for SolidColorDisplayItem {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.base.size_of_excluding_self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Paints text.
|
/// Paints text.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TextDisplayItem {
|
pub struct TextDisplayItem {
|
||||||
|
@ -747,6 +798,13 @@ pub struct TextDisplayItem {
|
||||||
pub blur_radius: Au,
|
pub blur_radius: Au,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for TextDisplayItem {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.base.size_of_excluding_self()
|
||||||
|
// We exclude `text_run` because it is non-owning.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub enum TextOrientation {
|
pub enum TextOrientation {
|
||||||
Upright,
|
Upright,
|
||||||
|
@ -770,6 +828,13 @@ pub struct ImageDisplayItem {
|
||||||
pub image_rendering: image_rendering::T,
|
pub image_rendering: image_rendering::T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for ImageDisplayItem {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.base.size_of_excluding_self()
|
||||||
|
// We exclude `image` here because it is non-owning.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Paints a gradient.
|
/// Paints a gradient.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GradientDisplayItem {
|
pub struct GradientDisplayItem {
|
||||||
|
@ -786,6 +851,20 @@ pub struct GradientDisplayItem {
|
||||||
pub stops: Vec<GradientStop>,
|
pub stops: Vec<GradientStop>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for GradientDisplayItem {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
use libc::c_void;
|
||||||
|
use util::memory::heap_size_of;
|
||||||
|
|
||||||
|
// We can't measure `stops` via Vec's SizeOf implementation because GradientStop isn't
|
||||||
|
// defined in this module, and we don't want to import GradientStop into util::memory where
|
||||||
|
// the SizeOf trait is defined. So we measure the elements directly.
|
||||||
|
self.base.size_of_excluding_self() +
|
||||||
|
heap_size_of(self.stops.as_ptr() as *const c_void)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Paints a border.
|
/// Paints a border.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BorderDisplayItem {
|
pub struct BorderDisplayItem {
|
||||||
|
@ -807,6 +886,12 @@ pub struct BorderDisplayItem {
|
||||||
pub radius: BorderRadii<Au>,
|
pub radius: BorderRadii<Au>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for BorderDisplayItem {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.base.size_of_excluding_self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Information about the border radii.
|
/// Information about the border radii.
|
||||||
///
|
///
|
||||||
/// TODO(pcwalton): Elliptical radii.
|
/// TODO(pcwalton): Elliptical radii.
|
||||||
|
@ -851,6 +936,12 @@ pub struct LineDisplayItem {
|
||||||
pub style: border_style::T
|
pub style: border_style::T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for LineDisplayItem {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.base.size_of_excluding_self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Paints a box shadow per CSS-BACKGROUNDS.
|
/// Paints a box shadow per CSS-BACKGROUNDS.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BoxShadowDisplayItem {
|
pub struct BoxShadowDisplayItem {
|
||||||
|
@ -876,6 +967,12 @@ pub struct BoxShadowDisplayItem {
|
||||||
pub clip_mode: BoxShadowClipMode,
|
pub clip_mode: BoxShadowClipMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for BoxShadowDisplayItem {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
self.base.size_of_excluding_self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// How a box shadow should be clipped.
|
/// How a box shadow should be clipped.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum BoxShadowClipMode {
|
pub enum BoxShadowClipMode {
|
||||||
|
@ -1038,3 +1135,17 @@ impl fmt::Debug for DisplayItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SizeOf for DisplayItem {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
match *self {
|
||||||
|
SolidColorClass(ref item) => item.size_of_excluding_self(),
|
||||||
|
TextClass(ref item) => item.size_of_excluding_self(),
|
||||||
|
ImageClass(ref item) => item.size_of_excluding_self(),
|
||||||
|
BorderClass(ref item) => item.size_of_excluding_self(),
|
||||||
|
GradientClass(ref item) => item.size_of_excluding_self(),
|
||||||
|
LineClass(ref item) => item.size_of_excluding_self(),
|
||||||
|
BoxShadowClass(ref item) => item.size_of_excluding_self(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,8 @@ use net::resource_task::{ResourceTask, load_bytes_iter};
|
||||||
use util::cursor::Cursor;
|
use util::cursor::Cursor;
|
||||||
use util::geometry::Au;
|
use util::geometry::Au;
|
||||||
use util::logical_geometry::LogicalPoint;
|
use util::logical_geometry::LogicalPoint;
|
||||||
|
use util::memory::{MemoryProfilerChan, MemoryProfilerMsg, MemoryReport, MemoryReportsChan};
|
||||||
|
use util::memory::{SizeOf};
|
||||||
use util::opts;
|
use util::opts;
|
||||||
use util::smallvec::{SmallVec, SmallVec1, VecLike};
|
use util::smallvec::{SmallVec, SmallVec1, VecLike};
|
||||||
use util::task::spawn_named_with_send_on_failure;
|
use util::task::spawn_named_with_send_on_failure;
|
||||||
|
@ -117,6 +119,9 @@ pub struct LayoutTask {
|
||||||
/// The ID of the pipeline that we belong to.
|
/// The ID of the pipeline that we belong to.
|
||||||
pub id: PipelineId,
|
pub id: PipelineId,
|
||||||
|
|
||||||
|
/// The URL of the pipeline that we belong to.
|
||||||
|
pub url: Url,
|
||||||
|
|
||||||
/// The port on which we receive messages from the script task.
|
/// The port on which we receive messages from the script task.
|
||||||
pub port: Receiver<Msg>,
|
pub port: Receiver<Msg>,
|
||||||
|
|
||||||
|
@ -138,6 +143,12 @@ pub struct LayoutTask {
|
||||||
/// The channel on which messages can be sent to the time profiler.
|
/// The channel on which messages can be sent to the time profiler.
|
||||||
pub time_profiler_chan: TimeProfilerChan,
|
pub time_profiler_chan: TimeProfilerChan,
|
||||||
|
|
||||||
|
/// The channel on which messages can be sent to the memory profiler.
|
||||||
|
pub memory_profiler_chan: MemoryProfilerChan,
|
||||||
|
|
||||||
|
/// The name used for the task's memory reporter.
|
||||||
|
pub memory_reporter_name: String,
|
||||||
|
|
||||||
/// The channel on which messages can be sent to the resource task.
|
/// The channel on which messages can be sent to the resource task.
|
||||||
pub resource_task: ResourceTask,
|
pub resource_task: ResourceTask,
|
||||||
|
|
||||||
|
@ -181,6 +192,7 @@ impl LayoutTaskFactory for LayoutTask {
|
||||||
/// Spawns a new layout task.
|
/// Spawns a new layout task.
|
||||||
fn create(_phantom: Option<&mut LayoutTask>,
|
fn create(_phantom: Option<&mut LayoutTask>,
|
||||||
id: PipelineId,
|
id: PipelineId,
|
||||||
|
url: Url,
|
||||||
chan: OpaqueScriptLayoutChannel,
|
chan: OpaqueScriptLayoutChannel,
|
||||||
pipeline_port: Receiver<LayoutControlMsg>,
|
pipeline_port: Receiver<LayoutControlMsg>,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
|
@ -191,6 +203,7 @@ impl LayoutTaskFactory for LayoutTask {
|
||||||
img_cache_task: ImageCacheTask,
|
img_cache_task: ImageCacheTask,
|
||||||
font_cache_task: FontCacheTask,
|
font_cache_task: FontCacheTask,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
|
memory_profiler_chan: MemoryProfilerChan,
|
||||||
shutdown_chan: Sender<()>) {
|
shutdown_chan: Sender<()>) {
|
||||||
let ConstellationChan(con_chan) = constellation_chan.clone();
|
let ConstellationChan(con_chan) = constellation_chan.clone();
|
||||||
spawn_named_with_send_on_failure("LayoutTask", task_state::LAYOUT, move || {
|
spawn_named_with_send_on_failure("LayoutTask", task_state::LAYOUT, move || {
|
||||||
|
@ -199,6 +212,7 @@ impl LayoutTaskFactory for LayoutTask {
|
||||||
let layout =
|
let layout =
|
||||||
LayoutTask::new(
|
LayoutTask::new(
|
||||||
id,
|
id,
|
||||||
|
url,
|
||||||
chan.receiver(),
|
chan.receiver(),
|
||||||
LayoutChan(sender),
|
LayoutChan(sender),
|
||||||
pipeline_port,
|
pipeline_port,
|
||||||
|
@ -208,7 +222,8 @@ impl LayoutTaskFactory for LayoutTask {
|
||||||
resource_task,
|
resource_task,
|
||||||
img_cache_task,
|
img_cache_task,
|
||||||
font_cache_task,
|
font_cache_task,
|
||||||
time_profiler_chan);
|
time_profiler_chan,
|
||||||
|
memory_profiler_chan);
|
||||||
layout.start();
|
layout.start();
|
||||||
}
|
}
|
||||||
shutdown_chan.send(()).unwrap();
|
shutdown_chan.send(()).unwrap();
|
||||||
|
@ -249,6 +264,7 @@ impl<'a> DerefMut for RWGuard<'a> {
|
||||||
impl LayoutTask {
|
impl LayoutTask {
|
||||||
/// Creates a new `LayoutTask` structure.
|
/// Creates a new `LayoutTask` structure.
|
||||||
fn new(id: PipelineId,
|
fn new(id: PipelineId,
|
||||||
|
url: Url,
|
||||||
port: Receiver<Msg>,
|
port: Receiver<Msg>,
|
||||||
chan: LayoutChan,
|
chan: LayoutChan,
|
||||||
pipeline_port: Receiver<LayoutControlMsg>,
|
pipeline_port: Receiver<LayoutControlMsg>,
|
||||||
|
@ -258,7 +274,8 @@ impl LayoutTask {
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
font_cache_task: FontCacheTask,
|
font_cache_task: FontCacheTask,
|
||||||
time_profiler_chan: TimeProfilerChan)
|
time_profiler_chan: TimeProfilerChan,
|
||||||
|
memory_profiler_chan: MemoryProfilerChan)
|
||||||
-> LayoutTask {
|
-> LayoutTask {
|
||||||
let local_image_cache =
|
let local_image_cache =
|
||||||
Arc::new(Mutex::new(LocalImageCache::new(image_cache_task.clone())));
|
Arc::new(Mutex::new(LocalImageCache::new(image_cache_task.clone())));
|
||||||
|
@ -271,8 +288,15 @@ impl LayoutTask {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Register this thread as a memory reporter, via its own channel.
|
||||||
|
let reporter = Box::new(chan.clone());
|
||||||
|
let reporter_name = format!("layout-reporter-{}", id.0);
|
||||||
|
memory_profiler_chan.send(MemoryProfilerMsg::RegisterMemoryReporter(reporter_name.clone(),
|
||||||
|
reporter));
|
||||||
|
|
||||||
LayoutTask {
|
LayoutTask {
|
||||||
id: id,
|
id: id,
|
||||||
|
url: url,
|
||||||
port: port,
|
port: port,
|
||||||
pipeline_port: pipeline_port,
|
pipeline_port: pipeline_port,
|
||||||
chan: chan,
|
chan: chan,
|
||||||
|
@ -280,6 +304,8 @@ impl LayoutTask {
|
||||||
constellation_chan: constellation_chan.clone(),
|
constellation_chan: constellation_chan.clone(),
|
||||||
paint_chan: paint_chan,
|
paint_chan: paint_chan,
|
||||||
time_profiler_chan: time_profiler_chan,
|
time_profiler_chan: time_profiler_chan,
|
||||||
|
memory_profiler_chan: memory_profiler_chan,
|
||||||
|
memory_reporter_name: reporter_name,
|
||||||
resource_task: resource_task,
|
resource_task: resource_task,
|
||||||
image_cache_task: image_cache_task.clone(),
|
image_cache_task: image_cache_task.clone(),
|
||||||
font_cache_task: font_cache_task,
|
font_cache_task: font_cache_task,
|
||||||
|
@ -361,13 +387,13 @@ impl LayoutTask {
|
||||||
PortToRead::Pipeline => {
|
PortToRead::Pipeline => {
|
||||||
match self.pipeline_port.recv().unwrap() {
|
match self.pipeline_port.recv().unwrap() {
|
||||||
LayoutControlMsg::ExitNowMsg(exit_type) => {
|
LayoutControlMsg::ExitNowMsg(exit_type) => {
|
||||||
self.handle_script_request(Msg::ExitNow(exit_type), possibly_locked_rw_data)
|
self.handle_request_helper(Msg::ExitNow(exit_type), possibly_locked_rw_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PortToRead::Script => {
|
PortToRead::Script => {
|
||||||
let msg = self.port.recv().unwrap();
|
let msg = self.port.recv().unwrap();
|
||||||
self.handle_script_request(msg, possibly_locked_rw_data)
|
self.handle_request_helper(msg, possibly_locked_rw_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,8 +424,8 @@ impl LayoutTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receives and dispatches messages from the script task.
|
/// Receives and dispatches messages from other tasks.
|
||||||
fn handle_script_request<'a>(&'a self,
|
fn handle_request_helper<'a>(&'a self,
|
||||||
request: Msg,
|
request: Msg,
|
||||||
possibly_locked_rw_data: &mut Option<MutexGuard<'a,
|
possibly_locked_rw_data: &mut Option<MutexGuard<'a,
|
||||||
LayoutTaskData>>)
|
LayoutTaskData>>)
|
||||||
|
@ -423,6 +449,9 @@ impl LayoutTask {
|
||||||
self.handle_reap_layout_data(dead_layout_data)
|
self.handle_reap_layout_data(dead_layout_data)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Msg::CollectMemoryReports(reports_chan) => {
|
||||||
|
self.collect_memory_reports(reports_chan, possibly_locked_rw_data);
|
||||||
|
},
|
||||||
Msg::PrepareToExit(response_chan) => {
|
Msg::PrepareToExit(response_chan) => {
|
||||||
debug!("layout: PrepareToExitMsg received");
|
debug!("layout: PrepareToExitMsg received");
|
||||||
self.prepare_to_exit(response_chan, possibly_locked_rw_data);
|
self.prepare_to_exit(response_chan, possibly_locked_rw_data);
|
||||||
|
@ -438,6 +467,23 @@ impl LayoutTask {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_memory_reports<'a>(&'a self,
|
||||||
|
reports_chan: MemoryReportsChan,
|
||||||
|
possibly_locked_rw_data:
|
||||||
|
&mut Option<MutexGuard<'a, LayoutTaskData>>) {
|
||||||
|
let mut reports = vec![];
|
||||||
|
|
||||||
|
// FIXME(njn): Just measuring the display tree for now.
|
||||||
|
let rw_data = self.lock_rw_data(possibly_locked_rw_data);
|
||||||
|
let stacking_context = rw_data.stacking_context.as_ref();
|
||||||
|
reports.push(MemoryReport {
|
||||||
|
name: format!("display-list::{}", self.url),
|
||||||
|
size: stacking_context.map_or(0, |sc| sc.size_of_excluding_self() as u64),
|
||||||
|
});
|
||||||
|
|
||||||
|
reports_chan.send(reports);
|
||||||
|
}
|
||||||
|
|
||||||
/// Enters a quiescent state in which no new messages except for `layout_interface::Msg::ReapLayoutData` will be
|
/// Enters a quiescent state in which no new messages except for `layout_interface::Msg::ReapLayoutData` will be
|
||||||
/// processed until an `ExitNowMsg` is received. A pong is immediately sent on the given
|
/// processed until an `ExitNowMsg` is received. A pong is immediately sent on the given
|
||||||
/// response channel.
|
/// response channel.
|
||||||
|
@ -481,6 +527,10 @@ impl LayoutTask {
|
||||||
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
|
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let unregister_msg =
|
||||||
|
MemoryProfilerMsg::UnregisterMemoryReporter(self.memory_reporter_name.clone());
|
||||||
|
self.memory_profiler_chan.send(unregister_msg);
|
||||||
|
|
||||||
self.paint_chan.send(PaintMsg::Exit(Some(response_chan), exit_type));
|
self.paint_chan.send(PaintMsg::Exit(Some(response_chan), exit_type));
|
||||||
response_port.recv().unwrap()
|
response_port.recv().unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,3 +21,6 @@ path = "../net"
|
||||||
|
|
||||||
[dependencies.util]
|
[dependencies.util]
|
||||||
path = "../util"
|
path = "../util"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
url = "0.2.16"
|
||||||
|
|
|
@ -8,6 +8,7 @@ extern crate gfx;
|
||||||
extern crate script_traits;
|
extern crate script_traits;
|
||||||
extern crate msg;
|
extern crate msg;
|
||||||
extern crate net;
|
extern crate net;
|
||||||
|
extern crate url;
|
||||||
extern crate util;
|
extern crate util;
|
||||||
|
|
||||||
// This module contains traits in layout used generically
|
// This module contains traits in layout used generically
|
||||||
|
@ -20,6 +21,8 @@ use gfx::paint_task::PaintChan;
|
||||||
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId, PipelineExitType};
|
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId, PipelineExitType};
|
||||||
use net::image_cache_task::ImageCacheTask;
|
use net::image_cache_task::ImageCacheTask;
|
||||||
use net::resource_task::ResourceTask;
|
use net::resource_task::ResourceTask;
|
||||||
|
use url::Url;
|
||||||
|
use util::memory::MemoryProfilerChan;
|
||||||
use util::time::TimeProfilerChan;
|
use util::time::TimeProfilerChan;
|
||||||
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel};
|
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel};
|
||||||
use std::sync::mpsc::{Sender, Receiver};
|
use std::sync::mpsc::{Sender, Receiver};
|
||||||
|
@ -38,6 +41,7 @@ pub trait LayoutTaskFactory {
|
||||||
// FIXME: use a proper static method
|
// FIXME: use a proper static method
|
||||||
fn create(_phantom: Option<&mut Self>,
|
fn create(_phantom: Option<&mut Self>,
|
||||||
id: PipelineId,
|
id: PipelineId,
|
||||||
|
url: Url,
|
||||||
chan: OpaqueScriptLayoutChannel,
|
chan: OpaqueScriptLayoutChannel,
|
||||||
pipeline_port: Receiver<LayoutControlMsg>,
|
pipeline_port: Receiver<LayoutControlMsg>,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
|
@ -48,5 +52,6 @@ pub trait LayoutTaskFactory {
|
||||||
img_cache_task: ImageCacheTask,
|
img_cache_task: ImageCacheTask,
|
||||||
font_cache_task: FontCacheTask,
|
font_cache_task: FontCacheTask,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
|
memory_profiler_chan: MemoryProfilerChan,
|
||||||
shutdown_chan: Sender<()>);
|
shutdown_chan: Sender<()>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ use geom::rect::Rect;
|
||||||
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress};
|
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress};
|
||||||
use msg::constellation_msg::{PipelineExitType, WindowSizeData};
|
use msg::constellation_msg::{PipelineExitType, WindowSizeData};
|
||||||
use util::geometry::Au;
|
use util::geometry::Au;
|
||||||
|
use util::memory::{MemoryReporter, MemoryReportsChan};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
use std::boxed::BoxAny;
|
use std::boxed::BoxAny;
|
||||||
|
@ -44,6 +45,10 @@ pub enum Msg {
|
||||||
/// TODO(pcwalton): Maybe think about batching to avoid message traffic.
|
/// TODO(pcwalton): Maybe think about batching to avoid message traffic.
|
||||||
ReapLayoutData(LayoutData),
|
ReapLayoutData(LayoutData),
|
||||||
|
|
||||||
|
/// Requests that the layout task measure its memory usage. The resulting reports are sent back
|
||||||
|
/// via the supplied channel.
|
||||||
|
CollectMemoryReports(MemoryReportsChan),
|
||||||
|
|
||||||
/// Requests that the layout task enter a quiescent state in which no more messages are
|
/// Requests that the layout task enter a quiescent state in which no more messages are
|
||||||
/// accepted except `ExitMsg`. A response message will be sent on the supplied channel when
|
/// accepted except `ExitMsg`. A response message will be sent on the supplied channel when
|
||||||
/// this happens.
|
/// this happens.
|
||||||
|
@ -128,6 +133,14 @@ impl LayoutChan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MemoryReporter for LayoutChan {
|
||||||
|
// Just injects an appropriate event into the layout task's queue.
|
||||||
|
fn collect_reports(&self, reports_chan: MemoryReportsChan) -> bool {
|
||||||
|
let LayoutChan(ref c) = *self;
|
||||||
|
c.send(Msg::CollectMemoryReports(reports_chan)).is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A trait to manage opaque references to script<->layout channels without needing
|
/// A trait to manage opaque references to script<->layout channels without needing
|
||||||
/// to expose the message type to crates that don't need to know about them.
|
/// to expose the message type to crates that don't need to know about them.
|
||||||
pub trait ScriptLayoutChan {
|
pub trait ScriptLayoutChan {
|
||||||
|
|
|
@ -534,6 +534,7 @@ dependencies = [
|
||||||
"msg 0.0.1",
|
"msg 0.0.1",
|
||||||
"net 0.0.1",
|
"net 0.0.1",
|
||||||
"script_traits 0.0.1",
|
"script_traits 0.0.1",
|
||||||
|
"url 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"util 0.0.1",
|
"util 0.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,7 @@ impl<Window> Browser<Window> where Window: WindowMethods + 'static {
|
||||||
|
|
||||||
let opts_clone = opts.clone();
|
let opts_clone = opts.clone();
|
||||||
let time_profiler_chan_clone = time_profiler_chan.clone();
|
let time_profiler_chan_clone = time_profiler_chan.clone();
|
||||||
|
let memory_profiler_chan_clone = memory_profiler_chan.clone();
|
||||||
|
|
||||||
let (result_chan, result_port) = channel();
|
let (result_chan, result_port) = channel();
|
||||||
let compositor_proxy_for_constellation = compositor_proxy.clone_compositor_proxy();
|
let compositor_proxy_for_constellation = compositor_proxy.clone_compositor_proxy();
|
||||||
|
@ -109,6 +110,7 @@ impl<Window> Browser<Window> where Window: WindowMethods + 'static {
|
||||||
image_cache_task,
|
image_cache_task,
|
||||||
font_cache_task,
|
font_cache_task,
|
||||||
time_profiler_chan_clone,
|
time_profiler_chan_clone,
|
||||||
|
memory_profiler_chan_clone,
|
||||||
devtools_chan,
|
devtools_chan,
|
||||||
storage_task);
|
storage_task);
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,175 @@
|
||||||
|
|
||||||
use libc::{c_char,c_int,c_void,size_t};
|
use libc::{c_char,c_int,c_void,size_t};
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
use std::collections::{DList, HashMap};
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use std::iter::AdditiveIterator;
|
use std::iter::AdditiveIterator;
|
||||||
use std::old_io::timer::sleep;
|
use std::old_io::timer::sleep;
|
||||||
#[cfg(target_os="linux")]
|
#[cfg(target_os="linux")]
|
||||||
use std::old_io::File;
|
use std::old_io::File;
|
||||||
use std::mem::size_of;
|
use std::mem::{size_of, transmute};
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::{Sender, channel, Receiver};
|
use std::sync::mpsc::{Sender, channel, Receiver};
|
||||||
use std::time::duration::Duration;
|
use std::time::duration::Duration;
|
||||||
use task::spawn_named;
|
use task::spawn_named;
|
||||||
#[cfg(target_os="macos")]
|
#[cfg(target_os="macos")]
|
||||||
use task_info::task_basic_info::{virtual_size,resident_size};
|
use task_info::task_basic_info::{virtual_size,resident_size};
|
||||||
|
|
||||||
|
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 DList 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 DList into an identical type (DList2) and
|
||||||
|
// measuring that.
|
||||||
|
impl<T: SizeOf> SizeOf for DList<T> {
|
||||||
|
fn size_of_excluding_self(&self) -> usize {
|
||||||
|
let list2: &DList2<T> = unsafe { transmute(self) };
|
||||||
|
list2.size_of_excluding_self()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DList2<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, DList2<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 DList2<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 DList changes such that it becomes a
|
||||||
|
// different size to DList2, this will fail at compile-time.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
unsafe fn dlist2_check() {
|
||||||
|
transmute::<DList<i32>, DList2<i32>>(panic!());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently, types that implement the Drop type are larger than those that don't. Because DList
|
||||||
|
// implements Drop, DList2 must also so that dlist2_check() doesn't fail.
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<T> Drop for DList2<T> {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct MemoryProfilerChan(pub Sender<MemoryProfilerMsg>);
|
pub struct MemoryProfilerChan(pub Sender<MemoryProfilerMsg>);
|
||||||
|
|
||||||
impl MemoryProfilerChan {
|
impl MemoryProfilerChan {
|
||||||
|
@ -29,62 +184,98 @@ impl MemoryProfilerChan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct MemoryReport {
|
||||||
|
/// The identifying name for this report.
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// The size, in bytes.
|
||||||
|
pub size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A channel through which memory reports can be sent.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MemoryReportsChan(pub Sender<Vec<MemoryReport>>);
|
||||||
|
|
||||||
|
impl MemoryReportsChan {
|
||||||
|
pub fn send(&self, report: Vec<MemoryReport>) {
|
||||||
|
let MemoryReportsChan(ref c) = *self;
|
||||||
|
c.send(report).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A memory reporter is capable of measuring some data structure of interest. Because it needs
|
||||||
|
/// to be passed to and registered with the MemoryProfiler, it's typically a "small" (i.e. easily
|
||||||
|
/// cloneable) value that provides access to a "large" data structure, e.g. a channel that can
|
||||||
|
/// inject a request for measurements into the event queue associated with the "large" data
|
||||||
|
/// structure.
|
||||||
|
pub trait MemoryReporter {
|
||||||
|
/// Collect one or more memory reports. Returns true on success, and false on failure.
|
||||||
|
fn collect_reports(&self, reports_chan: MemoryReportsChan) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Messages that can be sent to the memory profiler thread.
|
||||||
pub enum MemoryProfilerMsg {
|
pub enum MemoryProfilerMsg {
|
||||||
/// Message used to force print the memory profiling metrics.
|
/// Register a MemoryReporter with the memory profiler. The String is only used to identify the
|
||||||
|
/// reporter so it can be unregistered later. The String must be distinct from that used by any
|
||||||
|
/// other registered reporter otherwise a panic will occur.
|
||||||
|
RegisterMemoryReporter(String, Box<MemoryReporter + Send>),
|
||||||
|
|
||||||
|
/// Unregister a MemoryReporter with the memory profiler. The String must match the name given
|
||||||
|
/// when the reporter was registered. If the String does not match the name of a registered
|
||||||
|
/// reporter a panic will occur.
|
||||||
|
UnregisterMemoryReporter(String),
|
||||||
|
|
||||||
|
/// Triggers printing of the memory profiling metrics.
|
||||||
Print,
|
Print,
|
||||||
|
|
||||||
/// Tells the memory profiler to shut down.
|
/// Tells the memory profiler to shut down.
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MemoryProfiler {
|
pub struct MemoryProfiler {
|
||||||
|
/// The port through which messages are received.
|
||||||
pub port: Receiver<MemoryProfilerMsg>,
|
pub port: Receiver<MemoryProfilerMsg>,
|
||||||
|
|
||||||
|
/// Registered memory reporters.
|
||||||
|
reporters: HashMap<String, Box<MemoryReporter + Send>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemoryProfiler {
|
impl MemoryProfiler {
|
||||||
pub fn create(period: Option<f64>) -> MemoryProfilerChan {
|
pub fn create(period: Option<f64>) -> MemoryProfilerChan {
|
||||||
let (chan, port) = channel();
|
let (chan, port) = channel();
|
||||||
match period {
|
|
||||||
Some(period) => {
|
// Create the timer thread if a period was provided.
|
||||||
let period = Duration::milliseconds((period * 1000f64) as i64);
|
if let Some(period) = period {
|
||||||
let chan = chan.clone();
|
let period_ms = Duration::milliseconds((period * 1000f64) as i64);
|
||||||
spawn_named("Memory profiler timer".to_owned(), move || {
|
let chan = chan.clone();
|
||||||
loop {
|
spawn_named("Memory profiler timer".to_owned(), move || {
|
||||||
sleep(period);
|
loop {
|
||||||
if chan.send(MemoryProfilerMsg::Print).is_err() {
|
sleep(period_ms);
|
||||||
break;
|
if chan.send(MemoryProfilerMsg::Print).is_err() {
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
// Spawn the memory profiler.
|
});
|
||||||
spawn_named("Memory profiler".to_owned(), move || {
|
|
||||||
let memory_profiler = MemoryProfiler::new(port);
|
|
||||||
memory_profiler.start();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// No-op to handle messages when the memory profiler is
|
|
||||||
// inactive.
|
|
||||||
spawn_named("Memory profiler".to_owned(), move || {
|
|
||||||
loop {
|
|
||||||
match port.recv() {
|
|
||||||
Err(_) | Ok(MemoryProfilerMsg::Exit) => break,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always spawn the memory profiler. If there is no timer thread it won't receive regular
|
||||||
|
// `Print` events, but it will still receive the other events.
|
||||||
|
spawn_named("Memory profiler".to_owned(), move || {
|
||||||
|
let mut memory_profiler = MemoryProfiler::new(port);
|
||||||
|
memory_profiler.start();
|
||||||
|
});
|
||||||
|
|
||||||
MemoryProfilerChan(chan)
|
MemoryProfilerChan(chan)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(port: Receiver<MemoryProfilerMsg>) -> MemoryProfiler {
|
pub fn new(port: Receiver<MemoryProfilerMsg>) -> MemoryProfiler {
|
||||||
MemoryProfiler {
|
MemoryProfiler {
|
||||||
port: port
|
port: port,
|
||||||
|
reporters: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&self) {
|
pub fn start(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
match self.port.recv() {
|
match self.port.recv() {
|
||||||
Ok(msg) => {
|
Ok(msg) => {
|
||||||
|
@ -97,12 +288,33 @@ impl MemoryProfiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_msg(&self, msg: MemoryProfilerMsg) -> bool {
|
fn handle_msg(&mut self, msg: MemoryProfilerMsg) -> bool {
|
||||||
match msg {
|
match msg {
|
||||||
|
MemoryProfilerMsg::RegisterMemoryReporter(name, reporter) => {
|
||||||
|
// Panic if it has already been registered.
|
||||||
|
let name_clone = name.clone();
|
||||||
|
match self.reporters.insert(name, reporter) {
|
||||||
|
None => true,
|
||||||
|
Some(_) =>
|
||||||
|
panic!(format!("RegisterMemoryReporter: '{}' name is already in use",
|
||||||
|
name_clone)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MemoryProfilerMsg::UnregisterMemoryReporter(name) => {
|
||||||
|
// Panic if it hasn't previously been registered.
|
||||||
|
match self.reporters.remove(&name) {
|
||||||
|
Some(_) => true,
|
||||||
|
None =>
|
||||||
|
panic!(format!("UnregisterMemoryReporter: '{}' name is unknown", &name)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
MemoryProfilerMsg::Print => {
|
MemoryProfilerMsg::Print => {
|
||||||
self.handle_print_msg();
|
self.handle_print_msg();
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
|
|
||||||
MemoryProfilerMsg::Exit => false
|
MemoryProfilerMsg::Exit => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,8 +332,11 @@ impl MemoryProfiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_print_msg(&self) {
|
fn handle_print_msg(&self) {
|
||||||
|
|
||||||
println!("{:12}: {}", "_size (MiB)_", "_category_");
|
println!("{:12}: {}", "_size (MiB)_", "_category_");
|
||||||
|
|
||||||
|
// Collect global measurements from the OS and heap allocators.
|
||||||
|
|
||||||
// Virtual and physical memory usage, as reported by the OS.
|
// Virtual and physical memory usage, as reported by the OS.
|
||||||
MemoryProfiler::print_measurement("vsize", get_vsize());
|
MemoryProfiler::print_measurement("vsize", get_vsize());
|
||||||
MemoryProfiler::print_measurement("resident", get_resident());
|
MemoryProfiler::print_measurement("resident", get_resident());
|
||||||
|
@ -154,6 +369,24 @@ impl MemoryProfiler {
|
||||||
MemoryProfiler::print_measurement("jemalloc-heap-mapped",
|
MemoryProfiler::print_measurement("jemalloc-heap-mapped",
|
||||||
get_jemalloc_stat("stats.mapped"));
|
get_jemalloc_stat("stats.mapped"));
|
||||||
|
|
||||||
|
// Collect reports from memory reporters.
|
||||||
|
|
||||||
|
// This serializes the report-gathering. It might be worth creating a new scoped thread for
|
||||||
|
// each reporter once we have enough of them.
|
||||||
|
//
|
||||||
|
// If anything goes wrong with a reporter, we just skip it.
|
||||||
|
for reporter in self.reporters.values() {
|
||||||
|
let (chan, port) = channel();
|
||||||
|
if reporter.collect_reports(MemoryReportsChan(chan)) {
|
||||||
|
if let Ok(reports) = port.recv() {
|
||||||
|
for report in reports {
|
||||||
|
MemoryProfiler::print_measurement(report.name.as_slice(),
|
||||||
|
Some(report.size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!("");
|
println!("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -542,6 +542,7 @@ dependencies = [
|
||||||
"msg 0.0.1",
|
"msg 0.0.1",
|
||||||
"net 0.0.1",
|
"net 0.0.1",
|
||||||
"script_traits 0.0.1",
|
"script_traits 0.0.1",
|
||||||
|
"url 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"util 0.0.1",
|
"util 0.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -453,6 +453,7 @@ dependencies = [
|
||||||
"msg 0.0.1",
|
"msg 0.0.1",
|
||||||
"net 0.0.1",
|
"net 0.0.1",
|
||||||
"script_traits 0.0.1",
|
"script_traits 0.0.1",
|
||||||
|
"url 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"util 0.0.1",
|
"util 0.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ impl<Window> Browser<Window> where Window: WindowMethods + 'static {
|
||||||
|
|
||||||
let opts_clone = opts.clone();
|
let opts_clone = opts.clone();
|
||||||
let time_profiler_chan_clone = time_profiler_chan.clone();
|
let time_profiler_chan_clone = time_profiler_chan.clone();
|
||||||
|
let memory_profiler_chan_clone = memory_profiler_chan.clone();
|
||||||
|
|
||||||
let (result_chan, result_port) = channel();
|
let (result_chan, result_port) = channel();
|
||||||
let compositor_proxy_for_constellation = compositor_proxy.clone_compositor_proxy();
|
let compositor_proxy_for_constellation = compositor_proxy.clone_compositor_proxy();
|
||||||
|
@ -113,6 +114,7 @@ impl<Window> Browser<Window> where Window: WindowMethods + 'static {
|
||||||
image_cache_task,
|
image_cache_task,
|
||||||
font_cache_task,
|
font_cache_task,
|
||||||
time_profiler_chan_clone,
|
time_profiler_chan_clone,
|
||||||
|
memory_profiler_chan_clone,
|
||||||
devtools_chan,
|
devtools_chan,
|
||||||
storage_task);
|
storage_task);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче