Add a basic memory profiler, invoked with -m.

This commit is contained in:
Nicholas Nethercote 2014-06-25 23:07:01 -07:00
parent be2e27db54
commit fad919ccf7
8 changed files with 205 additions and 13 deletions

View file

@ -52,6 +52,7 @@ pub extern "C" fn cef_run_message_loop() {
tile_size: 512, tile_size: 512,
device_pixels_per_px: None, device_pixels_per_px: None,
profiler_period: None, profiler_period: None,
memory_profiler_period: None,
layout_threads: 1, layout_threads: 1,
//layout_threads: cmp::max(rt::default_sched_threads() * 3 / 4, 1), //layout_threads: cmp::max(rt::default_sched_threads() * 3 / 4, 1),
exit_after_load: false, exit_after_load: false,

View file

@ -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::{PipelineId, ResizedWindowMsg, WindowSizeData};
use servo_msg::constellation_msg; use servo_msg::constellation_msg;
use servo_util::geometry::{DevicePixel, PagePx, ScreenPx, ViewportPx}; use servo_util::geometry::{DevicePixel, PagePx, ScreenPx, ViewportPx};
use servo_util::memory::MemoryProfilerChan;
use servo_util::opts::Opts; use servo_util::opts::Opts;
use servo_util::time::{profile, ProfilerChan}; use servo_util::time::{profile, ProfilerChan};
use servo_util::{time, url}; use servo_util::{memory, time, url};
use std::io::timer::sleep; use std::io::timer::sleep;
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
@ -116,6 +117,9 @@ pub struct IOCompositor {
/// The channel on which messages can be sent to the profiler. /// The channel on which messages can be sent to the profiler.
profiler_chan: ProfilerChan, 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 /// Pending scroll to fragment event, if any
fragment_point: Option<Point2D<f32>> fragment_point: Option<Point2D<f32>>
} }
@ -125,7 +129,8 @@ impl IOCompositor {
opts: Opts, opts: Opts,
port: Receiver<Msg>, port: Receiver<Msg>,
constellation_chan: ConstellationChan, 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()); let window: Rc<Window> = WindowMethods::new(app, opts.output_file.is_none());
// Create an initial layer tree. // Create an initial layer tree.
@ -160,6 +165,7 @@ impl IOCompositor {
compositor_layer: None, compositor_layer: None,
constellation_chan: constellation_chan, constellation_chan: constellation_chan,
profiler_chan: profiler_chan, profiler_chan: profiler_chan,
memory_profiler_chan: memory_profiler_chan,
fragment_point: None fragment_point: None
} }
} }
@ -168,12 +174,14 @@ impl IOCompositor {
opts: Opts, opts: Opts,
port: Receiver<Msg>, port: Receiver<Msg>,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
profiler_chan: ProfilerChan) { profiler_chan: ProfilerChan,
memory_profiler_chan: MemoryProfilerChan) {
let mut compositor = IOCompositor::new(app, let mut compositor = IOCompositor::new(app,
opts, opts,
port, port,
constellation_chan, constellation_chan,
profiler_chan); profiler_chan,
memory_profiler_chan);
compositor.update_zoom_transform(); compositor.update_zoom_transform();
// Starts the compositor, which listens for messages on the specified port. // Starts the compositor, which listens for messages on the specified port.
@ -231,9 +239,12 @@ impl IOCompositor {
} }
} }
// Tell the profiler to shut down. // Tell the profiler and memory profiler to shut down.
let ProfilerChan(ref chan) = self.profiler_chan; let ProfilerChan(ref profiler_chan) = self.profiler_chan;
chan.send(time::ExitMsg); 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) { fn handle_message(&mut self) {

View file

@ -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::{Epoch, LayerBufferSet, LayerId, LayerMetadata, ReadyState};
use servo_msg::compositor_msg::{RenderListener, RenderState, ScriptListener, ScrollPolicy}; use servo_msg::compositor_msg::{RenderListener, RenderState, ScriptListener, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_util::memory::MemoryProfilerChan;
use servo_util::opts::Opts; use servo_util::opts::Opts;
use servo_util::time::ProfilerChan; use servo_util::time::ProfilerChan;
use std::comm::{channel, Sender, Receiver}; use std::comm::{channel, Sender, Receiver};
@ -224,7 +225,8 @@ impl CompositorTask {
pub fn create(opts: Opts, pub fn create(opts: Opts,
port: Receiver<Msg>, port: Receiver<Msg>,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
profiler_chan: ProfilerChan) { profiler_chan: ProfilerChan,
memory_profiler_chan: MemoryProfilerChan) {
let compositor = CompositorTask::new(opts.headless); let compositor = CompositorTask::new(opts.headless);
@ -234,12 +236,14 @@ impl CompositorTask {
opts, opts,
port, port,
constellation_chan.clone(), constellation_chan.clone(),
profiler_chan) profiler_chan,
memory_profiler_chan)
} }
Headless => { Headless => {
headless::NullCompositor::create(port, headless::NullCompositor::create(port,
constellation_chan.clone(), constellation_chan.clone(),
profiler_chan) profiler_chan,
memory_profiler_chan)
} }
}; };
} }

View file

@ -7,6 +7,8 @@ use compositing::*;
use geom::scale_factor::ScaleFactor; use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D; use geom::size::TypedSize2D;
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, ResizedWindowMsg, WindowSizeData}; 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::ProfilerChan;
use servo_util::time; use servo_util::time;
@ -28,7 +30,8 @@ impl NullCompositor {
pub fn create(port: Receiver<Msg>, pub fn create(port: Receiver<Msg>,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
profiler_chan: ProfilerChan) { profiler_chan: ProfilerChan,
memory_profiler_chan: MemoryProfilerChan) {
let compositor = NullCompositor::new(port); let compositor = NullCompositor::new(port);
// Tell the constellation about the initial fake size. // Tell the constellation about the initial fake size.
@ -52,6 +55,7 @@ impl NullCompositor {
} }
profiler_chan.send(time::ExitMsg); profiler_chan.send(time::ExitMsg);
memory_profiler_chan.send(memory::ExitMsg);
} }
fn handle_message(&self, constellation_chan: ConstellationChan) { fn handle_message(&self, constellation_chan: ConstellationChan) {

View file

@ -64,6 +64,8 @@ use servo_net::image_cache_task::{ImageCacheTask, SyncImageCacheTask};
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
#[cfg(not(test))] #[cfg(not(test))]
use servo_util::time::Profiler; use servo_util::time::Profiler;
#[cfg(not(test))]
use servo_util::memory::MemoryProfiler;
#[cfg(not(test))] #[cfg(not(test))]
use servo_util::opts; use servo_util::opts;
@ -169,6 +171,7 @@ pub fn run(opts: opts::Opts) {
let (compositor_port, compositor_chan) = CompositorChan::new(); let (compositor_port, compositor_chan) = CompositorChan::new();
let profiler_chan = Profiler::create(opts.profiler_period); let profiler_chan = Profiler::create(opts.profiler_period);
let memory_profiler_chan = MemoryProfiler::create(opts.memory_profiler_period);
let opts_clone = opts.clone(); let opts_clone = opts.clone();
let profiler_chan_clone = profiler_chan.clone(); let profiler_chan_clone = profiler_chan.clone();
@ -217,7 +220,8 @@ pub fn run(opts: opts::Opts) {
CompositorTask::create(opts, CompositorTask::create(opts,
compositor_port, compositor_port,
constellation_chan, constellation_chan,
profiler_chan); profiler_chan,
memory_profiler_chan);
pool.shutdown(); pool.shutdown();
} }

View file

@ -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
}

View file

@ -45,6 +45,10 @@ pub struct Opts {
/// it to produce output on that interval (`-p`). /// it to produce output on that interval (`-p`).
pub profiler_period: Option<f64>, 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 /// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive
/// sequential algorithm. /// sequential algorithm.
pub layout_threads: uint, 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("", "device-pixel-ratio", "Device pixels per px", ""),
getopts::optopt("t", "threads", "Number of render threads", "1"), getopts::optopt("t", "threads", "Number of render threads", "1"),
getopts::optflagopt("p", "profile", "Profiler flag and output interval", "10"), 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::optflag("x", "exit", "Exit after load flag"),
getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "1"), getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "1"),
getopts::optflag("z", "headless", "Headless mode"), 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. 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| { let profiler_period = opt_match.opt_default("p", "5").map(|period| {
from_str(period.as_slice()).unwrap() 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"); 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, tile_size: tile_size,
device_pixels_per_px: device_pixels_per_px, device_pixels_per_px: device_pixels_per_px,
profiler_period: profiler_period, profiler_period: profiler_period,
memory_profiler_period: memory_profiler_period,
layout_threads: layout_threads, layout_threads: layout_threads,
exit_after_load: opt_match.opt_present("x"), exit_after_load: opt_match.opt_present("x"),
output_file: opt_match.opt_str("o"), output_file: opt_match.opt_str("o"),

View file

@ -29,6 +29,7 @@ extern crate std_url = "url";
pub mod cache; pub mod cache;
pub mod debug_utils; pub mod debug_utils;
pub mod geometry; pub mod geometry;
pub mod memory;
pub mod namespace; pub mod namespace;
pub mod opts; pub mod opts;
pub mod range; pub mod range;