• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1998, 2013, 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 #if defined(_ALLBSD_SOURCE)
27 #include <stdint.h>                     /* for uintptr_t */
28 #endif
29 
30 #include "util.h"
31 #include "commonRef.h"
32 
33 #define ALL_REFS -1
34 
35 /*
36  * Each object sent to the front end is tracked with the RefNode struct
37  * (see util.h).
38  * External to this module, objects are identified by a jlong id which is
39  * simply the sequence number. A weak reference is usually used so that
40  * the presence of a debugger-tracked object will not prevent
41  * its collection. Once an object is collected, its RefNode may be
42  * deleted and the weak ref inside may be reused (these may happen in
43  * either order). Using the sequence number
44  * as the object id prevents ambiguity in the object id when the weak ref
45  * is reused. The RefNode* is stored with the object as it's JVMTI Tag.
46  *
47  * The ref member is changed from weak to strong when
48  * gc of the object is to be prevented.
49  * Whether or not it is strong, it is never exported from this module.
50  *
51  * A reference count of each jobject is also maintained here. It tracks
52  * the number times an object has been referenced through
53  * commonRef_refToID. A RefNode is freed once the reference
54  * count is decremented to 0 (with commonRef_release*), even if the
55  * corresponding object has not been collected.
56  *
57  * One hash table is maintained. The mapping of ID to jobject (or RefNode*)
58  * is handled with one hash table that will re-size itself as the number
59  * of RefNode's grow.
60  */
61 
62 /* Initial hash table size (must be power of 2) */
63 #define HASH_INIT_SIZE 512
64 /* If element count exceeds HASH_EXPAND_SCALE*hash_size we expand & re-hash */
65 #define HASH_EXPAND_SCALE 8
66 /* Maximum hash table size (must be power of 2) */
67 #define HASH_MAX_SIZE  (1024*HASH_INIT_SIZE)
68 
69 /* Map a key (ID) to a hash bucket */
70 static jint
hashBucket(jlong key)71 hashBucket(jlong key)
72 {
73     /* Size should always be a power of 2, use mask instead of mod operator */
74     /*LINTED*/
75     return ((jint)key) & (gdata->objectsByIDsize-1);
76 }
77 
78 /* Generate a new ID */
79 static jlong
newSeqNum(void)80 newSeqNum(void)
81 {
82     return gdata->nextSeqNum++;
83 }
84 
85 /* Create a fresh RefNode structure, create a weak ref and tag the object */
86 static RefNode *
createNode(JNIEnv * env,jobject ref)87 createNode(JNIEnv *env, jobject ref)
88 {
89     RefNode   *node;
90     jobject    weakRef;
91     jvmtiError error;
92 
93     /* Could allocate RefNode's in blocks, not sure it would help much */
94     node = (RefNode*)jvmtiAllocate((int)sizeof(RefNode));
95     if (node == NULL) {
96         return NULL;
97     }
98 
99     /* Create weak reference to make sure we have a reference */
100     weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref);
101     if (weakRef == NULL) {
102         jvmtiDeallocate(node);
103         return NULL;
104     }
105 
106     /* Set tag on weakRef */
107     error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)
108                           (gdata->jvmti, weakRef, ptr_to_jlong(node));
109     if ( error != JVMTI_ERROR_NONE ) {
110         JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef);
111         jvmtiDeallocate(node);
112         return NULL;
113     }
114 
115     /* Fill in RefNode */
116     node->ref      = weakRef;
117     node->isStrong = JNI_FALSE;
118     node->count    = 1;
119     node->seqNum   = newSeqNum();
120 
121     /* Count RefNode's created */
122     gdata->objectsByIDcount++;
123     return node;
124 }
125 
126 /* Delete a RefNode allocation, delete weak/global ref and clear tag */
127 static void
deleteNode(JNIEnv * env,RefNode * node)128 deleteNode(JNIEnv *env, RefNode *node)
129 {
130     LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref));
131 
132     if ( node->ref != NULL ) {
133         /* Clear tag */
134         (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
135                             (gdata->jvmti, node->ref, NULL_OBJECT_ID);
136         if (node->isStrong) {
137             JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
138         } else {
139             JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
140         }
141     }
142     gdata->objectsByIDcount--;
143     jvmtiDeallocate(node);
144 }
145 
146 /* Change a RefNode to have a strong reference */
147 static jobject
strengthenNode(JNIEnv * env,RefNode * node)148 strengthenNode(JNIEnv *env, RefNode *node)
149 {
150     if (!node->isStrong) {
151         jobject strongRef;
152 
153         strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref);
154         /*
155          * NewGlobalRef on a weak ref will return NULL if the weak
156          * reference has been collected or if out of memory.
157          * We need to distinguish those two occurrences.
158          */
159         if ((strongRef == NULL) && !isSameObject(env, node->ref, NULL)) {
160             EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
161         }
162         if (strongRef != NULL) {
163             JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
164             node->ref      = strongRef;
165             node->isStrong = JNI_TRUE;
166         }
167         return strongRef;
168     } else {
169         return node->ref;
170     }
171 }
172 
173 /* Change a RefNode to have a weak reference */
174 static jweak
weakenNode(JNIEnv * env,RefNode * node)175 weakenNode(JNIEnv *env, RefNode *node)
176 {
177     if (node->isStrong) {
178         jweak weakRef;
179 
180         weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref);
181         if (weakRef != NULL) {
182             JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
183             node->ref      = weakRef;
184             node->isStrong = JNI_FALSE;
185         }
186         return weakRef;
187     } else {
188         return node->ref;
189     }
190 }
191 
192 /*
193  * Returns the node which contains the common reference for the
194  * given object. The passed reference should not be a weak reference
195  * managed in the object hash table (i.e. returned by commonRef_idToRef)
196  * because no sequence number checking is done.
197  */
198 static RefNode *
findNodeByRef(JNIEnv * env,jobject ref)199 findNodeByRef(JNIEnv *env, jobject ref)
200 {
201     jvmtiError error;
202     jlong      tag;
203 
204     tag   = NULL_OBJECT_ID;
205     error = JVMTI_FUNC_PTR(gdata->jvmti,GetTag)(gdata->jvmti, ref, &tag);
206     if ( error == JVMTI_ERROR_NONE ) {
207         RefNode   *node;
208 
209         node = (RefNode*)jlong_to_ptr(tag);
210         return node;
211     }
212     return NULL;
213 }
214 
215 /* Locate and delete a node based on ID */
216 static void
deleteNodeByID(JNIEnv * env,jlong id,jint refCount)217 deleteNodeByID(JNIEnv *env, jlong id, jint refCount)
218 {
219     jint     slot;
220     RefNode *node;
221     RefNode *prev;
222 
223     slot = hashBucket(id);
224     node = gdata->objectsByID[slot];
225     prev = NULL;
226 
227     while (node != NULL) {
228         if (id == node->seqNum) {
229             if (refCount != ALL_REFS) {
230                 node->count -= refCount;
231             } else {
232                 node->count = 0;
233             }
234             if (node->count <= 0) {
235                 if ( node->count < 0 ) {
236                     EXIT_ERROR(AGENT_ERROR_INTERNAL,"RefNode count < 0");
237                 }
238                 /* Detach from id hash table */
239                 if (prev == NULL) {
240                     gdata->objectsByID[slot] = node->next;
241                 } else {
242                     prev->next = node->next;
243                 }
244                 deleteNode(env, node);
245             }
246             break;
247         }
248         prev = node;
249         node = node->next;
250     }
251 }
252 
253 /*
254  * Returns the node stored in the object hash table for the given object
255  * id. The id should be a value previously returned by
256  * commonRef_refToID.
257  *
258  *  NOTE: It is possible that a match is found here, but that the object
259  *        is garbage collected by the time the caller inspects node->ref.
260  *        Callers should take care using the node->ref object returned here.
261  *
262  */
263 static RefNode *
findNodeByID(JNIEnv * env,jlong id)264 findNodeByID(JNIEnv *env, jlong id)
265 {
266     jint     slot;
267     RefNode *node;
268     RefNode *prev;
269 
270     slot = hashBucket(id);
271     node = gdata->objectsByID[slot];
272     prev = NULL;
273 
274     while (node != NULL) {
275         if ( id == node->seqNum ) {
276             if ( prev != NULL ) {
277                 /* Re-order hash list so this one is up front */
278                 prev->next = node->next;
279                 node->next = gdata->objectsByID[slot];
280                 gdata->objectsByID[slot] = node;
281             }
282             break;
283         }
284         node = node->next;
285     }
286     return node;
287 }
288 
289 /* Initialize the hash table stored in gdata area */
290 static void
initializeObjectsByID(int size)291 initializeObjectsByID(int size)
292 {
293     /* Size should always be a power of 2 */
294     if ( size > HASH_MAX_SIZE ) size = HASH_MAX_SIZE;
295     gdata->objectsByIDsize  = size;
296     gdata->objectsByIDcount = 0;
297     gdata->objectsByID      = (RefNode**)jvmtiAllocate((int)sizeof(RefNode*)*size);
298     (void)memset(gdata->objectsByID, 0, (int)sizeof(RefNode*)*size);
299 }
300 
301 /* hash in a RefNode */
302 static void
hashIn(RefNode * node)303 hashIn(RefNode *node)
304 {
305     jint     slot;
306 
307     /* Add to id hashtable */
308     slot                     = hashBucket(node->seqNum);
309     node->next               = gdata->objectsByID[slot];
310     gdata->objectsByID[slot] = node;
311 }
312 
313 /* Allocate and add RefNode to hash table */
314 static RefNode *
newCommonRef(JNIEnv * env,jobject ref)315 newCommonRef(JNIEnv *env, jobject ref)
316 {
317     RefNode *node;
318 
319     /* Allocate the node and set it up */
320     node = createNode(env, ref);
321     if ( node == NULL ) {
322         return NULL;
323     }
324 
325     /* See if hash table needs expansion */
326     if ( gdata->objectsByIDcount > gdata->objectsByIDsize*HASH_EXPAND_SCALE &&
327          gdata->objectsByIDsize < HASH_MAX_SIZE ) {
328         RefNode **old;
329         int       oldsize;
330         int       newsize;
331         int       i;
332 
333         /* Save old information */
334         old     = gdata->objectsByID;
335         oldsize = gdata->objectsByIDsize;
336         /* Allocate new hash table */
337         gdata->objectsByID = NULL;
338         newsize = oldsize*HASH_EXPAND_SCALE;
339         if ( newsize > HASH_MAX_SIZE ) newsize = HASH_MAX_SIZE;
340         initializeObjectsByID(newsize);
341         /* Walk over old one and hash in all the RefNodes */
342         for ( i = 0 ; i < oldsize ; i++ ) {
343             RefNode *onode;
344 
345             onode = old[i];
346             while (onode != NULL) {
347                 RefNode *next;
348 
349                 next = onode->next;
350                 hashIn(onode);
351                 onode = next;
352             }
353         }
354         jvmtiDeallocate(old);
355     }
356 
357     /* Add to id hashtable */
358     hashIn(node);
359     return node;
360 }
361 
362 /* Initialize the commonRefs usage */
363 void
commonRef_initialize(void)364 commonRef_initialize(void)
365 {
366     gdata->refLock = debugMonitorCreate("JDWP Reference Table Monitor");
367     gdata->nextSeqNum       = 1; /* 0 used for error indication */
368     initializeObjectsByID(HASH_INIT_SIZE);
369 }
370 
371 /* Reset the commonRefs usage */
372 void
commonRef_reset(JNIEnv * env)373 commonRef_reset(JNIEnv *env)
374 {
375     debugMonitorEnter(gdata->refLock); {
376         int i;
377 
378         for (i = 0; i < gdata->objectsByIDsize; i++) {
379             RefNode *node;
380 
381             node = gdata->objectsByID[i];
382             while (node != NULL) {
383                 RefNode *next;
384 
385                 next = node->next;
386                 deleteNode(env, node);
387                 node = next;
388             }
389             gdata->objectsByID[i] = NULL;
390         }
391 
392         /* Toss entire hash table and re-create a new one */
393         jvmtiDeallocate(gdata->objectsByID);
394         gdata->objectsByID      = NULL;
395         gdata->nextSeqNum       = 1; /* 0 used for error indication */
396         initializeObjectsByID(HASH_INIT_SIZE);
397 
398     } debugMonitorExit(gdata->refLock);
399 }
400 
401 /*
402  * Given a reference obtained from JNI or JVMTI, return an object
403  * id suitable for sending to the debugger front end.
404  */
405 jlong
commonRef_refToID(JNIEnv * env,jobject ref)406 commonRef_refToID(JNIEnv *env, jobject ref)
407 {
408     jlong id;
409 
410     if (ref == NULL) {
411         return NULL_OBJECT_ID;
412     }
413 
414     id = NULL_OBJECT_ID;
415     debugMonitorEnter(gdata->refLock); {
416         RefNode *node;
417 
418         node = findNodeByRef(env, ref);
419         if (node == NULL) {
420             node = newCommonRef(env, ref);
421             if ( node != NULL ) {
422                 id = node->seqNum;
423             }
424         } else {
425             id = node->seqNum;
426             node->count++;
427         }
428     } debugMonitorExit(gdata->refLock);
429     return id;
430 }
431 
432 /*
433  * Given an object ID obtained from the debugger front end, return a
434  * strong, global reference to that object (or NULL if the object
435  * has been collected). The reference can then be used for JNI and
436  * JVMTI calls. Caller is resposible for deleting the returned reference.
437  */
438 jobject
commonRef_idToRef(JNIEnv * env,jlong id)439 commonRef_idToRef(JNIEnv *env, jlong id)
440 {
441     jobject ref;
442 
443     ref = NULL;
444     debugMonitorEnter(gdata->refLock); {
445         RefNode *node;
446 
447         node = findNodeByID(env, id);
448         if (node != NULL) {
449             if (node->isStrong) {
450                 saveGlobalRef(env, node->ref, &ref);
451             } else {
452                 jobject lref;
453 
454                 lref = JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);
455                 if ( lref == NULL ) {
456                     /* Object was GC'd shortly after we found the node */
457                     deleteNodeByID(env, node->seqNum, ALL_REFS);
458                 } else {
459                     saveGlobalRef(env, node->ref, &ref);
460                     JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref);
461                 }
462             }
463         }
464     } debugMonitorExit(gdata->refLock);
465     return ref;
466 }
467 
468 /* Deletes the global reference that commonRef_idToRef() created */
469 void
commonRef_idToRef_delete(JNIEnv * env,jobject ref)470 commonRef_idToRef_delete(JNIEnv *env, jobject ref)
471 {
472     if ( ref==NULL ) {
473         return;
474     }
475     tossGlobalRef(env, &ref);
476 }
477 
478 
479 /* Prevent garbage collection of an object */
480 jvmtiError
commonRef_pin(jlong id)481 commonRef_pin(jlong id)
482 {
483     jvmtiError error;
484 
485     error = JVMTI_ERROR_NONE;
486     if (id == NULL_OBJECT_ID) {
487         return error;
488     }
489     debugMonitorEnter(gdata->refLock); {
490         JNIEnv  *env;
491         RefNode *node;
492 
493         env  = getEnv();
494         node = findNodeByID(env, id);
495         if (node == NULL) {
496             error = AGENT_ERROR_INVALID_OBJECT;
497         } else {
498             jobject strongRef;
499 
500             strongRef = strengthenNode(env, node);
501             if (strongRef == NULL) {
502                 /*
503                  * Referent has been collected, clean up now.
504                  */
505                 error = AGENT_ERROR_INVALID_OBJECT;
506                 deleteNodeByID(env, id, ALL_REFS);
507             }
508         }
509     } debugMonitorExit(gdata->refLock);
510     return error;
511 }
512 
513 /* Permit garbage collection of an object */
514 jvmtiError
commonRef_unpin(jlong id)515 commonRef_unpin(jlong id)
516 {
517     jvmtiError error;
518 
519     error = JVMTI_ERROR_NONE;
520     debugMonitorEnter(gdata->refLock); {
521         JNIEnv  *env;
522         RefNode *node;
523 
524         env  = getEnv();
525         node = findNodeByID(env, id);
526         if (node != NULL) {
527             jweak weakRef;
528 
529             weakRef = weakenNode(env, node);
530             if (weakRef == NULL) {
531                 error = AGENT_ERROR_OUT_OF_MEMORY;
532             }
533         }
534     } debugMonitorExit(gdata->refLock);
535     return error;
536 }
537 
538 /* Release tracking of an object by ID */
539 void
commonRef_release(JNIEnv * env,jlong id)540 commonRef_release(JNIEnv *env, jlong id)
541 {
542     debugMonitorEnter(gdata->refLock); {
543         deleteNodeByID(env, id, 1);
544     } debugMonitorExit(gdata->refLock);
545 }
546 
547 void
commonRef_releaseMultiple(JNIEnv * env,jlong id,jint refCount)548 commonRef_releaseMultiple(JNIEnv *env, jlong id, jint refCount)
549 {
550     debugMonitorEnter(gdata->refLock); {
551         deleteNodeByID(env, id, refCount);
552     } debugMonitorExit(gdata->refLock);
553 }
554 
555 /* Get rid of RefNodes for objects that no longer exist */
556 void
commonRef_compact(void)557 commonRef_compact(void)
558 {
559     JNIEnv  *env;
560     RefNode *node;
561     RefNode *prev;
562     int      i;
563 
564     env = getEnv();
565     debugMonitorEnter(gdata->refLock); {
566         if ( gdata->objectsByIDsize > 0 ) {
567             /*
568              * Walk through the id-based hash table. Detach any nodes
569              * for which the ref has been collected.
570              */
571             for (i = 0; i < gdata->objectsByIDsize; i++) {
572                 node = gdata->objectsByID[i];
573                 prev = NULL;
574                 while (node != NULL) {
575                     /* Has the object been collected? */
576                     if ( (!node->isStrong) &&
577                           isSameObject(env, node->ref, NULL)) {
578                         RefNode *freed;
579 
580                         /* Detach from the ID list */
581                         if (prev == NULL) {
582                             gdata->objectsByID[i] = node->next;
583                         } else {
584                             prev->next = node->next;
585                         }
586                         freed = node;
587                         node = node->next;
588                         deleteNode(env, freed);
589                     } else {
590                         prev = node;
591                         node = node->next;
592                     }
593                 }
594             }
595         }
596     } debugMonitorExit(gdata->refLock);
597 }
598 
599 /* Lock the commonRef tables */
600 void
commonRef_lock(void)601 commonRef_lock(void)
602 {
603     debugMonitorEnter(gdata->refLock);
604 }
605 
606 /* Unlock the commonRef tables */
607 void
commonRef_unlock(void)608 commonRef_unlock(void)
609 {
610     debugMonitorExit(gdata->refLock);
611 }
612