mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Add a basic memory profiler, invoked with -m.
This commit is contained in:
parent
be2e27db54
commit
fad919ccf7
8 changed files with 205 additions and 13 deletions
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
158
src/components/util/memory.rs
Normal file
158
src/components/util/memory.rs
Normal 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
|
||||
}
|
||||
|
|
@ -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"),
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue