15
15
*/
16
16
17
17
use std:: mem:: size_of;
18
+ use std:: mem:: transmute;
18
19
use :: std:: ptr;
19
20
use :: std:: ffi:: CString ;
21
+ use :: std:: ffi:: CStr ;
20
22
use :: std:: sync:: Mutex ;
21
23
use :: jvmti:: jvmtiEnv;
22
24
use :: jvmti:: jrawMonitorID;
25
+ use :: heap:: Tag ;
23
26
24
27
pub trait JvmTI {
28
+ // TODO: rework the following methods not to return values since they only ever return Ok(()) or panic.
25
29
fn create_raw_monitor ( & mut self , name : String , monitor : & Mutex < RawMonitorId > ) -> Result < ( ) , :: jvmti:: jint > ;
26
30
fn raw_monitor_enter ( & mut self , monitor : & Mutex < RawMonitorId > ) -> Result < ( ) , :: jvmti:: jint > ;
27
31
fn raw_monitor_exit ( & mut self , monitor : & Mutex < RawMonitorId > ) -> Result < ( ) , :: jvmti:: jint > ;
28
32
fn on_resource_exhausted ( & mut self , callback : FnResourceExhausted ) -> Result < ( ) , :: jvmti:: jint > ;
33
+ fn enable_object_tagging ( & mut self ) -> Result < ( ) , :: jvmti:: jint > ;
34
+ fn tag_loaded_classes ( & self , tagger : & mut Tag ) ;
35
+
36
+ // Restriction: traverse_live_heap may be called at most once in the lifetime of a JVM.
37
+ fn traverse_live_heap < F > ( & self , closure : F ) where F : FnMut ( :: jvmti:: jlong , :: jvmti:: jlong ) ;
29
38
}
30
39
31
40
pub struct RawMonitorId {
@@ -66,43 +75,37 @@ impl JvmTiEnv {
66
75
}
67
76
}
68
77
69
- impl JvmTI for JvmTiEnv {
70
- fn create_raw_monitor ( & mut self , name : String , monitor : & Mutex < RawMonitorId > ) -> Result < ( ) , :: jvmti :: jint > {
78
+ macro_rules! jvmtifn (
79
+ ( $r : expr , $f : ident , $ ( $arg : tt ) * ) => { {
71
80
let rc;
81
+ #[ allow( unused_unsafe) ] // suppress warning if used inside unsafe block
72
82
unsafe {
73
- let create_raw_monitor_fn = ( * * self . jvmti ) . CreateRawMonitor . unwrap ( ) ;
74
- rc = create_raw_monitor_fn ( self . jvmti , CString :: new ( name ) . unwrap ( ) . into_raw ( ) , monitor . lock ( ) . unwrap ( ) . id ) ;
83
+ let fnc = ( * * $r ) . $f . unwrap( ) ;
84
+ rc = fnc ( $r , $ ( $arg ) * ) ;
75
85
}
76
86
if rc != :: jvmti:: jvmtiError:: JVMTI_ERROR_NONE {
77
- eprintln ! ( "ERROR: CreateRawMonitor failed: {:?}" , rc) ;
78
- return Err ( :: jvmti:: JNI_ERR ) ;
87
+ eprintln!( "ERROR: JVMTI {} failed: {:?}" , stringify! ( $f ) , rc) ;
88
+ panic! ( :: jvmti:: JNI_ERR ) ;
79
89
}
90
+ } }
91
+ ) ;
92
+
93
+ // Pick a suitable object tag mask greater than tags used to tag classes.
94
+ const TAG_VISITED_MASK : :: jvmti:: jlong = 1 << 31 ;
95
+
96
+ impl JvmTI for JvmTiEnv {
97
+ fn create_raw_monitor ( & mut self , name : String , monitor : & Mutex < RawMonitorId > ) -> Result < ( ) , :: jvmti:: jint > {
98
+ jvmtifn ! ( self . jvmti, CreateRawMonitor , CString :: new( name) . unwrap( ) . into_raw( ) , monitor. lock( ) . unwrap( ) . id) ;
80
99
Ok ( ( ) )
81
100
}
82
101
83
102
fn raw_monitor_enter ( & mut self , monitor : & Mutex < RawMonitorId > ) -> Result < ( ) , :: jvmti:: jint > {
84
- let rc;
85
- unsafe {
86
- let raw_monitor_enter_fn = ( * * self . jvmti ) . RawMonitorEnter . unwrap ( ) ;
87
- rc = raw_monitor_enter_fn ( self . jvmti , * monitor. lock ( ) . unwrap ( ) . id ) ;
88
- }
89
- if rc != :: jvmti:: jvmtiError:: JVMTI_ERROR_NONE {
90
- eprintln ! ( "ERROR: RawMonitorEnter failed: {:?}" , rc) ;
91
- return Err ( :: jvmti:: JNI_ERR ) ;
92
- }
103
+ jvmtifn ! ( self . jvmti, RawMonitorEnter , * monitor. lock( ) . unwrap( ) . id) ;
93
104
Ok ( ( ) )
94
105
}
95
106
96
107
fn raw_monitor_exit ( & mut self , monitor : & Mutex < RawMonitorId > ) -> Result < ( ) , :: jvmti:: jint > {
97
- let rc;
98
- unsafe {
99
- let raw_monitor_exit_fn = ( * * self . jvmti ) . RawMonitorExit . unwrap ( ) ;
100
- rc = raw_monitor_exit_fn ( self . jvmti , * monitor. lock ( ) . unwrap ( ) . id ) ;
101
- }
102
- if rc != :: jvmti:: jvmtiError:: JVMTI_ERROR_NONE {
103
- eprintln ! ( "ERROR: RawMonitorExit failed: {:?}" , rc) ;
104
- return Err ( :: jvmti:: JNI_ERR ) ;
105
- }
108
+ jvmtifn ! ( self . jvmti, RawMonitorExit , * monitor. lock( ) . unwrap( ) . id) ;
106
109
Ok ( ( ) )
107
110
}
108
111
@@ -111,65 +114,125 @@ impl JvmTI for JvmTiEnv {
111
114
EVENT_CALLBACKS . resource_exhausted = Some ( callback) ;
112
115
}
113
116
114
- let rc;
115
- unsafe {
116
- let set_event_callbacks_fn = ( * * self . jvmti ) . SetEventCallbacks . unwrap ( ) ;
117
- let callbacks = :: jvmti:: jvmtiEventCallbacks {
118
- VMInit : None ,
119
- VMDeath : None ,
120
- ThreadStart : None ,
121
- ThreadEnd : None ,
122
- ClassFileLoadHook : None ,
123
- ClassLoad : None ,
124
- ClassPrepare : None ,
125
- VMStart : None ,
126
- Exception : None ,
127
- ExceptionCatch : None ,
128
- SingleStep : None ,
129
- FramePop : None ,
130
- Breakpoint : None ,
131
- FieldAccess : None ,
132
- FieldModification : None ,
133
- MethodEntry : None ,
134
- MethodExit : None ,
135
- NativeMethodBind : None ,
136
- CompiledMethodLoad : None ,
137
- CompiledMethodUnload : None ,
138
- DynamicCodeGenerated : None ,
139
- DataDumpRequest : None ,
140
- reserved72 : None ,
141
- MonitorWait : None ,
142
- MonitorWaited : None ,
143
- MonitorContendedEnter : None ,
144
- MonitorContendedEntered : None ,
145
- reserved77 : None ,
146
- reserved78 : None ,
147
- reserved79 : None ,
148
- ResourceExhausted : Some ( resource_exhausted) ,
149
- GarbageCollectionStart : None ,
150
- GarbageCollectionFinish : None ,
151
- ObjectFree : None ,
152
- VMObjectAlloc : None
153
- } ;
154
- rc = set_event_callbacks_fn ( self . jvmti , & callbacks, size_of :: < :: jvmti:: jvmtiEventCallbacks > ( ) as i32 ) ;
155
- }
156
- if rc != :: jvmti:: jvmtiError:: JVMTI_ERROR_NONE {
157
- eprintln ! ( "ERROR: SetEventCallbacks failed: {:?}" , rc) ;
158
- return Err ( :: jvmti:: JNI_ERR ) ;
159
- }
117
+ let callbacks = :: jvmti:: jvmtiEventCallbacks {
118
+ VMInit : None ,
119
+ VMDeath : None ,
120
+ ThreadStart : None ,
121
+ ThreadEnd : None ,
122
+ ClassFileLoadHook : None ,
123
+ ClassLoad : None ,
124
+ ClassPrepare : None ,
125
+ VMStart : None ,
126
+ Exception : None ,
127
+ ExceptionCatch : None ,
128
+ SingleStep : None ,
129
+ FramePop : None ,
130
+ Breakpoint : None ,
131
+ FieldAccess : None ,
132
+ FieldModification : None ,
133
+ MethodEntry : None ,
134
+ MethodExit : None ,
135
+ NativeMethodBind : None ,
136
+ CompiledMethodLoad : None ,
137
+ CompiledMethodUnload : None ,
138
+ DynamicCodeGenerated : None ,
139
+ DataDumpRequest : None ,
140
+ reserved72 : None ,
141
+ MonitorWait : None ,
142
+ MonitorWaited : None ,
143
+ MonitorContendedEnter : None ,
144
+ MonitorContendedEntered : None ,
145
+ reserved77 : None ,
146
+ reserved78 : None ,
147
+ reserved79 : None ,
148
+ ResourceExhausted : Some ( resource_exhausted) ,
149
+ GarbageCollectionStart : None ,
150
+ GarbageCollectionFinish : None ,
151
+ ObjectFree : None ,
152
+ VMObjectAlloc : None
153
+ } ;
154
+ jvmtifn ! ( self . jvmti, SetEventCallbacks , & callbacks, size_of:: <:: jvmti:: jvmtiEventCallbacks>( ) as i32 ) ;
160
155
161
- let rc;
162
- unsafe {
163
- let set_event_notification_mode_fn = ( * * self . jvmti ) . SetEventNotificationMode . unwrap ( ) ;
164
- rc = set_event_notification_mode_fn ( self . jvmti , :: jvmti:: jvmtiEventMode:: JVMTI_ENABLE , :: jvmti:: jvmtiEvent:: JVMTI_EVENT_RESOURCE_EXHAUSTED , :: std:: ptr:: null_mut ( ) ) ;
165
- }
166
- if rc != :: jvmti:: jvmtiError:: JVMTI_ERROR_NONE {
167
- eprintln ! ( "ERROR: SetEventNotificationMode failed: {:?}" , rc) ;
168
- return Err ( :: jvmti:: JNI_ERR ) ;
169
- }
156
+ jvmtifn ! ( self . jvmti, SetEventNotificationMode , :: jvmti:: jvmtiEventMode:: JVMTI_ENABLE , :: jvmti:: jvmtiEvent:: JVMTI_EVENT_RESOURCE_EXHAUSTED , :: std:: ptr:: null_mut( ) ) ;
157
+
158
+ Ok ( ( ) )
159
+ }
160
+
161
+ fn enable_object_tagging ( & mut self ) -> Result < ( ) , :: jvmti:: jint > {
162
+ let mut capabilities = :: jvmti:: jvmtiCapabilities {
163
+ _bitfield_1 : [ 0 ; 4 ] ,
164
+ _bitfield_2 : [ 0 ; 2 ] ,
165
+ _bitfield_3 : [ 0 ; 2 ] ,
166
+ _bitfield_4 : [ 0 ; 2 ] ,
167
+ __bindgen_align : [ ] ,
168
+ // FIXME: seems dangeous to reference a field with this name. Same may be true of other fields in this struct.
169
+ } ;
170
+
171
+ jvmtifn ! ( self . jvmti, GetCapabilities , & mut capabilities) ;
172
+
173
+ capabilities. set_can_tag_objects ( 1 ) ;
174
+
175
+ jvmtifn ! ( self . jvmti, AddCapabilities , & capabilities) ;
170
176
171
177
Ok ( ( ) )
172
178
}
179
+
180
+ fn tag_loaded_classes ( & self , tagger : & mut Tag ) {
181
+ let mut class_count = 0 ;
182
+ let mut class_ptr = :: std:: ptr:: null_mut ( ) ;
183
+ jvmtifn ! ( self . jvmti, GetLoadedClasses , & mut class_count, & mut class_ptr) ;
184
+
185
+ while class_count > 0 {
186
+ let mut sig_ptr = :: std:: ptr:: null_mut ( ) ;
187
+ jvmtifn ! ( self . jvmti, GetClassSignature , * class_ptr, & mut sig_ptr, :: std:: ptr:: null_mut( ) ) ;
188
+ unsafe {
189
+ let cstr = CStr :: from_ptr ( sig_ptr) ; // sig_ptr is deallocated later
190
+ let tag = tagger. class_tag ( & cstr. to_str ( ) . unwrap ( ) . to_string ( ) ) ;
191
+ jvmtifn ! ( self . jvmti, SetTag , * class_ptr, tag) ;
192
+ }
193
+ jvmtifn ! ( self . jvmti, Deallocate , sig_ptr as * mut u8 ) ;
194
+
195
+ class_count -= 1 ;
196
+ unsafe { class_ptr = class_ptr. offset ( 1 ) ; }
197
+ }
198
+ }
199
+
200
+ fn traverse_live_heap < F > ( & self , mut closure : F )
201
+ where F : FnMut ( :: jvmti:: jlong , :: jvmti:: jlong ) {
202
+ let callbacks = :: jvmti:: jvmtiHeapCallbacks {
203
+ heap_iteration_callback : None ,
204
+ heap_reference_callback : Some ( heapReferenceCallback) ,
205
+ primitive_field_callback : None ,
206
+ array_primitive_value_callback : None ,
207
+ string_primitive_value_callback : None ,
208
+ reserved5 : None ,
209
+ reserved6 : None ,
210
+ reserved7 : None ,
211
+ reserved8 : None ,
212
+ reserved9 : None ,
213
+ reserved10 : None ,
214
+ reserved11 : None ,
215
+ reserved12 : None ,
216
+ reserved13 : None ,
217
+ reserved14 : None ,
218
+ reserved15 : None ,
219
+ } ;
220
+ // Pass closure to the callback as a thin pointer pointing to a fat pointer pointing to the closure.
221
+ // See: https://stackoverflow.com/questions/38995701/how-do-i-pass-closures-through-raw-pointers-as-arguments-to-c-functions
222
+ let mut closure_ptr: & mut FnMut ( :: jvmti:: jlong , :: jvmti:: jlong ) = & mut closure;
223
+ let closure_ptr_ptr = unsafe { transmute ( & mut closure_ptr) } ;
224
+
225
+ // Need to pass the traversal state into FollowReferences and pick it up in the callback, which may be called multiple times
226
+ jvmtifn ! ( self . jvmti, FollowReferences , 0 , :: std:: ptr:: null_mut( ) , :: std:: ptr:: null_mut( ) , & callbacks, closure_ptr_ptr) ;
227
+ // jvmtiHeapCallbacks callbacks = {};
228
+ // callbacks.heap_reference_callback = &heapRefCallback;
229
+ //
230
+ // jvmtiError err = jvmti -> FollowReferences(0, NULL, NULL, &callbacks, this);
231
+ // if (err != JVMTI_ERROR_NONE) {
232
+ // std::cerr << "ERROR: FollowReferences failed: " << err << std::endl;
233
+ // throw new std::runtime_error("FollowReferences failed");
234
+ // }
235
+ }
173
236
}
174
237
175
238
#[ allow( unused_variables) ]
@@ -198,13 +261,41 @@ pub static mut EVENT_CALLBACKS: EventCallbacks = EventCallbacks {
198
261
resource_exhausted : None
199
262
} ;
200
263
264
+ #[ allow( unused_variables) ]
265
+ unsafe extern "C" fn heapReferenceCallback ( reference_kind : :: jvmti:: jvmtiHeapReferenceKind ,
266
+ reference_info : * const :: jvmti:: jvmtiHeapReferenceInfo ,
267
+ class_tag : :: jvmti:: jlong ,
268
+ referrer_class_tag : :: jvmti:: jlong ,
269
+ size : :: jvmti:: jlong ,
270
+ tag_ptr : * mut :: jvmti:: jlong ,
271
+ referrer_tag_ptr : * mut :: jvmti:: jlong ,
272
+ length : :: jvmti:: jint ,
273
+ user_data : * mut :: std:: os:: raw:: c_void )
274
+ -> :: jvmti:: jint {
275
+ if * tag_ptr & TAG_VISITED_MASK == TAG_VISITED_MASK {
276
+ return 0 ;
277
+ }
278
+
279
+ // For each object encountered, tag it so we can avoid visiting it again
280
+ // noting that traverse_live_heap is called at most once in the lifetime of a JVM
281
+ * tag_ptr |= TAG_VISITED_MASK ;
282
+
283
+ // Add the object to the heap stats along with its class signature.
284
+ let unmaskedClassTag = class_tag & !TAG_VISITED_MASK ;
285
+ let closure: & mut & mut FnMut ( :: jvmti:: jlong , :: jvmti:: jlong ) -> :: jvmti:: jint = transmute ( user_data) ;
286
+ closure ( unmaskedClassTag, size) ;
287
+
288
+ :: jvmti:: JVMTI_VISIT_OBJECTS as :: jvmti:: jint
289
+ }
290
+
201
291
#[ derive( Clone , Copy ) ]
202
292
pub struct JniEnv {
293
+ #[ allow( dead_code) ] // TODO: revisit this once port is complete
203
294
jni : * mut :: jvmti:: JNIEnv
204
295
}
205
296
206
297
impl JniEnv {
207
298
pub fn new ( jni_env : * mut :: jvmti:: JNIEnv ) -> JniEnv {
208
- JniEnv { jni : jni_env}
299
+ JniEnv { jni : jni_env }
209
300
}
210
301
}
0 commit comments