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 /* ClassTrack table slot count */ 49 #define CT_SLOT_COUNT 263 /* Prime which eauals 4k+3 for some k */ 50 51 typedef struct KlassNode { 52 jlong klass_tag; /* Klass's tag in tracking env */ 53 char *signature; /* class signature */ 54 struct KlassNode *next; /* next node in this slot */ 55 } KlassNode; 56 57 /* 58 * Table mapping tag % CT_SLOT_COUNT to linked-list of KlassNode*. 59 */ 60 static KlassNode** table; 61 62 /* 63 * The JVMTI tracking env to keep track of klass tags, for class-unloads 64 */ 65 static jvmtiEnv* trackingEnv; 66 67 /* 68 * The current highest tag number 69 */ 70 static jlong currentClassTag; 71 72 /* 73 * Lock to protect deletedSignatureBag 74 */ 75 static jrawMonitorID deletedSignatureLock; 76 77 /* 78 * A bag containing all the deleted classes' signatures. Must be accessed under 79 * deletedTagLock, 80 */ 81 struct bag* deletedSignatureBag; 82 83 /* 84 * Callback when classes are freed, Finds the signature and remembers it in deletedSignatureBag. 85 */ 86 static void JNICALL 87 cbTrackingObjectFree(jvmtiEnv* jvmti_env, jlong tag) 88 { 89 debugMonitorEnter(deletedSignatureLock); 90 if (currentClassTag == -1) { 91 // Class tracking not initialized, nobody's interested 92 debugMonitorExit(deletedSignatureLock); 93 return; 94 } 95 96 // Find deleted KlassNode 97 size_t idx = tag % CT_SLOT_COUNT; 98 KlassNode** klass_ptr = &table[idx]; 99 KlassNode* klass = *klass_ptr; 100 101 // Tag not found? Ignore. 102 if (klass == NULL) { 103 debugMonitorExit(deletedSignatureLock); 104 return; 105 } 106 107 // Scan linked-list. 108 jlong found_tag = klass->klass_tag; 109 while (klass != NULL && found_tag != tag) { 110 klass_ptr = &klass->next; 111 klass = *klass_ptr; 112 found_tag = klass->klass_tag; 113 } 114 115 // Tag not found? Ignore. 116 if (found_tag != tag) { 117 debugMonitorExit(deletedSignatureLock); 118 return; 119 } 120 121 // At this point we have the KlassNode corresponding to the tag 122 // in klass, and the pointer to it in klass_node. 123 // Remember the unloaded signature. 124 *(char**)bagAdd(deletedSignatureBag) = klass->signature; 125 126 // Unlink the KlassNode. 127 *klass_ptr = klass->next; 128 jvmtiDeallocate(klass); 129 130 // Done. 131 debugMonitorExit(deletedSignatureLock); 132 } 133 134 /* 135 * Called after class unloads have occurred. 136 * The signatures of classes which were unloaded are returned. 137 */ 138 struct bag * 139 classTrack_processUnloads(JNIEnv *env) 140 { 141 debugMonitorEnter(deletedSignatureLock); 142 if (currentClassTag == -1) { 143 // Class tracking not initialized, nobody's interested 144 debugMonitorExit(deletedSignatureLock); 145 return bagCreateBag(sizeof(char*), 0); 146 } 147 struct bag* deleted = deletedSignatureBag; 148 deletedSignatureBag = bagCreateBag(sizeof(char*), 10); 149 debugMonitorExit(deletedSignatureLock); 150 return deleted; 151 } 152 153 /* 154 * Add a class to the prepared class table. 155 */ 156 void 157 classTrack_addPreparedClass(JNIEnv *env, jclass klass) 158 { 159 jvmtiError error; 160 161 debugMonitorEnter(deletedSignatureLock); 162 if (currentClassTag == -1) { 163 // Class tracking not initialized yet, nobody's interested 164 debugMonitorExit(deletedSignatureLock); 165 return; 166 } 167 168 /* Check this is not a duplicate */ 169 jlong tag; 170 error = JVMTI_FUNC_PTR(trackingEnv, GetTag)(trackingEnv, klass, &tag); 171 if (error != JVMTI_ERROR_NONE) { 172 EXIT_ERROR(error, "Unable to GetTag with class trackingEnv"); 173 } 174 if (tag != 0l) { 175 debugMonitorExit(deletedSignatureLock); 176 return; // Already added 177 } 178 179 KlassNode* node = jvmtiAllocate(sizeof(KlassNode)); 180 if (node == NULL) { 181 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"KlassNode"); 182 } 183 error = classSignature(klass, &(node->signature), NULL); 184 if (error != JVMTI_ERROR_NONE) { 185 jvmtiDeallocate(node); 186 EXIT_ERROR(error,"signature"); 187 } 188 node->klass_tag = ++currentClassTag; 189 error = JVMTI_FUNC_PTR(trackingEnv, SetTag)(trackingEnv, klass, node->klass_tag); 190 if (error != JVMTI_ERROR_NONE) { 191 jvmtiDeallocate(node->signature); 192 jvmtiDeallocate(node); 193 EXIT_ERROR(error,"SetTag"); 194 } 195 196 /* Insert the new node */ 197 size_t idx = node->klass_tag % CT_SLOT_COUNT; 198 node->next = table[idx]; 199 table[idx] = node; 200 debugMonitorExit(deletedSignatureLock); 201 } 202 203 static jboolean 204 setupEvents() 205 { 206 jvmtiCapabilities caps; 207 memset(&caps, 0, sizeof(caps)); 208 caps.can_generate_object_free_events = 1; 209 jvmtiError error = JVMTI_FUNC_PTR(trackingEnv, AddCapabilities)(trackingEnv, &caps); 210 if (error != JVMTI_ERROR_NONE) { 211 return JNI_FALSE; 212 } 213 jvmtiEventCallbacks cb; 214 memset(&cb, 0, sizeof(cb)); 215 cb.ObjectFree = cbTrackingObjectFree; 216 error = JVMTI_FUNC_PTR(trackingEnv, SetEventCallbacks)(trackingEnv, &cb, sizeof(cb)); 217 if (error != JVMTI_ERROR_NONE) { 218 return JNI_FALSE; 219 } 220 error = JVMTI_FUNC_PTR(trackingEnv, SetEventNotificationMode)(trackingEnv, JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL); 221 if (error != JVMTI_ERROR_NONE) { 222 return JNI_FALSE; 223 } 224 return JNI_TRUE; 225 } 226 227 /* 228 * Called once to initialize class-tracking. 229 */ 230 void 231 classTrack_initialize(JNIEnv *env) 232 { 233 trackingEnv = getSpecialJvmti(); 234 if (trackingEnv == NULL) { 235 EXIT_ERROR(AGENT_ERROR_INTERNAL, "Failed to allocate tag-tracking jvmtiEnv"); 236 } 237 deletedSignatureLock = debugMonitorCreate("Deleted class tag lock"); 238 deletedSignatureBag = bagCreateBag(sizeof(char*), 10); 239 currentClassTag = -1l; 240 table = NULL; 241 } 242 243 /* 244 * Called to activate class-tracking when a listener registers for EI_GC_FINISH. 245 */ 246 void 247 classTrack_activate(JNIEnv *env) 248 { 249 if (!setupEvents()) { 250 EXIT_ERROR(AGENT_ERROR_INTERNAL, "Unable to setup ObjectFree tracking"); 251 } 252 currentClassTag = 0l; 253 table = jvmtiAllocate(CT_SLOT_COUNT * sizeof(KlassNode*)); 254 if (table != NULL) { 255 (void)memset(table, 0, CT_SLOT_COUNT * sizeof(KlassNode*)); 256 } else { 257 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "failed allocating class-track table"); 258 } 259 260 WITH_LOCAL_REFS(env, 1) { 261 262 jint classCount; 263 jclass *classes; 264 jvmtiError error; 265 jint i; 266 267 error = allLoadedClasses(&classes, &classCount); 268 if ( error == JVMTI_ERROR_NONE ) { 269 for (i = 0; i < classCount; i++) { 270 jclass klass = classes[i]; 271 jint status; 272 jint wanted = JVMTI_CLASS_STATUS_PREPARED | JVMTI_CLASS_STATUS_ARRAY; 273 status = classStatus(klass); 274 if ((status & wanted) != 0) { 275 classTrack_addPreparedClass(env, klass); 276 } 277 } 278 jvmtiDeallocate(classes); 279 } else { 280 EXIT_ERROR(error,"loaded classes array"); 281 } 282 283 } END_WITH_LOCAL_REFS(env) 284 } 285 286 static jboolean 287 cleanDeleted(void *signatureVoid, void *arg) 288 { 289 char* sig = (char*)signatureVoid; 290 jvmtiDeallocate(sig); 291 return JNI_TRUE; 292 } 293 294 /* 295 * Called when agent detaches. 296 */ 297 void 298 classTrack_reset(void) 299 { 300 int idx; 301 debugMonitorEnter(deletedSignatureLock); 302 303 for (idx = 0; idx < CT_SLOT_COUNT; ++idx) { 304 KlassNode* node = table[idx]; 305 while (node != NULL) { 306 KlassNode* next = node->next; 307 jvmtiDeallocate(node->signature); 308 jvmtiDeallocate(node); 309 node = next; 310 } 311 } 312 jvmtiDeallocate(table); 313 314 bagEnumerateOver(deletedSignatureBag, cleanDeleted, NULL); 315 bagDestroyBag(deletedSignatureBag); 316 317 currentClassTag = -1; 318 319 (void)JVMTI_FUNC_PTR(trackingEnv,DisposeEnvironment)(trackingEnv); 320 trackingEnv = NULL; 321 322 debugMonitorExit(deletedSignatureLock); 323 }