auto merge of #4894 : nnethercote/servo/read-smaps, r=jdm

Here's example -m output after these changesets are applied:

```
_size (MiB)_: _category_
     2798.61: vsize
      136.80: resident
      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)
       30.85: system-heap-allocated
        4.91: jemalloc-heap-allocated
        6.11: jemalloc-heap-active
      120.00: jemalloc-heap-mapped
```
The `resident-according-to-smaps` line is new, as are all the indented lines beneath it. This is useful particularly because it shows how much memory is taken up by code, e.g. the line ending in `servo (r-xp)` shows that the `servo` executable's code alone takes up 24 MiB of physical memory.
This commit is contained in:
bors-servo 2015-02-23 19:54:50 -07:00
commit eb7e86ac4e
6 changed files with 147 additions and 12 deletions

View file

@ -696,6 +696,11 @@ dependencies = [
"log 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "regex"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "rustc-serialize" name = "rustc-serialize"
version = "0.2.12" version = "0.2.12"
@ -867,6 +872,7 @@ dependencies = [
"libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"rand 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)",
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)",

View file

@ -43,10 +43,11 @@ git = "https://github.com/servo/string-cache"
git = "https://github.com/Kimundi/lazy-static.rs" git = "https://github.com/Kimundi/lazy-static.rs"
[dependencies] [dependencies]
text_writer = "0.1.1"
url = "0.2.16"
time = "0.1.12"
bitflags = "*" bitflags = "*"
rand = "*"
rustc-serialize = "0.2"
libc = "*" libc = "*"
rand = "*"
regex = "0.1.14"
rustc-serialize = "0.2"
text_writer = "0.1.1"
time = "0.1.12"
url = "0.2.16"

View file

@ -32,6 +32,8 @@ extern crate layers;
extern crate libc; extern crate libc;
#[no_link] #[macro_use] extern crate cssparser; #[no_link] #[macro_use] extern crate cssparser;
extern crate rand; extern crate rand;
#[cfg(target_os="linux")]
extern crate regex;
extern crate "rustc-serialize" as rustc_serialize; extern crate "rustc-serialize" as rustc_serialize;
#[cfg(target_os="macos")] #[cfg(target_os="macos")]
extern crate task_info; extern crate task_info;

View file

@ -11,8 +11,6 @@ use std::old_io::timer::sleep;
#[cfg(target_os="linux")] #[cfg(target_os="linux")]
use std::old_io::File; use std::old_io::File;
use std::mem::size_of; use std::mem::size_of;
#[cfg(target_os="linux")]
use std::env::page_size;
use std::ptr::null_mut; use std::ptr::null_mut;
use std::sync::mpsc::{Sender, channel, Receiver}; use std::sync::mpsc::{Sender, channel, Receiver};
use std::time::duration::Duration; use std::time::duration::Duration;
@ -111,21 +109,25 @@ impl MemoryProfiler {
match nbytes { match nbytes {
Some(nbytes) => { Some(nbytes) => {
let mebi = 1024f64 * 1024f64; let mebi = 1024f64 * 1024f64;
println!("{:24}: {:12.2}", path, (nbytes as f64) / mebi); println!("{:12.2}: {}", (nbytes as f64) / mebi, path);
} }
None => { None => {
println!("{:24}: {:>12}", path, "???"); println!("{:>12}: {}", "???", path);
} }
} }
} }
fn handle_print_msg(&self) { fn handle_print_msg(&self) {
println!("{:24}: {:12}", "_category_", "_size (MiB)_"); println!("{:12}: {}", "_size (MiB)_", "_category_");
// Virtual and physical memory usage, as reported by the OS. // Virtual and physical memory usage, as reported by the OS.
MemoryProfiler::print_measurement("vsize", get_vsize()); MemoryProfiler::print_measurement("vsize", get_vsize());
MemoryProfiler::print_measurement("resident", get_resident()); 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 // Total number of bytes allocated by the application on the system
// heap. // heap.
MemoryProfiler::print_measurement("system-heap-allocated", 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() { match f.read_to_string() {
Ok(contents) => { Ok(contents) => {
let s = option_try!(contents.as_slice().words().nth(field)); let s = option_try!(contents.as_slice().words().nth(field));
let npages: u64 = option_try!(s.parse().ok()); let npages = option_try!(s.parse::<u64>().ok());
Some(npages * (page_size() as u64)) Some(npages * (::std::env::page_size() as u64))
} }
Err(_) => None Err(_) => None
} }
@ -280,3 +282,115 @@ fn get_vsize() -> Option<u64> {
fn get_resident() -> Option<u64> { fn get_resident() -> Option<u64> {
None 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![]
}

6
ports/cef/Cargo.lock generated
View file

@ -704,6 +704,11 @@ dependencies = [
"log 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "regex"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "rustc-serialize" name = "rustc-serialize"
version = "0.2.12" version = "0.2.12"
@ -894,6 +899,7 @@ dependencies = [
"libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"rand 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)",
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)",

6
ports/gonk/Cargo.lock generated
View file

@ -616,6 +616,11 @@ dependencies = [
"log 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "regex"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "rustc-serialize" name = "rustc-serialize"
version = "0.2.12" version = "0.2.12"
@ -797,6 +802,7 @@ dependencies = [
"libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"rand 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)",
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)",