mirror of
https://github.com/servo/servo.git
synced 2025-06-13 02:44:29 +00:00
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.
This commit is contained in:
parent
ef9715203e
commit
7429b90e02
15 changed files with 177 additions and 21 deletions
|
@ -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) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue