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 = (MyTag*) 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(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(obj); 133 134 err = jvmti->GetTag(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(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 = (refLink*) 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_heapref(JavaVM *jvm, char *options, void *reserved) { 258 return Agent_Initialize(jvm, options, reserved); 259 } 260 261 JNIEXPORT jint JNICALL Agent_OnAttach_heapref(JavaVM *jvm, char *options, void *reserved) { 262 return Agent_Initialize(jvm, options, reserved); 263 } 264 265 JNIEXPORT jint JNI_OnLoad_heapref(JavaVM *jvm, char *options, void *reserved) { 266 return JNI_VERSION_1_8; 267 } 268 #endif 269 270 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 271 jint res; 272 jvmtiError err; 273 274 if (options != NULL && strcmp(options, "printdump") == 0) { 275 printdump = JNI_TRUE; 276 } 277 278 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), 279 JVMTI_VERSION_1_1); 280 if (res != JNI_OK || jvmti == NULL) { 281 printf("Wrong result of a valid call to GetEnv!\n"); 282 return JNI_ERR; 283 } 284 285 memset((void*)&jvmti_caps, 0, sizeof(jvmtiCapabilities)); 286 jvmti_caps.can_tag_objects = 1; 287 err = jvmti->AddCapabilities(&jvmti_caps); 288 if (err != JVMTI_ERROR_NONE) { 289 printf("Error (AddCapabilities): %s (%d)\n", TranslateError(err), err); 290 return JNI_ERR; 291 } 292 293 return JNI_OK; 294 } 295 296 jvmtiIterationControl JNICALL 297 heapMarkCallback(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data) { 298 const MyTag* const tag = newTag(rmark, (const MyTag*)(intptr_t)class_tag, size, NULL); 299 *tag_ptr = (intptr_t)tag; 300 301 if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) { 302 user_data_error_flag = JNI_TRUE; 303 printf("Error (heapMarkCallback): unexpected value of user_data\n"); 304 result = STATUS_FAILED; 305 } 306 return JVMTI_ITERATION_CONTINUE; 307 } 308 309 jvmtiIterationControl JNICALL 310 heapRootCallback(jvmtiHeapRootKind root_kind, 311 jlong class_tag, jlong size, 312 jlong* tag_ptr, void* user_data) { 313 refKind kind = rother; 314 315 if (0 == *tag_ptr) { 316 /* new tag */ 317 MyTag* tag = newTag(kind, (MyTag*)(intptr_t)class_tag, size, NULL); 318 addRef(fakeRoot, HEAP_ROOT_REF_KIND_BASE+root_kind, tag); 319 *tag_ptr = (intptr_t)tag; 320 } else { 321 /* existing tag */ 322 addRef(fakeRoot, HEAP_ROOT_REF_KIND_BASE+root_kind, (MyTag*)(intptr_t)*tag_ptr); 323 } 324 325 if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) { 326 user_data_error_flag = JNI_TRUE; 327 printf("Error (heapRootCallback): unexpected value of user_data\n"); 328 result = STATUS_FAILED; 329 } 330 return JVMTI_ITERATION_CONTINUE; 331 } 332 333 jvmtiIterationControl JNICALL 334 stackReferenceCallback(jvmtiHeapRootKind root_kind, 335 jlong class_tag, jlong size, 336 jlong* tag_ptr, jlong thread_tag, 337 jint depth, jmethodID method, 338 jint slot, void* user_data) { 339 refKind kind = rother; 340 341 if (0 == *tag_ptr) { 342 /* new tag */ 343 MyTag* tag = newTag(kind, (MyTag*)(intptr_t)class_tag, size, NULL); 344 addRef(fakeRoot, HEAP_ROOT_REF_KIND_BASE+root_kind, tag); 345 *tag_ptr = (intptr_t)tag; 346 } else { 347 /* existing tag */ 348 addRef(fakeRoot, HEAP_ROOT_REF_KIND_BASE+root_kind, (MyTag*)(intptr_t)*tag_ptr); 349 } 350 if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) { 351 user_data_error_flag = JNI_TRUE; 352 printf("Error (stackReferenceCallback): unexpected value of user_data\n"); 353 result = STATUS_FAILED; 354 } 355 return JVMTI_ITERATION_CONTINUE; 356 } 357 358 jvmtiIterationControl JNICALL 359 objectReferenceCallback(jvmtiObjectReferenceKind reference_kind, 360 jlong class_tag, jlong size, 361 jlong* tag_ptr, jlong referrer_tag, 362 jint referrer_index, void* user_data) { 363 refKind kind = rother; 364 MyTag* referrer = NULL; 365 366 if (0 == referrer_tag) { 367 referrer = missed; 368 } else { 369 referrer = (MyTag *)(intptr_t)referrer_tag; 370 } 371 372 if (0 == *tag_ptr) { 373 /* new tag */ 374 MyTag* tag = newTag(kind, (MyTag*)(intptr_t)class_tag, size, NULL); 375 addRef(referrer, reference_kind, tag); 376 *tag_ptr = (intptr_t) tag; 377 } else { 378 /* existing tag */ 379 MyTag* tag = (MyTag*)(intptr_t)*tag_ptr; 380 addRef(referrer, reference_kind, tag); 381 } 382 if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) { 383 user_data_error_flag = JNI_TRUE; 384 printf("Error (objectReferenceCallback): unexpected value of user_data\n"); 385 result = STATUS_FAILED; 386 } 387 return JVMTI_ITERATION_CONTINUE; 388 } 389 390 JNIEXPORT jint JNICALL 391 Java_nsk_jvmti_unit_heapref_check(JNIEnv *env, jclass cls) { 392 jvmtiError err; 393 jclass *classes; 394 jint classCount = 0; 395 jthread *threads; 396 jint threadCount = 0; 397 jint i; 398 399 if (jvmti == NULL) { 400 printf("JVMTI client was not properly loaded!\n"); 401 return STATUS_FAILED; 402 } 403 404 fakeRoot = newTag(rother, (const MyTag *)NULL, 0, "FAKE_ROOT"); 405 missed = newTag(rother, (const MyTag *)NULL, 0, "MISSED"); 406 407 if (env->PushLocalFrame(500) != 0) { 408 printf("Error (PushLocalFrame): failed\n"); 409 result = STATUS_FAILED; 410 } 411 412 err = jvmti->GetLoadedClasses(&classCount, &classes); 413 if (err != JVMTI_ERROR_NONE) { 414 printf("Error (GetLoadedClasses): %s (%d)\n", TranslateError(err), err); 415 result = STATUS_FAILED; 416 } 417 418 for (i = 0; i < classCount; ++i) { 419 char *classSig; 420 jclass k = classes[i]; 421 err = jvmti->GetClassSignature(k, &classSig, NULL); 422 if (err != JVMTI_ERROR_NONE) { 423 printf("Error (getClassSignature): %s (%d)\n", TranslateError(err), err); 424 result = STATUS_FAILED; 425 } else { 426 char* slash = strrchr(classSig, '/'); 427 const size_t len = strlen(classSig); 428 if (classSig[len-1] == ';') { 429 classSig[len-1] = 0; 430 } 431 if (*classSig == 'L' && slash != NULL) { 432 classSig = slash + 1; 433 } 434 setTag(env, k, rclass, (const char*)classSig); 435 } 436 } 437 438 err = jvmti->GetAllThreads(&threadCount, &threads); 439 if (err != JVMTI_ERROR_NONE) { 440 printf("Error (GetAllThreads): %s (%d)\n", TranslateError(err), err); 441 result = STATUS_FAILED; 442 } 443 444 for (i = 0; i < threadCount; ++i) { 445 jvmtiThreadInfo info; 446 jthread t = threads[i]; 447 err = jvmti->GetThreadInfo(t, &info); 448 if (err != JVMTI_ERROR_NONE) { 449 printf("Error (GetThreadInfo): %s (%d)\n", TranslateError(err), err); 450 result = STATUS_FAILED; 451 } else { 452 setTag(env, t, rthread, (const char*)info.name); 453 } 454 } 455 456 env->PopLocalFrame(NULL); 457 458 user_data_error_flag = JNI_FALSE; 459 err = jvmti->IterateOverHeap( 460 JVMTI_HEAP_OBJECT_UNTAGGED, 461 heapMarkCallback, 462 &dummy_user_data); 463 if (err != JVMTI_ERROR_NONE) { 464 printf("Error (IterateOverHeap): %s (%d)\n", TranslateError(err), err); 465 result = STATUS_FAILED; 466 } 467 468 user_data_error_flag = JNI_FALSE; 469 err = jvmti->IterateOverReachableObjects( 470 heapRootCallback, 471 stackReferenceCallback, 472 objectReferenceCallback, 473 &dummy_user_data); 474 if (err != JVMTI_ERROR_NONE) { 475 printf("Error (IterateOverReachableObjects): %s (%d)\n", TranslateError(err), err); 476 result = STATUS_FAILED; 477 } 478 479 if (printdump == JNI_TRUE) { 480 printf("<html><head><title>Heap Dump</title></head><body><pre>\n"); 481 walk(fakeRoot, 0, "roots"); 482 printf("\n------------------- MISSED ------------------\n\n"); 483 walk(missed, 0, "missed"); 484 printf("</pre></body></html>\n"); 485 } 486 487 return result; 488 } 489 490 #ifdef __cplusplus 491 } 492 #endif