diff options
-rw-r--r-- | src/components/embedding/core.rs | 1 | ||||
-rw-r--r-- | src/components/main/compositing/compositor.rs | 25 | ||||
-rw-r--r-- | src/components/main/compositing/compositor_task.rs | 10 | ||||
-rw-r--r-- | src/components/main/compositing/headless.rs | 6 | ||||
-rw-r--r-- | src/components/main/servo.rs | 6 | ||||
-rw-r--r-- | src/components/util/memory.rs | 158 | ||||
-rw-r--r-- | src/components/util/opts.rs | 11 | ||||
-rw-r--r-- | src/components/util/util.rs | 1 |
8 files changed, 205 insertions, 13 deletions
diff --git a/src/components/embedding/core.rs b/src/components/embedding/core.rs index 4529d3799f6..a2ba98b7f6c 100644 --- a/src/components/embedding/core.rs +++ b/src/components/embedding/core.rs @@ -52,6 +52,7 @@ pub extern "C" fn cef_run_message_loop() { tile_size: 512, device_pixels_per_px: None, profiler_period: None, + memory_profiler_period: None, layout_threads: 1, //layout_threads: cmp::max(rt::default_sched_threads() * 3 / 4, 1), exit_after_load: false, diff --git a/src/components/main/compositing/compositor.rs b/src/components/main/compositing/compositor.rs index 9a29ca083f8..4ad536681b4 100644 --- a/src/components/main/compositing/compositor.rs +++ b/src/components/main/compositing/compositor.rs @@ -34,9 +34,10 @@ use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg, Navig use servo_msg::constellation_msg::{PipelineId, ResizedWindowMsg, WindowSizeData}; use servo_msg::constellation_msg; use servo_util::geometry::{DevicePixel, PagePx, ScreenPx, ViewportPx}; +use servo_util::memory::MemoryProfilerChan; use servo_util::opts::Opts; use servo_util::time::{profile, ProfilerChan}; -use servo_util::{time, url}; +use servo_util::{memory, time, url}; use std::io::timer::sleep; use std::path::Path; use std::rc::Rc; @@ -116,6 +117,9 @@ pub struct IOCompositor { /// The channel on which messages can be sent to the profiler. profiler_chan: ProfilerChan, + /// The channel on which messages can be sent to the memory profiler. + memory_profiler_chan: MemoryProfilerChan, + /// Pending scroll to fragment event, if any fragment_point: Option<Point2D<f32>> } @@ -125,7 +129,8 @@ impl IOCompositor { opts: Opts, port: Receiver<Msg>, constellation_chan: ConstellationChan, - profiler_chan: ProfilerChan) -> IOCompositor { + profiler_chan: ProfilerChan, + memory_profiler_chan: MemoryProfilerChan) -> IOCompositor { let window: Rc<Window> = WindowMethods::new(app, opts.output_file.is_none()); // Create an initial layer tree. @@ -160,6 +165,7 @@ impl IOCompositor { compositor_layer: None, constellation_chan: constellation_chan, profiler_chan: profiler_chan, + memory_profiler_chan: memory_profiler_chan, fragment_point: None } } @@ -168,12 +174,14 @@ impl IOCompositor { opts: Opts, port: Receiver<Msg>, constellation_chan: ConstellationChan, - profiler_chan: ProfilerChan) { + profiler_chan: ProfilerChan, + memory_profiler_chan: MemoryProfilerChan) { let mut compositor = IOCompositor::new(app, opts, port, constellation_chan, - profiler_chan); + profiler_chan, + memory_profiler_chan); compositor.update_zoom_transform(); // Starts the compositor, which listens for messages on the specified port. @@ -231,9 +239,12 @@ impl IOCompositor { } } - // Tell the profiler to shut down. - let ProfilerChan(ref chan) = self.profiler_chan; - chan.send(time::ExitMsg); + // Tell the profiler and memory profiler to shut down. + let ProfilerChan(ref profiler_chan) = self.profiler_chan; + profiler_chan.send(time::ExitMsg); + + let MemoryProfilerChan(ref memory_profiler_chan) = self.memory_profiler_chan; + memory_profiler_chan.send(memory::ExitMsg); } fn handle_message(&mut self) { diff --git a/src/components/main/compositing/compositor_task.rs b/src/components/main/compositing/compositor_task.rs index ebdb65d60fc..237db1d4cab 100644 --- a/src/components/main/compositing/compositor_task.rs +++ b/src/components/main/compositing/compositor_task.rs @@ -16,6 +16,7 @@ use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphics use servo_msg::compositor_msg::{Epoch, LayerBufferSet, LayerId, LayerMetadata, ReadyState}; use servo_msg::compositor_msg::{RenderListener, RenderState, ScriptListener, ScrollPolicy}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; +use servo_util::memory::MemoryProfilerChan; use servo_util::opts::Opts; use servo_util::time::ProfilerChan; use std::comm::{channel, Sender, Receiver}; @@ -224,7 +225,8 @@ impl CompositorTask { pub fn create(opts: Opts, port: Receiver<Msg>, constellation_chan: ConstellationChan, - profiler_chan: ProfilerChan) { + profiler_chan: ProfilerChan, + memory_profiler_chan: MemoryProfilerChan) { let compositor = CompositorTask::new(opts.headless); @@ -234,12 +236,14 @@ impl CompositorTask { opts, port, constellation_chan.clone(), - profiler_chan) + profiler_chan, + memory_profiler_chan) } Headless => { headless::NullCompositor::create(port, constellation_chan.clone(), - profiler_chan) + profiler_chan, + memory_profiler_chan) } }; } diff --git a/src/components/main/compositing/headless.rs b/src/components/main/compositing/headless.rs index 0260518c582..cadc315fc4d 100644 --- a/src/components/main/compositing/headless.rs +++ b/src/components/main/compositing/headless.rs @@ -7,6 +7,8 @@ use compositing::*; use geom::scale_factor::ScaleFactor; use geom::size::TypedSize2D; use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, ResizedWindowMsg, WindowSizeData}; +use servo_util::memory::MemoryProfilerChan; +use servo_util::memory; use servo_util::time::ProfilerChan; use servo_util::time; @@ -28,7 +30,8 @@ impl NullCompositor { pub fn create(port: Receiver<Msg>, constellation_chan: ConstellationChan, - profiler_chan: ProfilerChan) { + profiler_chan: ProfilerChan, + memory_profiler_chan: MemoryProfilerChan) { let compositor = NullCompositor::new(port); // Tell the constellation about the initial fake size. @@ -52,6 +55,7 @@ impl NullCompositor { } profiler_chan.send(time::ExitMsg); + memory_profiler_chan.send(memory::ExitMsg); } fn handle_message(&self, constellation_chan: ConstellationChan) { diff --git a/src/components/main/servo.rs b/src/components/main/servo.rs index c7a2a6150b7..aae08a011d4 100644 --- a/src/components/main/servo.rs +++ b/src/components/main/servo.rs @@ -64,6 +64,8 @@ use servo_net::image_cache_task::{ImageCacheTask, SyncImageCacheTask}; use servo_net::resource_task::ResourceTask; #[cfg(not(test))] use servo_util::time::Profiler; +#[cfg(not(test))] +use servo_util::memory::MemoryProfiler; #[cfg(not(test))] use servo_util::opts; @@ -169,6 +171,7 @@ pub fn run(opts: opts::Opts) { let (compositor_port, compositor_chan) = CompositorChan::new(); let profiler_chan = Profiler::create(opts.profiler_period); + let memory_profiler_chan = MemoryProfiler::create(opts.memory_profiler_period); let opts_clone = opts.clone(); let profiler_chan_clone = profiler_chan.clone(); @@ -217,7 +220,8 @@ pub fn run(opts: opts::Opts) { CompositorTask::create(opts, compositor_port, constellation_chan, - profiler_chan); + profiler_chan, + memory_profiler_chan); pool.shutdown(); } diff --git a/src/components/util/memory.rs b/src/components/util/memory.rs new file mode 100644 index 00000000000..7b29c07ef0c --- /dev/null +++ b/src/components/util/memory.rs @@ -0,0 +1,158 @@ +/* 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/. */ + +//! Memory profiling functions. + +use std::io::timer::sleep; +#[cfg(target_os="linux")] +use std::io::File; +#[cfg(target_os="linux")] +use std::os::page_size; +use task::spawn_named; + +pub struct MemoryProfilerChan(pub Sender<MemoryProfilerMsg>); + +impl MemoryProfilerChan { + pub fn send(&self, msg: MemoryProfilerMsg) { + let MemoryProfilerChan(ref c) = *self; + c.send(msg); + } +} + +pub enum MemoryProfilerMsg { + /// Message used to force print the memory profiling metrics. + PrintMsg, + /// Tells the memory profiler to shut down. + ExitMsg, +} + +pub struct MemoryProfiler { + pub port: Receiver<MemoryProfilerMsg>, +} + +impl MemoryProfiler { + pub fn create(period: Option<f64>) -> MemoryProfilerChan { + let (chan, port) = channel(); + match period { + Some(period) => { + let period = (period * 1000f64) as u64; + let chan = chan.clone(); + spawn_named("Memory profiler timer", proc() { + loop { + sleep(period); + if chan.send_opt(PrintMsg).is_err() { + break; + } + } + }); + // Spawn the memory profiler. + spawn_named("Memory profiler", proc() { + let memory_profiler = MemoryProfiler::new(port); + memory_profiler.start(); + }); + } + None => { + // No-op to handle profiler messages when the memory profiler + // is inactive. + spawn_named("Memory profiler", proc() { + loop { + match port.recv_opt() { + Err(_) | Ok(ExitMsg) => break, + _ => {} + } + } + }); + } + } + + MemoryProfilerChan(chan) + } + + pub fn new(port: Receiver<MemoryProfilerMsg>) -> MemoryProfiler { + MemoryProfiler { + port: port + } + } + + pub fn start(&self) { + loop { + match self.port.recv_opt() { + Ok(msg) => { + if !self.handle_msg(msg) { + break + } + } + _ => break + } + } + } + + fn handle_msg(&self, msg: MemoryProfilerMsg) -> bool { + match msg { + PrintMsg => { + self.handle_print_msg(); + true + }, + ExitMsg => false + } + } + + fn print_measurement(path: &str, nbytes: Option<i64>) { + match nbytes { + Some(nbytes) => { + let mebi = 1024f64 * 1024f64; + println!("{:12s}: {:12.2f}", path, (nbytes as f64) / mebi); + } + None => { + println!("{:12s}: {:>12s}", path, "???"); + } + } + } + + fn handle_print_msg(&self) { + println!("{:12s}: {:12s}", "_category_", "_size (MiB)_"); + MemoryProfiler::print_measurement("vsize", get_vsize()); + MemoryProfiler::print_measurement("resident", get_resident()); + println!(""); + } +} + +// Like std::macros::try!, but for Option<>. +macro_rules! option_try( + ($e:expr) => (match $e { Some(e) => e, None => return None }) +) + +#[cfg(target_os="linux")] +fn get_proc_self_statm_field(field: uint) -> Option<i64> { + let mut f = File::open(&Path::new("/proc/self/statm")); + match f.read_to_str() { + Ok(contents) => { + let s = option_try!(contents.as_slice().words().nth(field)); + let npages: i64 = option_try!(from_str(s)); + Some(npages * (page_size() as i64)) + } + Err(_) => None + } +} + +#[cfg(target_os="linux")] +fn get_vsize() -> Option<i64> { + get_proc_self_statm_field(0) +} + +#[cfg(not(target_os="linux"))] +fn get_vsize() -> Option<i64> { + None +} + +#[cfg(target_os="linux")] +fn get_resident() -> Option<i64> { + get_proc_self_statm_field(1) +} + +#[cfg(not(target_os="linux"))] +fn get_resident() -> Option<i64> { + None +} + diff --git a/src/components/util/opts.rs b/src/components/util/opts.rs index 166ff0a9a9a..2e9972aeae2 100644 --- a/src/components/util/opts.rs +++ b/src/components/util/opts.rs @@ -45,6 +45,10 @@ pub struct Opts { /// it to produce output on that interval (`-p`). pub profiler_period: Option<f64>, + /// `None` to disable the memory profiler or `Some` with an interval in seconds to enable it + /// and cause it to produce output on that interval (`-m`). + pub memory_profiler_period: Option<f64>, + /// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive /// sequential algorithm. pub layout_threads: uint, @@ -85,6 +89,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { getopts::optopt("", "device-pixel-ratio", "Device pixels per px", ""), getopts::optopt("t", "threads", "Number of render threads", "1"), getopts::optflagopt("p", "profile", "Profiler flag and output interval", "10"), + getopts::optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10"), getopts::optflag("x", "exit", "Exit after load flag"), getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "1"), getopts::optflag("z", "headless", "Headless mode"), @@ -147,10 +152,13 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { None => 1, // FIXME: Number of cores. }; - // if only flag is present, default to 5 second period + // If only the flag is present, default to a 5 second period for both profilers. let profiler_period = opt_match.opt_default("p", "5").map(|period| { from_str(period.as_slice()).unwrap() }); + let memory_profiler_period = opt_match.opt_default("m", "5").map(|period| { + from_str(period.as_slice()).unwrap() + }); let cpu_painting = opt_match.opt_present("c"); @@ -167,6 +175,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { tile_size: tile_size, device_pixels_per_px: device_pixels_per_px, profiler_period: profiler_period, + memory_profiler_period: memory_profiler_period, layout_threads: layout_threads, exit_after_load: opt_match.opt_present("x"), output_file: opt_match.opt_str("o"), diff --git a/src/components/util/util.rs b/src/components/util/util.rs index 80c3fd115d4..483de9ed6b6 100644 --- a/src/components/util/util.rs +++ b/src/components/util/util.rs @@ -29,6 +29,7 @@ extern crate std_url = "url"; pub mod cache; pub mod debug_utils; pub mod geometry; +pub mod memory; pub mod namespace; pub mod opts; pub mod range; |