diff --git a/components/profile/heartbeats.rs b/components/profile/heartbeats.rs index 6eaff00e79f..0101f62e25b 100644 --- a/components/profile/heartbeats.rs +++ b/components/profile/heartbeats.rs @@ -11,6 +11,8 @@ use std::env::var_os; use std::error::Error; use std::fs::File; use std::mem; +use std::path::Path; +use util::opts; static mut HBS: Option<*mut HashMap> = None; @@ -72,6 +74,7 @@ pub fn cleanup() { } } +/// Check if a heartbeat exists for the given category pub fn is_heartbeat_enabled(category: &ProfilerCategory) -> bool { unsafe { HBS.map_or(false, |m| (*m).contains_key(category)) @@ -93,22 +96,51 @@ pub fn maybe_heartbeat(category: &ProfilerCategory, } } -/// Create a heartbeat if the correct environment variable is set +// TODO(cimes): Android doesn't really do environment variables. Need a better way to configure dynamically. + +fn is_create_heartbeat(category: &ProfilerCategory) -> bool { + opts::get().profile_heartbeats || var_os(format!("SERVO_HEARTBEAT_ENABLE_{:?}", category)).is_some() +} + +fn open_heartbeat_log>(name: P) -> Option { + match File::create(name) { + Ok(f) => Some(f), + Err(e) => { + warn!("Failed to open heartbeat log: {}", Error::description(&e)); + None + }, + } +} + +#[cfg(target_os = "android")] +fn get_heartbeat_log(category: &ProfilerCategory) -> Option { + open_heartbeat_log(format!("/sdcard/servo/heartbeat-{:?}.log", category)) +} + +#[cfg(not(target_os = "android"))] +fn get_heartbeat_log(category: &ProfilerCategory) -> Option { + var_os(format!("SERVO_HEARTBEAT_LOG_{:?}", category)).and_then(|name| open_heartbeat_log(&name)) +} + +fn get_heartbeat_window_size(category: &ProfilerCategory) -> usize { + const WINDOW_SIZE_DEFAULT: usize = 1; + match var_os(format!("SERVO_HEARTBEAT_WINDOW_{:?}", category)) { + Some(w) => match w.into_string() { + Ok(s) => s.parse::().unwrap_or(WINDOW_SIZE_DEFAULT), + _ => WINDOW_SIZE_DEFAULT, + }, + None => WINDOW_SIZE_DEFAULT, + } +} + +/// Possibly create a heartbeat fn maybe_create_heartbeat(hbs: &mut HashMap, category: ProfilerCategory) { - static WINDOW_SIZE_DEFAULT: usize = 20; - if let Some(_) = var_os(format!("SERVO_HEARTBEAT_ENABLE_{:?}", category)) { + if is_create_heartbeat(&category) { // get optional log file - let logfile: Option = var_os(format!("SERVO_HEARTBEAT_LOG_{:?}", category)) - .and_then(|name| File::create(name).ok()); - // get window size - let window_size: usize = match var_os(format!("SERVO_HEARTBEAT_WINDOW_{:?}", category)) { - Some(w) => match w.into_string() { - Ok(s) => s.parse::().unwrap_or(WINDOW_SIZE_DEFAULT), - _ => WINDOW_SIZE_DEFAULT, - }, - None => WINDOW_SIZE_DEFAULT, - }; + let logfile: Option = get_heartbeat_log(&category); + // window size + let window_size: usize = get_heartbeat_window_size(&category); // create the heartbeat match Heartbeat::new(window_size, Some(heartbeat_window_callback), logfile) { Ok(hb) => { diff --git a/components/profile_traits/Cargo.toml b/components/profile_traits/Cargo.toml index 72c97f7f63e..90eb74479d2 100644 --- a/components/profile_traits/Cargo.toml +++ b/components/profile_traits/Cargo.toml @@ -19,7 +19,7 @@ features = [ "serde_serialization" ] [dependencies.energymon] git = "https://github.com/energymon/energymon-rust.git" -rev = "67f74732ac" +rev = "eba1d8a" optional = true [dependencies.energy-monitor] diff --git a/components/profile_traits/energy.rs b/components/profile_traits/energy.rs index a4b60e1316f..23554386f77 100644 --- a/components/profile_traits/energy.rs +++ b/components/profile_traits/energy.rs @@ -36,8 +36,8 @@ mod energymon { static mut EM: Option<*mut EnergyMon> = None; - /// Read energy from the energy monitor, otherwise return 0. - pub fn read_energy_uj() -> u64 { + fn init() { + // can't use lazy_static macro for EM (no Sync trait for EnergyMon) static ONCE: Once = ONCE_INIT; ONCE.call_once(|| { if let Ok(em) = EnergyMon::new() { @@ -47,7 +47,11 @@ mod energymon { } } }); + } + /// Read energy from the energy monitor, otherwise return 0. + pub fn read_energy_uj() -> u64 { + init(); unsafe { // EnergyMon implementations of EnergyMonitor always return a value EM.map_or(0, |em| (*em).read_uj().unwrap()) @@ -55,6 +59,7 @@ mod energymon { } pub fn get_min_interval_ms() -> u32 { + init(); unsafe { EM.map_or(0, |em| ((*em).interval_us() as f64 / 1000.0).ceil() as u32) } diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index f1642248dd7..2edee037257 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -474,18 +474,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "energymon" version = "0.1.0" -source = "git+https://github.com/energymon/energymon-rust.git?rev=67f74732ac#67f74732ac0acc682659f7e81836155a32188fd0" +source = "git+https://github.com/energymon/energymon-rust.git?rev=eba1d8a#eba1d8a7256b710230ea2c1b26525b88bfcb529d" dependencies = [ "energy-monitor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "energymon-default-sys 0.1.0 (git+https://github.com/energymon/energymon-default-sys.git)", - "energymon-sys 0.1.0 (git+https://github.com/energymon/energymon-sys.git)", + "energymon-default-sys 0.1.0 (git+https://github.com/energymon/energymon-sys.git)", "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "energymon-default-sys" version = "0.1.0" -source = "git+https://github.com/energymon/energymon-default-sys.git#82dd2f6762b492ced15d8446b70569778200c287" +source = "git+https://github.com/energymon/energymon-sys.git#82756eee725db3ccb658f14857947a0260743d4d" dependencies = [ "energymon-sys 0.1.0 (git+https://github.com/energymon/energymon-sys.git)", "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -495,7 +494,7 @@ dependencies = [ [[package]] name = "energymon-sys" version = "0.1.0" -source = "git+https://github.com/energymon/energymon-sys.git#6306f70e5d5c824fa168e43521385798fd1b890b" +source = "git+https://github.com/energymon/energymon-sys.git#82756eee725db3ccb658f14857947a0260743d4d" dependencies = [ "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1436,7 +1435,7 @@ name = "profile_traits" version = "0.0.1" dependencies = [ "energy-monitor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "energymon 0.1.0 (git+https://github.com/energymon/energymon-rust.git?rev=67f74732ac)", + "energymon 0.1.0 (git+https://github.com/energymon/energymon-rust.git?rev=eba1d8a)", "ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)", "plugins 0.0.1", "serde 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/util/opts.rs b/components/util/opts.rs index 785f5909240..9f68ffb432b 100644 --- a/components/util/opts.rs +++ b/components/util/opts.rs @@ -121,6 +121,9 @@ pub struct Opts { /// Periodically print out on which events script tasks spend their processing time. pub profile_script_events: bool, + /// Enable all heartbeats for profiling. + pub profile_heartbeats: 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, @@ -223,6 +226,9 @@ pub struct DebugOptions { /// Profile which events script tasks spend their time on. pub profile_script_events: bool, + /// Enable all heartbeats for profiling. + pub profile_heartbeats: bool, + /// Paint borders along layer and tile boundaries. pub show_compositor_borders: bool, @@ -286,6 +292,7 @@ impl DebugOptions { "relayout-event" => debug_options.relayout_event = true, "profile-tasks" => debug_options.profile_tasks = true, "profile-script-events" => debug_options.profile_script_events = true, + "profile-heartbeats" => debug_options.profile_heartbeats = true, "show-compositor-borders" => debug_options.show_compositor_borders = true, "show-fragment-borders" => debug_options.show_fragment_borders = true, "show-parallel-paint" => debug_options.show_parallel_paint = true, @@ -325,6 +332,8 @@ pub fn print_debug_usage(app: &str) -> ! { print_option("dump-layer-tree", "Print the layer tree whenever it changes."); print_option("relayout-event", "Print notifications when there is a relayout."); print_option("profile-tasks", "Instrument each task, writing the output to a file."); + print_option("profile-script-events", "Enable profiling of script-related events."); + print_option("profile-heartbeats", "Enable heartbeats for all task categories."); print_option("show-compositor-borders", "Paint borders along layer and tile boundaries."); print_option("show-fragment-borders", "Paint borders along fragment boundaries."); print_option("show-parallel-paint", "Overlay tiles with colors showing which thread painted them."); @@ -336,10 +345,10 @@ pub fn print_debug_usage(app: &str) -> ! { print_option("disable-share-style-cache", "Disable the style sharing cache."); print_option("parallel-display-list-building", "Build display lists in parallel."); + print_option("convert-mouse-to-touch", "Send touch events instead of mouse events"); print_option("replace-surrogates", "Replace unpaires surrogates in DOM strings with U+FFFD. \ See https://github.com/servo/servo/issues/6564"); print_option("gc-profile", "Log GC passes and their durations."); - print_option("convert-mouse-to-touch", "Send touch events instead of mouse events"); println!(""); @@ -433,6 +442,7 @@ pub fn default_opts() -> Opts { validate_display_list_geometry: false, profile_tasks: false, profile_script_events: false, + profile_heartbeats: false, sniff_mime_types: false, disable_share_style_cache: false, parallel_display_list_building: false, @@ -620,6 +630,7 @@ pub fn from_cmdline_args(args: &[String]) { bubble_inline_sizes_separately: bubble_inline_sizes_separately, profile_tasks: debug_options.profile_tasks, profile_script_events: debug_options.profile_script_events, + profile_heartbeats: debug_options.profile_heartbeats, trace_layout: debug_options.trace_layout, devtools_port: devtools_port, webdriver_port: webdriver_port,