Skip to content

Commit

Permalink
Print heap histogram
Browse files Browse the repository at this point in the history
Also:
* Avoid unhelpful compiler warnings
* Capture common code in a macro

Note: it would have been nice to alias the closure type, but
rust-lang/rfcs#1733 is not yet implemented and macros
can't cope (rust-lang/rust#24010).
  • Loading branch information
glyn committed Jun 29, 2017
1 parent dbda939 commit a4c4bce
Show file tree
Hide file tree
Showing 9 changed files with 526 additions and 92 deletions.
7 changes: 4 additions & 3 deletions src/agentcontroller/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

pub struct AgentController<'a> {
#[allow(dead_code)] // TODO: revisit this once port is complete
jvmti: ::env::JvmTiEnv,
heuristic: Box<super::Heuristic + 'a>,
actions: Vec<Box<super::Action>>
Expand Down Expand Up @@ -126,9 +127,9 @@ mod tests {
ac.on_oom(dummy_jni_env(), 0);
}

unsafe extern "C" fn test_get_env(vm: *mut ::jvmti::JavaVM,
penv: *mut *mut ::std::os::raw::c_void,
version: ::jvmti::jint)
unsafe extern "C" fn test_get_env(_: *mut ::jvmti::JavaVM,
_: *mut *mut ::std::os::raw::c_void,
_: ::jvmti::jint)
-> ::jvmti::jint {
0
}
Expand Down
144 changes: 137 additions & 7 deletions src/agentcontroller/heaphistogram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,149 @@
* limitations under the License.
*/

pub struct HeapHistogram {
jvmti: ::env::JvmTiEnv,
use env::JvmTI;
use std::io::Write;
use std::io::stdout;
use heap::tagger::Tagger;
use heap::tagger::Tag;
use heap::stats::Stats;
use heap::stats::Record;
use heap::stats::Print;

pub struct HeapHistogram<T: JvmTI + Clone> {
jvmti: T,
}

impl HeapHistogram {
pub fn new(jvmti: ::env::JvmTiEnv) -> Result<Self, ::jvmti::jint> {
impl<T: JvmTI + Clone> HeapHistogram<T> {
pub fn new(mut jvmti: T) -> Result<Self, ::jvmti::jint> {
jvmti.enable_object_tagging()?;
Ok(Self {
jvmti: jvmti
jvmti: jvmti.clone(),
})
}

fn print(&self, writer: &mut Write) {
let mut tagger = Tagger::new();

// Tag all loaded classes so we can determine each object's class signature during heap traversal.
self.jvmti.tag_loaded_classes(&mut tagger);

let mut heap_stats = Stats::new();

// Traverse the live heap and add objects to the heap stats.
self.jvmti.traverse_live_heap(|class_tag: ::jvmti::jlong, size: ::jvmti::jlong| {
if let Some(sig) = tagger.class_signature(class_tag) {
heap_stats.recordObject(sig, size);
}
});

heap_stats.print(writer);
}
}

impl<T: JvmTI + Clone> super::Action for HeapHistogram<T> {
fn on_oom(&self, _: ::env::JniEnv, _: ::jvmti::jint) {
self.print(&mut stdout());
}
}

impl super::Action for HeapHistogram {
fn on_oom(&self, jni_env: ::env::JniEnv, resource_exhaustion_flags: ::jvmti::jint) {
#[cfg(test)]
mod tests {
use super::HeapHistogram;
use ::env::JvmTI;
use ::env::FnResourceExhausted;
use std::cell::RefCell;
use std::sync::Mutex;
use ::env::RawMonitorId;
use ::heap::tagger::Tag;

const test_error_code: ::jvmti::jint = 54;

#[test]
fn new_calls_enable_object_tagging() {
let mockJvmti = MockJvmti::new();
let hh = HeapHistogram::new(mockJvmti);
assert!(hh.is_ok());
assert!((hh.unwrap().jvmti as MockJvmti).object_tagging_enabled);
}

#[test]
fn new_percolates_enable_object_tagging_failure() {
let mut mockJvmti = MockJvmti::new();
mockJvmti.object_tagging_enabled_result = Err(test_error_code);
let hh = HeapHistogram::new(mockJvmti);
assert!(hh.is_err());
assert_eq!(hh.err().unwrap(), test_error_code);
}

#[test]
fn print_works() {
let mockJvmti = MockJvmti::new();
let hh = HeapHistogram::new(mockJvmti);

let mut buff: Vec<u8> = Vec::new();
hh.unwrap().print(&mut buff);
let string_buff = String::from_utf8(buff).expect("invalid UTF-8");
assert_eq!(string_buff, "| Instance Count | Total Bytes | Class Name |\n| 2 | 200 | sig2 |\n| 1 | 10 | sig1 |\n".to_string());

}

#[derive(Clone, Copy, Default)]
struct Classes {
t1: ::jvmti::jlong,
t2: ::jvmti::jlong
}

#[derive(Clone)]
struct MockJvmti {
pub object_tagging_enabled_result: Result<(), ::jvmti::jint>,
pub object_tagging_enabled: bool,
classes: RefCell<Classes>
}

impl MockJvmti {
fn new() -> MockJvmti {
MockJvmti {
object_tagging_enabled_result: Ok(()),
object_tagging_enabled: false,
classes: RefCell::new(Default::default())
}
}
}

impl JvmTI for MockJvmti {
fn create_raw_monitor(&mut self, name: String, monitor: &Mutex<RawMonitorId>) -> Result<(), ::jvmti::jint> {
unimplemented!()
}

fn raw_monitor_enter(&mut self, monitor: &Mutex<RawMonitorId>) -> Result<(), ::jvmti::jint> {
unimplemented!()
}

fn raw_monitor_exit(&mut self, monitor: &Mutex<RawMonitorId>) -> Result<(), ::jvmti::jint> {
unimplemented!()
}

fn on_resource_exhausted(&mut self, callback: FnResourceExhausted) -> Result<(), ::jvmti::jint> {
unimplemented!()
}

fn enable_object_tagging(&mut self) -> Result<(), ::jvmti::jint> {
self.object_tagging_enabled = true;
self.object_tagging_enabled_result
}

fn tag_loaded_classes(&self, tagger: &mut Tag) {
let mut c = self.classes.borrow_mut();
c.t1 = tagger.class_tag(&"sig1".to_string());
c.t2 = tagger.class_tag(&"sig2".to_string());
}

fn traverse_live_heap<F>(&self, mut closure: F) where F: FnMut(::jvmti::jlong, ::jvmti::jlong) {
let c = self.classes.borrow();
closure(c.t1, 10);
closure(c.t2, 100);
closure(c.t2, 100);
}
}
}
3 changes: 2 additions & 1 deletion src/agentcontroller/kill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl Kill {
}
}

#[cfg(test)]
pub fn setSignal(&mut self, signal: c_int) {
self.signal = signal;
}
Expand Down Expand Up @@ -93,7 +94,7 @@ mod tests {
signal::SaFlags::empty(),
signal::SigSet::empty());
unsafe {
signal::sigaction(signal::SIGUSR1, &sig_action);
signal::sigaction(signal::SIGUSR1, &sig_action).unwrap();
}
}
}
2 changes: 1 addition & 1 deletion src/agentcontroller/threshold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ mod tests {
assert!(!threshold.on_oom());
assert!(!threshold.on_oom());

thread::sleep(time::Duration::from_millis(1000));
thread::sleep(time::Duration::from_millis(1100));

assert!(!threshold.on_oom());
assert!(!threshold.on_oom());
Expand Down
Loading

0 comments on commit a4c4bce

Please sign in to comment.