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