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