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,
|
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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
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`).
|
/// 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"),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue