• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * ANDROID-CHANGED: This module is almost totally re-written
36  * for android. On android, we have a limited number of jweak
37  * references that can be around at any one time. In order to
38  * preserve this limited resource for user-code use we keep
39  * track of the status of classes using JVMTI tags.
40  *
41  * We keep a linked-list of the signatures of loaded classes
42  * associated with the tag we gave to that class. The tag is
43  * simply incremented every time we add a new class.
44  *
45  * We also request (on the separate tracking jvmtiEnv) an
46  * ObjectFree event be called for each of these classes. This
47  * allows us to keep a running list of all the classes known to
48  * have been collected since the last call to
49  * classTrack_processUnloads. On each call to processUnloads we
50  * iterate through this list and remove from the main list all
51  * the objects that have been collected. We then return a list of
52  * the class-signatures that have been collected.
53  *
54  * For efficiency and simplicity we don't bother retagging or
55  * re-using old tags, instead relying on the fact that no
56  * program will ever be able to exhaust the (2^64 - 1) possible
57  * tag values (which would require that many class-loads).
58  *
59  * This relies on the tagging and ObjectFree implementation being
60  * relatively efficient for performance. It has the advantage of
61  * not requiring any jweaks.
62  *
63  * All calls into any function of this module must be either
64  * done before the event-handler system is setup or done while
65  * holding the event handlerLock. The list of freed classes is
66  * protected by the classTagLock.
67  */
68 
69 #include "util.h"
70 #include "bag.h"
71 #include "classTrack.h"
72 
73 typedef struct KlassNode {
74     jlong klass_tag;         /* Tag the klass has in the tracking-env */
75     char *signature;         /* class signature */
76     struct KlassNode *next;  /* next node in this slot */
77 } KlassNode;
78 
79 /*
80  * pointer to first node of a linked list of prepared classes KlassNodes.
81  */
82 static KlassNode *list;
83 
84 /*
85  * The JVMTI env we use to keep track of klass tags which allows us to detect class-unloads.
86  */
87 static jvmtiEnv *trackingEnv;
88 
89 /*
90  * The current highest tag number in use by the trackingEnv.
91  *
92  * No need for synchronization since everything is done under the handlerLock.
93  */
94 static jlong currentKlassTag;
95 
96 /*
97  * A lock to protect access to 'deletedTagBag'
98  */
99 static jrawMonitorID deletedTagLock;
100 
101 /*
102  * A bag containing all the deleted klass_tags ids. This must be accessed under the
103  * deletedTagLock.
104  *
105  * It is cleared each time classTrack_processUnloads is called.
106  */
107 struct bag* deletedTagBag;
108 
109 /*
110  * The callback for when classes are freed. Only classes are called because this is registered with
111  * the trackingEnv which only tags classes.
112  */
113 static void JNICALL
cbTrackingObjectFree(jvmtiEnv * jvmti_env,jlong tag)114 cbTrackingObjectFree(jvmtiEnv* jvmti_env, jlong tag)
115 {
116     debugMonitorEnter(deletedTagLock);
117     *(jlong*)bagAdd(deletedTagBag) = tag;
118     debugMonitorExit(deletedTagLock);
119 }
120 
121 /*
122  * Returns true (thus continuing the iteration) if the item is not the searched for tag.
123  */
124 static jboolean
isNotTag(void * item,void * needle)125 isNotTag(void* item, void* needle)
126 {
127     return *(jlong*)item != *(jlong*)needle;
128 }
129 
130 /*
131  * This requires that deletedTagLock and the handlerLock are both held.
132  */
133 static jboolean
isClassUnloaded(jlong tag)134 isClassUnloaded(jlong tag)
135 {
136     /* bagEnumerateOver returns true if 'func' returns true on all items and aborts early if not. */
137     return !bagEnumerateOver(deletedTagBag, isNotTag, &tag);
138 }
139 
140 /*
141  * Called after class unloads have occurred.  Creates a new hash table
142  * of currently loaded prepared classes.
143  * The signatures of classes which were unloaded (not present in the
144  * new table) are returned.
145  *
146  * NB This relies on addPreparedClass being called for every class loaded after the
147  * classTrack_initialize function is called. We will not request all loaded classes again after
148  * that. It also relies on not being called concurrently with any classTrack_addPreparedClass or
149  * other classTrack_processUnloads calls.
150  */
151 struct bag *
classTrack_processUnloads(JNIEnv * env)152 classTrack_processUnloads(JNIEnv *env)
153 {
154     /* We could optimize this somewhat by holding the deletedTagLock for a much shorter time,
155      * replacing it as soon as we enter and then destroying it once we are done with it. This will
156      * cause a lot of memory churn and this function is not expected to be called that often.
157      * Furthermore due to the check for an empty bag (which should be very common) normally this
158      * will finish very quickly. In cases where there is a concurrent GC occuring and a class is
159      * being collected the GC-ing threads could be blocked until we are done but this is expected to
160      * be very rare.
161      */
162     debugMonitorEnter(deletedTagLock);
163     /* Take and return the deletedTagBag */
164     struct bag* deleted = bagCreateBag(sizeof(char*), bagSize(deletedTagBag));
165     /* The deletedTagBag is going to be much shorter than the klassNode list so we should walk the
166      * KlassNode list once and scan the deletedTagBag each time. We only need to this in the rare
167      * case that there was anything deleted though.
168      */
169     if (bagSize(deletedTagBag) != 0) {
170         KlassNode* node = list;
171         KlassNode** previousNext = &list;
172 
173         while (node != NULL) {
174             if (isClassUnloaded(node->klass_tag)) {
175                 /* Update the previous node's next pointer to point after this node. Note that we
176                  * update the value pointed to by previousNext but not the value of previousNext
177                  * itself.
178                  */
179                 *previousNext = node->next;
180                 /* Put this nodes signature into the deleted bag */
181                 *(char**)bagAdd(deleted) = node->signature;
182                 /* Deallocate the node */
183                 jvmtiDeallocate(node);
184             } else {
185                 /* This node will become the previous node so update the previousNext pointer to
186                  * this nodes next pointer.
187                  */
188                 previousNext = &(node->next);
189             }
190             node = *previousNext;
191         }
192         bagDeleteAll(deletedTagBag);
193     }
194     debugMonitorExit(deletedTagLock);
195     return deleted;
196 }
197 
198 /*
199  * Add a class to the prepared class list.
200  * Assumes no duplicates.
201  */
202 void
classTrack_addPreparedClass(JNIEnv * env,jclass klass)203 classTrack_addPreparedClass(JNIEnv *env, jclass klass)
204 {
205     KlassNode *node;
206     jvmtiError error;
207 
208     if (gdata->assertOn) {
209         /* Check this is not a duplicate */
210         jlong tag;
211         error = JVMTI_FUNC_PTR(trackingEnv,GetTag)(trackingEnv, klass, &tag);
212         if (error != JVMTI_ERROR_NONE) {
213             EXIT_ERROR(error,"unable to get-tag with class trackingEnv!");
214         }
215         if (tag != 0l) {
216             JDI_ASSERT_FAILED("Attempting to insert duplicate class");
217         }
218     }
219 
220     node = jvmtiAllocate(sizeof(KlassNode));
221     if (node == NULL) {
222         EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"KlassNode");
223     }
224     error = classSignature(klass, &(node->signature), NULL);
225     if (error != JVMTI_ERROR_NONE) {
226         jvmtiDeallocate(node);
227         EXIT_ERROR(error,"signature");
228     }
229     node->klass_tag = ++currentKlassTag;
230     error = JVMTI_FUNC_PTR(trackingEnv,SetTag)(trackingEnv, klass, node->klass_tag);
231     if (error != JVMTI_ERROR_NONE) {
232         jvmtiDeallocate(node->signature);
233         jvmtiDeallocate(node);
234         EXIT_ERROR(error,"SetTag");
235     }
236 
237     /* Insert the new node */
238     node->next = list;
239     list = node;
240 }
241 
242 static jboolean
setupEvents()243 setupEvents()
244 {
245     jvmtiCapabilities caps;
246     memset(&caps, 0, sizeof(caps));
247     caps.can_generate_object_free_events = 1;
248     jvmtiError error = JVMTI_FUNC_PTR(trackingEnv,AddCapabilities)(trackingEnv, &caps);
249     if (error != JVMTI_ERROR_NONE) {
250         return JNI_FALSE;
251     }
252     jvmtiEventCallbacks cb;
253     memset(&cb, 0, sizeof(cb));
254     cb.ObjectFree = cbTrackingObjectFree;
255     error = JVMTI_FUNC_PTR(trackingEnv,SetEventCallbacks)(trackingEnv, &cb, sizeof(cb));
256     if (error != JVMTI_ERROR_NONE) {
257         return JNI_FALSE;
258     }
259     error = JVMTI_FUNC_PTR(trackingEnv,SetEventNotificationMode)
260             (trackingEnv, JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
261     if (error != JVMTI_ERROR_NONE) {
262         return JNI_FALSE;
263     }
264     return JNI_TRUE;
265 }
266 
267 /*
268  * Called once to build the initial prepared class hash table.
269  */
270 void
classTrack_initialize(JNIEnv * env)271 classTrack_initialize(JNIEnv *env)
272 {
273     /* ANDROID_CHANGED: Setup the tracking env and the currentKlassTag */
274     trackingEnv = getSpecialJvmti();
275     if ( trackingEnv == NULL ) {
276         EXIT_ERROR(AGENT_ERROR_INTERNAL,"Failed to allocate tag-tracking jvmtiEnv");
277     }
278     /* We want to create these before turning on the events or tagging anything. */
279     deletedTagLock = debugMonitorCreate("Deleted class tag lock");
280     deletedTagBag = bagCreateBag(sizeof(jlong), 10);
281     /* ANDROID-CHANGED: Setup the trackingEnv's ObjectFree event */
282     if (!setupEvents()) {
283         /* On android classes are usually not unloaded too often so this is not a huge loss. */
284         ERROR_MESSAGE(("Unable to setup class ObjectFree tracking! Class unloads will not "
285                        "be reported!"));
286     }
287     currentKlassTag = 0l;
288     list = NULL;
289     WITH_LOCAL_REFS(env, 1) {
290 
291         jint classCount;
292         jclass *classes;
293         jvmtiError error;
294         jint i;
295 
296         error = allLoadedClasses(&classes, &classCount);
297         if ( error == JVMTI_ERROR_NONE ) {
298             for (i=0; i<classCount; i++) {
299                 jclass klass = classes[i];
300                 jint status;
301                 jint wanted =
302                     (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY);
303 
304                 /* We only want prepared classes and arrays */
305                 status = classStatus(klass);
306                 if ( (status & wanted) != 0 ) {
307                     classTrack_addPreparedClass(env, klass);
308                 }
309             }
310             jvmtiDeallocate(classes);
311         } else {
312             EXIT_ERROR(error,"loaded classes array");
313         }
314 
315     } END_WITH_LOCAL_REFS(env)
316 
317 }
318 
319 void
classTrack_reset(void)320 classTrack_reset(void)
321 {
322 }
323