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 }