1 /* 2 * Copyright (c) 2003, 2018, Oracle and/or its affiliates. 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 <stdlib.h> 27 #include "jvmti.h" 28 #include "jni_tools.h" 29 #include "agent_common.h" 30 #include "JVMTITools.h" 31 32 #ifdef __cplusplus 33 extern "C" { 34 #endif 35 36 #ifndef JNI_ENV_ARG 37 38 #ifdef __cplusplus 39 #define JNI_ENV_ARG(x, y) y 40 #define JNI_ENV_PTR(x) x 41 #else 42 #define JNI_ENV_ARG(x,y) x, y 43 #define JNI_ENV_PTR(x) (*x) 44 #endif 45 46 #endif 47 48 #define PASSED 0 49 #define STATUS_FAILED 2 50 51 static jvmtiEnv *jvmti = NULL; 52 static jint result = PASSED; 53 static jboolean printdump = JNI_FALSE; 54 static jvmtiCapabilities jvmti_caps; 55 static jint dummy_user_data = 0; 56 static jboolean user_data_error_flag = JNI_FALSE; 57 58 #define HEAP_ROOT_REF_KIND_BASE 100 59 #define MISSED_REF_KIND_BASE 300 60 61 typedef enum { 62 rthread, 63 rclass, 64 rother, 65 rmark 66 } refKind; 67 68 struct _refLink; 69 70 typedef struct _myTag { 71 refKind kind; 72 const struct _myTag* class_tag; 73 jlong size; 74 jlong sequence; 75 jboolean visited; 76 const char* name; 77 struct _refLink *ref; 78 } MyTag; 79 80 typedef struct _refLink { 81 MyTag* tag; 82 int reference_kind; 83 struct _refLink *next; 84 } refLink; 85 86 static MyTag *fakeRoot = NULL; 87 static MyTag *missed = NULL; 88 89 static void breakpoint() { 90 printf("Continuing from BREAKPOINT\n"); 91 } 92 93 static MyTag *newTag(refKind kind, 94 const MyTag* class_tag, 95 jlong size, 96 const char* name) { 97 static jlong seq_num = 0; 98 MyTag* new_tag = NULL; 99 100 new_tag = malloc(sizeof(MyTag)); 101 if (NULL == new_tag) { 102 printf("Error (newTag malloc): failed\n"); 103 result = STATUS_FAILED; 104 } 105 new_tag->kind = kind; 106 new_tag->class_tag = class_tag; 107 new_tag->size = size; 108 new_tag->sequence = ++seq_num; 109 new_tag->visited = JNI_FALSE; 110 new_tag->name = name; 111 new_tag->ref = NULL; 112 return new_tag; 113 } 114 115 static void setTag(JNIEnv *env, 116 jobject obj, 117 refKind kind, 118 const char* name) { 119 MyTag *new_tag = NULL; 120 MyTag *class_tag = NULL; 121 jvmtiError err; 122 jlong size = 0; 123 jclass obj_class = NULL; 124 jlong haba = 0; 125 126 err = (*jvmti)->GetObjectSize(jvmti, obj, &size); 127 if (err != JVMTI_ERROR_NONE) { 128 printf("Error (ObjectSize): %s (%d)\n", TranslateError(err), err); 129 result = STATUS_FAILED; 130 } 131 132 obj_class = (*env)->GetObjectClass(env, obj); 133 134 err = (*jvmti)->GetTag(jvmti, obj_class, &haba); 135 class_tag = (MyTag*)(intptr_t)haba; 136 if (err != JVMTI_ERROR_NONE) { 137 printf("Error (GetTag): %s (%d)\n", TranslateError(err), err); 138 result = STATUS_FAILED; 139 } 140 if (class_tag != NULL && class_tag->kind != rclass) { 141 printf("Error class tag which is not a class\n"); 142 result = STATUS_FAILED; 143 } 144 145 new_tag = newTag(kind, class_tag, size, name); 146 147 err = (*jvmti)->SetTag(jvmti, obj, (intptr_t)new_tag); 148 if (err != JVMTI_ERROR_NONE) { 149 printf("Error (SetTag): %s (%d)\n", TranslateError(err), err); 150 result = STATUS_FAILED; 151 } 152 } 153 154 static void addRef(MyTag *from, int reference_kind, MyTag *to) { 155 refLink *new_ref; 156 157 new_ref = malloc(sizeof(refLink)); 158 if (NULL == new_ref) { 159 printf("Error (addRef malloc): failed\n"); 160 result = STATUS_FAILED; 161 } 162 new_ref->tag = to; 163 new_ref->reference_kind = reference_kind; 164 new_ref->next = from->ref; 165 from->ref = new_ref; 166 } 167 168 static const char* reference_label(refLink *link) { 169 int reference_kind = link->reference_kind; 170 const char *name = "<font color=\"red\">**unknown**</font>"; 171 switch (reference_kind) { 172 case JVMTI_REFERENCE_CLASS: 173 name = "<font color=\"black\">class</font>"; 174 break; 175 case JVMTI_REFERENCE_FIELD: 176 name = "<font color=\"black\">field</font>"; 177 break; 178 case JVMTI_REFERENCE_ARRAY_ELEMENT: 179 name = "<font color=\"green\">array_element</font>"; 180 break; 181 case JVMTI_REFERENCE_CLASS_LOADER: 182 name = "<font color=\"purple\">class_loader</font>"; 183 break; 184 case JVMTI_REFERENCE_SIGNERS: 185 name = "<font color=\"purple\">signers</font>"; 186 break; 187 case JVMTI_REFERENCE_PROTECTION_DOMAIN: 188 name = "<font color=\"purple\">protection_domain</font>"; 189 break; 190 case JVMTI_REFERENCE_INTERFACE: 191 name = "<font color=\"purple\">interface</font>"; 192 break; 193 case JVMTI_REFERENCE_STATIC_FIELD: 194 name = "<font color=\"black\">static_field</font>"; 195 break; 196 case HEAP_ROOT_REF_KIND_BASE+JVMTI_HEAP_ROOT_JNI_GLOBAL: 197 name = "<font color=\"orange\">root::jni_global</font>"; 198 break; 199 case HEAP_ROOT_REF_KIND_BASE+JVMTI_HEAP_ROOT_SYSTEM_CLASS: 200 name = "<font color=\"orange\">root::system_class</font>"; 201 break; 202 case HEAP_ROOT_REF_KIND_BASE+JVMTI_HEAP_ROOT_MONITOR: 203 name = "<font color=\"orange\">root::monitor</font>"; 204 break; 205 case HEAP_ROOT_REF_KIND_BASE+JVMTI_HEAP_ROOT_STACK_LOCAL: 206 name = "<font color=\"orange\">root::local_var</font>"; 207 break; 208 case HEAP_ROOT_REF_KIND_BASE+JVMTI_HEAP_ROOT_JNI_LOCAL: 209 name = "<font color=\"orange\">root::jni_local</font>"; 210 break; 211 case HEAP_ROOT_REF_KIND_BASE+JVMTI_HEAP_ROOT_THREAD: 212 name = "<font color=\"orange\">root::thread</font>"; 213 break; 214 case HEAP_ROOT_REF_KIND_BASE+JVMTI_HEAP_ROOT_OTHER: 215 name = "<font color=\"orange\">root::other</font>"; 216 break; 217 default: 218 printf("Error: Unexpected reference kind %d\n", reference_kind); 219 result = STATUS_FAILED; 220 break; 221 } 222 return name; 223 } 224 225 static void walk(MyTag* tag, jint depth, const char* ref_label) { 226 static const char* const spaces = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "; 227 static const int len = 86; 228 const char *indent = spaces + (len - 2 * depth); 229 230 const MyTag* const ctag = tag->class_tag; 231 const char* const cname = ctag != NULL ? ctag->name : ""; 232 233 printf("%s", indent); 234 235 if (tag->visited) { 236 printf("<a href=\"#%"LL"d\">", tag->sequence); 237 } else { 238 printf("<a name=\"%"LL"d\">", tag->sequence); 239 } 240 if (tag->name) { 241 printf("<b>%s(%s)</b>", cname, tag->name); 242 } else { 243 printf("%s(%"LL"d)", cname, tag->sequence); 244 } 245 printf("</a> -- "); 246 printf("%s\n", ref_label); 247 if (!tag->visited) { 248 refLink *ref; 249 tag->visited = JNI_TRUE; 250 for (ref = tag->ref; ref; ref = ref->next) { 251 walk(ref->tag, depth + 1, reference_label(ref)); 252 } 253 } 254 } 255 256 #ifdef STATIC_BUILD 257 JNIEXPORT jint JNICALL Agent_OnLoad_refignore(JavaVM *jvm, char *options, void *reserved) { 258 return Agent_Initialize(jvm, options, reserved); 259 } 260 JNIEXPORT jint JNICALL Agent_OnAttach_refignore(JavaVM *jvm, char *options, void *reserved) { 261 return Agent_Initialize(jvm, options, reserved); 262 } 263 JNIEXPORT jint JNI_OnLoad_refignore(JavaVM *jvm, char *options, void *reserved) { 264 return JNI_VERSION_1_8; 265 } 266 #endif 267 268 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 269 jint res; 270 jvmtiError err; 271 272 if (options != NULL && strcmp(options, "printdump") == 0) { 273 printdump = JNI_TRUE; 274 } 275 276 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), 277 JVMTI_VERSION_1_1); 278 if (res != JNI_OK || jvmti == NULL) { 279 printf("Wrong result of a valid call to GetEnv!\n"); 280 return JNI_ERR; 281 } 282 283 memset((void*)&jvmti_caps, 0, sizeof(jvmtiCapabilities)); 284 jvmti_caps.can_tag_objects = 1; 285 err = (*jvmti)->AddCapabilities(jvmti, &jvmti_caps); 286 if (err != JVMTI_ERROR_NONE) { 287 printf("Error (AddCapabilities): %s (%d)\n", TranslateError(err), err); 288 return JNI_ERR; 289 } 290 291 return JNI_OK; 292 } 293 294 jvmtiIterationControl JNICALL 295 heapMarkCallback(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data) { 296 const MyTag* const tag = newTag(rmark, (const MyTag*)(intptr_t)class_tag, size, NULL); 297 *tag_ptr = (intptr_t)tag; 298 299 if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) { 300 user_data_error_flag = JNI_TRUE; 301 printf("Error (heapMarkCallback): unexpected value of user_data\n"); 302 result = STATUS_FAILED; 303 } 304 return JVMTI_ITERATION_CONTINUE; 305 } 306 307 jvmtiIterationControl JNICALL 308 heapRootCallback(jvmtiHeapRootKind root_kind, 309 jlong class_tag, jlong size, 310 jlong* tag_ptr, void* user_data) { 311 refKind kind = rother; 312 313 if (0 == *tag_ptr) { 314 /* new tag */ 315 MyTag* tag = newTag(kind, (MyTag*)(intptr_t)class_tag, size, NULL); 316 addRef(fakeRoot, HEAP_ROOT_REF_KIND_BASE+root_kind, tag); 317 *tag_ptr = (intptr_t)tag; 318 } else { 319 /* existing tag */ 320 addRef(fakeRoot, HEAP_ROOT_REF_KIND_BASE+root_kind, (MyTag*)(intptr_t)*tag_ptr); 321 } 322 323 if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) { 324 user_data_error_flag = JNI_TRUE; 325 printf("Error (heapRootCallback): unexpected value of user_data\n"); 326 result = STATUS_FAILED; 327 } 328 return JVMTI_ITERATION_CONTINUE; 329 } 330 331 jvmtiIterationControl JNICALL 332 stackReferenceCallback(jvmtiHeapRootKind root_kind, 333 jlong class_tag, jlong size, 334 jlong* tag_ptr, jlong thread_tag, 335 jint depth, jmethodID method, 336 jint slot, void* user_data) { 337 refKind kind = rother; 338 339 if (0 == *tag_ptr) { 340 /* new tag */ 341 MyTag* tag = newTag(kind, (MyTag*)(intptr_t)class_tag, size, NULL); 342 addRef(fakeRoot, HEAP_ROOT_REF_KIND_BASE+root_kind, tag); 343 *tag_ptr = (intptr_t)tag; 344 } else { 345 /* existing tag */ 346 addRef(fakeRoot, HEAP_ROOT_REF_KIND_BASE+root_kind, (MyTag*)(intptr_t)*tag_ptr); 347 } 348 if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) { 349 user_data_error_flag = JNI_TRUE; 350 printf("Error (stackReferenceCallback): unexpected value of user_data\n"); 351 result = STATUS_FAILED; 352 } 353 return JVMTI_ITERATION_CONTINUE; 354 } 355 356 jvmtiIterationControl JNICALL 357 objectReferenceCallback(jvmtiObjectReferenceKind reference_kind, 358 jlong class_tag, jlong size, 359 jlong* tag_ptr, jlong referrer_tag, 360 jint referrer_index, void* user_data) { 361 refKind kind = rother; 362 MyTag* referrer = NULL; 363 364 if (0 == referrer_tag) { 365 referrer = missed; 366 } else { 367 referrer = (MyTag *)(intptr_t)referrer_tag; 368 } 369 370 if (0 == *tag_ptr) { 371 /* new tag */ 372 MyTag* tag = newTag(kind, (MyTag*)(intptr_t)class_tag, size, NULL); 373 addRef(referrer, reference_kind, tag); 374 *tag_ptr = (intptr_t) tag; 375 } else { 376 /* existing tag */ 377 MyTag* tag = (MyTag*)(intptr_t)*tag_ptr; 378 addRef(referrer, reference_kind, tag); 379 } 380 if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) { 381 user_data_error_flag = JNI_TRUE; 382 printf("Error (objectReferenceCallback): unexpected value of user_data\n"); 383 result = STATUS_FAILED; 384 } 385 return JVMTI_ITERATION_IGNORE; 386 } 387 388 JNIEXPORT jint JNICALL 389 Java_nsk_jvmti_unit_refignore_check(JNIEnv *env, jclass cls) { 390 jvmtiError err; 391 jclass *classes; 392 jint classCount = 0; 393 jthread *threads; 394 jint threadCount = 0; 395 jint i; 396 397 if (jvmti == NULL) { 398 printf("JVMTI client was not properly loaded!\n"); 399 return STATUS_FAILED; 400 } 401 402 fakeRoot = newTag(rother, (const MyTag *)NULL, 0, "FAKE_ROOT"); 403 missed = newTag(rother, (const MyTag *)NULL, 0, "MISSED"); 404 405 if ((*env)->PushLocalFrame(env, 500) != 0) { 406 printf("Error (PushLocalFrame): failed\n"); 407 result = STATUS_FAILED; 408 } 409 410 err = (*jvmti)->GetLoadedClasses(jvmti, &classCount, &classes); 411 if (err != JVMTI_ERROR_NONE) { 412 printf("Error (GetLoadedClasses): %s (%d)\n", TranslateError(err), err); 413 result = STATUS_FAILED; 414 } 415 416 for (i = 0; i < classCount; ++i) { 417 char *classSig; 418 jclass k = classes[i]; 419 err = (*jvmti)->GetClassSignature(jvmti, k, &classSig, NULL); 420 if (err != JVMTI_ERROR_NONE) { 421 printf("Error (getClassSignature): %s (%d)\n", TranslateError(err), err); 422 result = STATUS_FAILED; 423 } else { 424 char* slash = strrchr(classSig, '/'); 425 const size_t len = strlen(classSig); 426 if (classSig[len-1] == ';') { 427 classSig[len-1] = 0; 428 } 429 if (*classSig == 'L' && slash != NULL) { 430 classSig = slash + 1; 431 } 432 setTag(env, k, rclass, (const char*)classSig); 433 } 434 } 435 436 err = (*jvmti)->GetAllThreads(jvmti, &threadCount, &threads); 437 if (err != JVMTI_ERROR_NONE) { 438 printf("Error (GetAllThreads): %s (%d)\n", TranslateError(err), err); 439 result = STATUS_FAILED; 440 } 441 442 for (i = 0; i < threadCount; ++i) { 443 jvmtiThreadInfo info; 444 jthread t = threads[i]; 445 err = (*jvmti)->GetThreadInfo(jvmti, t, &info); 446 if (err != JVMTI_ERROR_NONE) { 447 printf("Error (GetThreadInfo): %s (%d)\n", TranslateError(err), err); 448 result = STATUS_FAILED; 449 } else { 450 setTag(env, t, rthread, (const char*)info.name); 451 } 452 } 453 454 (*env)->PopLocalFrame(env, NULL); 455 456 user_data_error_flag = JNI_FALSE; 457 err = (*jvmti)->IterateOverHeap(jvmti, 458 JVMTI_HEAP_OBJECT_UNTAGGED, 459 heapMarkCallback, 460 &dummy_user_data); 461 if (err != JVMTI_ERROR_NONE) { 462 printf("Error (IterateOverHeap): %s (%d)\n", TranslateError(err), err); 463 result = STATUS_FAILED; 464 } 465 466 user_data_error_flag = JNI_FALSE; 467 err = (*jvmti)->IterateOverReachableObjects(jvmti, 468 heapRootCallback, 469 stackReferenceCallback, 470 objectReferenceCallback, 471 &dummy_user_data); 472 if (err != JVMTI_ERROR_NONE) { 473 printf("Error (IterateOverReachableObjects): %s (%d)\n", TranslateError(err), err); 474 result = STATUS_FAILED; 475 } 476 477 if (printdump == JNI_TRUE) { 478 printf("<html><head><title>Heap Dump</title></head><body><pre>\n"); 479 walk(fakeRoot, 0, "roots"); 480 printf("\n------------------- MISSED ------------------\n\n"); 481 walk(missed, 0, "missed"); 482 printf("</pre></body></html>\n"); 483 } 484 485 return result; 486 } 487 488 #ifdef __cplusplus 489 } 490 #endif