diff options
author | Nicholas Nethercote <nnethercote@mozilla.com> | 2015-07-02 22:14:38 -0700 |
---|---|---|
committer | Nicholas Nethercote <nnethercote@mozilla.com> | 2015-07-15 21:58:20 -0700 |
commit | 7429b90e02effd086681196050f400b2d1836903 (patch) | |
tree | 96e255399fde51f3c9cd093be407fe9cba909317 /components/script/script_task.rs | |
parent | ef9715203edf0a280d019b6e8823666f0e7020be (diff) | |
download | servo-7429b90e02effd086681196050f400b2d1836903.tar.gz servo-7429b90e02effd086681196050f400b2d1836903.zip |
Wire up the JS engine's memory reporting.
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.
Diffstat (limited to 'components/script/script_task.rs')
-rw-r--r-- | components/script/script_task.rs | 83 |
1 files changed, 78 insertions, 5 deletions
diff --git a/components/script/script_task.rs b/components/script/script_task.rs index d079d9f3e75..b1f7357a613 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -70,6 +70,7 @@ use net_traits::{ResourceTask, LoadConsumer, ControlMsg, Metadata}; use net_traits::LoadData as NetLoadData; use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageCacheResult}; use net_traits::storage_task::StorageTask; +use profile_traits::mem::{self, Report, Reporter, ReportsChan}; use string_cache::Atom; use util::str::DOMString; use util::task::spawn_named_with_send_on_failure; @@ -78,9 +79,10 @@ use util::task_state; use euclid::Rect; use euclid::point::Point2D; use hyper::header::{LastModified, Headers}; +use js::glue::CollectServoSizes; use js::jsapi::{JS_SetWrapObjectCallbacks, JS_AddExtraGCRootsTracer, DisableIncrementalGC}; use js::jsapi::{JSContext, JSRuntime, JSTracer}; -use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSAutoRequest, SetDOMCallbacks}; +use js::jsapi::{JS_GetRuntime, JS_SetGCCallback, JSGCStatus, JSAutoRequest, SetDOMCallbacks}; use js::jsapi::{SetDOMProxyInformation, DOMProxyShadowsResult, HandleObject, HandleId, RootedValue}; use js::jsval::UndefinedValue; use js::rust::Runtime; @@ -91,7 +93,7 @@ use std::any::Any; use std::borrow::ToOwned; use std::cell::{Cell, RefCell}; use std::collections::HashSet; -use std::mem; +use std::mem as std_mem; use std::option::Option; use std::ptr; use std::rc::Rc; @@ -196,6 +198,9 @@ pub enum ScriptMsg { RefcountCleanup(TrustedReference), /// Notify a document that all pending loads are complete. DocumentLoadsComplete(PipelineId), + /// Requests that the script task measure its memory usage. The results are sent back via the + /// supplied channel. + CollectReports(ReportsChan), } /// A cloneable interface for communicating with an event loop. @@ -247,6 +252,18 @@ impl NonWorkerScriptChan { let (chan, port) = channel(); (port, box NonWorkerScriptChan(chan)) } + + fn clone_as_reporter(&self) -> Box<Reporter+Send> { + let NonWorkerScriptChan(ref chan) = *self; + box NonWorkerScriptChan((*chan).clone()) + } +} + +impl Reporter for NonWorkerScriptChan { + // Just injects an appropriate event into the script task's queue. + fn collect_reports(&self, reports_chan: ReportsChan) -> bool { + self.send(ScriptMsg::CollectReports(reports_chan)).is_ok() + } } pub struct StackRootTLS; @@ -307,6 +324,9 @@ pub struct ScriptTask { /// The channel on which the image cache can send messages to ourself. image_cache_channel: ImageCacheChan, + /// For providing contact with the memory profiler. + mem_profiler_chan: mem::ProfilerChan, + /// For providing instructions to an optional devtools server. devtools_chan: Option<DevtoolsControlChan>, /// For receiving commands from an optional devtools server. Will be ignored if @@ -387,6 +407,7 @@ impl ScriptTaskFactory for ScriptTask { resource_task: ResourceTask, storage_task: StorageTask, image_cache_task: ImageCacheTask, + mem_profiler_chan: mem::ProfilerChan, devtools_chan: Option<DevtoolsControlChan>, window_size: Option<WindowSizeData>, load_data: LoadData) { @@ -396,15 +417,18 @@ impl ScriptTaskFactory for ScriptTask { spawn_named_with_send_on_failure(format!("ScriptTask {:?}", id), task_state::SCRIPT, move || { let roots = RootCollection::new(); let _stack_roots_tls = StackRootTLS::new(&roots); + let chan = NonWorkerScriptChan(script_chan); + let reporter = chan.clone_as_reporter(); let script_task = ScriptTask::new(compositor, script_port, - NonWorkerScriptChan(script_chan), + chan, control_chan, control_port, constellation_chan, resource_task, storage_task, image_cache_task, + mem_profiler_chan.clone(), devtools_chan); SCRIPT_TASK_ROOT.with(|root| { @@ -417,8 +441,17 @@ impl ScriptTaskFactory for ScriptTask { load_data.url.clone()); script_task.start_page_load(new_load, load_data); + // Register this task as a memory reporter. + let reporter_name = format!("script-reporter-{}", id.0); + let msg = mem::ProfilerMsg::RegisterReporter(reporter_name.clone(), reporter); + mem_profiler_chan.send(msg); + script_task.start(); + // Unregister this task as a memory reporter. + let msg = mem::ProfilerMsg::UnregisterReporter(reporter_name); + mem_profiler_chan.send(msg); + // This must always be the very last operation performed before the task completes failsafe.neuter(); }, ConstellationMsg::Failure(failure_msg), const_chan); @@ -473,6 +506,7 @@ impl ScriptTask { resource_task: ResourceTask, storage_task: StorageTask, image_cache_task: ImageCacheTask, + mem_profiler_chan: mem::ProfilerChan, devtools_chan: Option<DevtoolsControlChan>) -> ScriptTask { let runtime = ScriptTask::new_rt_and_cx(); @@ -502,6 +536,8 @@ impl ScriptTask { control_port: control_port, constellation_chan: constellation_chan, compositor: DOMRefCell::new(compositor), + mem_profiler_chan: mem_profiler_chan, + devtools_chan: devtools_chan, devtools_port: devtools_receiver, devtools_sender: devtools_sender, @@ -783,6 +819,8 @@ impl ScriptTask { LiveDOMReferences::cleanup(addr), ScriptMsg::DocumentLoadsComplete(id) => self.handle_loads_complete(id), + ScriptMsg::CollectReports(reports_chan) => + self.collect_reports(reports_chan), } } @@ -989,6 +1027,40 @@ impl ScriptTask { chan.send(ConstellationMsg::LoadComplete(pipeline)).unwrap(); } + pub fn get_reports(cx: *mut JSContext, path_seg: String) -> Vec<Report> { + let mut reports = vec![]; + + unsafe { + let rt = JS_GetRuntime(cx); + let mut stats = ::std::mem::zeroed(); + if CollectServoSizes(rt, &mut stats) { + let mut report = |mut path_suffix, size| { + let mut path = path!["pages", path_seg, "js"]; + path.append(&mut path_suffix); + reports.push(Report { path: path, size: size as usize }) + }; + + report(path!["gc-heap", "used"], stats.gcHeapUsed); + report(path!["gc-heap", "unused"], stats.gcHeapUnused); + report(path!["gc-heap", "admin"], stats.gcHeapAdmin); + report(path!["gc-heap", "decommitted"], stats.gcHeapDecommitted); + report(path!["malloc-heap"], stats.mallocHeap); + report(path!["non-heap"], stats.nonHeap); + } + } + reports + } + + fn collect_reports(&self, reports_chan: ReportsChan) { + let mut urls = vec![]; + for it_page in self.root_page().iter() { + urls.push(it_page.document().url().serialize()); + } + let path_seg = format!("url({})", urls.connect(", ")); + let reports = ScriptTask::get_reports(self.get_cx(), path_seg); + reports_chan.send(reports); + } + /// Handles a timer that fired. fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) { let page = self.root_page(); @@ -1287,6 +1359,7 @@ impl ScriptTask { self.image_cache_task.clone(), self.resource_task.clone(), self.storage_task.clone(), + self.mem_profiler_chan.clone(), self.devtools_chan.clone(), self.constellation_chan.clone(), incomplete.layout_chan, @@ -1429,9 +1502,9 @@ impl ScriptTask { // We temporarily steal the list of targets over which the mouse is to pass it to // handle_mouse_move_event() in a safe RootedVec container. let mut mouse_over_targets = RootedVec::new(); - mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); + std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); document.r().handle_mouse_move_event(self.js_runtime.rt(), point, &mut mouse_over_targets); - mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); + std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); } KeyEvent(key, state, modifiers) => { |