Skip to content

Commit

Permalink
Wire up the JS engine's memory reporting.
Browse files Browse the repository at this point in the history
SpiderMonkey provides an extremely fine-grained breakdown of memory
usage, but for Servo we aggregate the measurements into a small number
of coarse buckets, which seems appropriate for the current level of
detail provided by Servo's memory profiler. Sample output:
```
|   10.99 MiB -- pages
|       7.75 MiB -- url(http://html5demos.com/worker)
|          4.63 MiB -- js
|             2.00 MiB -- gc-heap
|                0.94 MiB -- decommitted
|                0.92 MiB -- used
|                0.09 MiB -- unused
|                0.05 MiB -- admin
|             1.44 MiB -- malloc-heap
|             1.19 MiB -- non-heap
|          [...]
|       3.24 MiB -- url(http://html5demos.com/js/worker-cruncher.js)
|          3.24 MiB -- js
|             1.17 MiB -- malloc-heap
|             1.06 MiB -- non-heap
|             1.00 MiB -- gc-heap
|                0.69 MiB -- used
|                0.19 MiB -- decommitted
|                0.09 MiB -- unused
|                0.03 MiB -- admin
```
Most of the changes are plumbing to get the script and worker tasks
communicating with the memory profiler task.
  • Loading branch information
nnethercote committed Jul 16, 2015
1 parent ef97152 commit 7429b90
Show file tree
Hide file tree
Showing 15 changed files with 177 additions and 21 deletions.
1 change: 1 addition & 0 deletions components/compositing/pipeline.rs
Expand Up @@ -318,6 +318,7 @@ impl PipelineContent {
self.resource_task,
self.storage_task.clone(),
self.image_cache_task.clone(),
self.mem_profiler_chan.clone(),
self.devtools_chan,
self.window_size,
self.load_data.clone());
Expand Down
11 changes: 10 additions & 1 deletion components/script/dom/bindings/global.rs
Expand Up @@ -19,6 +19,7 @@ use script_task::{ScriptChan, ScriptPort, ScriptMsg, ScriptTask};

use msg::constellation_msg::{PipelineId, WorkerId};
use net_traits::ResourceTask;
use profile_traits::mem;

use js::{JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS};
use js::jsapi::{GetGlobalForObjectCrossCompartment};
Expand Down Expand Up @@ -82,7 +83,15 @@ impl<'a> GlobalRef<'a> {
}
}

/// Get `DevtoolsControlChan` to send messages to Devtools
/// Get a `mem::ProfilerChan` to send messages to the memory profiler task.
pub fn mem_profiler_chan(&self) -> mem::ProfilerChan {
match *self {
GlobalRef::Window(window) => window.mem_profiler_chan(),
GlobalRef::Worker(worker) => worker.mem_profiler_chan(),
}
}

/// Get a `DevtoolsControlChan` to send messages to Devtools
/// task when available.
pub fn devtools_chan(&self) -> Option<DevtoolsControlChan> {
match *self {
Expand Down
2 changes: 2 additions & 0 deletions components/script/dom/bindings/trace.rs
Expand Up @@ -60,6 +60,7 @@ use smallvec::SmallVec1;
use msg::compositor_msg::ScriptListener;
use msg::constellation_msg::ConstellationChan;
use net_traits::image::base::Image;
use profile_traits::mem::ProfilerChan;
use util::str::{LengthOrPercentageOrAuto};
use std::cell::{Cell, UnsafeCell, RefCell};
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -299,6 +300,7 @@ no_jsmanaged_fields!(CanvasGradientStop, LinearGradientStyle, RadialGradientStyl
no_jsmanaged_fields!(LineCapStyle, LineJoinStyle, CompositionOrBlending);
no_jsmanaged_fields!(RepetitionStyle);
no_jsmanaged_fields!(WebGLError);
no_jsmanaged_fields!(ProfilerChan);

impl JSTraceable for Box<ScriptChan+Send> {
#[inline]
Expand Down
48 changes: 45 additions & 3 deletions components/script/dom/dedicatedworkerglobalscope.rs
Expand Up @@ -29,6 +29,7 @@ use msg::constellation_msg::PipelineId;
use devtools_traits::DevtoolsControlChan;

use net_traits::{load_whole_resource, ResourceTask};
use profile_traits::mem::{self, Reporter, ReportsChan};
use util::task::spawn_named;
use util::task_state;
use util::task_state::{SCRIPT, IN_WORKER};
Expand All @@ -39,6 +40,7 @@ use js::jsval::UndefinedValue;
use js::rust::Runtime;
use url::Url;

use rand::random;
use std::rc::Rc;
use std::sync::mpsc::{Sender, Receiver, channel};

Expand All @@ -64,6 +66,13 @@ impl ScriptChan for SendableWorkerScriptChan {
}
}

impl Reporter for SendableWorkerScriptChan {
// Just injects an appropriate event into the worker task's queue.
fn collect_reports(&self, reports_chan: ReportsChan) -> bool {
self.send(ScriptMsg::CollectReports(reports_chan)).is_ok()
}
}

/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
Expand Down Expand Up @@ -105,6 +114,7 @@ pub struct DedicatedWorkerGlobalScope {
impl DedicatedWorkerGlobalScope {
fn new_inherited(worker_url: Url,
id: PipelineId,
mem_profiler_chan: mem::ProfilerChan,
devtools_chan: Option<DevtoolsControlChan>,
runtime: Rc<Runtime>,
resource_task: ResourceTask,
Expand All @@ -115,7 +125,7 @@ impl DedicatedWorkerGlobalScope {
DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(
WorkerGlobalScopeTypeId::DedicatedGlobalScope, worker_url,
runtime, resource_task, devtools_chan),
runtime, resource_task, mem_profiler_chan, devtools_chan),
id: id,
receiver: receiver,
own_sender: own_sender,
Expand All @@ -126,6 +136,7 @@ impl DedicatedWorkerGlobalScope {

pub fn new(worker_url: Url,
id: PipelineId,
mem_profiler_chan: mem::ProfilerChan,
devtools_chan: Option<DevtoolsControlChan>,
runtime: Rc<Runtime>,
resource_task: ResourceTask,
Expand All @@ -134,7 +145,7 @@ impl DedicatedWorkerGlobalScope {
receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
-> Root<DedicatedWorkerGlobalScope> {
let scope = box DedicatedWorkerGlobalScope::new_inherited(
worker_url, id, devtools_chan, runtime.clone(), resource_task,
worker_url, id, mem_profiler_chan, devtools_chan, runtime.clone(), resource_task,
parent_sender, own_sender, receiver);
DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
}
Expand All @@ -143,6 +154,7 @@ impl DedicatedWorkerGlobalScope {
impl DedicatedWorkerGlobalScope {
pub fn run_worker_scope(worker_url: Url,
id: PipelineId,
mem_profiler_chan: mem::ProfilerChan,
devtools_chan: Option<DevtoolsControlChan>,
worker: TrustedWorkerAddress,
resource_task: ResourceTask,
Expand Down Expand Up @@ -171,8 +183,12 @@ impl DedicatedWorkerGlobalScope {
let runtime = Rc::new(ScriptTask::new_rt_and_cx());
let serialized_url = url.serialize();
let global = DedicatedWorkerGlobalScope::new(
url, id, devtools_chan, runtime.clone(), resource_task,
url, id, mem_profiler_chan.clone(), devtools_chan, runtime.clone(), resource_task,
parent_sender, own_sender, receiver);
// FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
// registration (#6631), so we instead use a random number and cross our fingers.
let reporter_name = format!("worker-reporter-{}", random::<u64>());
println!("reporter_name = {}", reporter_name);

{
let _ar = AutoWorkerReset::new(global.r(), worker);
Expand All @@ -188,6 +204,12 @@ impl DedicatedWorkerGlobalScope {
report_pending_exception(runtime.cx(), global.r().reflector().get_jsobject().get());
}
}

// Register this task as a memory reporter. This needs to be done within the
// scope of `_ar` otherwise script_chan_as_reporter() will panic.
let reporter = global.script_chan_as_reporter();
let msg = mem::ProfilerMsg::RegisterReporter(reporter_name.clone(), reporter);
mem_profiler_chan.send(msg);
}

loop {
Expand All @@ -199,12 +221,17 @@ impl DedicatedWorkerGlobalScope {
Err(_) => break,
}
}

// Unregister this task as a memory reporter.
let msg = mem::ProfilerMsg::UnregisterReporter(reporter_name);
mem_profiler_chan.send(msg);
});
}
}

pub trait DedicatedWorkerGlobalScopeHelpers {
fn script_chan(self) -> Box<ScriptChan+Send>;
fn script_chan_as_reporter(self) -> Box<Reporter+Send>;
fn pipeline(self) -> PipelineId;
fn new_script_pair(self) -> (Box<ScriptChan+Send>, Box<ScriptPort+Send>);
fn process_event(self, msg: ScriptMsg);
Expand All @@ -218,6 +245,14 @@ impl<'a> DedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalScope {
}
}

fn script_chan_as_reporter(self) -> Box<Reporter+Send> {
box SendableWorkerScriptChan {
sender: self.own_sender.clone(),
worker: self.worker.borrow().as_ref().unwrap().clone(),
}
}


fn pipeline(self) -> PipelineId {
self.id
}
Expand Down Expand Up @@ -263,6 +298,13 @@ impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for &'a DedicatedWorkerGlobalS
let scope = WorkerGlobalScopeCast::from_ref(self);
scope.handle_fire_timer(timer_id);
}
ScriptMsg::CollectReports(reports_chan) => {
let scope = WorkerGlobalScopeCast::from_ref(self);
let cx = scope.get_cx();
let path_seg = format!("url({})", scope.get_url());
let reports = ScriptTask::get_reports(cx, path_seg);
reports_chan.send(reports);
}
_ => panic!("Unexpected message"),
}
}
Expand Down
15 changes: 13 additions & 2 deletions components/script/dom/window.rs
Expand Up @@ -45,6 +45,7 @@ use msg::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use net_traits::ResourceTask;
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask};
use net_traits::storage_task::{StorageTask, StorageType};
use profile_traits::mem;
use util::geometry::{self, Au, MAX_RECT};
use util::opts;
use util::str::{DOMString,HTML_SPACE_CHARACTERS};
Expand All @@ -64,7 +65,7 @@ use std::cell::{Cell, Ref, RefMut, RefCell};
use std::collections::HashSet;
use std::default::Default;
use std::ffi::CString;
use std::mem;
use std::mem as std_mem;
use std::rc::Rc;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::mpsc::TryRecvError::{Empty, Disconnected};
Expand Down Expand Up @@ -118,6 +119,9 @@ pub struct Window {

next_worker_id: Cell<WorkerId>,

/// For sending messages to the memory profiler.
mem_profiler_chan: mem::ProfilerChan,

/// For providing instructions to an optional devtools server.
devtools_chan: Option<DevtoolsControlChan>,
/// For sending timeline markers. Will be ignored if
Expand Down Expand Up @@ -538,6 +542,7 @@ pub trait WindowHelpers {
fn window_size(self) -> Option<WindowSizeData>;
fn get_url(self) -> Url;
fn resource_task(self) -> ResourceTask;
fn mem_profiler_chan(self) -> mem::ProfilerChan;
fn devtools_chan(self) -> Option<DevtoolsControlChan>;
fn layout_chan(self) -> LayoutChan;
fn constellation_chan(self) -> ConstellationChan;
Expand Down Expand Up @@ -721,7 +726,7 @@ impl<'a> WindowHelpers for &'a Window {
/// layout task has finished any pending request messages.
fn join_layout(self) {
let mut layout_join_port = self.layout_join_port.borrow_mut();
if let Some(join_port) = mem::replace(&mut *layout_join_port, None) {
if let Some(join_port) = std_mem::replace(&mut *layout_join_port, None) {
match join_port.try_recv() {
Err(Empty) => {
info!("script: waiting on layout");
Expand Down Expand Up @@ -823,6 +828,10 @@ impl<'a> WindowHelpers for &'a Window {
self.resource_task.clone()
}

fn mem_profiler_chan(self) -> mem::ProfilerChan {
self.mem_profiler_chan.clone()
}

fn devtools_chan(self) -> Option<DevtoolsControlChan> {
self.devtools_chan.clone()
}
Expand Down Expand Up @@ -968,6 +977,7 @@ impl Window {
image_cache_task: ImageCacheTask,
resource_task: ResourceTask,
storage_task: StorageTask,
mem_profiler_chan: mem::ProfilerChan,
devtools_chan: Option<DevtoolsControlChan>,
constellation_chan: ConstellationChan,
layout_chan: LayoutChan,
Expand All @@ -993,6 +1003,7 @@ impl Window {
page: page,
navigator: Default::default(),
image_cache_task: image_cache_task,
mem_profiler_chan: mem_profiler_chan,
devtools_chan: devtools_chan,
browser_context: DOMRefCell::new(None),
performance: Default::default(),
Expand Down
4 changes: 2 additions & 2 deletions components/script/dom/worker.rs
Expand Up @@ -90,8 +90,8 @@ impl Worker {
}

DedicatedWorkerGlobalScope::run_worker_scope(
worker_url, global.pipeline(), global.devtools_chan(), worker_ref, resource_task, global.script_chan(),
sender, receiver);
worker_url, global.pipeline(), global.mem_profiler_chan(), global.devtools_chan(),
worker_ref, resource_task, global.script_chan(), sender, receiver);

Ok(worker)
}
Expand Down
8 changes: 8 additions & 0 deletions components/script/dom/workerglobalscope.rs
Expand Up @@ -23,6 +23,7 @@ use timers::{IsInterval, TimerId, TimerManager, TimerCallback};
use devtools_traits::DevtoolsControlChan;

use msg::constellation_msg::{PipelineId, WorkerId};
use profile_traits::mem;
use net_traits::{load_whole_resource, ResourceTask};
use util::str::DOMString;

Expand Down Expand Up @@ -52,6 +53,7 @@ pub struct WorkerGlobalScope {
console: MutNullableHeap<JS<Console>>,
crypto: MutNullableHeap<JS<Crypto>>,
timers: TimerManager,
mem_profiler_chan: mem::ProfilerChan,
devtools_chan: Option<DevtoolsControlChan>,
}

Expand All @@ -60,6 +62,7 @@ impl WorkerGlobalScope {
worker_url: Url,
runtime: Rc<Runtime>,
resource_task: ResourceTask,
mem_profiler_chan: mem::ProfilerChan,
devtools_chan: Option<DevtoolsControlChan>) -> WorkerGlobalScope {
WorkerGlobalScope {
eventtarget: EventTarget::new_inherited(EventTargetTypeId::WorkerGlobalScope(type_id)),
Expand All @@ -72,10 +75,15 @@ impl WorkerGlobalScope {
console: Default::default(),
crypto: Default::default(),
timers: TimerManager::new(),
mem_profiler_chan: mem_profiler_chan,
devtools_chan: devtools_chan,
}
}

pub fn mem_profiler_chan(&self) -> mem::ProfilerChan {
self.mem_profiler_chan.clone()
}

pub fn devtools_chan(&self) -> Option<DevtoolsControlChan> {
self.devtools_chan.clone()
}
Expand Down
1 change: 1 addition & 0 deletions components/script/lib.rs
Expand Up @@ -57,6 +57,7 @@ extern crate time;
extern crate canvas;
extern crate canvas_traits;
extern crate rand;
#[macro_use]
extern crate profile_traits;
extern crate script_traits;
extern crate selectors;
Expand Down

0 comments on commit 7429b90

Please sign in to comment.