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 }