view src/share/back/classTrack.c @ 0:37a05a11f281

Initial load
author duke
date Sat, 01 Dec 2007 00:00:00 +0000
parents
children 00cd9dc3c2b5
line wrap: on
line source
/*
 * Copyright 2001-2005 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
/*
 * This module tracks classes that have been prepared, so as to
 * be able to compute which have been unloaded.  On VM start-up
 * all prepared classes are put in a table.  As class prepare
 * events come in they are added to the table.  After an unload
 * event or series of them, the VM can be asked for the list
 * of classes; this list is compared against the table keep by
 * this module, any classes no longer present are known to
 * have been unloaded.
 *
 * For efficient access, classes are keep in a hash table.
 * Each slot in the hash table has a linked list of KlassNode.
 *
 * Comparing current set of classes is compared with previous
 * set by transferring all classes in the current set into
 * a new table, any that remain in the old table have been
 * unloaded.
 */

#include "util.h"
#include "bag.h"
#include "classTrack.h"

/* ClassTrack hash table slot count */
#define CT_HASH_SLOT_COUNT 263    /* Prime which eauals 4k+3 for some k */

typedef struct KlassNode {
    jclass klass;            /* weak global reference */
    char *signature;         /* class signature */
    struct KlassNode *next;  /* next node in this slot */
} KlassNode;

/*
 * Hash table of prepared classes.  Each entry is a pointer
 * to a linked list of KlassNode.
 */
static KlassNode **table;

/*
 * Return slot in hash table to use for this class.
 */
static jint
hashKlass(jclass klass)
{
    jint hashCode = objectHashCode(klass);
    return abs(hashCode) % CT_HASH_SLOT_COUNT;
}

/*
 * Transfer a node (which represents klass) from the current
 * table to the new table.
 */
static void
transferClass(JNIEnv *env, jclass klass, KlassNode **newTable) {
    jint slot = hashKlass(klass);
    KlassNode **head = &table[slot];
    KlassNode **newHead = &newTable[slot];
    KlassNode **nodePtr;
    KlassNode *node;

    /* Search the node list of the current table for klass */
    for (nodePtr = head; node = *nodePtr, node != NULL; nodePtr = &(node->next)) {
        if (isSameObject(env, klass, node->klass)) {
            /* Match found transfer node */

            /* unlink from old list */
            *nodePtr = node->next;

            /* insert in new list */
            node->next = *newHead;
            *newHead = node;

            return;
        }
    }

    /* we haven't found the class, only unloads should have happenned,
     * so the only reason a class should not have been found is
     * that it is not prepared yet, in which case we don't want it.
     * Asset that the above is true.
     */
/**** the HotSpot VM doesn't create prepare events for some internal classes ***
    JDI_ASSERT_MSG((classStatus(klass) &
                (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY))==0,
               classSignature(klass));
***/
}

/*
 * Delete a hash table of classes.
 * The signatures of classes in the table are returned.
 */
static struct bag *
deleteTable(JNIEnv *env, KlassNode *oldTable[])
{
    struct bag *signatures = bagCreateBag(sizeof(char*), 10);
    jint slot;

    if (signatures == NULL) {
        EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"signatures");
    }

    for (slot = 0; slot < CT_HASH_SLOT_COUNT; slot++) {
        KlassNode *node = oldTable[slot];

        while (node != NULL) {
            KlassNode *next;
            char **sigSpot;

            /* Add signature to the signature bag */
            sigSpot = bagAdd(signatures);
            if (sigSpot == NULL) {
                EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"signature bag");
            }
            *sigSpot = node->signature;

            /* Free weak ref and the node itself */
            JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->klass);
            next = node->next;
            jvmtiDeallocate(node);

            node = next;
        }
    }
    jvmtiDeallocate(oldTable);

    return signatures;
}

/*
 * Called after class unloads have occurred.  Creates a new hash table
 * of currently loaded prepared classes.
 * The signatures of classes which were unloaded (not present in the
 * new table) are returned.
 */
struct bag *
classTrack_processUnloads(JNIEnv *env)
{
    KlassNode **newTable;
    struct bag *unloadedSignatures;

    unloadedSignatures = NULL;
    newTable = jvmtiAllocate(CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
    if (newTable == NULL) {
        EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY, "classTrack table");
    } else {

        (void)memset(newTable, 0, CT_HASH_SLOT_COUNT * sizeof(KlassNode *));

        WITH_LOCAL_REFS(env, 1) {

            jint classCount;
            jclass *classes;
            jvmtiError error;
            int i;

            error = allLoadedClasses(&classes, &classCount);
            if ( error != JVMTI_ERROR_NONE ) {
                jvmtiDeallocate(newTable);
                EXIT_ERROR(error,"loaded classes");
            } else {

                /* Transfer each current class into the new table */
                for (i=0; i<classCount; i++) {
                    jclass klass = classes[i];
                    transferClass(env, klass, newTable);
                }
                jvmtiDeallocate(classes);

                /* Delete old table, install new one */
                unloadedSignatures = deleteTable(env, table);
                table = newTable;
            }

        } END_WITH_LOCAL_REFS(env)

    }

    return unloadedSignatures;
}

/*
 * Add a class to the prepared class hash table.
 * Assumes no duplicates.
 */
void
classTrack_addPreparedClass(JNIEnv *env, jclass klass)
{
    jint slot = hashKlass(klass);
    KlassNode **head = &table[slot];
    KlassNode *node;
    jvmtiError error;

    if (gdata->assertOn) {
        /* Check this is not a duplicate */
        for (node = *head; node != NULL; node = node->next) {
            if (isSameObject(env, klass, node->klass)) {
                JDI_ASSERT_FAILED("Attempting to insert duplicate class");
                break;
            }
        }
    }

    node = jvmtiAllocate(sizeof(KlassNode));
    if (node == NULL) {
        EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"KlassNode");
    }
    error = classSignature(klass, &(node->signature), NULL);
    if (error != JVMTI_ERROR_NONE) {
        jvmtiDeallocate(node);
        EXIT_ERROR(error,"signature");
    }
    if ((node->klass = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, klass)) == NULL) {
        jvmtiDeallocate(node->signature);
        jvmtiDeallocate(node);
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewWeakGlobalRef");
    }

    /* Insert the new node */
    node->next = *head;
    *head = node;
}

/*
 * Called once to build the initial prepared class hash table.
 */
void
classTrack_initialize(JNIEnv *env)
{
    WITH_LOCAL_REFS(env, 1) {

        jint classCount;
        jclass *classes;
        jvmtiError error;
        jint i;

        error = allLoadedClasses(&classes, &classCount);
        if ( error == JVMTI_ERROR_NONE ) {
            table = jvmtiAllocate(CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
            if (table != NULL) {
                (void)memset(table, 0, CT_HASH_SLOT_COUNT * sizeof(KlassNode *));
                for (i=0; i<classCount; i++) {
                    jclass klass = classes[i];
                    jint status;
                    jint wanted =
                        (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY);

                    /* We only want prepared classes and arrays */
                    status = classStatus(klass);
                    if ( (status & wanted) != 0 ) {
                        classTrack_addPreparedClass(env, klass);
                    }
                }
            } else {
                jvmtiDeallocate(classes);
                EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"KlassNode");
            }
            jvmtiDeallocate(classes);
        } else {
            EXIT_ERROR(error,"loaded classes array");
        }

    } END_WITH_LOCAL_REFS(env)

}

void
classTrack_reset(void)
{
}