aboutsummaryrefslogtreecommitdiffstats
path: root/components/util
diff options
context:
space:
mode:
authorGlenn Watson <gw@intuitionlibrary.com>2014-10-29 07:24:34 +1000
committerGlenn Watson <gw@intuitionlibrary.com>2014-10-29 07:34:47 +1000
commit90d793cdc8e4247e6ba59cae746bd49fca550ed5 (patch)
tree1d82c1e79e4fac02ec138273faa68d7a3630a95d /components/util
parent3aad350a648bd69ca3410011f6e7eccf5813cb31 (diff)
downloadservo-90d793cdc8e4247e6ba59cae746bd49fca550ed5.tar.gz
servo-90d793cdc8e4247e6ba59cae746bd49fca550ed5.zip
Add task profiler, which works by instrumenting each task runtime when enabled.
Diffstat (limited to 'components/util')
-rw-r--r--components/util/lib.rs3
-rw-r--r--components/util/opts.rs8
-rw-r--r--components/util/rtinstrument.rs206
-rw-r--r--components/util/task.rs26
4 files changed, 233 insertions, 10 deletions
diff --git a/components/util/lib.rs b/components/util/lib.rs
index 0c5c5f65e55..364a7e92712 100644
--- a/components/util/lib.rs
+++ b/components/util/lib.rs
@@ -30,6 +30,8 @@ extern crate url;
#[phase(plugin)]
extern crate string_cache_macros;
+#[phase(plugin)]
+extern crate lazy_static;
use std::sync::Arc;
@@ -45,6 +47,7 @@ pub mod namespace;
pub mod opts;
pub mod range;
pub mod resource_files;
+pub mod rtinstrument;
pub mod smallvec;
pub mod sort;
pub mod str;
diff --git a/components/util/opts.rs b/components/util/opts.rs
index f8b7ff621a9..175612ebd1c 100644
--- a/components/util/opts.rs
+++ b/components/util/opts.rs
@@ -87,6 +87,11 @@ pub struct Opts {
/// and render.
pub trace_layout: bool,
+ /// If true, instrument the runtime for each task created and dump
+ /// that information to a JSON file that can be viewed in the task
+ /// profile viewer.
+ pub profile_tasks: bool,
+
/// `None` to disable devtools or `Some` with a port number to start a server to listen to
/// remote Firefox devtools connections.
pub devtools_port: Option<u16>,
@@ -148,6 +153,7 @@ fn default_opts() -> Opts {
user_agent: None,
dump_flow_tree: false,
validate_display_list_geometry: false,
+ profile_tasks: false,
}
}
@@ -173,6 +179,7 @@ pub fn from_cmdline_args(args: &[String]) -> bool {
getopts::optflag("b", "bubble-widths", "Bubble intrinsic widths separately like other engines"),
getopts::optflag("", "show-debug-borders", "Show debugging borders on layers and tiles."),
getopts::optflag("", "show-debug-fragment-borders", "Show debugging borders on fragments."),
+ getopts::optflag("", "profile-tasks", "Instrument each task, writing the output to a file."),
getopts::optflag("", "disable-text-aa", "Disable antialiasing for text rendering."),
getopts::optflag("", "trace-layout", "Write layout trace to external file for debugging."),
getopts::optflagopt("", "devtools", "Start remote devtools server on port", "6000"),
@@ -277,6 +284,7 @@ pub fn from_cmdline_args(args: &[String]) -> bool {
show_debug_borders: opt_match.opt_present("show-debug-borders"),
show_debug_fragment_borders: opt_match.opt_present("show-debug-fragment-borders"),
enable_text_antialiasing: !opt_match.opt_present("disable-text-aa"),
+ profile_tasks: opt_match.opt_present("profile-tasks"),
trace_layout: trace_layout,
devtools_port: devtools_port,
initial_window_size: initial_window_size,
diff --git a/components/util/rtinstrument.rs b/components/util/rtinstrument.rs
new file mode 100644
index 00000000000..f0da89d9c4a
--- /dev/null
+++ b/components/util/rtinstrument.rs
@@ -0,0 +1,206 @@
+/* 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/. */
+
+use opts;
+use std::any::Any;
+#[cfg(not(test))]
+use std::io::File;
+use std::mem;
+use std::raw;
+use std::rt::Runtime;
+use std::rt::local::Local;
+use std::rt::rtio;
+use std::rt::task::{Task, TaskOpts, BlockedTask};
+use std_time;
+use sync::Mutex;
+#[cfg(not(test))]
+use serialize::json;
+
+#[deriving(Encodable)]
+pub enum Event {
+ Spawn,
+ Schedule,
+ Unschedule,
+ Death,
+}
+
+#[deriving(Encodable)]
+pub struct Message {
+ timestamp: u64,
+ thread_id: uint,
+ event: Event,
+}
+
+#[deriving(Encodable)]
+pub struct TaskStats {
+ pub name: String,
+ pub messages: Vec<Message>,
+ pub task_id: uint,
+}
+
+struct InstrumentedRuntime {
+ inner: Option<Box<Runtime + Send>>,
+ messages: Vec<Message>,
+}
+
+#[deriving(Encodable)]
+pub struct GlobalState {
+ task_stats: Vec<TaskStats>,
+}
+
+#[cfg(not(test))]
+pub fn teardown() {
+ if opts::get().profile_tasks {
+ let state = GLOBAL_STATE.lock();
+ let result = json::encode(&*state);
+ let path = Path::new("thread_trace.json");
+ let mut file = File::create(&path).unwrap();
+ file.write_str(result.as_slice()).unwrap();
+ }
+}
+
+impl GlobalState {
+ fn new() -> GlobalState {
+ GlobalState {
+ task_stats: vec!(),
+ }
+ }
+}
+
+lazy_static! {
+ pub static ref GLOBAL_STATE: Mutex<GlobalState> = Mutex::new(GlobalState::new());
+}
+
+/// Instrument all code run inside the specific block, returning a vector of all
+/// messages which occurred.
+pub fn instrument(f: proc()) {
+ if opts::get().profile_tasks {
+ install();
+ f();
+ let rt = uninstall();
+ let task_id = rt.task_id();
+ let name = {
+ let task = Local::borrow(None::<Task>);
+ match task.name {
+ Some(ref name) => name.to_string(),
+ None => "unknown".to_string(),
+ }
+ };
+ let stats = TaskStats {
+ name: name,
+ messages: rt.messages,
+ task_id: task_id,
+ };
+ let mut state = GLOBAL_STATE.lock();
+ state.task_stats.push(stats);
+ } else {
+ f();
+ }
+}
+
+/// Installs an instrumented runtime which will append to the given vector of
+/// messages.
+///
+/// The instrumented runtime is installed into the current task.
+fn install() {
+ let mut task = Local::borrow(None::<Task>);
+ let rt = task.take_runtime();
+ let mut new_rt = box InstrumentedRuntime {
+ inner: Some(rt),
+ messages: vec!(),
+ };
+ new_rt.log(Spawn);
+ task.put_runtime(new_rt);
+}
+
+/// Uninstalls the runtime from the current task, returning the instrumented
+/// runtime.
+fn uninstall() -> InstrumentedRuntime {
+ let mut task = Local::borrow(None::<Task>);
+ let mut rt = task.maybe_take_runtime::<InstrumentedRuntime>().unwrap();
+ rt.log(Death);
+ task.put_runtime(rt.inner.take().unwrap());
+ *rt
+}
+
+impl InstrumentedRuntime {
+ /// Puts this runtime back into the local task
+ fn put(mut self: Box<InstrumentedRuntime>, event: Option<Event>) {
+ assert!(self.inner.is_none());
+
+ let mut task: Box<Task> = Local::take();
+ let rt = task.take_runtime();
+ self.inner = Some(rt);
+ match event {
+ Some(event) => self.log(event),
+ None => {}
+ }
+ task.put_runtime(self);
+ Local::put(task);
+ }
+
+ /// Logs a message into this runtime
+ fn log(&mut self, event: Event) {
+ let id = self.thread_id();
+ self.messages.push(Message {
+ timestamp: std_time::precise_time_ns(),
+ event: event,
+ thread_id: id,
+ });
+ }
+
+ fn task_id(&self) -> uint { self as *const _ as uint }
+
+ fn thread_id(&mut self) -> uint {
+ self.inner.as_mut().unwrap().local_io().map(|mut i| {
+ let i: raw::TraitObject = unsafe { mem::transmute(i.get()) };
+ i.data as uint
+ }).unwrap_or(0)
+ }
+}
+
+impl Runtime for InstrumentedRuntime {
+ fn yield_now(mut self: Box<InstrumentedRuntime>, cur_task: Box<Task>) {
+ self.inner.take().unwrap().yield_now(cur_task);
+ self.put(None)
+ }
+
+ fn maybe_yield(mut self: Box<InstrumentedRuntime>, cur_task: Box<Task>) {
+ self.inner.take().unwrap().maybe_yield(cur_task);
+ self.put(None)
+ }
+
+ fn deschedule(mut self: Box<InstrumentedRuntime>, times: uint, cur_task: Box<Task>,
+ f: |BlockedTask| -> Result<(), BlockedTask>) {
+ self.log(Unschedule);
+ self.inner.take().unwrap().deschedule(times, cur_task, f);
+ self.put(Some(Schedule));
+ }
+
+ fn reawaken(mut self: Box<InstrumentedRuntime>, to_wake: Box<Task>) {
+ self.inner.take().unwrap().reawaken(to_wake);
+ self.put(None);
+ }
+
+ fn spawn_sibling(mut self: Box<InstrumentedRuntime>,
+ cur_task: Box<Task>,
+ opts: TaskOpts,
+ f: proc():Send) {
+ // Be sure to install an instrumented runtime for the spawned sibling by
+ // specifying a new runtime.
+ self.inner.take().unwrap().spawn_sibling(cur_task, opts, proc() {
+ install();
+ f();
+ drop(uninstall());
+ });
+ self.put(None)
+ }
+
+ fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> {
+ self.inner.as_mut().unwrap().local_io()
+ }
+ fn stack_bounds(&self) -> (uint, uint) { self.inner.as_ref().unwrap().stack_bounds() }
+ fn can_block(&self) -> bool { self.inner.as_ref().unwrap().can_block() }
+ fn wrap(self: Box<InstrumentedRuntime>) -> Box<Any+'static> { self as Box<Any> }
+}
diff --git a/components/util/task.rs b/components/util/task.rs
index f38d87d0907..362edb6b517 100644
--- a/components/util/task.rs
+++ b/components/util/task.rs
@@ -7,17 +7,21 @@ use std::task;
use std::comm::Sender;
use std::task::TaskBuilder;
use native::task::NativeTaskBuilder;
-
+use rtinstrument;
use task_state;
pub fn spawn_named<S: IntoMaybeOwned<'static>>(name: S, f: proc():Send) {
let builder = task::TaskBuilder::new().named(name);
- builder.spawn(f);
+ builder.spawn(proc() {
+ rtinstrument::instrument(f);
+ });
}
pub fn spawn_named_native<S: IntoMaybeOwned<'static>>(name: S, f: proc():Send) {
let builder = task::TaskBuilder::new().named(name).native();
- builder.spawn(f);
+ builder.spawn(proc() {
+ rtinstrument::instrument(f);
+ });
}
/// Arrange to send a particular message to a channel if the task fails.
@@ -29,7 +33,7 @@ pub fn spawn_named_with_send_on_failure<T: Send>(name: &'static str,
native: bool) {
let with_state = proc() {
task_state::initialize(state);
- f()
+ rtinstrument::instrument(f);
};
let future_result = if native {
@@ -41,12 +45,14 @@ pub fn spawn_named_with_send_on_failure<T: Send>(name: &'static str,
let watched_name = name.to_string();
let watcher_name = format!("{:s}Watcher", watched_name);
TaskBuilder::new().named(watcher_name).spawn(proc() {
- match future_result.unwrap() {
- Ok(()) => (),
- Err(..) => {
- debug!("{:s} failed, notifying constellation", name);
- dest.send(msg);
+ rtinstrument::instrument(proc() {
+ match future_result.unwrap() {
+ Ok(()) => (),
+ Err(..) => {
+ debug!("{:s} failed, notifying constellation", name);
+ dest.send(msg);
+ }
}
- }
+ });
});
}