0N/A/*
3149N/A * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
0N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
1472N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1472N/A *
1472N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
0N/A * or visit www.oracle.com if you need additional information or have any
0N/A * questions.
0N/A */
1879N/A
1879N/A#if defined(_ALLBSD_SOURCE)
1879N/A#include <stdint.h> /* for uintptr_t */
1879N/A#endif
1879N/A
1879N/A#include "util.h"
1879N/A#include "commonRef.h"
2772N/A
2772N/A#define ALL_REFS -1
2772N/A
2772N/A/*
2772N/A * Each object sent to the front end is tracked with the RefNode struct
1879N/A * (see util.h).
1879N/A * External to this module, objects are identified by a jlong id which is
1879N/A * simply the sequence number. A weak reference is usually used so that
1879N/A * the presence of a debugger-tracked object will not prevent
1879N/A * its collection. Once an object is collected, its RefNode may be
1879N/A * deleted and the weak ref inside may be reused (these may happen in
1879N/A * either order). Using the sequence number
1879N/A * as the object id prevents ambiguity in the object id when the weak ref
1879N/A * is reused. The RefNode* is stored with the object as it's JVMTI Tag.
2772N/A *
1879N/A * The ref member is changed from weak to strong when
1879N/A * gc of the object is to be prevented.
1879N/A * Whether or not it is strong, it is never exported from this module.
1879N/A *
1879N/A * A reference count of each jobject is also maintained here. It tracks
1879N/A * the number times an object has been referenced through
1879N/A * commonRef_refToID. A RefNode is freed once the reference
1879N/A * count is decremented to 0 (with commonRef_release*), even if the
1879N/A * correspoding object has not been collected.
1879N/A *
3695N/A * One hash table is maintained. The mapping of ID to jobject (or RefNode*)
1879N/A * is handled with one hash table that will re-size itself as the number
1879N/A * of RefNode's grow.
1879N/A */
1879N/A
1879N/A/* Initial hash table size (must be power of 2) */
4141N/A#define HASH_INIT_SIZE 512
1879N/A/* If element count exceeds HASH_EXPAND_SCALE*hash_size we expand & re-hash */
1879N/A#define HASH_EXPAND_SCALE 8
1879N/A/* Maximum hash table size (must be power of 2) */
1879N/A#define HASH_MAX_SIZE (1024*HASH_INIT_SIZE)
1879N/A
1879N/A/* Map a key (ID) to a hash bucket */
1879N/Astatic jint
1879N/AhashBucket(jlong key)
1879N/A{
1879N/A /* Size should always be a power of 2, use mask instead of mod operator */
1879N/A /*LINTED*/
1879N/A return ((jint)key) & (gdata->objectsByIDsize-1);
1879N/A}
1879N/A
1879N/A/* Generate a new ID */
1879N/Astatic jlong
2258N/AnewSeqNum(void)
1879N/A{
1879N/A return gdata->nextSeqNum++;
1879N/A}
1879N/A
1879N/A/* Create a fresh RefNode structure, create a weak ref and tag the object */
1879N/Astatic RefNode *
1879N/AcreateNode(JNIEnv *env, jobject ref)
1879N/A{
1879N/A RefNode *node;
1879N/A jobject weakRef;
1879N/A jvmtiError error;
1879N/A
1879N/A /* Could allocate RefNode's in blocks, not sure it would help much */
2062N/A node = (RefNode*)jvmtiAllocate((int)sizeof(RefNode));
1879N/A if (node == NULL) {
1879N/A return NULL;
1879N/A }
1879N/A
1879N/A /* Create weak reference to make sure we have a reference */
2772N/A weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref);
2772N/A if (weakRef == NULL) {
1879N/A jvmtiDeallocate(node);
1879N/A return NULL;
1879N/A }
1879N/A
2076N/A /* Set tag on weakRef */
1879N/A error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)
1879N/A (gdata->jvmti, weakRef, ptr_to_jlong(node));
1879N/A if ( error != JVMTI_ERROR_NONE ) {
1879N/A JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef);
1879N/A jvmtiDeallocate(node);
1879N/A return NULL;
1879N/A }
1879N/A
1879N/A /* Fill in RefNode */
1879N/A node->ref = weakRef;
1879N/A node->isStrong = JNI_FALSE;
1879N/A node->count = 1;
1879N/A node->seqNum = newSeqNum();
1879N/A
1879N/A /* Count RefNode's created */
2073N/A gdata->objectsByIDcount++;
2073N/A return node;
2073N/A}
2073N/A
2073N/A/* Delete a RefNode allocation, delete weak/global ref and clear tag */
2073N/Astatic void
1879N/AdeleteNode(JNIEnv *env, RefNode *node)
1879N/A{
1879N/A LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref));
1879N/A
1879N/A if ( node->ref != NULL ) {
1879N/A /* Clear tag */
1879N/A (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
1879N/A (gdata->jvmti, node->ref, NULL_OBJECT_ID);
1879N/A if (node->isStrong) {
2796N/A JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
2796N/A } else {
2796N/A JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
1879N/A }
1879N/A }
1879N/A gdata->objectsByIDcount--;
1879N/A jvmtiDeallocate(node);
1879N/A}
1879N/A
1879N/A/* Change a RefNode to have a strong reference */
1879N/Astatic jobject
1879N/AstrengthenNode(JNIEnv *env, RefNode *node)
1879N/A{
1879N/A if (!node->isStrong) {
1879N/A jobject strongRef;
1879N/A
1879N/A strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref);
1879N/A /*
1879N/A * NewGlobalRef on a weak ref will return NULL if the weak
1879N/A * reference has been collected or if out of memory.
1879N/A * We need to distinguish those two occurrences.
2073N/A */
2073N/A if ((strongRef == NULL) && !isSameObject(env, node->ref, NULL)) {
2073N/A EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
2073N/A }
2073N/A if (strongRef != NULL) {
2073N/A JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
2796N/A node->ref = strongRef;
2796N/A node->isStrong = JNI_TRUE;
2796N/A }
2796N/A return strongRef;
2796N/A } else {
2796N/A return node->ref;
1879N/A }
1879N/A}
1879N/A
1879N/A/* Change a RefNode to have a weak reference */
1879N/Astatic jweak
1879N/AweakenNode(JNIEnv *env, RefNode *node)
1879N/A{
1879N/A if (node->isStrong) {
1879N/A jweak weakRef;
1879N/A
1879N/A weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref);
1879N/A if (weakRef != NULL) {
1879N/A JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
1879N/A node->ref = weakRef;
1879N/A node->isStrong = JNI_FALSE;
1879N/A }
2808N/A return weakRef;
1879N/A } else {
1879N/A return node->ref;
2772N/A }
2772N/A}
2772N/A
2772N/A/*
2772N/A * Returns the node which contains the common reference for the
2772N/A * given object. The passed reference should not be a weak reference
2772N/A * managed in the object hash table (i.e. returned by commonRef_idToRef)
2772N/A * because no sequence number checking is done.
2772N/A */
1879N/Astatic RefNode *
2772N/AfindNodeByRef(JNIEnv *env, jobject ref)
2772N/A{
2772N/A jvmtiError error;
2772N/A jlong tag;
2772N/A
2772N/A tag = NULL_OBJECT_ID;
2772N/A error = JVMTI_FUNC_PTR(gdata->jvmti,GetTag)(gdata->jvmti, ref, &tag);
1879N/A if ( error == JVMTI_ERROR_NONE ) {
1879N/A RefNode *node;
1879N/A
1879N/A node = (RefNode*)jlong_to_ptr(tag);
1879N/A return node;
1879N/A }
1879N/A return NULL;
1879N/A}
1879N/A
1879N/A/* Locate and delete a node based on ID */
1879N/Astatic void
1879N/AdeleteNodeByID(JNIEnv *env, jlong id, jint refCount)
2073N/A{
2073N/A jint slot;
2073N/A RefNode *node;
2073N/A RefNode *prev;
2073N/A
2073N/A slot = hashBucket(id);
1879N/A node = gdata->objectsByID[slot];
0N/A prev = NULL;
0N/A
0N/A while (node != NULL) {
0N/A if (id == node->seqNum) {
0N/A if (refCount != ALL_REFS) {
0N/A node->count -= refCount;
0N/A } else {
0N/A node->count = 0;
0N/A }
0N/A if (node->count <= 0) {
0N/A if ( node->count < 0 ) {
0N/A EXIT_ERROR(AGENT_ERROR_INTERNAL,"RefNode count < 0");
0N/A }
0N/A /* Detach from id hash table */
0N/A if (prev == NULL) {
0N/A gdata->objectsByID[slot] = node->next;
0N/A } else {
0N/A prev->next = node->next;
0N/A }
0N/A deleteNode(env, node);
0N/A }
0N/A break;
3863N/A }
3863N/A prev = node;
3863N/A node = node->next;
3863N/A }
3863N/A}
3863N/A
3863N/A/*
3863N/A * Returns the node stored in the object hash table for the given object
3863N/A * id. The id should be a value previously returned by
3863N/A * commonRef_refToID.
0N/A *
0N/A * NOTE: It is possible that a match is found here, but that the object
0N/A * is garbage collected by the time the caller inspects node->ref.
0N/A * Callers should take care using the node->ref object returned here.
0N/A *
0N/A */
0N/Astatic RefNode *
0N/AfindNodeByID(JNIEnv *env, jlong id)
0N/A{
0N/A jint slot;
0N/A RefNode *node;
0N/A RefNode *prev;
0N/A
0N/A slot = hashBucket(id);
0N/A node = gdata->objectsByID[slot];
0N/A prev = NULL;
0N/A
0N/A while (node != NULL) {
0N/A if ( id == node->seqNum ) {
0N/A if ( prev != NULL ) {
0N/A /* Re-order hash list so this one is up front */
0N/A prev->next = node->next;
0N/A node->next = gdata->objectsByID[slot];
0N/A gdata->objectsByID[slot] = node;
113N/A }
113N/A break;
0N/A }
0N/A node = node->next;
2004N/A }
2004N/A return node;
0N/A}
0N/A
0N/A/* Initialize the hash table stored in gdata area */
0N/Astatic void
0N/AinitializeObjectsByID(int size)
0N/A{
0N/A /* Size should always be a power of 2 */
0N/A if ( size > HASH_MAX_SIZE ) size = HASH_MAX_SIZE;
0N/A gdata->objectsByIDsize = size;
1837N/A gdata->objectsByIDcount = 0;
113N/A gdata->objectsByID = (RefNode**)jvmtiAllocate((int)sizeof(RefNode*)*size);
113N/A (void)memset(gdata->objectsByID, 0, (int)sizeof(RefNode*)*size);
0N/A}
0N/A
0N/A/* hash in a RefNode */
0N/Astatic void
0N/AhashIn(RefNode *node)
0N/A{
0N/A jint slot;
3024N/A
0N/A /* Add to id hashtable */
0N/A slot = hashBucket(node->seqNum);
0N/A node->next = gdata->objectsByID[slot];
0N/A gdata->objectsByID[slot] = node;
2062N/A}
3869N/A
0N/A/* Allocate and add RefNode to hash table */
0N/Astatic RefNode *
0N/AnewCommonRef(JNIEnv *env, jobject ref)
3024N/A{
0N/A RefNode *node;
3052N/A
0N/A /* Allocate the node and set it up */
0N/A node = createNode(env, ref);
3019N/A if ( node == NULL ) {
0N/A return NULL;
0N/A }
0N/A
3186N/A /* See if hash table needs expansion */
0N/A if ( gdata->objectsByIDcount > gdata->objectsByIDsize*HASH_EXPAND_SCALE &&
0N/A gdata->objectsByIDsize < HASH_MAX_SIZE ) {
0N/A RefNode **old;
0N/A int oldsize;
2062N/A int newsize;
0N/A int i;
0N/A
0N/A /* Save old information */
0N/A old = gdata->objectsByID;
0N/A oldsize = gdata->objectsByIDsize;
0N/A /* Allocate new hash table */
0N/A gdata->objectsByID = NULL;
0N/A newsize = oldsize*HASH_EXPAND_SCALE;
2772N/A if ( newsize > HASH_MAX_SIZE ) newsize = HASH_MAX_SIZE;
2772N/A initializeObjectsByID(newsize);
2772N/A /* Walk over old one and hash in all the RefNodes */
2772N/A for ( i = 0 ; i < oldsize ; i++ ) {
0N/A RefNode *onode;
0N/A
0N/A onode = old[i];
0N/A while (onode != NULL) {
0N/A RefNode *next;
0N/A
0N/A next = onode->next;
0N/A hashIn(onode);
2062N/A onode = next;
0N/A }
0N/A }
0N/A jvmtiDeallocate(old);
0N/A }
0N/A
0N/A /* Add to id hashtable */
0N/A hashIn(node);
0N/A return node;
0N/A}
2772N/A
2772N/A/* Initialize the commonRefs usage */
2772N/Avoid
2772N/AcommonRef_initialize(void)
2772N/A{
2772N/A gdata->refLock = debugMonitorCreate("JDWP Reference Table Monitor");
2772N/A gdata->nextSeqNum = 1; /* 0 used for error indication */
2772N/A initializeObjectsByID(HASH_INIT_SIZE);
2772N/A}
0N/A
2772N/A/* Reset the commonRefs usage */
2772N/Avoid
0N/AcommonRef_reset(JNIEnv *env)
0N/A{
0N/A debugMonitorEnter(gdata->refLock); {
0N/A int i;
0N/A
0N/A for (i = 0; i < gdata->objectsByIDsize; i++) {
2772N/A RefNode *node;
0N/A
0N/A node = gdata->objectsByID[i];
0N/A while (node != NULL) {
0N/A RefNode *next;
0N/A
0N/A next = node->next;
0N/A deleteNode(env, node);
0N/A node = next;
0N/A }
0N/A gdata->objectsByID[i] = NULL;
3790N/A }
0N/A
0N/A /* Toss entire hash table and re-create a new one */
0N/A jvmtiDeallocate(gdata->objectsByID);
0N/A gdata->objectsByID = NULL;
0N/A gdata->nextSeqNum = 1; /* 0 used for error indication */
0N/A initializeObjectsByID(HASH_INIT_SIZE);
0N/A
0N/A } debugMonitorExit(gdata->refLock);
0N/A}
0N/A
0N/A/*
2062N/A * Given a reference obtained from JNI or JVMTI, return an object
2062N/A * id suitable for sending to the debugger front end.
2062N/A */
2062N/Ajlong
0N/AcommonRef_refToID(JNIEnv *env, jobject ref)
0N/A{
0N/A jlong id;
0N/A
0N/A if (ref == NULL) {
0N/A return NULL_OBJECT_ID;
0N/A }
0N/A
0N/A id = NULL_OBJECT_ID;
0N/A debugMonitorEnter(gdata->refLock); {
0N/A RefNode *node;
0N/A
0N/A node = findNodeByRef(env, ref);
0N/A if (node == NULL) {
0N/A node = newCommonRef(env, ref);
0N/A if ( node != NULL ) {
0N/A id = node->seqNum;
0N/A }
0N/A } else {
0N/A id = node->seqNum;
0N/A node->count++;
0N/A }
3879N/A } debugMonitorExit(gdata->refLock);
3879N/A return id;
3879N/A}
3879N/A
0N/A/*
0N/A * Given an object ID obtained from the debugger front end, return a
0N/A * strong, global reference to that object (or NULL if the object
0N/A * has been collected). The reference can then be used for JNI and
0N/A * JVMTI calls. Caller is resposible for deleting the returned reference.
0N/A */
0N/Ajobject
0N/AcommonRef_idToRef(JNIEnv *env, jlong id)
0N/A{
0N/A jobject ref;
0N/A
0N/A ref = NULL;
0N/A debugMonitorEnter(gdata->refLock); {
0N/A RefNode *node;
0N/A
0N/A node = findNodeByID(env, id);
0N/A if (node != NULL) {
0N/A if (node->isStrong) {
0N/A saveGlobalRef(env, node->ref, &ref);
0N/A } else {
0N/A jobject lref;
0N/A
0N/A lref = JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);
0N/A if ( lref == NULL ) {
0N/A /* Object was GC'd shortly after we found the node */
0N/A deleteNodeByID(env, node->seqNum, ALL_REFS);
0N/A } else {
0N/A saveGlobalRef(env, node->ref, &ref);
0N/A JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref);
0N/A }
0N/A }
0N/A }
0N/A } debugMonitorExit(gdata->refLock);
0N/A return ref;
0N/A}
0N/A
2772N/A/* Deletes the global reference that commonRef_idToRef() created */
0N/Avoid
0N/AcommonRef_idToRef_delete(JNIEnv *env, jobject ref)
0N/A{
0N/A if ( ref==NULL ) {
0N/A return;
0N/A }
0N/A tossGlobalRef(env, &ref);
0N/A}
0N/A
0N/A
0N/A/* Prevent garbage collection of an object */
0N/AjvmtiError
0N/AcommonRef_pin(jlong id)
0N/A{
0N/A jvmtiError error;
0N/A
0N/A error = JVMTI_ERROR_NONE;
0N/A if (id == NULL_OBJECT_ID) {
0N/A return error;
642N/A }
642N/A debugMonitorEnter(gdata->refLock); {
642N/A JNIEnv *env;
0N/A RefNode *node;
0N/A
0N/A env = getEnv();
0N/A node = findNodeByID(env, id);
0N/A if (node == NULL) {
0N/A error = AGENT_ERROR_INVALID_OBJECT;
0N/A } else {
0N/A jobject strongRef;
0N/A
0N/A strongRef = strengthenNode(env, node);
0N/A if (strongRef == NULL) {
0N/A /*
0N/A * Referent has been collected, clean up now.
0N/A */
0N/A error = AGENT_ERROR_INVALID_OBJECT;
0N/A deleteNodeByID(env, id, ALL_REFS);
0N/A }
0N/A }
0N/A } debugMonitorExit(gdata->refLock);
0N/A return error;
0N/A}
0N/A
0N/A/* Permit garbage collection of an object */
0N/AjvmtiError
0N/AcommonRef_unpin(jlong id)
0N/A{
0N/A jvmtiError error;
0N/A
0N/A error = JVMTI_ERROR_NONE;
0N/A debugMonitorEnter(gdata->refLock); {
0N/A JNIEnv *env;
0N/A RefNode *node;
0N/A
0N/A env = getEnv();
0N/A node = findNodeByID(env, id);
0N/A if (node != NULL) {
0N/A jweak weakRef;
0N/A
342N/A weakRef = weakenNode(env, node);
0N/A if (weakRef == NULL) {
0N/A error = AGENT_ERROR_OUT_OF_MEMORY;
0N/A }
0N/A }
1166N/A } debugMonitorExit(gdata->refLock);
0N/A return error;
0N/A}
0N/A
0N/A/* Release tracking of an object by ID */
0N/Avoid
0N/AcommonRef_release(JNIEnv *env, jlong id)
0N/A{
0N/A debugMonitorEnter(gdata->refLock); {
0N/A deleteNodeByID(env, id, 1);
0N/A } debugMonitorExit(gdata->refLock);
0N/A}
0N/A
0N/Avoid
0N/AcommonRef_releaseMultiple(JNIEnv *env, jlong id, jint refCount)
0N/A{
0N/A debugMonitorEnter(gdata->refLock); {
0N/A deleteNodeByID(env, id, refCount);
0N/A } debugMonitorExit(gdata->refLock);
0N/A}
0N/A
0N/A/* Get rid of RefNodes for objects that no longer exist */
0N/Avoid
0N/AcommonRef_compact(void)
0N/A{
0N/A JNIEnv *env;
0N/A RefNode *node;
0N/A RefNode *prev;
0N/A int i;
0N/A
0N/A env = getEnv();
0N/A debugMonitorEnter(gdata->refLock); {
0N/A if ( gdata->objectsByIDsize > 0 ) {
0N/A /*
0N/A * Walk through the id-based hash table. Detach any nodes
0N/A * for which the ref has been collected.
0N/A */
0N/A for (i = 0; i < gdata->objectsByIDsize; i++) {
0N/A node = gdata->objectsByID[i];
0N/A prev = NULL;
0N/A while (node != NULL) {
0N/A /* Has the object been collected? */
0N/A if ( (!node->isStrong) &&
0N/A isSameObject(env, node->ref, NULL)) {
0N/A RefNode *freed;
0N/A
0N/A /* Detach from the ID list */
0N/A if (prev == NULL) {
0N/A gdata->objectsByID[i] = node->next;
0N/A } else {
0N/A prev->next = node->next;
0N/A }
0N/A freed = node;
0N/A node = node->next;
0N/A deleteNode(env, freed);
0N/A } else {
0N/A prev = node;
0N/A node = node->next;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A } debugMonitorExit(gdata->refLock);
0N/A}
0N/A
0N/A/* Lock the commonRef tables */
0N/Avoid
0N/AcommonRef_lock(void)
0N/A{
0N/A debugMonitorEnter(gdata->refLock);
0N/A}
0N/A
0N/A/* Unlock the commonRef tables */
0N/Avoid
0N/AcommonRef_unlock(void)
0N/A{
0N/A debugMonitorExit(gdata->refLock);
0N/A}
0N/A