1 /* 2 * Copyright (c) 2019 SAP SE. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 #include <stdio.h> 25 #include <string.h> 26 #include "jvmti.h" 27 #include "jni.h" 28 29 #ifndef JNI_ENV_ARG 30 #define JNI_ENV_ARG(x,y) x, y 31 #define JNI_ENV_PTR(x) (*x) 32 #endif 33 34 #define FAILED -1 35 #define OK 0 36 37 static jvmtiEnv *jvmti; 38 39 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); 40 41 static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) { 42 char *errMsg; 43 jvmtiError result; 44 45 result = (*jvmti)->GetErrorName(jvmti, errCode, &errMsg); 46 if (result == JVMTI_ERROR_NONE) { 47 fprintf(stderr, "%s: %s (%d)\n", message, errMsg, errCode); 48 (*jvmti)->Deallocate(jvmti, (unsigned char *)errMsg); 49 } else { 50 fprintf(stderr, "%s (%d)\n", message, errCode); 51 } 52 } 53 54 JNIEXPORT jint JNICALL 55 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 56 return Agent_Initialize(jvm, options, reserved); 57 } 58 59 JNIEXPORT jint JNICALL 60 Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { 61 return Agent_Initialize(jvm, options, reserved); 62 } 63 64 JNIEXPORT jint JNICALL 65 JNI_OnLoad(JavaVM *jvm, void *reserved) { 66 jint res; 67 JNIEnv *env; 68 69 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &env), 70 JNI_VERSION_9); 71 if (res != JNI_OK || env == NULL) { 72 fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res); 73 return JNI_ERR; 74 } 75 76 return JNI_VERSION_9; 77 } 78 79 static jint 80 Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 81 jint res; 82 jvmtiError err; 83 jvmtiCapabilities caps; 84 85 printf("Agent_OnLoad started\n"); 86 87 memset(&caps, 0, sizeof(caps)); 88 89 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), 90 JVMTI_VERSION_9); 91 if (res != JNI_OK || jvmti == NULL) { 92 fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n"); 93 return JNI_ERR; 94 } 95 96 caps.can_tag_objects = 1; 97 98 err = (*jvmti)->AddCapabilities(jvmti, &caps); 99 if (err != JVMTI_ERROR_NONE) { 100 ShowErrorMessage(jvmti, err, 101 "Agent_OnLoad: error in JVMTI AddCapabilities"); 102 return JNI_ERR; 103 } 104 105 err = (*jvmti)->GetCapabilities(jvmti, &caps); 106 if (err != JVMTI_ERROR_NONE) { 107 ShowErrorMessage(jvmti, err, 108 "Agent_OnLoad: error in JVMTI GetCapabilities"); 109 return JNI_ERR; 110 } 111 112 if (!caps.can_tag_objects) { 113 fprintf(stderr, "Warning: didn't get the capability can_tag_objects\n"); 114 return JNI_ERR; 115 } 116 117 printf("Agent_OnLoad finished\n"); 118 return JNI_OK; 119 } 120 121 static jobject method_IterateOverReachableObjects; 122 static jobject method_IterateOverHeap; 123 static jobject method_IterateOverInstancesOfClass; 124 static jobject method_FollowReferences; 125 static jobject method_IterateThroughHeap; 126 127 JNIEXPORT jint JNICALL 128 Java_IterateHeapWithEscapeAnalysisEnabled_registerMethod(JNIEnv *env, jclass cls, jobject method, jstring name) { 129 const char *name_chars = (*env)->GetStringUTFChars(env, name, 0); 130 int rc = FAILED; 131 if (rc != OK && strcmp(name_chars, "IterateOverReachableObjects") == 0) { 132 method_IterateOverReachableObjects = (*env)->NewGlobalRef(env, method); 133 rc = OK; 134 } 135 if (rc != OK && strcmp(name_chars, "IterateOverHeap") == 0) { 136 method_IterateOverHeap = (*env)->NewGlobalRef(env, method); 137 rc = OK; 138 } 139 if (rc != OK && strcmp(name_chars, "IterateOverInstancesOfClass") == 0) { 140 method_IterateOverInstancesOfClass = (*env)->NewGlobalRef(env, method); 141 rc = OK; 142 } 143 if (rc != OK && strcmp(name_chars, "IterateThroughHeap") == 0) { 144 method_IterateThroughHeap = (*env)->NewGlobalRef(env, method); 145 rc = OK; 146 } 147 if (rc != OK && strcmp(name_chars, "FollowReferences") == 0) { 148 method_FollowReferences = (*env)->NewGlobalRef(env, method); 149 rc = OK; 150 } 151 (*env)->ReleaseStringUTFChars(env, name, name_chars); 152 return rc; 153 } 154 155 JNIEXPORT void JNICALL 156 Java_IterateHeapWithEscapeAnalysisEnabled_agentTearDown(JNIEnv *env, jclass cls) { 157 (*env)->DeleteGlobalRef(env, method_IterateOverReachableObjects); 158 (*env)->DeleteGlobalRef(env, method_IterateOverHeap); 159 (*env)->DeleteGlobalRef(env, method_IterateOverInstancesOfClass); 160 (*env)->DeleteGlobalRef(env, method_FollowReferences); 161 (*env)->DeleteGlobalRef(env, method_IterateThroughHeap); 162 } 163 164 JNIEXPORT jint JNICALL 165 Java_IterateHeapWithEscapeAnalysisEnabled_jvmtiTagClass(JNIEnv *env, jclass cls, jclass clsToTag, jlong tag) { 166 jvmtiError err; 167 err = (*jvmti)->SetTag(jvmti, clsToTag, tag); 168 if (err != JVMTI_ERROR_NONE) { 169 ShowErrorMessage(jvmti, err, 170 "jvmtiTagClass: error in JVMTI SetTag"); 171 return FAILED; 172 } 173 return OK; 174 } 175 176 typedef struct Tag_And_Counter { 177 jlong instance_counter; 178 jlong class_tag; 179 jlong instance_tag; 180 } Tag_And_Counter; 181 182 static jvmtiIterationControl JNICALL 183 __stackReferenceCallback(jvmtiHeapRootKind root_kind, 184 jlong class_tag, 185 jlong size, 186 jlong* tag_ptr, 187 jlong thread_tag, 188 jint depth, 189 jmethodID method, 190 jint slot, 191 void* d) { 192 Tag_And_Counter* data = (Tag_And_Counter*) d; 193 if (class_tag == data->class_tag && *tag_ptr == 0) { 194 data->instance_counter++; 195 *tag_ptr = data->instance_tag; 196 } 197 return JVMTI_ITERATION_CONTINUE; 198 } 199 200 static jvmtiIterationControl JNICALL 201 __jvmtiHeapObjectCallback(jlong class_tag, jlong size, jlong* tag_ptr, void* d) { 202 Tag_And_Counter* data = (Tag_And_Counter*) d; 203 if (class_tag == data->class_tag && *tag_ptr == 0) { 204 data->instance_counter++; 205 *tag_ptr = data->instance_tag; 206 } 207 return JVMTI_ITERATION_CONTINUE; 208 } 209 210 static jint JNICALL 211 __jvmtiHeapReferenceCallback(jvmtiHeapReferenceKind reference_kind, 212 const jvmtiHeapReferenceInfo* reference_info, 213 jlong class_tag, 214 jlong referrer_class_tag, 215 jlong size, 216 jlong* tag_ptr, 217 jlong* referrer_tag_ptr, 218 jint length, 219 void* d) { 220 Tag_And_Counter* data = (Tag_And_Counter*) d; 221 if (class_tag == data->class_tag && *tag_ptr == 0) { 222 data->instance_counter++; 223 *tag_ptr = data->instance_tag; 224 } 225 return JVMTI_VISIT_OBJECTS; 226 } 227 228 static jint JNICALL 229 __jvmtiHeapIterationCallback(jlong class_tag, 230 jlong size, 231 jlong* tag_ptr, 232 jint length, 233 void* d) { 234 Tag_And_Counter* data = (Tag_And_Counter*) d; 235 if (class_tag == data->class_tag && *tag_ptr == 0) { 236 data->instance_counter++; 237 *tag_ptr = data->instance_tag; 238 } 239 return JVMTI_VISIT_OBJECTS; 240 } 241 242 243 JNIEXPORT jlong JNICALL 244 Java_IterateHeapWithEscapeAnalysisEnabled_countAndTagInstancesOfClass(JNIEnv *env, 245 jclass cls, 246 jclass tagged_class, 247 jlong cls_tag, 248 jlong instance_tag, 249 jobject method) { 250 jvmtiError err; 251 Tag_And_Counter data = {0, cls_tag, instance_tag}; 252 jboolean method_found = JNI_FALSE; 253 254 jint idx = 0; 255 256 if ((*env)->IsSameObject(env, method, method_IterateOverReachableObjects)) { 257 method_found = JNI_TRUE; 258 err = (*jvmti)->IterateOverReachableObjects(jvmti, 259 NULL /*jvmtiHeapRootCallback*/, 260 __stackReferenceCallback, 261 NULL /* jvmtiObjectReferenceCallback */, 262 &data); 263 if (err != JVMTI_ERROR_NONE) { 264 ShowErrorMessage(jvmti, err, 265 "countAndTagInstancesOfClass: error in JVMTI IterateOverReachableObjects"); 266 return FAILED; 267 } 268 } 269 if ((*env)->IsSameObject(env, method, method_IterateOverHeap)) { 270 method_found = JNI_TRUE; 271 err = (*jvmti)->IterateOverHeap(jvmti, 272 JVMTI_HEAP_OBJECT_EITHER, 273 __jvmtiHeapObjectCallback, 274 &data); 275 if (err != JVMTI_ERROR_NONE) { 276 ShowErrorMessage(jvmti, err, 277 "countAndTagInstancesOfClass: error in JVMTI IterateOverHeap"); 278 return FAILED; 279 } 280 } 281 if ((*env)->IsSameObject(env, method, method_IterateOverInstancesOfClass)) { 282 method_found = JNI_TRUE; 283 err = (*jvmti)->IterateOverInstancesOfClass(jvmti, 284 tagged_class, 285 JVMTI_HEAP_OBJECT_EITHER, 286 __jvmtiHeapObjectCallback, 287 &data); 288 if (err != JVMTI_ERROR_NONE) { 289 ShowErrorMessage(jvmti, err, 290 "countAndTagInstancesOfClass: error in JVMTI IterateOverHeap"); 291 return FAILED; 292 } 293 } 294 if ((*env)->IsSameObject(env, method, method_FollowReferences)) { 295 method_found = JNI_TRUE; 296 jvmtiHeapCallbacks callbacks = {0}; 297 callbacks.heap_reference_callback = __jvmtiHeapReferenceCallback; 298 err = (*jvmti)->FollowReferences(jvmti, 299 0 /* filter nothing */, 300 NULL /* no class filter */, 301 NULL /* no initial object, follow roots */, 302 &callbacks, 303 &data); 304 if (err != JVMTI_ERROR_NONE) { 305 ShowErrorMessage(jvmti, err, 306 "countAndTagInstancesOfClass: error in JVMTI FollowReferences"); 307 return FAILED; 308 } 309 } 310 if ((*env)->IsSameObject(env, method, method_IterateThroughHeap)) { 311 method_found = JNI_TRUE; 312 jvmtiHeapCallbacks callbacks = {0}; 313 callbacks.heap_iteration_callback = __jvmtiHeapIterationCallback; 314 err = (*jvmti)->IterateThroughHeap(jvmti, 315 0 /* filter nothing */, 316 NULL /* no class filter */, 317 &callbacks, 318 &data); 319 if (err != JVMTI_ERROR_NONE) { 320 ShowErrorMessage(jvmti, err, 321 "countAndTagInstancesOfClass: error in JVMTI IterateThroughHeap"); 322 return FAILED; 323 } 324 } 325 326 if (!method_found) { 327 fprintf(stderr, "countAndTagInstancesOfClass: unknown method\n"); 328 return FAILED; 329 } 330 331 return data.instance_counter; 332 } 333 334 JNIEXPORT jlong JNICALL 335 Java_IterateHeapWithEscapeAnalysisEnabled_getObjectsWithTag(JNIEnv *env, 336 jclass cls, 337 jlong tag, 338 jarray res_instances) { 339 jvmtiError err; 340 const jlong tags[1] = {tag}; 341 jint res_count = -1; 342 jobject* res_instances_raw; 343 jlong* res_tags; 344 jint res_instances_length; 345 jint idx; 346 347 err = (*jvmti)->GetObjectsWithTags(jvmti, 348 1, 349 tags, 350 &res_count, 351 &res_instances_raw, 352 &res_tags); 353 if (err != JVMTI_ERROR_NONE) { 354 ShowErrorMessage(jvmti, err, 355 "getObjectsWithTags: error in JVMTI GetObjectsWithTags"); 356 return FAILED; 357 } 358 359 res_instances_length = (*env)->GetArrayLength(env, res_instances); 360 if (res_count != res_instances_length) { 361 fprintf(stderr, "getObjectsWithTags: result array lenght (%d) does not match instance count returned by GetObjectsWithTags (%d) \n", 362 res_instances_length, res_count); 363 return FAILED; 364 } 365 366 for (idx = 0; idx < res_count; idx++) { 367 (*env)->SetObjectArrayElement(env, res_instances, idx, res_instances_raw[idx]); 368 } 369 370 (*jvmti)->Deallocate(jvmti, (unsigned char *)res_instances_raw); 371 (*jvmti)->Deallocate(jvmti, (unsigned char *)res_tags); 372 373 return OK; 374 }