aboutsummaryrefslogtreecommitdiffstats
path: root/components/util/memory.rs
diff options
context:
space:
mode:
authorJack Moffitt <jack@metajack.im>2014-08-28 09:34:23 -0600
committerJack Moffitt <jack@metajack.im>2014-09-08 20:21:42 -0600
commitc6ab60dbfc6da7b4f800c9e40893c8b58413960c (patch)
treed1d74076cf7fa20e4f77ec7cb82cae98b67362cb /components/util/memory.rs
parentdb2f642c32fc5bed445bb6f2e45b0f6f0b4342cf (diff)
downloadservo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.tar.gz
servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.zip
Cargoify servo
Diffstat (limited to 'components/util/memory.rs')
-rw-r--r--components/util/memory.rs209
1 files changed, 209 insertions, 0 deletions
diff --git a/components/util/memory.rs b/components/util/memory.rs
new file mode 100644
index 00000000000..25aa13c8316
--- /dev/null
+++ b/components/util/memory.rs
@@ -0,0 +1,209 @@
+/* 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 libc::{c_char,c_int,c_void,size_t};
+use std::io::timer::sleep;
+#[cfg(target_os="linux")]
+use std::io::File;
+use std::mem::size_of;
+#[cfg(target_os="linux")]
+use std::os::page_size;
+use std::ptr::mut_null;
+use task::spawn_named;
+#[cfg(target_os="macos")]
+use task_info::task_basic_info::{virtual_size,resident_size};
+
+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 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<u64>) {
+ match nbytes {
+ Some(nbytes) => {
+ let mebi = 1024f64 * 1024f64;
+ println!("{:16s}: {:12.2f}", path, (nbytes as f64) / mebi);
+ }
+ None => {
+ println!("{:16s}: {:>12s}", path, "???");
+ }
+ }
+ }
+
+ fn handle_print_msg(&self) {
+ println!("{:16s}: {:12s}", "_category_", "_size (MiB)_");
+
+ // Virtual and physical memory usage, as reported by the OS.
+ MemoryProfiler::print_measurement("vsize", get_vsize());
+ MemoryProfiler::print_measurement("resident", get_resident());
+
+ // The descriptions of the jemalloc measurements are taken directly
+ // from the jemalloc documentation.
+
+ // Total number of bytes allocated by the application.
+ MemoryProfiler::print_measurement("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|.
+ MemoryProfiler::print_measurement("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.
+ MemoryProfiler::print_measurement("heap-mapped", get_jemalloc_stat("stats.mapped"));
+
+ println!("");
+ }
+}
+
+extern {
+ fn je_mallctl(name: *const c_char, oldp: *mut c_void, oldlenp: *mut size_t,
+ newp: *mut c_void, newlen: size_t) -> c_int;
+}
+
+fn get_jemalloc_stat(name: &'static str) -> Option<u64> {
+ let mut old: size_t = 0;
+ let c_name = name.to_c_str();
+ let oldp = &mut old as *mut _ as *mut c_void;
+ let mut oldlen = size_of::<size_t>() as size_t;
+ let rv: c_int;
+ unsafe {
+ rv = je_mallctl(c_name.unwrap(), oldp, &mut oldlen, mut_null(), 0);
+ }
+ if rv == 0 { Some(old as u64) } else { None }
+}
+
+// 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<u64> {
+ let mut f = File::open(&Path::new("/proc/self/statm"));
+ match f.read_to_string() {
+ Ok(contents) => {
+ let s = option_try!(contents.as_slice().words().nth(field));
+ let npages: u64 = option_try!(from_str(s));
+ Some(npages * (page_size() as u64))
+ }
+ Err(_) => None
+ }
+}
+
+#[cfg(target_os="linux")]
+fn get_vsize() -> Option<u64> {
+ get_proc_self_statm_field(0)
+}
+
+#[cfg(target_os="linux")]
+fn get_resident() -> Option<u64> {
+ get_proc_self_statm_field(1)
+}
+
+#[cfg(target_os="macos")]
+fn get_vsize() -> Option<u64> {
+ virtual_size()
+}
+
+#[cfg(target_os="macos")]
+fn get_resident() -> Option<u64> {
+ resident_size()
+}
+
+#[cfg(not(target_os="linux"), not(target_os = "macos"))]
+fn get_vsize() -> Option<u64> {
+ None
+}
+
+#[cfg(not(target_os="linux"), not(target_os = "macos"))]
+fn get_resident() -> Option<u64> {
+ None
+}
+