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 }