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
44 #include "util.h"
45 #include "bag.h"
46 #include "classTrack.h"
47
48 /* ClassTrack hash table slot count */
49 #define CT_HASH_SLOT_COUNT 263 /* Prime which eauals 4k+3 for some k */
50
51 typedef struct KlassNode {
52 jclass klass; /* weak global reference */
53 char *signature; /* class signature */
54 struct KlassNode *next; /* next node in this slot */
55 } KlassNode;
56
57 /*
58 * Hash table of prepared classes. Each entry is a pointer
59 * to a linked list of KlassNode.
60 */
61 static KlassNode **table;
62
63 /*
64 * Return slot in hash table to use for this class.
65 */
66 static jint
67 hashKlass(jclass klass)
68 {
69 jint hashCode = objectHashCode(klass);
70 return abs(hashCode) % CT_HASH_SLOT_COUNT;
71 }
72
73 /*
74 * Transfer a node (which represents klass) from the current
75 * table to the new table.
76 */
77 static void
78 transferClass(JNIEnv *env, jclass klass, KlassNode **newTable) {
79 jint slot = hashKlass(klass);
80 KlassNode **head = &table[slot];
81 KlassNode **newHead = &newTable[slot];
82 KlassNode **nodePtr;
83 KlassNode *node;
160 struct bag *
161 classTrack_processUnloads(JNIEnv *env)
162 {
163 KlassNode **newTable;
164 struct bag *unloadedSignatures;
165
166 unloadedSignatures = NULL;
167 newTable = jvmtiAllocate(CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
168 if (newTable == NULL) {
169 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "classTrack table");
170 } else {
171
172 (void)memset(newTable, 0, CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
173
174 WITH_LOCAL_REFS(env, 1) {
175
176 jint classCount;
177 jclass *classes;
178 jvmtiError error;
179 int i;
180
181 error = allLoadedClasses(&classes, &classCount);
182 if ( error != JVMTI_ERROR_NONE ) {
183 jvmtiDeallocate(newTable);
184 EXIT_ERROR(error,"loaded classes");
185 } else {
186
187 /* Transfer each current class into the new table */
188 for (i=0; i<classCount; i++) {
189 jclass klass = classes[i];
190 transferClass(env, klass, newTable);
191 }
192 jvmtiDeallocate(classes);
193
194 /* Delete old table, install new one */
195 unloadedSignatures = deleteTable(env, table);
196 table = newTable;
197 }
198
199 } END_WITH_LOCAL_REFS(env)
200
201 }
202
203 return unloadedSignatures;
204 }
205
206 /*
207 * Add a class to the prepared class hash table.
208 * Assumes no duplicates.
209 */
210 void
211 classTrack_addPreparedClass(JNIEnv *env, jclass klass)
212 {
213 jint slot = hashKlass(klass);
214 KlassNode **head = &table[slot];
215 KlassNode *node;
216 jvmtiError error;
217
218 if (gdata->assertOn) {
219 /* Check this is not a duplicate */
220 for (node = *head; node != NULL; node = node->next) {
221 if (isSameObject(env, klass, node->klass)) {
222 JDI_ASSERT_FAILED("Attempting to insert duplicate class");
223 break;
224 }
225 }
226 }
227
228 node = jvmtiAllocate(sizeof(KlassNode));
229 if (node == NULL) {
230 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"KlassNode");
231 }
232 error = classSignature(klass, &(node->signature), NULL);
233 if (error != JVMTI_ERROR_NONE) {
234 jvmtiDeallocate(node);
235 EXIT_ERROR(error,"signature");
236 }
237 if ((node->klass = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, klass)) == NULL) {
238 jvmtiDeallocate(node->signature);
239 jvmtiDeallocate(node);
240 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewWeakGlobalRef");
241 }
242
243 /* Insert the new node */
244 node->next = *head;
245 *head = node;
246 }
247
248 /*
249 * Called once to build the initial prepared class hash table.
250 */
251 void
252 classTrack_initialize(JNIEnv *env)
253 {
254 WITH_LOCAL_REFS(env, 1) {
255
256 jint classCount;
257 jclass *classes;
258 jvmtiError error;
259 jint i;
260
261 error = allLoadedClasses(&classes, &classCount);
262 if ( error == JVMTI_ERROR_NONE ) {
263 table = jvmtiAllocate(CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
264 if (table != NULL) {
265 (void)memset(table, 0, CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
266 for (i=0; i<classCount; i++) {
267 jclass klass = classes[i];
268 jint status;
269 jint wanted =
270 (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY);
271
272 /* We only want prepared classes and arrays */
273 status = classStatus(klass);
|
1 /*
2 * Copyright (c) 2001, 2019, 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
44 #include "util.h"
45 #include "bag.h"
46 #include "classTrack.h"
47
48 /* ClassTrack hash table slot count */
49 #define CT_HASH_SLOT_COUNT 263 /* Prime which eauals 4k+3 for some k */
50
51 typedef struct KlassNode {
52 jclass klass; /* weak global reference */
53 char *signature; /* class signature */
54 struct KlassNode *next; /* next node in this slot */
55 } KlassNode;
56
57 /*
58 * Hash table of prepared classes. Each entry is a pointer
59 * to a linked list of KlassNode.
60 */
61 static KlassNode **table;
62
63 /*
64 * The JVMTI env we use to keep track of klass tags which allows us to detect class-unloads.
65 */
66 static jvmtiEnv *trackingEnv;
67
68 /*
69 * The current highest tag number in use by the trackingEnv.
70 *
71 * No need for synchronization since everything is done under the handlerLock.
72 */
73 static jlong currentKlassTag;
74
75 /*
76 * A lock to protect access to 'deletedTagBag'
77 */
78 static jrawMonitorID deletedTagLock;
79
80 /*
81 * A flag indicating whether classes have been unloaded.
82 *
83 * It is cleared each time classTrack_processUnloads is called.
84 */
85 jboolean hasUnloadedClasses;
86
87 /*
88 * The callback for when classes are freed. Only classes are called because this is registered with
89 * the trackingEnv which only tags classes.
90 */
91 static void JNICALL
92 cbTrackingObjectFree(jvmtiEnv* jvmti_env, jlong tag)
93 {
94 debugMonitorEnter(deletedTagLock);
95 hasUnloadedClasses = JNI_TRUE;
96 debugMonitorExit(deletedTagLock);
97 }
98
99 /*
100 * Return slot in hash table to use for this class.
101 */
102 static jint
103 hashKlass(jclass klass)
104 {
105 jint hashCode = objectHashCode(klass);
106 return abs(hashCode) % CT_HASH_SLOT_COUNT;
107 }
108
109 /*
110 * Transfer a node (which represents klass) from the current
111 * table to the new table.
112 */
113 static void
114 transferClass(JNIEnv *env, jclass klass, KlassNode **newTable) {
115 jint slot = hashKlass(klass);
116 KlassNode **head = &table[slot];
117 KlassNode **newHead = &newTable[slot];
118 KlassNode **nodePtr;
119 KlassNode *node;
196 struct bag *
197 classTrack_processUnloads(JNIEnv *env)
198 {
199 KlassNode **newTable;
200 struct bag *unloadedSignatures;
201
202 unloadedSignatures = NULL;
203 newTable = jvmtiAllocate(CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
204 if (newTable == NULL) {
205 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "classTrack table");
206 } else {
207
208 (void)memset(newTable, 0, CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
209
210 WITH_LOCAL_REFS(env, 1) {
211
212 jint classCount;
213 jclass *classes;
214 jvmtiError error;
215 int i;
216 jboolean hasUnloaded = JNI_FALSE;
217
218 debugMonitorEnter(deletedTagLock);
219 hasUnloaded = hasUnloadedClasses;
220 hasUnloadedClasses = JNI_FALSE;
221 debugMonitorExit(deletedTagLock);
222
223 if (hasUnloaded) {
224 error = allLoadedClasses(&classes, &classCount);
225 if ( error != JVMTI_ERROR_NONE ) {
226 jvmtiDeallocate(newTable);
227 EXIT_ERROR(error,"loaded classes");
228 } else {
229
230 /* Transfer each current class into the new table */
231 for (i=0; i<classCount; i++) {
232 jclass klass = classes[i];
233 transferClass(env, klass, newTable);
234 }
235 jvmtiDeallocate(classes);
236
237 /* Delete old table, install new one */
238 unloadedSignatures = deleteTable(env, table);
239 table = newTable;
240 }
241 }
242 } END_WITH_LOCAL_REFS(env)
243
244 }
245
246 return unloadedSignatures;
247 }
248
249 /*
250 * Add a class to the prepared class hash table.
251 * Assumes no duplicates.
252 */
253 void
254 classTrack_addPreparedClass(JNIEnv *env, jclass klass)
255 {
256 jint slot = hashKlass(klass);
257 KlassNode **head = &table[slot];
258 KlassNode *node;
259 jvmtiError error;
260
261 if (gdata->assertOn) {
262 /* Check this is not a duplicate */
263 for (node = *head; node != NULL; node = node->next) {
264 if (isSameObject(env, klass, node->klass)) {
265 JDI_ASSERT_FAILED("Attempting to insert duplicate class");
266 break;
267 }
268 }
269 }
270
271 node = jvmtiAllocate(sizeof(KlassNode));
272 if (node == NULL) {
273 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"KlassNode");
274 }
275 error = classSignature(klass, &(node->signature), NULL);
276 if (error != JVMTI_ERROR_NONE) {
277 jvmtiDeallocate(node);
278 EXIT_ERROR(error,"signature");
279 }
280 ++currentKlassTag;
281 error = JVMTI_FUNC_PTR(trackingEnv,SetTag)(trackingEnv, klass, currentKlassTag);
282 if (error != JVMTI_ERROR_NONE) {
283 jvmtiDeallocate(node->signature);
284 jvmtiDeallocate(node);
285 EXIT_ERROR(error,"SetTag");
286 }
287 if ((node->klass = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, klass)) == NULL) {
288 jvmtiDeallocate(node->signature);
289 jvmtiDeallocate(node);
290 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewWeakGlobalRef");
291 }
292
293 /* Insert the new node */
294 node->next = *head;
295 *head = node;
296 }
297
298 static jboolean
299 setupEvents()
300 {
301 jvmtiCapabilities caps;
302 memset(&caps, 0, sizeof(caps));
303 caps.can_generate_object_free_events = 1;
304 jvmtiError error = JVMTI_FUNC_PTR(trackingEnv,AddCapabilities)(trackingEnv, &caps);
305 if (error != JVMTI_ERROR_NONE) {
306 return JNI_FALSE;
307 }
308 jvmtiEventCallbacks cb;
309 memset(&cb, 0, sizeof(cb));
310 cb.ObjectFree = cbTrackingObjectFree;
311 error = JVMTI_FUNC_PTR(trackingEnv,SetEventCallbacks)(trackingEnv, &cb, sizeof(cb));
312 if (error != JVMTI_ERROR_NONE) {
313 return JNI_FALSE;
314 }
315 error = JVMTI_FUNC_PTR(trackingEnv,SetEventNotificationMode)
316 (trackingEnv, JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
317 if (error != JVMTI_ERROR_NONE) {
318 return JNI_FALSE;
319 }
320 return JNI_TRUE;
321 }
322
323 /*
324 * Called once to build the initial prepared class hash table.
325 */
326 void
327 classTrack_initialize(JNIEnv *env)
328 {
329 /* Setup the tracking env */
330 trackingEnv = getSpecialJvmti();
331 if ( trackingEnv == NULL ) {
332 EXIT_ERROR(AGENT_ERROR_INTERNAL,"Failed to allocate tag-tracking jvmtiEnv");
333 }
334 /* We want to create these before turning on the events or tagging anything. */
335 deletedTagLock = debugMonitorCreate("Deleted class tag lock");
336 hasUnloadedClasses = JNI_FALSE;
337 /* Setup the trackingEnv's ObjectFree event */
338 if (!setupEvents()) {
339 ERROR_MESSAGE(("Unable to setup class ObjectFree tracking! Class unloads will not "
340 "be reported!"));
341 }
342 currentKlassTag = 0l;
343 WITH_LOCAL_REFS(env, 1) {
344
345 jint classCount;
346 jclass *classes;
347 jvmtiError error;
348 jint i;
349
350 error = allLoadedClasses(&classes, &classCount);
351 if ( error == JVMTI_ERROR_NONE ) {
352 table = jvmtiAllocate(CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
353 if (table != NULL) {
354 (void)memset(table, 0, CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
355 for (i=0; i<classCount; i++) {
356 jclass klass = classes[i];
357 jint status;
358 jint wanted =
359 (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY);
360
361 /* We only want prepared classes and arrays */
362 status = classStatus(klass);
|