diff --git a/Cargo.toml b/Cargo.toml index bc8508a..90c91a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,6 @@ authors = ["Thijs Cadier ", "Robert Beekman ", "Dax Huiberts ", "Timon Vonk "] + +[dependencies] +libc = "*" diff --git a/fixtures/linux/process_memory/proc_self_statm b/fixtures/linux/process_memory/proc_self_statm new file mode 100644 index 0000000..1b91c9f --- /dev/null +++ b/fixtures/linux/process_memory/proc_self_statm @@ -0,0 +1 @@ +6373 1138 428 214 0 755 0 diff --git a/fixtures/linux/process_memory/proc_self_statm_garbage b/fixtures/linux/process_memory/proc_self_statm_garbage new file mode 100644 index 0000000..c496177 --- /dev/null +++ b/fixtures/linux/process_memory/proc_self_statm_garbage @@ -0,0 +1 @@ +eceveveev diff --git a/fixtures/linux/process_memory/proc_self_statm_incomplete b/fixtures/linux/process_memory/proc_self_statm_incomplete new file mode 100644 index 0000000..75864bc --- /dev/null +++ b/fixtures/linux/process_memory/proc_self_statm_incomplete @@ -0,0 +1 @@ +1086 diff --git a/src/lib.rs b/src/lib.rs index 615109d..f294077 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,10 @@ +extern crate libc; + mod error; pub mod load; pub mod memory; pub mod cpu_stat; +pub mod process_memory; use std::fs; use std::io; diff --git a/src/process_memory.rs b/src/process_memory.rs new file mode 100644 index 0000000..e08aec9 --- /dev/null +++ b/src/process_memory.rs @@ -0,0 +1,140 @@ +// These methods are described in: +// http://nadeausoftware.com/articles/2012/07/c_c_tip_how_get_process_resident_set_size_physical_memory_use + +use libc; +use super::Result; + +/// Get the current RSS memory of this process in KB +#[cfg(target_os = "linux")] +pub fn current_rss() -> Result { + os::current_rss() +} + +/// Get the current RSS memory of a process with given pid in KB +#[cfg(target_os = "linux")] +pub fn current_rss_of(pid: libc::pid_t) -> Result { + os::current_rss_of(pid) +} + +/// Get the max RSS memory of this process in KB +#[cfg(target_os = "linux")] +pub fn max_rss() -> u64 { + os::max_rss() +} + +#[cfg(target_os = "linux")] +mod os { + use std::mem; + use libc; + use std::path::Path; + use super::super::Result; + use super::super::ProbeError; + use super::super::file_to_string; + + #[inline] + pub fn current_rss() -> Result { + read_and_get_current_rss(&Path::new("/proc/self/statm")) + } + + #[inline] + pub fn current_rss_of(pid: libc::pid_t) -> Result { + read_and_get_current_rss(&Path::new(&format!("/proc/{}/statm", pid))) + } + + #[inline] + pub fn read_and_get_current_rss(path: &Path) -> Result { + let raw_data = try!(file_to_string(path)); + let segments: Vec<&str> = raw_data.split_whitespace().collect(); + + if segments.len() < 2 { + return Err(ProbeError::UnexpectedContent("Incorrect number of segments".to_owned())) + } + + let pages: u64 = try!(segments[1].parse().map_err(|_| { + ProbeError::UnexpectedContent("Could not parse segment".to_owned()) + })); + + // Value is in pages, needs to be multiplied by the page size to get a value in KB. We ask the OS + // for this information using sysconf. + let pagesize = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as u64; + + Ok(pages * pagesize) + } + + #[inline] + pub fn max_rss() -> u64 { + let mut rusage: libc::rusage = unsafe { mem::uninitialized() }; + unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut rusage) }; + rusage.ru_maxrss as u64 + } +} + +#[cfg(test)] +mod tests { + use libc; + use std::path::Path; + use super::super::ProbeError; + + #[test] + fn test_current_rss() { + assert!(super::current_rss().is_ok()); + // See if it's a sort of sane value, between 1 and 10 mb + assert!(super::current_rss().unwrap() > 1_000_000); + assert!(super::current_rss().unwrap() < 10_000_000); + } + + #[test] + fn test_read_and_get_current_rss() { + let path = Path::new("fixtures/linux/process_memory/proc_self_statm"); + let value = super::os::read_and_get_current_rss(&path).unwrap(); + assert_eq!(4_661_248, value); + } + + #[test] + fn test_read_and_get_current_rss_wrong_path() { + let path = Path::new("/nonsense"); + match super::os::read_and_get_current_rss(&path) { + Err(ProbeError::IO(_)) => (), + r => panic!("Unexpected result: {:?}", r) + } + } + + #[test] + fn test_read_and_get_current_rss_incomplete() { + let path = Path::new("fixtures/linux/process_memory/proc_self_statm_incomplete"); + match super::os::read_and_get_current_rss(&path) { + Err(ProbeError::UnexpectedContent(_)) => (), + r => panic!("Unexpected result: {:?}", r) + } + } + + #[test] + fn test_read_and_get_current_rss_garbage() { + let path = Path::new("fixtures/linux/process_memory/proc_self_statm_garbage"); + match super::os::read_and_get_current_rss(&path) { + Err(ProbeError::UnexpectedContent(_)) => (), + r => panic!("Unexpected result: {:?}", r) + } + } + + #[test] + fn test_current_rss_of() { + let pid = unsafe { libc::getpid() }; + assert!(super::current_rss_of(pid).is_ok()); + // See if it's a sort of sane value, between 1 and 10 mb + assert!(super::current_rss_of(pid).unwrap() > 1_000_000); + assert!(super::current_rss_of(pid).unwrap() < 10_000_000); + } + + #[test] + fn test_current_rss_of_invalid_pid() { + assert!(super::current_rss_of(0).is_err()); + } + + #[test] + fn test_max_rss() { + // See if it's a sort of sane value, between 1 and 10 mb + assert!(super::current_rss().unwrap() > 1_000_000); + assert!(super::current_rss().unwrap() < 10_000_000); + } +}