Home | History | Annotate | Download | only in back
      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
     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
     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 *
     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
    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
    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
    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 *
    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
    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 *
    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
    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
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    601 commonRef_lock(void)
    602 {
    603     debugMonitorEnter(gdata->refLock);
    604 }
    605 
    606 /* Unlock the commonRef tables */
    607 void
    608 commonRef_unlock(void)
    609 {
    610     debugMonitorExit(gdata->refLock);
    611 }
    612