diff options
author | Glenn Watson <gw@intuitionlibrary.com> | 2014-10-29 07:24:34 +1000 |
---|---|---|
committer | Glenn Watson <gw@intuitionlibrary.com> | 2014-10-29 07:34:47 +1000 |
commit | 90d793cdc8e4247e6ba59cae746bd49fca550ed5 (patch) | |
tree | 1d82c1e79e4fac02ec138273faa68d7a3630a95d /components/util | |
parent | 3aad350a648bd69ca3410011f6e7eccf5813cb31 (diff) | |
download | servo-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.rs | 3 | ||||
-rw-r--r-- | components/util/opts.rs | 8 | ||||
-rw-r--r-- | components/util/rtinstrument.rs | 206 | ||||
-rw-r--r-- | components/util/task.rs | 26 |
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); + } } - } + }); }); } |