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