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 /*
  27  * This module tracks classes that have been prepared, so as to
  28  * be able to report which have been unloaded. On VM start-up
  29  * and whenever new classes are loaded, all prepared classes'
  30  * signatures are attached as JVMTI tag to the class object.
  31  * Class unloading is tracked by registering
  32  * ObjectFree callback on class objects. When this happens, we find
  33  * the signature of the unloaded class(es) and report them back
  34  * to the event handler to synthesize class-unload-events.
  35  */
  36 
  37 #include "util.h"
  38 #include "bag.h"
  39 #include "classTrack.h"
  40 
  41 /*
  42  * The JVMTI tracking env to keep track of klass tags for class-unloads
  43  */
  44 static jvmtiEnv* trackingEnv;
  45 
  46 /*
  47  * A bag containing all the deleted classes' signatures. Must be accessed under
  48  * classTrackLock.
  49  */
  50 struct bag* deletedSignatures;
  51 
  52 /*
  53  * Lock to keep integrity of deletedSignatures.
  54  */
  55 static jrawMonitorID classTrackLock;
  56 
  57 /*
  58  * Invoke the callback when classes are freed, find and record the signature
  59  * in deletedSignatures. Those are only used in addPreparedClass() by the
  60  * same thread.
  61  */
  62 static void JNICALL
  63 cbTrackingObjectFree(jvmtiEnv* jvmti_env, jlong tag)
  64 {
  65     debugMonitorEnter(classTrackLock);
  66     if (deletedSignatures == NULL) {
  67       debugMonitorExit(classTrackLock);
  68       return;
  69     }
  70     *(char**)bagAdd(deletedSignatures) = (char*)tag;
  71 
  72     debugMonitorExit(classTrackLock);
  73 }
  74 
  75 /*
  76  * Called after class unloads have occurred.
  77  * The signatures of classes which were unloaded are returned.
  78  */
  79 struct bag *
  80 classTrack_processUnloads(JNIEnv *env)
  81 {
  82     debugMonitorEnter(classTrackLock);
  83     if (deletedSignatures == NULL) {
  84         // Class tracking not initialized, nobody's interested.
  85         debugMonitorExit(classTrackLock);
  86         return NULL;
  87     }
  88     struct bag* deleted = deletedSignatures;
  89     deletedSignatures = bagCreateBag(sizeof(char*), 10);
  90     debugMonitorExit(classTrackLock);
  91     return deleted;
  92 }
  93 
  94 /*
  95  * Add a class to the prepared class table.
  96  */
  97 void
  98 classTrack_addPreparedClass(JNIEnv *env_unused, jclass klass)
  99 {
 100     jvmtiError error;
 101 
 102     jvmtiEnv* env = trackingEnv;
 103 
 104     // Check this is not already tagged.
 105     jlong tag;
 106     error = JVMTI_FUNC_PTR(trackingEnv, GetTag)(env, klass, &tag);
 107     if (error != JVMTI_ERROR_NONE) {
 108         EXIT_ERROR(error, "Unable to GetTag with class trackingEnv");
 109     }
 110     if (tag != 0l) {
 111         return; // Already added
 112     }
 113 
 114     char* signature;
 115     error = classSignature(klass, &signature, NULL);
 116     if (error != JVMTI_ERROR_NONE) {
 117         EXIT_ERROR(error,"signature");
 118     }
 119     error = JVMTI_FUNC_PTR(trackingEnv, SetTag)(env, klass, (jlong)signature);
 120     if (error != JVMTI_ERROR_NONE) {
 121         jvmtiDeallocate(signature);
 122         EXIT_ERROR(error,"SetTag");
 123     }
 124 
 125 }
 126 
 127 static jboolean
 128 setupEvents()
 129 {
 130     jvmtiCapabilities caps;
 131     memset(&caps, 0, sizeof(caps));
 132     caps.can_generate_object_free_events = 1;
 133     jvmtiError error = JVMTI_FUNC_PTR(trackingEnv, AddCapabilities)(trackingEnv, &caps);
 134     if (error != JVMTI_ERROR_NONE) {
 135         return JNI_FALSE;
 136     }
 137     jvmtiEventCallbacks cb;
 138     memset(&cb, 0, sizeof(cb));
 139     cb.ObjectFree = cbTrackingObjectFree;
 140     error = JVMTI_FUNC_PTR(trackingEnv, SetEventCallbacks)(trackingEnv, &cb, sizeof(cb));
 141     if (error != JVMTI_ERROR_NONE) {
 142         return JNI_FALSE;
 143     }
 144     error = JVMTI_FUNC_PTR(trackingEnv, SetEventNotificationMode)(trackingEnv, JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
 145     if (error != JVMTI_ERROR_NONE) {
 146         return JNI_FALSE;
 147     }
 148     return JNI_TRUE;
 149 }
 150 
 151 /*
 152  * Called once to initialize class-tracking.
 153  */
 154 void
 155 classTrack_initialize(JNIEnv *env)
 156 {
 157     deletedSignatures = NULL;
 158     classTrackLock = debugMonitorCreate("Deleted class tag lock");
 159     trackingEnv = getSpecialJvmti();
 160     if (trackingEnv == NULL) {
 161         EXIT_ERROR(AGENT_ERROR_INTERNAL, "Failed to allocate tag-tracking jvmtiEnv");
 162     }
 163 
 164 
 165     if (!setupEvents()) {
 166         EXIT_ERROR(AGENT_ERROR_INTERNAL, "Unable to setup ObjectFree tracking");
 167     }
 168 
 169     jint classCount;
 170     jclass *classes;
 171     jvmtiError error;
 172     jint i;
 173 
 174     error = allLoadedClasses(&classes, &classCount);
 175     if ( error == JVMTI_ERROR_NONE ) {
 176         for (i = 0; i < classCount; i++) {
 177             jclass klass = classes[i];
 178             jint status;
 179             jint wanted = JVMTI_CLASS_STATUS_PREPARED | JVMTI_CLASS_STATUS_ARRAY;
 180             status = classStatus(klass);
 181             if ((status & wanted) != 0) {
 182                 classTrack_addPreparedClass(env, klass);
 183             }
 184         }
 185         jvmtiDeallocate(classes);
 186     } else {
 187         EXIT_ERROR(error,"loaded classes array");
 188     }
 189 }
 190 
 191 /*
 192  * Called to activate class-tracking when a listener registers for EI_GC_FINISH.
 193  */
 194 void
 195 classTrack_activate(JNIEnv *env)
 196 {
 197     debugMonitorEnter(classTrackLock);
 198     deletedSignatures = bagCreateBag(sizeof(char*), 1000);
 199     debugMonitorExit(classTrackLock);
 200 }
 201 
 202 static jboolean
 203 cleanDeleted(void *signatureVoid, void *arg)
 204 {
 205     char* sig = *(char**)signatureVoid;
 206     jvmtiDeallocate(sig);
 207     return JNI_TRUE;
 208 }
 209 
 210 /*
 211  * Called when agent detaches.
 212  */
 213 void
 214 classTrack_reset(void)
 215 {
 216     debugMonitorEnter(classTrackLock);
 217 
 218     bagEnumerateOver(deletedSignatures, cleanDeleted, NULL);
 219     bagDestroyBag(deletedSignatures);
 220     deletedSignatures = NULL;
 221 
 222     debugMonitorExit(classTrackLock);
 223 }