profile: Make the time and memory profilers run over IPC.

Uses the `Router` abstraction inside `ipc-channel` to avoid spawning new
threads.
This commit is contained in:
Patrick Walton 2015-07-14 18:28:57 -07:00
parent ed1b6a3513
commit f10c076180
19 changed files with 212 additions and 168 deletions

View file

@ -13,6 +13,9 @@ path = "../profile_traits"
[dependencies.util]
path = "../util"
[dependencies.ipc-channel]
git = "https://github.com/pcwalton/ipc-channel"
[dependencies]
log = "0.3"
libc = "0.1"

View file

@ -9,6 +9,7 @@
#[macro_use] extern crate log;
extern crate ipc_channel;
extern crate libc;
#[macro_use]
extern crate profile_traits;

View file

@ -4,26 +4,26 @@
//! Memory profiling functions.
use profile_traits::mem::{ProfilerChan, ProfilerMsg, Reporter, ReportsChan};
use self::system_reporter::SystemReporter;
use ipc_channel::ipc::{self, IpcReceiver};
use ipc_channel::router::ROUTER;
use profile_traits::mem::{ProfilerChan, ProfilerMsg, Reporter, ReporterRequest, ReportsChan};
use std::borrow::ToOwned;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::thread::sleep_ms;
use std::sync::mpsc::{channel, Receiver};
use util::task::spawn_named;
pub struct Profiler {
/// The port through which messages are received.
pub port: Receiver<ProfilerMsg>,
pub port: IpcReceiver<ProfilerMsg>,
/// Registered memory reporters.
reporters: HashMap<String, Box<Reporter + Send>>,
reporters: HashMap<String, Reporter>,
}
impl Profiler {
pub fn create(period: Option<f64>) -> ProfilerChan {
let (chan, port) = channel();
let (chan, port) = ipc::channel().unwrap();
// Create the timer thread if a period was provided.
if let Some(period) = period {
@ -48,16 +48,21 @@ impl Profiler {
let mem_profiler_chan = ProfilerChan(chan);
// Register the system memory reporter, which will run on the memory profiler's own thread.
// It never needs to be unregistered, because as long as the memory profiler is running the
// system memory reporter can make measurements.
let system_reporter = box SystemReporter;
mem_profiler_chan.send(ProfilerMsg::RegisterReporter("system".to_owned(), system_reporter));
// Register the system memory reporter, which will run on its own thread. It never needs to
// be unregistered, because as long as the memory profiler is running the system memory
// reporter can make measurements.
let (system_reporter_sender, system_reporter_receiver) = ipc::channel().unwrap();
ROUTER.add_route(system_reporter_receiver.to_opaque(), box |message| {
let request: ReporterRequest = message.to().unwrap();
system_reporter::collect_reports(request)
});
mem_profiler_chan.send(ProfilerMsg::RegisterReporter("system".to_owned(),
Reporter(system_reporter_sender)));
mem_profiler_chan
}
pub fn new(port: Receiver<ProfilerMsg>) -> Profiler {
pub fn new(port: IpcReceiver<ProfilerMsg>) -> Profiler {
Profiler {
port: port,
reporters: HashMap::new(),
@ -119,12 +124,11 @@ impl Profiler {
// If anything goes wrong with a reporter, we just skip it.
let mut forest = ReportsForest::new();
for reporter in self.reporters.values() {
let (chan, port) = channel();
if reporter.collect_reports(ReportsChan(chan)) {
if let Ok(reports) = port.recv() {
for report in reports.iter() {
forest.insert(&report.path, report.size);
}
let (chan, port) = ipc::channel().unwrap();
reporter.collect_reports(ReportsChan(chan));
if let Ok(reports) = port.recv() {
for report in reports.iter() {
forest.insert(&report.path, report.size);
}
}
}
@ -297,7 +301,7 @@ impl ReportsForest {
mod system_reporter {
use libc::{c_char, c_int, c_void, size_t};
use profile_traits::mem::{Report, Reporter, ReportsChan};
use profile_traits::mem::{Report, ReporterRequest};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::mem::size_of;
@ -306,51 +310,46 @@ mod system_reporter {
use task_info::task_basic_info::{virtual_size, resident_size};
/// Collects global measurements from the OS and heap allocators.
pub struct SystemReporter;
impl Reporter for SystemReporter {
fn collect_reports(&self, reports_chan: ReportsChan) -> bool {
let mut reports = vec![];
{
let mut report = |path, size| {
if let Some(size) = size {
reports.push(Report { path: path, size: size });
}
};
// Virtual and physical memory usage, as reported by the OS.
report(path!["vsize"], get_vsize());
report(path!["resident"], get_resident());
// Memory segments, as reported by the OS.
for seg in get_resident_segments().iter() {
report(path!["resident-according-to-smaps", seg.0], Some(seg.1));
pub fn collect_reports(request: ReporterRequest) {
let mut reports = vec![];
{
let mut report = |path, size| {
if let Some(size) = size {
reports.push(Report { path: path, size: size });
}
};
// Total number of bytes allocated by the application on the system
// heap.
report(path!["system-heap-allocated"], get_system_heap_allocated());
// Virtual and physical memory usage, as reported by the OS.
report(path!["vsize"], get_vsize());
report(path!["resident"], get_resident());
// The descriptions of the following jemalloc measurements are taken
// directly from the jemalloc documentation.
// "Total number of bytes allocated by the application."
report(path!["jemalloc-heap-allocated"], get_jemalloc_stat("stats.allocated"));
// "Total number of bytes in active pages allocated by the application.
// This is a multiple of the page size, and greater than or equal to
// |stats.allocated|."
report(path!["jemalloc-heap-active"], get_jemalloc_stat("stats.active"));
// "Total number of bytes in chunks mapped on behalf of the application.
// This is a multiple of the chunk size, and is at least as large as
// |stats.active|. This does not include inactive chunks."
report(path!["jemalloc-heap-mapped"], get_jemalloc_stat("stats.mapped"));
// Memory segments, as reported by the OS.
for seg in get_resident_segments().iter() {
report(path!["resident-according-to-smaps", seg.0], Some(seg.1));
}
reports_chan.send(reports);
true
// Total number of bytes allocated by the application on the system
// heap.
report(path!["system-heap-allocated"], get_system_heap_allocated());
// The descriptions of the following jemalloc measurements are taken
// directly from the jemalloc documentation.
// "Total number of bytes allocated by the application."
report(path!["jemalloc-heap-allocated"], get_jemalloc_stat("stats.allocated"));
// "Total number of bytes in active pages allocated by the application.
// This is a multiple of the page size, and greater than or equal to
// |stats.allocated|."
report(path!["jemalloc-heap-active"], get_jemalloc_stat("stats.active"));
// "Total number of bytes in chunks mapped on behalf of the application.
// This is a multiple of the chunk size, and is at least as large as
// |stats.active|. This does not include inactive chunks."
report(path!["jemalloc-heap-mapped"], get_jemalloc_stat("stats.mapped"));
}
request.reports_channel.send(reports);
}
#[cfg(target_os="linux")]

View file

@ -4,12 +4,12 @@
//! Timing functions.
use ipc_channel::ipc::{self, IpcReceiver};
use profile_traits::time::{ProfilerCategory, ProfilerChan, ProfilerMsg, TimerMetadata};
use std::borrow::ToOwned;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::f64;
use std::sync::mpsc::{channel, Receiver};
use std::thread::sleep_ms;
use std_time::precise_time_ns;
use util::task::spawn_named;
@ -86,14 +86,14 @@ type ProfilerBuckets = BTreeMap<(ProfilerCategory, Option<TimerMetadata>), Vec<f
// back end of the profiler that handles data aggregation and performance metrics
pub struct Profiler {
pub port: Receiver<ProfilerMsg>,
pub port: IpcReceiver<ProfilerMsg>,
buckets: ProfilerBuckets,
pub last_msg: Option<ProfilerMsg>,
}
impl Profiler {
pub fn create(period: Option<f64>) -> ProfilerChan {
let (chan, port) = channel();
let (chan, port) = ipc::channel().unwrap();
match period {
Some(period) => {
let period = (period * 1000.) as u32;
@ -128,7 +128,7 @@ impl Profiler {
ProfilerChan(chan)
}
pub fn new(port: Receiver<ProfilerMsg>) -> Profiler {
pub fn new(port: IpcReceiver<ProfilerMsg>) -> Profiler {
Profiler {
port: port,
buckets: BTreeMap::new(),