aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/components/embedding/core.rs1
-rw-r--r--src/components/main/compositing/compositor.rs25
-rw-r--r--src/components/main/compositing/compositor_task.rs10
-rw-r--r--src/components/main/compositing/headless.rs6
-rw-r--r--src/components/main/servo.rs6
-rw-r--r--src/components/util/memory.rs158
-rw-r--r--src/components/util/opts.rs11
-rw-r--r--src/components/util/util.rs1
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;