1 /*
   2  * Copyright (c) 2001, 2005, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 /*
  26  * This module tracks classes that have been prepared, so as to
  27  * be able to compute which have been unloaded.  On VM start-up
  28  * all prepared classes are put in a table.  As class prepare
  29  * events come in they are added to the table.  After an unload
  30  * event or series of them, the VM can be asked for the list
  31  * of classes; this list is compared against the table keep by
  32  * this module, any classes no longer present are known to
  33  * have been unloaded.
  34  *
  35  * For efficient access, classes are keep in a hash table.
  36  * Each slot in the hash table has a linked list of KlassNode.
  37  *
  38  * Comparing current set of classes is compared with previous
  39  * set by transferring all classes in the current set into
  40  * a new table, any that remain in the old table have been
  41  * unloaded.
  42  */
  43 
  44 #include "util.h"
  45 #include "bag.h"
  46 #include "classTrack.h"
  47 
  48 typedef struct KlassNode {
  49     jlong klass_tag;         /* Klass's tag in tracking env */
  50     char *signature;         /* class signature */
  51     struct KlassNode *next;  /* next node in this slot */
  52 } KlassNode;
  53 
  54 /*
  55  * Linked-list of prepared classes
  56  */
  57 static KlassNode *list;
  58 
  59 /*
  60  * The JVMTI tracking env to keep track of klass tags, for class-unloads
  61  */
  62 static jvmtiEnv* trackingEnv;
  63 
  64 /*
  65  * The current highest tag number
  66  */
  67 static jlong currentClassTag;
  68 
  69 /*
  70  * Lock to protect deletedTagBag
  71  */
  72 static jrawMonitorID deletedTagLock;
  73 
  74 /*
  75  * A bag containing all the deleted klass_tags ids. Must be accessed under
  76  * deletedTagLock,
  77  */
  78 struct bag* deletedTagBag;
  79 
  80 /*
  81  * Callback when classes are freed,
  82  */
  83 static void JNICALL
  84 cbTrackingObjectFree(jvmtiEnv* jvmti_env, jlong tag)
  85 {
  86     debugMonitorEnter(deletedTagLock);
  87     *(jlong*)bagAdd(deletedTagBag) = tag;
  88     debugMonitorExit(deletedTagLock);
  89 }
  90 
  91 /*
  92  * Returns true if the item is not the searched-for tag.
  93  */
  94 static jboolean
  95 isNotTag(void* item, void* needle)
  96 {
  97     return *(jlong*)item != *(jlong*)needle;
  98 }
  99 
 100 static jboolean
 101 isClassUnloaded(jlong tag)
 102 {
 103     return !bagEnumerateOver(deletedTagBag, isNotTag, &tag);
 104 }
 105 
 106 /*
 107  * Called after class unloads have occurred.  Creates a new hash table
 108  * of currently loaded prepared classes.
 109  * The signatures of classes which were unloaded (not present in the
 110  * new table) are returned.
 111  */
 112 struct bag *
 113 classTrack_processUnloads(JNIEnv *env)
 114 {
 115     debugMonitorEnter(deletedTagLock);
 116     // printf("num unloaded classes: %d", bagSize(deletedTagBag));
 117     struct bag* deleted = bagCreateBag(sizeof(char*), bagSize(deletedTagBag));
 118     if (bagSize(deletedTagBag) != 0) {
 119         KlassNode* node = list;
 120         KlassNode** previousNext = &list;
 121         while (node != NULL) {
 122             if (isClassUnloaded(node->klass_tag)) {
 123                 *previousNext = node->next;
 124                 *(char**)bagAdd(deleted) = node->signature;
 125                 jvmtiDeallocate(node);
 126             } else {
 127                 previousNext = &(node->next);
 128             }
 129             node = *previousNext;
 130         }
 131         bagDeleteAll(deletedTagBag);
 132     }
 133     debugMonitorExit(deletedTagLock);
 134     return deleted;
 135 }
 136 
 137 /*
 138  * Add a class to the prepared class list.
 139  * Assumes no duplicates.
 140  */
 141 void
 142 classTrack_addPreparedClass(JNIEnv *env, jclass klass)
 143 {
 144     if (currentClassTag == -1) {
 145       // Class tracking not initialized yet, nobody's interested
 146       return;
 147     }
 148     KlassNode *node;
 149     jvmtiError error;
 150 
 151     if (gdata->assertOn) {
 152         /* Check this is not a duplicate */
 153         jlong tag;
 154         error = JVMTI_FUNC_PTR(trackingEnv, GetTag)(trackingEnv, klass, &tag);
 155         if (error != JVMTI_ERROR_NONE) {
 156             EXIT_ERROR(error, "Unable to GetTag with class trackingEnv");
 157         }
 158         if (tag != 0l) {
 159             JDI_ASSERT_FAILED("Attempting to insert duplicate class");
 160         }
 161     }
 162 
 163     node = jvmtiAllocate(sizeof(KlassNode));
 164     if (node == NULL) {
 165         EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"KlassNode");
 166     }
 167     error = classSignature(klass, &(node->signature), NULL);
 168     if (error != JVMTI_ERROR_NONE) {
 169         jvmtiDeallocate(node);
 170         EXIT_ERROR(error,"signature");
 171     }
 172     node->klass_tag = ++currentClassTag;
 173     error = JVMTI_FUNC_PTR(trackingEnv, SetTag)(trackingEnv, klass, node->klass_tag);
 174     if (error != JVMTI_ERROR_NONE) {
 175         jvmtiDeallocate(node->signature);
 176         jvmtiDeallocate(node);
 177         EXIT_ERROR(error,"SetTag");
 178     }
 179 
 180     /* Insert the new node */
 181     node->next = list;
 182     list = node;
 183 }
 184 
 185 static jboolean
 186 setupEvents()
 187 {
 188     jvmtiCapabilities caps;
 189     memset(&caps, 0, sizeof(caps));
 190     caps.can_generate_object_free_events = 1;
 191     jvmtiError error = JVMTI_FUNC_PTR(trackingEnv, AddCapabilities)(trackingEnv, &caps);
 192     if (error != JVMTI_ERROR_NONE) {
 193         return JNI_FALSE;
 194     }
 195     jvmtiEventCallbacks cb;
 196     memset(&cb, 0, sizeof(cb));
 197     cb.ObjectFree = cbTrackingObjectFree;
 198     error = JVMTI_FUNC_PTR(trackingEnv, SetEventCallbacks)(trackingEnv, &cb, sizeof(cb));
 199     if (error != JVMTI_ERROR_NONE) {
 200         return JNI_FALSE;
 201     }
 202     error = JVMTI_FUNC_PTR(trackingEnv, SetEventNotificationMode)(trackingEnv, JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
 203     if (error != JVMTI_ERROR_NONE) {
 204       return JNI_FALSE;
 205     }
 206     return JNI_TRUE;
 207 }
 208 
 209 /*
 210  * Called once to build the initial prepared class hash list.
 211  */
 212 void
 213 classTrack_initialize(JNIEnv *env)
 214 {
 215     trackingEnv = getSpecialJvmti();
 216     if (trackingEnv == NULL) {
 217         EXIT_ERROR(AGENT_ERROR_INTERNAL, "Failed to allocate tag-tracking jvmtiEnv");
 218     }
 219     deletedTagLock = debugMonitorCreate("Deleted class tag lock");
 220     deletedTagBag = bagCreateBag(sizeof(jlong), 10);
 221     currentClassTag = -1l;
 222     list = NULL;
 223 }
 224 
 225 void
 226 classTrack_activate(JNIEnv *env)
 227 {
 228   if (!setupEvents()) {
 229     EXIT_ERROR(AGENT_ERROR_INTERNAL, "Unable to setup ObjectFree tracking");
 230   }
 231   currentClassTag = 0l;
 232   list = NULL;
 233   WITH_LOCAL_REFS(env, 1) {
 234 
 235       jint classCount;
 236       jclass *classes;
 237       jvmtiError error;
 238       jint i;
 239 
 240       error = allLoadedClasses(&classes, &classCount);
 241       if ( error == JVMTI_ERROR_NONE ) {
 242         for (i = 0; i < classCount; i++) {
 243           jclass klass = classes[i];
 244           jint status;
 245           jint wanted = JVMTI_CLASS_STATUS_PREPARED | JVMTI_CLASS_STATUS_ARRAY;
 246           status = classStatus(klass);
 247           if ((status & wanted) != 0) {
 248             classTrack_addPreparedClass(env, klass);
 249           }
 250         }
 251         jvmtiDeallocate(classes);
 252       } else {
 253         EXIT_ERROR(error,"loaded classes array");
 254       }
 255 
 256     } END_WITH_LOCAL_REFS(env)
 257 }
 258 void
 259 classTrack_reset(void)
 260 {
 261 }