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