aboutsummaryrefslogtreecommitdiffstats
path: root/components/util/memory.rs
diff options
context:
space:
mode:
authorNicholas Nethercote <nnethercote@mozilla.com>2015-02-09 23:00:27 -0800
committerNicholas Nethercote <nnethercote@mozilla.com>2015-02-23 15:20:32 -0800
commit34a384241aae7dc79a500cdd6738a11b560d8352 (patch)
tree6b9ec6d78332a5f723fb1190c574a1bd163dcb7c /components/util/memory.rs
parent121394a121be5a2a8be491cc5c5b6ef13ca9f74d (diff)
downloadservo-34a384241aae7dc79a500cdd6738a11b560d8352.tar.gz
servo-34a384241aae7dc79a500cdd6738a11b560d8352.zip
Report detailed RSS measurements from /proc/<pid>/smaps on Linux.
All anonymous segments are aggregated into a single measurement, as are all segments smaller than 512 KiB. Example output: 142.89: resident-according-to-smaps 97.84: - anonymous (rw-p) 23.98: - /home/njn/moz/servo/components/servo/target/servo (r-xp) 6.58: - [heap] (rw-p) 5.36: - other 3.51: - /usr/lib/x86_64-linux-gnu/dri/i965_dri.so (r-xp) 1.33: - /lib/x86_64-linux-gnu/libc-2.19.so (r-xp) 0.93: - /home/njn/moz/servo/components/servo/target/servo (r--p) 0.76: - /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.20 (r-xp) 0.74: - /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0 (r-xp) 0.50: - /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (r-xp) 0.50: - /lib/x86_64-linux-gnu/libglib-2.0.so.0.4200.1 (r-xp) 0.45: - /usr/lib/x86_64-linux-gnu/mesa/libGL.so.1.2.0 (r-xp) 0.43: - /lib/x86_64-linux-gnu/libm-2.19.so (r-xp)
Diffstat (limited to 'components/util/memory.rs')
-rw-r--r--components/util/memory.rs122
1 files changed, 118 insertions, 4 deletions
diff --git a/components/util/memory.rs b/components/util/memory.rs
index 15d7902eff4..f895c129fea 100644
--- a/components/util/memory.rs
+++ b/components/util/memory.rs
@@ -11,8 +11,6 @@ use std::old_io::timer::sleep;
#[cfg(target_os="linux")]
use std::old_io::File;
use std::mem::size_of;
-#[cfg(target_os="linux")]
-use std::env::page_size;
use std::ptr::null_mut;
use std::sync::mpsc::{Sender, channel, Receiver};
use std::time::duration::Duration;
@@ -126,6 +124,10 @@ impl MemoryProfiler {
MemoryProfiler::print_measurement("vsize", get_vsize());
MemoryProfiler::print_measurement("resident", get_resident());
+ for seg in get_resident_segments().iter() {
+ MemoryProfiler::print_measurement(seg.0.as_slice(), Some(seg.1));
+ }
+
// Total number of bytes allocated by the application on the system
// heap.
MemoryProfiler::print_measurement("system-heap-allocated",
@@ -244,8 +246,8 @@ fn get_proc_self_statm_field(field: uint) -> Option<u64> {
match f.read_to_string() {
Ok(contents) => {
let s = option_try!(contents.as_slice().words().nth(field));
- let npages: u64 = option_try!(s.parse().ok());
- Some(npages * (page_size() as u64))
+ let npages = option_try!(s.parse::<u64>().ok());
+ Some(npages * (::std::env::page_size() as u64))
}
Err(_) => None
}
@@ -280,3 +282,115 @@ fn get_vsize() -> Option<u64> {
fn get_resident() -> Option<u64> {
None
}
+
+#[cfg(target_os="linux")]
+fn get_resident_segments() -> Vec<(String, u64)> {
+ use regex::Regex;
+ use std::collections::HashMap;
+ use std::collections::hash_map::Entry;
+
+ // The first line of an entry in /proc/<pid>/smaps looks just like an entry
+ // in /proc/<pid>/maps:
+ //
+ // address perms offset dev inode pathname
+ // 02366000-025d8000 rw-p 00000000 00:00 0 [heap]
+ //
+ // Each of the following lines contains a key and a value, separated
+ // by ": ", where the key does not contain either of those characters.
+ // For example:
+ //
+ // Rss: 132 kB
+
+ let path = Path::new("/proc/self/smaps");
+ let mut f = ::std::old_io::BufferedReader::new(File::open(&path));
+
+ let seg_re = Regex::new(
+ r"^[:xdigit:]+-[:xdigit:]+ (....) [:xdigit:]+ [:xdigit:]+:[:xdigit:]+ \d+ +(.*)").unwrap();
+ let rss_re = Regex::new(r"^Rss: +(\d+) kB").unwrap();
+
+ // We record each segment's resident size.
+ let mut seg_map: HashMap<String, u64> = HashMap::new();
+
+ #[derive(PartialEq)]
+ enum LookingFor { Segment, Rss }
+ let mut looking_for = LookingFor::Segment;
+
+ let mut curr_seg_name = String::new();
+
+ // Parse the file.
+ for line in f.lines() {
+ let line = match line {
+ Ok(line) => line,
+ Err(_) => continue,
+ };
+ if looking_for == LookingFor::Segment {
+ // Look for a segment info line.
+ let cap = match seg_re.captures(line.as_slice()) {
+ Some(cap) => cap,
+ None => continue,
+ };
+ let perms = cap.at(1).unwrap();
+ let pathname = cap.at(2).unwrap();
+
+ // Construct the segment name from its pathname and permissions.
+ curr_seg_name.clear();
+ curr_seg_name.push_str("- ");
+ if pathname == "" || pathname.starts_with("[stack:") {
+ // Anonymous memory. Entries marked with "[stack:nnn]"
+ // look like thread stacks but they may include other
+ // anonymous mappings, so we can't trust them and just
+ // treat them as entirely anonymous.
+ curr_seg_name.push_str("anonymous");
+ } else {
+ curr_seg_name.push_str(pathname);
+ }
+ curr_seg_name.push_str(" (");
+ curr_seg_name.push_str(perms);
+ curr_seg_name.push_str(")");
+
+ looking_for = LookingFor::Rss;
+ } else {
+ // Look for an "Rss:" line.
+ let cap = match rss_re.captures(line.as_slice()) {
+ Some(cap) => cap,
+ None => continue,
+ };
+ let rss = cap.at(1).unwrap().parse::<u64>().unwrap() * 1024;
+
+ if rss > 0 {
+ // Aggregate small segments into "- other".
+ let seg_name = if rss < 512 * 1024 {
+ "- other".to_owned()
+ } else {
+ curr_seg_name.clone()
+ };
+ match seg_map.entry(seg_name) {
+ Entry::Vacant(entry) => { entry.insert(rss); },
+ Entry::Occupied(mut entry) => *entry.get_mut() += rss,
+ }
+ }
+
+ looking_for = LookingFor::Segment;
+ }
+ }
+
+ let mut segs: Vec<(String, u64)> = seg_map.into_iter().collect();
+
+ // Get the total and add it to the vector. Note that this total differs
+ // from the "resident" measurement obtained via /proc/<pid>/statm in
+ // get_resident(). It's unclear why this difference occurs; for some
+ // processes the measurements match, but for Servo they do not.
+ let total = segs.iter().fold(0u64, |total, &(_, size)| total + size);
+ segs.push(("resident-according-to-smaps".to_owned(), total));
+
+ // Sort by size; the total will be first.
+ segs.sort_by(|&(_, rss1), &(_, rss2)| rss2.cmp(&rss1));
+
+ segs
+}
+
+#[cfg(not(target_os="linux"))]
+fn get_resident_segments() -> Vec<(String, u64)> {
+ vec![]
+}
+