Skip to content

Commit a3ac9e1

Browse files
committed
Print heap histogram
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).
1 parent d802c4a commit a3ac9e1

File tree

9 files changed

+526
-92
lines changed

9 files changed

+526
-92
lines changed

src/agentcontroller/controller.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

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

129-
unsafe extern "C" fn test_get_env(vm: *mut ::jvmti::JavaVM,
130-
penv: *mut *mut ::std::os::raw::c_void,
131-
version: ::jvmti::jint)
130+
unsafe extern "C" fn test_get_env(_: *mut ::jvmti::JavaVM,
131+
_: *mut *mut ::std::os::raw::c_void,
132+
_: ::jvmti::jint)
132133
-> ::jvmti::jint {
133134
0
134135
}

src/agentcontroller/heaphistogram.rs

+137-7
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,149 @@
1414
* limitations under the License.
1515
*/
1616

17-
pub struct HeapHistogram {
18-
jvmti: ::env::JvmTiEnv,
17+
use env::JvmTI;
18+
use std::io::Write;
19+
use std::io::stdout;
20+
use heap::tagger::Tagger;
21+
use heap::tagger::Tag;
22+
use heap::stats::Stats;
23+
use heap::stats::Record;
24+
use heap::stats::Print;
25+
26+
pub struct HeapHistogram<T: JvmTI + Clone> {
27+
jvmti: T,
1928
}
2029

21-
impl HeapHistogram {
22-
pub fn new(jvmti: ::env::JvmTiEnv) -> Result<Self, ::jvmti::jint> {
30+
impl<T: JvmTI + Clone> HeapHistogram<T> {
31+
pub fn new(mut jvmti: T) -> Result<Self, ::jvmti::jint> {
32+
jvmti.enable_object_tagging()?;
2333
Ok(Self {
24-
jvmti: jvmti
34+
jvmti: jvmti.clone(),
2535
})
2636
}
37+
38+
fn print(&self, writer: &mut Write) {
39+
let mut tagger = Tagger::new();
40+
41+
// Tag all loaded classes so we can determine each object's class signature during heap traversal.
42+
self.jvmti.tag_loaded_classes(&mut tagger);
43+
44+
let mut heap_stats = Stats::new();
45+
46+
// Traverse the live heap and add objects to the heap stats.
47+
self.jvmti.traverse_live_heap(|class_tag: ::jvmti::jlong, size: ::jvmti::jlong| {
48+
if let Some(sig) = tagger.class_signature(class_tag) {
49+
heap_stats.recordObject(sig, size);
50+
}
51+
});
52+
53+
heap_stats.print(writer);
54+
}
55+
}
56+
57+
impl<T: JvmTI + Clone> super::Action for HeapHistogram<T> {
58+
fn on_oom(&self, _: ::env::JniEnv, _: ::jvmti::jint) {
59+
self.print(&mut stdout());
60+
}
2761
}
2862

29-
impl super::Action for HeapHistogram {
30-
fn on_oom(&self, jni_env: ::env::JniEnv, resource_exhaustion_flags: ::jvmti::jint) {
63+
#[cfg(test)]
64+
mod tests {
65+
use super::HeapHistogram;
66+
use ::env::JvmTI;
67+
use ::env::FnResourceExhausted;
68+
use std::cell::RefCell;
69+
use std::sync::Mutex;
70+
use ::env::RawMonitorId;
71+
use ::heap::tagger::Tag;
72+
73+
const test_error_code: ::jvmti::jint = 54;
74+
75+
#[test]
76+
fn new_calls_enable_object_tagging() {
77+
let mockJvmti = MockJvmti::new();
78+
let hh = HeapHistogram::new(mockJvmti);
79+
assert!(hh.is_ok());
80+
assert!((hh.unwrap().jvmti as MockJvmti).object_tagging_enabled);
81+
}
82+
83+
#[test]
84+
fn new_percolates_enable_object_tagging_failure() {
85+
let mut mockJvmti = MockJvmti::new();
86+
mockJvmti.object_tagging_enabled_result = Err(test_error_code);
87+
let hh = HeapHistogram::new(mockJvmti);
88+
assert!(hh.is_err());
89+
assert_eq!(hh.err().unwrap(), test_error_code);
90+
}
91+
92+
#[test]
93+
fn print_works() {
94+
let mockJvmti = MockJvmti::new();
95+
let hh = HeapHistogram::new(mockJvmti);
96+
97+
let mut buff: Vec<u8> = Vec::new();
98+
hh.unwrap().print(&mut buff);
99+
let string_buff = String::from_utf8(buff).expect("invalid UTF-8");
100+
assert_eq!(string_buff, "| Instance Count | Total Bytes | Class Name |\n| 2 | 200 | sig2 |\n| 1 | 10 | sig1 |\n".to_string());
101+
102+
}
103+
104+
#[derive(Clone, Copy, Default)]
105+
struct Classes {
106+
t1: ::jvmti::jlong,
107+
t2: ::jvmti::jlong
108+
}
109+
110+
#[derive(Clone)]
111+
struct MockJvmti {
112+
pub object_tagging_enabled_result: Result<(), ::jvmti::jint>,
113+
pub object_tagging_enabled: bool,
114+
classes: RefCell<Classes>
115+
}
116+
117+
impl MockJvmti {
118+
fn new() -> MockJvmti {
119+
MockJvmti {
120+
object_tagging_enabled_result: Ok(()),
121+
object_tagging_enabled: false,
122+
classes: RefCell::new(Default::default())
123+
}
124+
}
125+
}
126+
127+
impl JvmTI for MockJvmti {
128+
fn create_raw_monitor(&mut self, name: String, monitor: &Mutex<RawMonitorId>) -> Result<(), ::jvmti::jint> {
129+
unimplemented!()
130+
}
131+
132+
fn raw_monitor_enter(&mut self, monitor: &Mutex<RawMonitorId>) -> Result<(), ::jvmti::jint> {
133+
unimplemented!()
134+
}
135+
136+
fn raw_monitor_exit(&mut self, monitor: &Mutex<RawMonitorId>) -> Result<(), ::jvmti::jint> {
137+
unimplemented!()
138+
}
139+
140+
fn on_resource_exhausted(&mut self, callback: FnResourceExhausted) -> Result<(), ::jvmti::jint> {
141+
unimplemented!()
142+
}
143+
144+
fn enable_object_tagging(&mut self) -> Result<(), ::jvmti::jint> {
145+
self.object_tagging_enabled = true;
146+
self.object_tagging_enabled_result
147+
}
148+
149+
fn tag_loaded_classes(&self, tagger: &mut Tag) {
150+
let mut c = self.classes.borrow_mut();
151+
c.t1 = tagger.class_tag(&"sig1".to_string());
152+
c.t2 = tagger.class_tag(&"sig2".to_string());
153+
}
154+
155+
fn traverse_live_heap<F>(&self, mut closure: F) where F: FnMut(::jvmti::jlong, ::jvmti::jlong) {
156+
let c = self.classes.borrow();
157+
closure(c.t1, 10);
158+
closure(c.t2, 100);
159+
closure(c.t2, 100);
160+
}
31161
}
32162
}

src/agentcontroller/kill.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ impl Kill {
3030
}
3131
}
3232

33+
#[cfg(test)]
3334
pub fn setSignal(&mut self, signal: c_int) {
3435
self.signal = signal;
3536
}
@@ -93,7 +94,7 @@ mod tests {
9394
signal::SaFlags::empty(),
9495
signal::SigSet::empty());
9596
unsafe {
96-
signal::sigaction(signal::SIGUSR1, &sig_action);
97+
signal::sigaction(signal::SIGUSR1, &sig_action).unwrap();
9798
}
9899
}
99100
}

src/agentcontroller/threshold.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ mod tests {
9090
assert!(!threshold.on_oom());
9191
assert!(!threshold.on_oom());
9292

93-
thread::sleep(time::Duration::from_millis(1000));
93+
thread::sleep(time::Duration::from_millis(1100));
9494

9595
assert!(!threshold.on_oom());
9696
assert!(!threshold.on_oom());

0 commit comments

Comments
 (0)