alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (util.c)

This example Java source code file (util.c) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

agent_error_illegal_argument, ei_min, exit_error, jdwp_error, jdwp_event, jdwp_tag, jni_false, jni_func_ptr, jni_true, jnienv, jvmti_error_none, jvmti_func_ptr, jvmti_visit_abort, null

The util.c Java example source code

/*
 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include <ctype.h>

#include "util.h"
#include "transport.h"
#include "eventHandler.h"
#include "threadControl.h"
#include "outStream.h"
#include "inStream.h"
#include "invoker.h"

/* Global data area */
BackendGlobalData *gdata = NULL;

/* Forward declarations */
static jboolean isInterface(jclass clazz);
static jboolean isArrayClass(jclass clazz);
static char * getPropertyUTF8(JNIEnv *env, char *propertyName);

/* Save an object reference for use later (create a NewGlobalRef) */
void
saveGlobalRef(JNIEnv *env, jobject obj, jobject *pobj)
{
    jobject newobj;

    if ( pobj == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"saveGlobalRef pobj");
    }
    if ( *pobj != NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"saveGlobalRef *pobj");
    }
    if ( env == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"saveGlobalRef env");
    }
    if ( obj == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"saveGlobalRef obj");
    }
    newobj = JNI_FUNC_PTR(env,NewGlobalRef)(env, obj);
    if ( newobj == NULL ) {
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
    }
    *pobj = newobj;
}

/* Toss a previously saved object reference */
void
tossGlobalRef(JNIEnv *env, jobject *pobj)
{
    jobject obj;

    if ( pobj == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"tossGlobalRef pobj");
    }
    obj = *pobj;
    if ( env == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"tossGlobalRef env");
    }
    if ( obj == NULL ) {
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"tossGlobalRef obj");
    }
    JNI_FUNC_PTR(env,DeleteGlobalRef)(env, obj);
    *pobj = NULL;
}

static jclass
findClass(JNIEnv *env, const char * name)
{
    jclass x;

    if ( env == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"findClass env");
    }
    if ( name == NULL || name[0] == 0 ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"findClass name");
    }
    x = JNI_FUNC_PTR(env,FindClass)(env, name);
    if (x == NULL) {
        ERROR_MESSAGE(("JDWP Can't find class %s", name));
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL);
    }
    if ( JNI_FUNC_PTR(env,ExceptionOccurred)(env) ) {
        ERROR_MESSAGE(("JDWP Exception occurred finding class %s", name));
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL);
    }
    return x;
}

static jmethodID
getMethod(JNIEnv *env, jclass clazz, const char * name, const char *signature)
{
    jmethodID method;

    if ( env == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getMethod env");
    }
    if ( clazz == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getMethod clazz");
    }
    if ( name == NULL || name[0] == 0 ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getMethod name");
    }
    if ( signature == NULL || signature[0] == 0 ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getMethod signature");
    }
    method = JNI_FUNC_PTR(env,GetMethodID)(env, clazz, name, signature);
    if (method == NULL) {
        ERROR_MESSAGE(("JDWP Can't find method %s with signature %s",
                                name, signature));
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL);
    }
    if ( JNI_FUNC_PTR(env,ExceptionOccurred)(env) ) {
        ERROR_MESSAGE(("JDWP Exception occurred finding method %s with signature %s",
                                name, signature));
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL);
    }
    return method;
}

static jmethodID
getStaticMethod(JNIEnv *env, jclass clazz, const char * name, const char *signature)
{
    jmethodID method;

    if ( env == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getStaticMethod env");
    }
    if ( clazz == NULL ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getStaticMethod clazz");
    }
    if ( name == NULL || name[0] == 0 ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getStaticMethod name");
    }
    if ( signature == NULL || signature[0] == 0 ) {
        EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"getStaticMethod signature");
    }
    method = JNI_FUNC_PTR(env,GetStaticMethodID)(env, clazz, name, signature);
    if (method == NULL) {
        ERROR_MESSAGE(("JDWP Can't find method %s with signature %s",
                                name, signature));
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL);
    }
    if ( JNI_FUNC_PTR(env,ExceptionOccurred)(env) ) {
        ERROR_MESSAGE(("JDWP Exception occurred finding method %s with signature %s",
                                name, signature));
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL);
    }
    return method;
}

void
util_initialize(JNIEnv *env)
{
    WITH_LOCAL_REFS(env, 6) {

        jvmtiError error;
        jclass localClassClass;
        jclass localThreadClass;
        jclass localThreadGroupClass;
        jclass localClassLoaderClass;
        jclass localStringClass;
        jclass localSystemClass;
        jclass localPropertiesClass;
        jclass localVMSupportClass;
        jobject localAgentProperties;
        jmethodID getAgentProperties;
        jint groupCount;
        jthreadGroup *groups;
        jthreadGroup localSystemThreadGroup;

        /* Find some standard classes */

        localClassClass         = findClass(env,"java/lang/Class");
        localThreadClass        = findClass(env,"java/lang/Thread");
        localThreadGroupClass   = findClass(env,"java/lang/ThreadGroup");
        localClassLoaderClass   = findClass(env,"java/lang/ClassLoader");
        localStringClass        = findClass(env,"java/lang/String");
        localSystemClass        = findClass(env,"java/lang/System");
        localPropertiesClass    = findClass(env,"java/util/Properties");

        /* Save references */

        saveGlobalRef(env, localClassClass,       &(gdata->classClass));
        saveGlobalRef(env, localThreadClass,      &(gdata->threadClass));
        saveGlobalRef(env, localThreadGroupClass, &(gdata->threadGroupClass));
        saveGlobalRef(env, localClassLoaderClass, &(gdata->classLoaderClass));
        saveGlobalRef(env, localStringClass,      &(gdata->stringClass));
        saveGlobalRef(env, localSystemClass,      &(gdata->systemClass));

        /* Find some standard methods */

        gdata->threadConstructor =
                getMethod(env, gdata->threadClass,
                    "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V");
        gdata->threadSetDaemon =
                getMethod(env, gdata->threadClass, "setDaemon", "(Z)V");
        gdata->threadResume =
                getMethod(env, gdata->threadClass, "resume", "()V");
        gdata->systemGetProperty =
                getStaticMethod(env, gdata->systemClass,
                    "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
        gdata->setProperty =
                getMethod(env, localPropertiesClass,
                    "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");

        /* Find the system thread group */

        groups = NULL;
        groupCount = 0;
        error = JVMTI_FUNC_PTR(gdata->jvmti,GetTopThreadGroups)
                    (gdata->jvmti, &groupCount, &groups);
        if (error != JVMTI_ERROR_NONE ) {
            EXIT_ERROR(error, "Can't get system thread group");
        }
        if ( groupCount == 0 ) {
            EXIT_ERROR(AGENT_ERROR_NULL_POINTER, "Can't get system thread group");
        }
        localSystemThreadGroup = groups[0];
        saveGlobalRef(env, localSystemThreadGroup, &(gdata->systemThreadGroup));

        /* Get some basic Java property values we will need at some point */
        gdata->property_java_version
                        = getPropertyUTF8(env, "java.version");
        gdata->property_java_vm_name
                        = getPropertyUTF8(env, "java.vm.name");
        gdata->property_java_vm_info
                        = getPropertyUTF8(env, "java.vm.info");
        gdata->property_java_class_path
                        = getPropertyUTF8(env, "java.class.path");
        gdata->property_sun_boot_class_path
                        = getPropertyUTF8(env, "sun.boot.class.path");
        gdata->property_sun_boot_library_path
                        = getPropertyUTF8(env, "sun.boot.library.path");
        gdata->property_path_separator
                        = getPropertyUTF8(env, "path.separator");
        gdata->property_user_dir
                        = getPropertyUTF8(env, "user.dir");

        /* Get agent properties: invoke sun.misc.VMSupport.getAgentProperties */
        localVMSupportClass = JNI_FUNC_PTR(env,FindClass)
                                          (env, "sun/misc/VMSupport");
        if (localVMSupportClass == NULL) {
            gdata->agent_properties = NULL;
            if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
                JNI_FUNC_PTR(env,ExceptionClear)(env);
            }
        } else {
            getAgentProperties  =
                getStaticMethod(env, localVMSupportClass,
                                "getAgentProperties", "()Ljava/util/Properties;");
            localAgentProperties =
                JNI_FUNC_PTR(env,CallStaticObjectMethod)
                            (env, localVMSupportClass, getAgentProperties);
            saveGlobalRef(env, localAgentProperties, &(gdata->agent_properties));
            if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
                JNI_FUNC_PTR(env,ExceptionClear)(env);
                EXIT_ERROR(AGENT_ERROR_INTERNAL,
                    "Exception occurred calling sun.misc.VMSupport.getAgentProperties");
            }
        }

    } END_WITH_LOCAL_REFS(env);

}

void
util_reset(void)
{
}

jboolean
isObjectTag(jbyte tag) {
    return (tag == JDWP_TAG(OBJECT)) ||
           (tag == JDWP_TAG(STRING)) ||
           (tag == JDWP_TAG(THREAD)) ||
           (tag == JDWP_TAG(THREAD_GROUP)) ||
           (tag == JDWP_TAG(CLASS_LOADER)) ||
           (tag == JDWP_TAG(CLASS_OBJECT)) ||
           (tag == JDWP_TAG(ARRAY));
}

jbyte
specificTypeKey(JNIEnv *env, jobject object)
{
    if (object == NULL) {
        return JDWP_TAG(OBJECT);
    } else if (JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->stringClass)) {
        return JDWP_TAG(STRING);
    } else if (JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->threadClass)) {
        return JDWP_TAG(THREAD);
    } else if (JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->threadGroupClass)) {
        return JDWP_TAG(THREAD_GROUP);
    } else if (JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->classLoaderClass)) {
        return JDWP_TAG(CLASS_LOADER);
    } else if (JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->classClass)) {
        return JDWP_TAG(CLASS_OBJECT);
    } else {
        jboolean classIsArray;

        WITH_LOCAL_REFS(env, 1) {
            jclass clazz;
            clazz = JNI_FUNC_PTR(env,GetObjectClass)(env, object);
            classIsArray = isArrayClass(clazz);
        } END_WITH_LOCAL_REFS(env);

        return (classIsArray ? JDWP_TAG(ARRAY) : JDWP_TAG(OBJECT));
    }
}

static void
writeFieldValue(JNIEnv *env, PacketOutputStream *out, jobject object,
                jfieldID field)
{
    jclass clazz;
    char *signature = NULL;
    jvmtiError error;
    jbyte typeKey;

    clazz = JNI_FUNC_PTR(env,GetObjectClass)(env, object);
    error = fieldSignature(clazz, field, NULL, &signature, NULL);
    if (error != JVMTI_ERROR_NONE) {
        outStream_setError(out, map2jdwpError(error));
        return;
    }
    typeKey = signature[0];
    jvmtiDeallocate(signature);

    /*
     * For primitive types, the type key is bounced back as is. Objects
     * are handled in the switch statement below.
     */
    if ((typeKey != JDWP_TAG(OBJECT)) && (typeKey != JDWP_TAG(ARRAY))) {
        (void)outStream_writeByte(out, typeKey);
    }

    switch (typeKey) {
        case JDWP_TAG(OBJECT):
        case JDWP_TAG(ARRAY):   {
            jobject value = JNI_FUNC_PTR(env,GetObjectField)(env, object, field);
            (void)outStream_writeByte(out, specificTypeKey(env, value));
            (void)outStream_writeObjectRef(env, out, value);
            break;
        }

        case JDWP_TAG(BYTE):
            (void)outStream_writeByte(out,
                      JNI_FUNC_PTR(env,GetByteField)(env, object, field));
            break;

        case JDWP_TAG(CHAR):
            (void)outStream_writeChar(out,
                      JNI_FUNC_PTR(env,GetCharField)(env, object, field));
            break;

        case JDWP_TAG(FLOAT):
            (void)outStream_writeFloat(out,
                      JNI_FUNC_PTR(env,GetFloatField)(env, object, field));
            break;

        case JDWP_TAG(DOUBLE):
            (void)outStream_writeDouble(out,
                      JNI_FUNC_PTR(env,GetDoubleField)(env, object, field));
            break;

        case JDWP_TAG(INT):
            (void)outStream_writeInt(out,
                      JNI_FUNC_PTR(env,GetIntField)(env, object, field));
            break;

        case JDWP_TAG(LONG):
            (void)outStream_writeLong(out,
                      JNI_FUNC_PTR(env,GetLongField)(env, object, field));
            break;

        case JDWP_TAG(SHORT):
            (void)outStream_writeShort(out,
                      JNI_FUNC_PTR(env,GetShortField)(env, object, field));
            break;

        case JDWP_TAG(BOOLEAN):
            (void)outStream_writeBoolean(out,
                      JNI_FUNC_PTR(env,GetBooleanField)(env, object, field));
            break;
    }
}

static void
writeStaticFieldValue(JNIEnv *env, PacketOutputStream *out, jclass clazz,
                      jfieldID field)
{
    jvmtiError error;
    char *signature = NULL;
    jbyte typeKey;

    error = fieldSignature(clazz, field, NULL, &signature, NULL);
    if (error != JVMTI_ERROR_NONE) {
        outStream_setError(out, map2jdwpError(error));
        return;
    }
    typeKey = signature[0];
    jvmtiDeallocate(signature);

    /*
     * For primitive types, the type key is bounced back as is. Objects
     * are handled in the switch statement below.
     */
    if ((typeKey != JDWP_TAG(OBJECT)) && (typeKey != JDWP_TAG(ARRAY))) {
        (void)outStream_writeByte(out, typeKey);
    }

    switch (typeKey) {
        case JDWP_TAG(OBJECT):
        case JDWP_TAG(ARRAY):   {
            jobject value = JNI_FUNC_PTR(env,GetStaticObjectField)(env, clazz, field);
            (void)outStream_writeByte(out, specificTypeKey(env, value));
            (void)outStream_writeObjectRef(env, out, value);
            break;
        }

        case JDWP_TAG(BYTE):
            (void)outStream_writeByte(out,
                      JNI_FUNC_PTR(env,GetStaticByteField)(env, clazz, field));
            break;

        case JDWP_TAG(CHAR):
            (void)outStream_writeChar(out,
                      JNI_FUNC_PTR(env,GetStaticCharField)(env, clazz, field));
            break;

        case JDWP_TAG(FLOAT):
            (void)outStream_writeFloat(out,
                      JNI_FUNC_PTR(env,GetStaticFloatField)(env, clazz, field));
            break;

        case JDWP_TAG(DOUBLE):
            (void)outStream_writeDouble(out,
                      JNI_FUNC_PTR(env,GetStaticDoubleField)(env, clazz, field));
            break;

        case JDWP_TAG(INT):
            (void)outStream_writeInt(out,
                      JNI_FUNC_PTR(env,GetStaticIntField)(env, clazz, field));
            break;

        case JDWP_TAG(LONG):
            (void)outStream_writeLong(out,
                      JNI_FUNC_PTR(env,GetStaticLongField)(env, clazz, field));
            break;

        case JDWP_TAG(SHORT):
            (void)outStream_writeShort(out,
                      JNI_FUNC_PTR(env,GetStaticShortField)(env, clazz, field));
            break;

        case JDWP_TAG(BOOLEAN):
            (void)outStream_writeBoolean(out,
                      JNI_FUNC_PTR(env,GetStaticBooleanField)(env, clazz, field));
            break;
    }
}

void
sharedGetFieldValues(PacketInputStream *in, PacketOutputStream *out,
                     jboolean isStatic)
{
    JNIEnv *env = getEnv();
    jint length;
    jobject object;
    jclass clazz;

    object = NULL;
    clazz  = NULL;

    if (isStatic) {
        clazz = inStream_readClassRef(env, in);
    } else {
        object = inStream_readObjectRef(env, in);
    }

    length = inStream_readInt(in);
    if (inStream_error(in)) {
        return;
    }

    WITH_LOCAL_REFS(env, length + 1) { /* +1 for class with instance fields */

        int i;

        (void)outStream_writeInt(out, length);
        for (i = 0; (i < length) && !outStream_error(out); i++) {
            jfieldID field = inStream_readFieldID(in);

            if (isStatic) {
                writeStaticFieldValue(env, out, clazz, field);
            } else {
                writeFieldValue(env, out, object, field);
            }
        }

    } END_WITH_LOCAL_REFS(env);
}

jboolean
sharedInvoke(PacketInputStream *in, PacketOutputStream *out)
{
    jvalue *arguments = NULL;
    jint options;
    jvmtiError error;
    jbyte invokeType;
    jclass clazz;
    jmethodID method;
    jint argumentCount;
    jobject instance;
    jthread thread;
    JNIEnv *env;

    /*
     * Instance methods start with the instance, thread and class,
     * and statics and constructors start with the class and then the
     * thread.
     */
    env = getEnv();
    if (inStream_command(in) == JDWP_COMMAND(ObjectReference, InvokeMethod)) {
        instance = inStream_readObjectRef(env, in);
        thread = inStream_readThreadRef(env, in);
        clazz = inStream_readClassRef(env, in);
    } else { /* static method or constructor */
        instance = NULL;
        clazz = inStream_readClassRef(env, in);
        thread = inStream_readThreadRef(env, in);
    }

    /*
     * ... and the rest of the packet is identical for all commands
     */
    method = inStream_readMethodID(in);
    argumentCount = inStream_readInt(in);
    if (inStream_error(in)) {
        return JNI_TRUE;
    }

    /* If count == 0, don't try and allocate 0 bytes, you'll get NULL */
    if ( argumentCount > 0 ) {
        int i;
        /*LINTED*/
        arguments = jvmtiAllocate(argumentCount * (jint)sizeof(*arguments));
        if (arguments == NULL) {
            outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY));
            return JNI_TRUE;
        }
        for (i = 0; (i < argumentCount) && !inStream_error(in); i++) {
            arguments[i] = inStream_readValue(in, NULL);
        }
        if (inStream_error(in)) {
            return JNI_TRUE;
        }
    }

    options = inStream_readInt(in);
    if (inStream_error(in)) {
        if ( arguments != NULL ) {
            jvmtiDeallocate(arguments);
        }
        return JNI_TRUE;
    }

    if (inStream_command(in) == JDWP_COMMAND(ClassType, NewInstance)) {
        invokeType = INVOKE_CONSTRUCTOR;
    } else if (inStream_command(in) == JDWP_COMMAND(ClassType, InvokeMethod)) {
        invokeType = INVOKE_STATIC;
    } else if (inStream_command(in) == JDWP_COMMAND(ObjectReference, InvokeMethod)) {
        invokeType = INVOKE_INSTANCE;
    } else {
        outStream_setError(out, JDWP_ERROR(INTERNAL));
        if ( arguments != NULL ) {
            jvmtiDeallocate(arguments);
        }
        return JNI_TRUE;
    }

    /*
     * Request the invoke. If there are no errors in the request,
     * the interrupting thread will actually do the invoke and a
     * reply will be generated subsequently, so we don't reply here.
     */
    error = invoker_requestInvoke(invokeType, (jbyte)options, inStream_id(in),
                                  thread, clazz, method,
                                  instance, arguments, argumentCount);
    if (error != JVMTI_ERROR_NONE) {
        outStream_setError(out, map2jdwpError(error));
        if ( arguments != NULL ) {
            jvmtiDeallocate(arguments);
        }
        return JNI_TRUE;
    }

    return JNI_FALSE;   /* Don't reply */
}

jint
uniqueID(void)
{
    static jint currentID = 0;
    return currentID++;
}

int
filterDebugThreads(jthread *threads, int count)
{
    int i;
    int current;

    /* Squish out all of the debugger-spawned threads */
    for (i = 0, current = 0; i < count; i++) {
        jthread thread = threads[i];
        if (!threadControl_isDebugThread(thread)) {
            if (i > current) {
                threads[current] = thread;
            }
            current++;
        }
    }
    return current;
}

jbyte
referenceTypeTag(jclass clazz)
{
    jbyte tag;

    if (isInterface(clazz)) {
        tag = JDWP_TYPE_TAG(INTERFACE);
    } else if (isArrayClass(clazz)) {
        tag = JDWP_TYPE_TAG(ARRAY);
    } else {
        tag = JDWP_TYPE_TAG(CLASS);
    }

    return tag;
}

/**
 * Get field modifiers
 */
jvmtiError
fieldModifiers(jclass clazz, jfieldID field, jint *pmodifiers)
{
    jvmtiError error;

    *pmodifiers = 0;
    error = JVMTI_FUNC_PTR(gdata->jvmti,GetFieldModifiers)
            (gdata->jvmti, clazz, field, pmodifiers);
    return error;
}

/**
 * Get method modifiers
 */
jvmtiError
methodModifiers(jmethodID method, jint *pmodifiers)
{
    jvmtiError error;

    *pmodifiers = 0;
    error = JVMTI_FUNC_PTR(gdata->jvmti,GetMethodModifiers)
            (gdata->jvmti, method, pmodifiers);
    return error;
}

/* Returns a local ref to the declaring class for a method, or NULL. */
jvmtiError
methodClass(jmethodID method, jclass *pclazz)
{
    jvmtiError error;

    *pclazz = NULL;
    error = FUNC_PTR(gdata->jvmti,GetMethodDeclaringClass)
                                (gdata->jvmti, method, pclazz);
    return error;
}

/* Returns a local ref to the declaring class for a method, or NULL. */
jvmtiError
methodLocation(jmethodID method, jlocation *ploc1, jlocation *ploc2)
{
    jvmtiError error;

    error = JVMTI_FUNC_PTR(gdata->jvmti,GetMethodLocation)
                                (gdata->jvmti, method, ploc1, ploc2);
    return error;
}

/**
 * Get method signature
 */
jvmtiError
methodSignature(jmethodID method,
        char **pname, char **psignature, char **pgeneric_signature)
{
    jvmtiError error;
    char *name = NULL;
    char *signature = NULL;
    char *generic_signature = NULL;

    error = FUNC_PTR(gdata->jvmti,GetMethodName)
            (gdata->jvmti, method, &name, &signature, &generic_signature);

    if ( pname != NULL ) {
        *pname = name;
    } else if ( name != NULL )  {
        jvmtiDeallocate(name);
    }
    if ( psignature != NULL ) {
        *psignature = signature;
    } else if ( signature != NULL ) {
        jvmtiDeallocate(signature);
    }
    if ( pgeneric_signature != NULL ) {
        *pgeneric_signature = generic_signature;
    } else if ( generic_signature != NULL )  {
        jvmtiDeallocate(generic_signature);
    }
    return error;
}

/*
 * Get the return type key of the method
 *     V or B C D F I J S Z L  [
 */
jvmtiError
methodReturnType(jmethodID method, char *typeKey)
{
    char       *signature;
    jvmtiError  error;

    signature = NULL;
    error     = methodSignature(method, NULL, &signature, NULL);
    if (error == JVMTI_ERROR_NONE) {
        if (signature == NULL ) {
            error = AGENT_ERROR_INVALID_TAG;
        } else {
            char * xx;

            xx = strchr(signature, ')');
            if (xx == NULL || *(xx + 1) == 0) {
                error = AGENT_ERROR_INVALID_TAG;
            } else {
               *typeKey = *(xx + 1);
            }
            jvmtiDeallocate(signature);
        }
    }
    return error;
}


/**
 * Return class loader for a class (must be inside a WITH_LOCAL_REFS)
 */
jvmtiError
classLoader(jclass clazz, jobject *pclazz)
{
    jvmtiError error;

    *pclazz = NULL;
    error = JVMTI_FUNC_PTR(gdata->jvmti,GetClassLoader)
            (gdata->jvmti, clazz, pclazz);
    return error;
}

/**
 * Get field signature
 */
jvmtiError
fieldSignature(jclass clazz, jfieldID field,
        char **pname, char **psignature, char **pgeneric_signature)
{
    jvmtiError error;
    char *name = NULL;
    char *signature = NULL;
    char *generic_signature = NULL;

    error = JVMTI_FUNC_PTR(gdata->jvmti,GetFieldName)
            (gdata->jvmti, clazz, field, &name, &signature, &generic_signature);

    if ( pname != NULL ) {
        *pname = name;
    } else if ( name != NULL )  {
        jvmtiDeallocate(name);
    }
    if ( psignature != NULL ) {
        *psignature = signature;
    } else if ( signature != NULL )  {
        jvmtiDeallocate(signature);
    }
    if ( pgeneric_signature != NULL ) {
        *pgeneric_signature = generic_signature;
    } else if ( generic_signature != NULL )  {
        jvmtiDeallocate(generic_signature);
    }
    return error;
}

JNIEnv *
getEnv(void)
{
    JNIEnv *env = NULL;
    jint rc;

    rc = FUNC_PTR(gdata->jvm,GetEnv)
                (gdata->jvm, (void **)&env, JNI_VERSION_1_2);
    if (rc != JNI_OK) {
        ERROR_MESSAGE(("JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = %d",
                rc));
        EXIT_ERROR(AGENT_ERROR_NO_JNI_ENV,NULL);
    }
    return env;
}

jvmtiError
spawnNewThread(jvmtiStartFunction func, void *arg, char *name)
{
    JNIEnv *env = getEnv();
    jvmtiError error;

    LOG_MISC(("Spawning new thread: %s", name));

    WITH_LOCAL_REFS(env, 3) {

        jthread thread;
        jstring nameString;

        nameString = JNI_FUNC_PTR(env,NewStringUTF)(env, name);
        if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
            JNI_FUNC_PTR(env,ExceptionClear)(env);
            error = AGENT_ERROR_OUT_OF_MEMORY;
            goto err;
        }

        thread = JNI_FUNC_PTR(env,NewObject)
                        (env, gdata->threadClass, gdata->threadConstructor,
                                   gdata->systemThreadGroup, nameString);
        if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
            JNI_FUNC_PTR(env,ExceptionClear)(env);
            error = AGENT_ERROR_OUT_OF_MEMORY;
            goto err;
        }

        /*
         * Make the debugger thread a daemon
         */
        JNI_FUNC_PTR(env,CallVoidMethod)
                        (env, thread, gdata->threadSetDaemon, JNI_TRUE);
        if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
            JNI_FUNC_PTR(env,ExceptionClear)(env);
            error = AGENT_ERROR_JNI_EXCEPTION;
            goto err;
        }

        error = threadControl_addDebugThread(thread);
        if (error == JVMTI_ERROR_NONE) {
            /*
             * Debugger threads need cycles in all sorts of strange
             * situations (e.g. infinite cpu-bound loops), so give the
             * thread a high priority. Note that if the VM has an application
             * thread running at the max priority, there is still a chance
             * that debugger threads will be starved. (There needs to be
             * a way to give debugger threads a priority higher than any
             * application thread).
             */
            error = JVMTI_FUNC_PTR(gdata->jvmti,RunAgentThread)
                        (gdata->jvmti, thread, func, arg,
                                        JVMTI_THREAD_MAX_PRIORITY);
        }

        err: ;

    } END_WITH_LOCAL_REFS(env);

    return error;
}

jvmtiError
jvmtiGetCapabilities(jvmtiCapabilities *caps)
{
    if ( gdata->vmDead ) {
        return AGENT_ERROR_VM_DEAD;
    }
    if (!gdata->haveCachedJvmtiCapabilities) {
        jvmtiError error;

        error = JVMTI_FUNC_PTR(gdata->jvmti,GetCapabilities)
                        (gdata->jvmti, &(gdata->cachedJvmtiCapabilities));
        if (error != JVMTI_ERROR_NONE) {
            return error;
        }
        gdata->haveCachedJvmtiCapabilities = JNI_TRUE;
    }

    *caps = gdata->cachedJvmtiCapabilities;

    return JVMTI_ERROR_NONE;
}

static jint
jvmtiVersion(void)
{
    if (gdata->cachedJvmtiVersion == 0) {
        jvmtiError error;
        error = JVMTI_FUNC_PTR(gdata->jvmti,GetVersionNumber)
                        (gdata->jvmti, &(gdata->cachedJvmtiVersion));
        if (error != JVMTI_ERROR_NONE) {
            EXIT_ERROR(error, "on getting the JVMTI version number");
        }
    }
    return gdata->cachedJvmtiVersion;
}

jint
jvmtiMajorVersion(void)
{
    return (jvmtiVersion() & JVMTI_VERSION_MASK_MAJOR)
                    >> JVMTI_VERSION_SHIFT_MAJOR;
}

jint
jvmtiMinorVersion(void)
{
    return (jvmtiVersion() & JVMTI_VERSION_MASK_MINOR)
                    >> JVMTI_VERSION_SHIFT_MINOR;
}

jint
jvmtiMicroVersion(void)
{
    return (jvmtiVersion() & JVMTI_VERSION_MASK_MICRO)
                    >> JVMTI_VERSION_SHIFT_MICRO;
}

jboolean
canSuspendResumeThreadLists(void)
{
    jvmtiError error;
    jvmtiCapabilities cap;

    error = jvmtiGetCapabilities(&cap);
    return (error == JVMTI_ERROR_NONE && cap.can_suspend);
}

jvmtiError
getSourceDebugExtension(jclass clazz, char **extensionPtr)
{
    return JVMTI_FUNC_PTR(gdata->jvmti,GetSourceDebugExtension)
                (gdata->jvmti, clazz, extensionPtr);
}

/*
 * Convert the signature "Ljava/lang/Foo;" to a
 * classname "java.lang.Foo" compatible with the pattern.
 * Signature is overwritten in-place.
 */
void
convertSignatureToClassname(char *convert)
{
    char *p;

    p = convert + 1;
    while ((*p != ';') && (*p != '\0')) {
        char c = *p;
        if (c == '/') {
            *(p-1) = '.';
        } else {
            *(p-1) = c;
        }
        p++;
    }
    *(p-1) = '\0';
}

static void
handleInterrupt(void)
{
    /*
     * An interrupt is handled:
     *
     * 1) for running application threads by deferring the interrupt
     * until the current event handler has concluded.
     *
     * 2) for debugger threads by ignoring the interrupt; this is the
     * most robust solution since debugger threads don't use interrupts
     * to signal any condition.
     *
     * 3) for application threads that have not started or already
     * ended by ignoring the interrupt. In the former case, the application
     * is relying on timing to determine whether or not the thread sees
     * the interrupt; in the latter case, the interrupt is meaningless.
     */
    jthread thread = threadControl_currentThread();
    if ((thread != NULL) && (!threadControl_isDebugThread(thread))) {
        threadControl_setPendingInterrupt(thread);
    }
}

static jvmtiError
ignore_vm_death(jvmtiError error)
{
    if (error == JVMTI_ERROR_WRONG_PHASE) {
        LOG_MISC(("VM_DEAD, in debugMonitor*()?"));
        return JVMTI_ERROR_NONE; /* JVMTI does this, not JVMDI? */
    }
    return error;
}

void
debugMonitorEnter(jrawMonitorID monitor)
{
    jvmtiError error;
    while (JNI_TRUE) {
        error = FUNC_PTR(gdata->jvmti,RawMonitorEnter)
                        (gdata->jvmti, monitor);
        error = ignore_vm_death(error);
        if (error == JVMTI_ERROR_INTERRUPT) {
            handleInterrupt();
        } else {
            break;
        }
    }
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on raw monitor enter");
    }
}

void
debugMonitorExit(jrawMonitorID monitor)
{
    jvmtiError error;

    error = FUNC_PTR(gdata->jvmti,RawMonitorExit)
                (gdata->jvmti, monitor);
    error = ignore_vm_death(error);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on raw monitor exit");
    }
}

void
debugMonitorWait(jrawMonitorID monitor)
{
    jvmtiError error;
    error = FUNC_PTR(gdata->jvmti,RawMonitorWait)
        (gdata->jvmti, monitor, ((jlong)(-1)));

    /*
     * According to the JLS (17.8), here we have
     * either :
     * a- been notified
     * b- gotten a suprious wakeup
     * c- been interrupted
     * If both a and c have happened, the VM must choose
     * which way to return - a or c.  If it chooses c
     * then the notify is gone - either to some other
     * thread that is also waiting, or it is dropped
     * on the floor.
     *
     * a is what we expect.  b won't hurt us any -
     * callers should be programmed to handle
     * spurious wakeups.  In case of c,
     * then the interrupt has been cleared, but
     * we don't want to consume it.  It came from
     * user code and is intended for user code, not us.
     * So, we will remember that the interrupt has
     * occurred and re-activate it when this thread
     * goes back into user code.
     * That being said, what do we do here?  Since
     * we could have been notified too, here we will
     * just pretend that we have been.  It won't hurt
     * anything to return in the same way as if
     * we were notified since callers have to be able to
     * handle spurious wakeups anyway.
     */
    if (error == JVMTI_ERROR_INTERRUPT) {
        handleInterrupt();
        error = JVMTI_ERROR_NONE;
    }
    error = ignore_vm_death(error);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on raw monitor wait");
    }
}

void
debugMonitorTimedWait(jrawMonitorID monitor, jlong millis)
{
    jvmtiError error;
    error = FUNC_PTR(gdata->jvmti,RawMonitorWait)
        (gdata->jvmti, monitor, millis);
    if (error == JVMTI_ERROR_INTERRUPT) {
        /* See comment above */
        handleInterrupt();
        error = JVMTI_ERROR_NONE;
    }
    error = ignore_vm_death(error);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on raw monitor timed wait");
    }
}

void
debugMonitorNotify(jrawMonitorID monitor)
{
    jvmtiError error;

    error = FUNC_PTR(gdata->jvmti,RawMonitorNotify)
                (gdata->jvmti, monitor);
    error = ignore_vm_death(error);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on raw monitor notify");
    }
}

void
debugMonitorNotifyAll(jrawMonitorID monitor)
{
    jvmtiError error;

    error = FUNC_PTR(gdata->jvmti,RawMonitorNotifyAll)
                (gdata->jvmti, monitor);
    error = ignore_vm_death(error);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on raw monitor notify all");
    }
}

jrawMonitorID
debugMonitorCreate(char *name)
{
    jrawMonitorID monitor;
    jvmtiError error;

    error = FUNC_PTR(gdata->jvmti,CreateRawMonitor)
                (gdata->jvmti, name, &monitor);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on creation of a raw monitor");
    }
    return monitor;
}

void
debugMonitorDestroy(jrawMonitorID monitor)
{
    jvmtiError error;

    error = FUNC_PTR(gdata->jvmti,DestroyRawMonitor)
                (gdata->jvmti, monitor);
    error = ignore_vm_death(error);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on destruction of raw monitor");
    }
}

/**
 * Return array of all threads (must be inside a WITH_LOCAL_REFS)
 */
jthread *
allThreads(jint *count)
{
    jthread *threads;
    jvmtiError error;

    *count = 0;
    threads = NULL;
    error = JVMTI_FUNC_PTR(gdata->jvmti,GetAllThreads)
                (gdata->jvmti, count, &threads);
    if (error == AGENT_ERROR_OUT_OF_MEMORY) {
        return NULL; /* Let caller deal with no memory? */
    }
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "getting all threads");
    }
    return threads;
}

/**
 * Fill the passed in structure with thread group info.
 * name field is JVMTI allocated.  parent is global ref.
 */
void
threadGroupInfo(jthreadGroup group, jvmtiThreadGroupInfo *info)
{
    jvmtiError error;

    error = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadGroupInfo)
                (gdata->jvmti, group, info);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on getting thread group info");
    }
}

/**
 * Return class signature string
 */
jvmtiError
classSignature(jclass clazz, char **psignature, char **pgeneric_signature)
{
    jvmtiError error;
    char *signature = NULL;

    /*
     * pgeneric_signature can be NULL, and GetClassSignature
     * accepts NULL.
     */
    error = FUNC_PTR(gdata->jvmti,GetClassSignature)
                (gdata->jvmti, clazz, &signature, pgeneric_signature);

    if ( psignature != NULL ) {
        *psignature = signature;
    } else if ( signature != NULL )  {
        jvmtiDeallocate(signature);
    }
    return error;
}

/* Get class name (not signature) */
char *
getClassname(jclass clazz)
{
    char *classname;

    classname = NULL;
    if ( clazz != NULL ) {
        if (classSignature(clazz, &classname, NULL) != JVMTI_ERROR_NONE) {
            classname = NULL;
        } else {
            /* Convert in place */
            convertSignatureToClassname(classname);
        }
    }
    return classname; /* Caller must free this memory */
}

void
writeGenericSignature(PacketOutputStream *out, char *genericSignature)
{
    if (genericSignature == NULL) {
        (void)outStream_writeString(out, "");
    } else {
        (void)outStream_writeString(out, genericSignature);
    }
}

jint
classStatus(jclass clazz)
{
    jint status;
    jvmtiError error;

    error = JVMTI_FUNC_PTR(gdata->jvmti,GetClassStatus)
                (gdata->jvmti, clazz, &status);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on getting class status");
    }
    return status;
}

static jboolean
isArrayClass(jclass clazz)
{
    jboolean isArray = JNI_FALSE;
    jvmtiError error;

    error = JVMTI_FUNC_PTR(gdata->jvmti,IsArrayClass)
                (gdata->jvmti, clazz, &isArray);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on checking for an array class");
    }
    return isArray;
}

static jboolean
isInterface(jclass clazz)
{
    jboolean isInterface = JNI_FALSE;
    jvmtiError error;

    error = JVMTI_FUNC_PTR(gdata->jvmti,IsInterface)
                (gdata->jvmti, clazz, &isInterface);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on checking for an interface");
    }
    return isInterface;
}

jvmtiError
isFieldSynthetic(jclass clazz, jfieldID field, jboolean *psynthetic)
{
    jvmtiError error;

    error = JVMTI_FUNC_PTR(gdata->jvmti,IsFieldSynthetic)
                (gdata->jvmti, clazz, field, psynthetic);
    if ( error == JVMTI_ERROR_MUST_POSSESS_CAPABILITY ) {
        /* If the query is not supported, we assume it is not synthetic. */
        *psynthetic = JNI_FALSE;
        return JVMTI_ERROR_NONE;
    }
    return error;
}

jvmtiError
isMethodSynthetic(jmethodID method, jboolean *psynthetic)
{
    jvmtiError error;

    error = JVMTI_FUNC_PTR(gdata->jvmti,IsMethodSynthetic)
                (gdata->jvmti, method, psynthetic);
    if ( error == JVMTI_ERROR_MUST_POSSESS_CAPABILITY ) {
        /* If the query is not supported, we assume it is not synthetic. */
        *psynthetic = JNI_FALSE;
        return JVMTI_ERROR_NONE;
    }
    return error;
}

jboolean
isMethodNative(jmethodID method)
{
    jboolean isNative = JNI_FALSE;
    jvmtiError error;

    error = JVMTI_FUNC_PTR(gdata->jvmti,IsMethodNative)
                (gdata->jvmti, method, &isNative);
    if (error != JVMTI_ERROR_NONE) {
        EXIT_ERROR(error, "on checking for a native interface");
    }
    return isNative;
}

jboolean
isSameObject(JNIEnv *env, jobject o1, jobject o2)
{
    if ( o1==o2 ) {
        return JNI_TRUE;
    }
    return FUNC_PTR(env,IsSameObject)(env, o1, o2);
}

jint
objectHashCode(jobject object)
{
    jint hashCode = 0;
    jvmtiError error;

    if ( object!=NULL ) {
        error = JVMTI_FUNC_PTR(gdata->jvmti,GetObjectHashCode)
                    (gdata->jvmti, object, &hashCode);
        if (error != JVMTI_ERROR_NONE) {
            EXIT_ERROR(error, "on getting an object hash code");
        }
    }
    return hashCode;
}

/* Get all implemented interfaces (must be inside a WITH_LOCAL_REFS) */
jvmtiError
allInterfaces(jclass clazz, jclass **ppinterfaces, jint *pcount)
{
    jvmtiError error;

    *pcount = 0;
    *ppinterfaces = NULL;
    error = JVMTI_FUNC_PTR(gdata->jvmti,GetImplementedInterfaces)
                (gdata->jvmti, clazz, pcount, ppinterfaces);
    return error;
}

/* Get all loaded classes (must be inside a WITH_LOCAL_REFS) */
jvmtiError
allLoadedClasses(jclass **ppclasses, jint *pcount)
{
    jvmtiError error;

    *pcount = 0;
    *ppclasses = NULL;
    error = JVMTI_FUNC_PTR(gdata->jvmti,GetLoadedClasses)
                (gdata->jvmti, pcount, ppclasses);
    return error;
}

/* Get all loaded classes for a loader (must be inside a WITH_LOCAL_REFS) */
jvmtiError
allClassLoaderClasses(jobject loader, jclass **ppclasses, jint *pcount)
{
    jvmtiError error;

    *pcount = 0;
    *ppclasses = NULL;
    error = JVMTI_FUNC_PTR(gdata->jvmti,GetClassLoaderClasses)
                (gdata->jvmti, loader, pcount, ppclasses);
    return error;
}

static jboolean
is_a_nested_class(char *outer_sig, int outer_sig_len, char *sig, int sep)
{
    char *inner;

    /* Assumed outer class signature is  "LOUTERCLASSNAME;"
     *         inner class signature is  "LOUTERCLASSNAME$INNERNAME;"
     *
     * INNERNAME can take the form:
     *    [0-9][1-9]*        anonymous class somewhere in the file
     *    [0-9][1-9]*NAME    local class somewhere in the OUTER class
     *    NAME               nested class in OUTER
     *
     * If NAME itself contains a $ (sep) then classname is further nested
     *    inside another class.
     *
     */

    /* Check prefix first */
    if ( strncmp(sig, outer_sig, outer_sig_len-1) != 0 ) {
        return JNI_FALSE;
    }

    /* Prefix must be followed by a $ (sep) */
    if ( sig[outer_sig_len-1] != sep ) {
        return JNI_FALSE;  /* No sep follows the match, must not be nested. */
    }

    /* Walk past any digits, if we reach the end, must be pure anonymous */
    inner = sig + outer_sig_len;
#if 1 /* We want to return local classes */
    while ( *inner && isdigit(*inner) ) {
        inner++;
    }
    /* But anonymous class names can't be trusted. */
    if ( *inner == ';' ) {
        return JNI_FALSE;  /* A pure anonymous class */
    }
#else
    if ( *inner && isdigit(*inner) ) {
        return JNI_FALSE;  /* A pure anonymous or local class */
    }
#endif

    /* Nested deeper? */
    if ( strchr(inner, sep) != NULL ) {
        return JNI_FALSE;  /* Nested deeper than we want? */
    }
    return JNI_TRUE;
}

/* Get all nested classes for a class (must be inside a WITH_LOCAL_REFS) */
jvmtiError
allNestedClasses(jclass parent_clazz, jclass **ppnested, jint *pcount)
{
    jvmtiError error;
    jobject parent_loader;
    jclass *classes;
    char *signature;
    size_t len;
    jint count;
    jint ncount;
    int i;

    *ppnested   = NULL;
    *pcount     = 0;

    parent_loader = NULL;
    classes       = NULL;
    signature     = NULL;
    count         = 0;
    ncount        = 0;

    error = classLoader(parent_clazz, &parent_loader);
    if (error != JVMTI_ERROR_NONE) {
        return error;
    }
    error = classSignature(parent_clazz, &signature, NULL);
    if (error != JVMTI_ERROR_NONE) {
        return error;
    }
    len = strlen(signature);

    error = allClassLoaderClasses(parent_loader, &classes, &count);
    if ( error != JVMTI_ERROR_NONE ) {
        jvmtiDeallocate(signature);
        return error;
    }

    for (i=0; i<count; i++) {
        jclass clazz;
        char *candidate_signature;

        clazz = classes[i];
        candidate_signature = NULL;
        error = classSignature(clazz, &candidate_signature, NULL);
        if (error != JVMTI_ERROR_NONE) {
            break;
        }

        if ( is_a_nested_class(signature, (int)len, candidate_signature, '$') ||
             is_a_nested_class(signature, (int)len, candidate_signature, '#') ) {
            /* Float nested classes to top */
            classes[i] = classes[ncount];
            classes[ncount++] = clazz;
        }
        jvmtiDeallocate(candidate_signature);
    }

    jvmtiDeallocate(signature);

    if ( count != 0 &&  ncount == 0 ) {
        jvmtiDeallocate(classes);
        classes = NULL;
    }

    *ppnested = classes;
    *pcount = ncount;
    return error;
}

void
createLocalRefSpace(JNIEnv *env, jint capacity)
{
    /*
     * Save current exception since it might get overwritten by
     * the calls below. Note we must depend on space in the existing
     * frame because asking for a new frame may generate an exception.
     */
    jobject throwable = JNI_FUNC_PTR(env,ExceptionOccurred)(env);

    /*
     * Use the current frame if necessary; otherwise create a new one
     */
    if (JNI_FUNC_PTR(env,PushLocalFrame)(env, capacity) < 0) {
        EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"PushLocalFrame: Unable to push JNI frame");
    }

    /*
     * TO DO: This could be more efficient if it used EnsureLocalCapacity,
     * but that would not work if two functions on the call stack
     * use this function. We would need to either track reserved
     * references on a per-thread basis or come up with a convention
     * that would prevent two functions from depending on this function
     * at the same time.
     */

    /*
     * Restore exception state from before call
     */
    if (throwable != NULL) {
        JNI_FUNC_PTR(env,Throw)(env, throwable);
    } else {
        JNI_FUNC_PTR(env,ExceptionClear)(env);
    }
}

jboolean
isClass(jobject object)
{
    JNIEnv *env = getEnv();
    return JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->classClass);
}

jboolean
isThread(jobject object)
{
    JNIEnv *env = getEnv();
    return JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->threadClass);
}

jboolean
isThreadGroup(jobject object)
{
    JNIEnv *env = getEnv();
    return JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->threadGroupClass);
}

jboolean
isString(jobject object)
{
    JNIEnv *env = getEnv();
    return JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->stringClass);
}

jboolean
isClassLoader(jobject object)
{
    JNIEnv *env = getEnv();
    return JNI_FUNC_PTR(env,IsInstanceOf)(env, object, gdata->classLoaderClass);
}

jboolean
isArray(jobject object)
{
    JNIEnv *env = getEnv();
    jboolean is;

    WITH_LOCAL_REFS(env, 1) {
        jclass clazz;
        clazz = JNI_FUNC_PTR(env,GetObjectClass)(env, object);
        is = isArrayClass(clazz);
    } END_WITH_LOCAL_REFS(env);

    return is;
}

/**
 * Return property value as jstring
 */
static jstring
getPropertyValue(JNIEnv *env, char *propertyName)
{
    jstring valueString;
    jstring nameString;

    valueString = NULL;

    /* Create new String object to hold the property name */
    nameString = JNI_FUNC_PTR(env,NewStringUTF)(env, propertyName);
    if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
        JNI_FUNC_PTR(env,ExceptionClear)(env);
        /* NULL will be returned below */
    } else {
        /* Call valueString = System.getProperty(nameString) */
        valueString = JNI_FUNC_PTR(env,CallStaticObjectMethod)
            (env, gdata->systemClass, gdata->systemGetProperty, nameString);
        if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
            JNI_FUNC_PTR(env,ExceptionClear)(env);
            valueString = NULL;
        }
    }
    return valueString;
}

/**
 * Set an agent property
 */
void
setAgentPropertyValue(JNIEnv *env, char *propertyName, char* propertyValue)
{
    jstring nameString;
    jstring valueString;

    if (gdata->agent_properties == NULL) {
        /* VMSupport doesn't exist; so ignore */
        return;
    }

    /* Create jstrings for property name and value */
    nameString = JNI_FUNC_PTR(env,NewStringUTF)(env, propertyName);
    if (nameString != NULL) {
        valueString = JNI_FUNC_PTR(env,NewStringUTF)(env, propertyValue);
        if (valueString != NULL) {
            /* invoke Properties.setProperty */
            JNI_FUNC_PTR(env,CallObjectMethod)
                (env, gdata->agent_properties,
                 gdata->setProperty,
                 nameString, valueString);
        }
    }
    if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
        JNI_FUNC_PTR(env,ExceptionClear)(env);
    }
}

/**
 * Return property value as JDWP allocated string in UTF8 encoding
 */
static char *
getPropertyUTF8(JNIEnv *env, char *propertyName)
{
    jvmtiError  error;
    char       *value;

    value = NULL;
    error = JVMTI_FUNC_PTR(gdata->jvmti,GetSystemProperty)
                (gdata->jvmti, (const char *)propertyName, &value);
    if (error != JVMTI_ERROR_NONE) {
        jstring valueString;

        value = NULL;
        valueString = getPropertyValue(env, propertyName);

        if (valueString != NULL) {
            const char *utf;

            /* Get the UTF8 encoding for this property value string */
            utf = JNI_FUNC_PTR(env,GetStringUTFChars)(env, valueString, NULL);
            /* Make a copy for returning, release the JNI copy */
            value = jvmtiAllocate((int)strlen(utf) + 1);
            if (value != NULL) {
                (void)strcpy(value, utf);
            }
            JNI_FUNC_PTR(env,ReleaseStringUTFChars)(env, valueString, utf);
        }
    }
    if ( value == NULL ) {
        ERROR_MESSAGE(("JDWP Can't get property value for %s", propertyName));
        EXIT_ERROR(AGENT_ERROR_NULL_POINTER,NULL);
    }
    return value;
}

jboolean
isMethodObsolete(jmethodID method)
{
    jvmtiError error;
    jboolean obsolete = JNI_TRUE;

    if ( method != NULL ) {
        error = JVMTI_FUNC_PTR(gdata->jvmti,IsMethodObsolete)
                    (gdata->jvmti, method, &obsolete);
        if (error != JVMTI_ERROR_NONE) {
            obsolete = JNI_TRUE;
        }
    }
    return obsolete;
}

/* Get the jvmti environment to be used with tags */
static jvmtiEnv *
getSpecialJvmti(void)
{
    jvmtiEnv  *jvmti;
    jvmtiError error;
    int        rc;

    /* Get one time use JVMTI Env */
    jvmtiCapabilities caps;

    rc = JVM_FUNC_PTR(gdata->jvm,GetEnv)
                     (gdata->jvm, (void **)&jvmti, JVMTI_VERSION_1);
    if (rc != JNI_OK) {
        return NULL;
    }
    (void)memset(&caps, 0, (int)sizeof(caps));
    caps.can_tag_objects = 1;
    error = JVMTI_FUNC_PTR(jvmti,AddCapabilities)(jvmti, &caps);
    if ( error != JVMTI_ERROR_NONE ) {
        return NULL;
    }
    return jvmti;
}

void
writeCodeLocation(PacketOutputStream *out, jclass clazz,
                       jmethodID method, jlocation location)
{
    jbyte tag;

    if (clazz != NULL) {
        tag = referenceTypeTag(clazz);
    } else {
        tag = JDWP_TYPE_TAG(CLASS);
    }
    (void)outStream_writeByte(out, tag);
    (void)outStream_writeObjectRef(getEnv(), out, clazz);
    (void)outStream_writeMethodID(out, isMethodObsolete(method)?NULL:method);
    (void)outStream_writeLocation(out, location);
}

void *
jvmtiAllocate(jint numBytes)
{
    void *ptr;
    jvmtiError error;
    if ( numBytes == 0 ) {
        return NULL;
    }
    error = FUNC_PTR(gdata->jvmti,Allocate)
                (gdata->jvmti, numBytes, (unsigned char**)&ptr);
    if (error != JVMTI_ERROR_NONE ) {
        EXIT_ERROR(error, "Can't allocate jvmti memory");
    }
    return ptr;
}

void
jvmtiDeallocate(void *ptr)
{
    jvmtiError error;
    if ( ptr == NULL ) {
        return;
    }
    error = FUNC_PTR(gdata->jvmti,Deallocate)
                (gdata->jvmti, ptr);
    if (error != JVMTI_ERROR_NONE ) {
        EXIT_ERROR(error, "Can't deallocate jvmti memory");
    }
}

/* Rarely needed, transport library uses JDWP errors, only use? */
jvmtiError
map2jvmtiError(jdwpError error)
{
    switch ( error ) {
        case JDWP_ERROR(NONE):
            return JVMTI_ERROR_NONE;
        case JDWP_ERROR(INVALID_THREAD):
            return JVMTI_ERROR_INVALID_THREAD;
        case JDWP_ERROR(INVALID_THREAD_GROUP):
            return JVMTI_ERROR_INVALID_THREAD_GROUP;
        case JDWP_ERROR(INVALID_PRIORITY):
            return JVMTI_ERROR_INVALID_PRIORITY;
        case JDWP_ERROR(THREAD_NOT_SUSPENDED):
            return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
        case JDWP_ERROR(THREAD_SUSPENDED):
            return JVMTI_ERROR_THREAD_SUSPENDED;
        case JDWP_ERROR(INVALID_OBJECT):
            return JVMTI_ERROR_INVALID_OBJECT;
        case JDWP_ERROR(INVALID_CLASS):
            return JVMTI_ERROR_INVALID_CLASS;
        case JDWP_ERROR(CLASS_NOT_PREPARED):
            return JVMTI_ERROR_CLASS_NOT_PREPARED;
        case JDWP_ERROR(INVALID_METHODID):
            return JVMTI_ERROR_INVALID_METHODID;
        case JDWP_ERROR(INVALID_LOCATION):
            return JVMTI_ERROR_INVALID_LOCATION;
        case JDWP_ERROR(INVALID_FIELDID):
            return JVMTI_ERROR_INVALID_FIELDID;
        case JDWP_ERROR(INVALID_FRAMEID):
            return AGENT_ERROR_INVALID_FRAMEID;
        case JDWP_ERROR(NO_MORE_FRAMES):
            return JVMTI_ERROR_NO_MORE_FRAMES;
        case JDWP_ERROR(OPAQUE_FRAME):
            return JVMTI_ERROR_OPAQUE_FRAME;
        case JDWP_ERROR(NOT_CURRENT_FRAME):
            return AGENT_ERROR_NOT_CURRENT_FRAME;
        case JDWP_ERROR(TYPE_MISMATCH):
            return JVMTI_ERROR_TYPE_MISMATCH;
        case JDWP_ERROR(INVALID_SLOT):
            return JVMTI_ERROR_INVALID_SLOT;
        case JDWP_ERROR(DUPLICATE):
            return JVMTI_ERROR_DUPLICATE;
        case JDWP_ERROR(NOT_FOUND):
            return JVMTI_ERROR_NOT_FOUND;
        case JDWP_ERROR(INVALID_MONITOR):
            return JVMTI_ERROR_INVALID_MONITOR;
        case JDWP_ERROR(NOT_MONITOR_OWNER):
            return JVMTI_ERROR_NOT_MONITOR_OWNER;
        case JDWP_ERROR(INTERRUPT):
            return JVMTI_ERROR_INTERRUPT;
        case JDWP_ERROR(INVALID_CLASS_FORMAT):
            return JVMTI_ERROR_INVALID_CLASS_FORMAT;
        case JDWP_ERROR(CIRCULAR_CLASS_DEFINITION):
            return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION;
        case JDWP_ERROR(FAILS_VERIFICATION):
            return JVMTI_ERROR_FAILS_VERIFICATION;
        case JDWP_ERROR(ADD_METHOD_NOT_IMPLEMENTED):
            return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED;
        case JDWP_ERROR(SCHEMA_CHANGE_NOT_IMPLEMENTED):
            return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED;
        case JDWP_ERROR(INVALID_TYPESTATE):
            return JVMTI_ERROR_INVALID_TYPESTATE;
        case JDWP_ERROR(HIERARCHY_CHANGE_NOT_IMPLEMENTED):
            return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED;
        case JDWP_ERROR(DELETE_METHOD_NOT_IMPLEMENTED):
            return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED;
        case JDWP_ERROR(UNSUPPORTED_VERSION):
            return JVMTI_ERROR_UNSUPPORTED_VERSION;
        case JDWP_ERROR(NAMES_DONT_MATCH):
            return JVMTI_ERROR_NAMES_DONT_MATCH;
        case JDWP_ERROR(CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED):
            return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED;
        case JDWP_ERROR(METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED):
            return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED;
        case JDWP_ERROR(NOT_IMPLEMENTED):
            return JVMTI_ERROR_NOT_AVAILABLE;
        case JDWP_ERROR(NULL_POINTER):
            return JVMTI_ERROR_NULL_POINTER;
        case JDWP_ERROR(ABSENT_INFORMATION):
            return JVMTI_ERROR_ABSENT_INFORMATION;
        case JDWP_ERROR(INVALID_EVENT_TYPE):
            return JVMTI_ERROR_INVALID_EVENT_TYPE;
        case JDWP_ERROR(ILLEGAL_ARGUMENT):
            return JVMTI_ERROR_ILLEGAL_ARGUMENT;
        case JDWP_ERROR(OUT_OF_MEMORY):
            return JVMTI_ERROR_OUT_OF_MEMORY;
        case JDWP_ERROR(ACCESS_DENIED):
            return JVMTI_ERROR_ACCESS_DENIED;
        case JDWP_ERROR(VM_DEAD):
            return JVMTI_ERROR_WRONG_PHASE;
        case JDWP_ERROR(UNATTACHED_THREAD):
            return JVMTI_ERROR_UNATTACHED_THREAD;
        case JDWP_ERROR(INVALID_TAG):
            return AGENT_ERROR_INVALID_TAG;
        case JDWP_ERROR(ALREADY_INVOKING):
            return AGENT_ERROR_ALREADY_INVOKING;
        case JDWP_ERROR(INVALID_INDEX):
            return AGENT_ERROR_INVALID_INDEX;
        case JDWP_ERROR(INVALID_LENGTH):
            return AGENT_ERROR_INVALID_LENGTH;
        case JDWP_ERROR(INVALID_STRING):
            return AGENT_ERROR_INVALID_STRING;
        case JDWP_ERROR(INVALID_CLASS_LOADER):
            return AGENT_ERROR_INVALID_CLASS_LOADER;
        case JDWP_ERROR(INVALID_ARRAY):
            return AGENT_ERROR_INVALID_ARRAY;
        case JDWP_ERROR(TRANSPORT_LOAD):
            return AGENT_ERROR_TRANSPORT_LOAD;
        case JDWP_ERROR(TRANSPORT_INIT):
            return AGENT_ERROR_TRANSPORT_INIT;
        case JDWP_ERROR(NATIVE_METHOD):
            return AGENT_ERROR_NATIVE_METHOD;
        case JDWP_ERROR(INVALID_COUNT):
            return AGENT_ERROR_INVALID_COUNT;
        case JDWP_ERROR(INTERNAL):
            return AGENT_ERROR_JDWP_INTERNAL;
    }
    return AGENT_ERROR_INTERNAL;
}

static jvmtiEvent index2jvmti[EI_max-EI_min+1];
static jdwpEvent  index2jdwp [EI_max-EI_min+1];

void
eventIndexInit(void)
{
    (void)memset(index2jvmti, 0, (int)sizeof(index2jvmti));
    (void)memset(index2jdwp,  0, (int)sizeof(index2jdwp));

    index2jvmti[EI_SINGLE_STEP        -EI_min] = JVMTI_EVENT_SINGLE_STEP;
    index2jvmti[EI_BREAKPOINT         -EI_min] = JVMTI_EVENT_BREAKPOINT;
    index2jvmti[EI_FRAME_POP          -EI_min] = JVMTI_EVENT_FRAME_POP;
    index2jvmti[EI_EXCEPTION          -EI_min] = JVMTI_EVENT_EXCEPTION;
    index2jvmti[EI_THREAD_START       -EI_min] = JVMTI_EVENT_THREAD_START;
    index2jvmti[EI_THREAD_END         -EI_min] = JVMTI_EVENT_THREAD_END;
    index2jvmti[EI_CLASS_PREPARE      -EI_min] = JVMTI_EVENT_CLASS_PREPARE;
    index2jvmti[EI_GC_FINISH          -EI_min] = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH;
    index2jvmti[EI_CLASS_LOAD         -EI_min] = JVMTI_EVENT_CLASS_LOAD;
    index2jvmti[EI_FIELD_ACCESS       -EI_min] = JVMTI_EVENT_FIELD_ACCESS;
    index2jvmti[EI_FIELD_MODIFICATION -EI_min] = JVMTI_EVENT_FIELD_MODIFICATION;
    index2jvmti[EI_EXCEPTION_CATCH    -EI_min] = JVMTI_EVENT_EXCEPTION_CATCH;
    index2jvmti[EI_METHOD_ENTRY       -EI_min] = JVMTI_EVENT_METHOD_ENTRY;
    index2jvmti[EI_METHOD_EXIT        -EI_min] = JVMTI_EVENT_METHOD_EXIT;
    index2jvmti[EI_MONITOR_CONTENDED_ENTER      -EI_min] = JVMTI_EVENT_MONITOR_CONTENDED_ENTER;
    index2jvmti[EI_MONITOR_CONTENDED_ENTERED    -EI_min] = JVMTI_EVENT_MONITOR_CONTENDED_ENTERED;
    index2jvmti[EI_MONITOR_WAIT       -EI_min] = JVMTI_EVENT_MONITOR_WAIT;
    index2jvmti[EI_MONITOR_WAITED     -EI_min] = JVMTI_EVENT_MONITOR_WAITED;
    index2jvmti[EI_VM_INIT            -EI_min] = JVMTI_EVENT_VM_INIT;
    index2jvmti[EI_VM_DEATH           -EI_min] = JVMTI_EVENT_VM_DEATH;

    index2jdwp[EI_SINGLE_STEP         -EI_min] = JDWP_EVENT(SINGLE_STEP);
    index2jdwp[EI_BREAKPOINT          -EI_min] = JDWP_EVENT(BREAKPOINT);
    index2jdwp[EI_FRAME_POP           -EI_min] = JDWP_EVENT(FRAME_POP);
    index2jdwp[EI_EXCEPTION           -EI_min] = JDWP_EVENT(EXCEPTION);
    index2jdwp[EI_THREAD_START        -EI_min] = JDWP_EVENT(THREAD_START);
    index2jdwp[EI_THREAD_END          -EI_min] = JDWP_EVENT(THREAD_END);
    index2jdwp[EI_CLASS_PREPARE       -EI_min] = JDWP_EVENT(CLASS_PREPARE);
    index2jdwp[EI_GC_FINISH           -EI_min] = JDWP_EVENT(CLASS_UNLOAD);
    index2jdwp[EI_CLASS_LOAD          -EI_min] = JDWP_EVENT(CLASS_LOAD);
    index2jdwp[EI_FIELD_ACCESS        -EI_min] = JDWP_EVENT(FIELD_ACCESS);
    index2jdwp[EI_FIELD_MODIFICATION  -EI_min] = JDWP_EVENT(FIELD_MODIFICATION);
    index2jdwp[EI_EXCEPTION_CATCH     -EI_min] = JDWP_EVENT(EXCEPTION_CATCH);
    index2jdwp[EI_METHOD_ENTRY        -EI_min] = JDWP_EVENT(METHOD_ENTRY);
    index2jdwp[EI_METHOD_EXIT         -EI_min] = JDWP_EVENT(METHOD_EXIT);
    index2jdwp[EI_MONITOR_CONTENDED_ENTER             -EI_min] = JDWP_EVENT(MONITOR_CONTENDED_ENTER);
    index2jdwp[EI_MONITOR_CONTENDED_ENTERED           -EI_min] = JDWP_EVENT(MONITOR_CONTENDED_ENTERED);
    index2jdwp[EI_MONITOR_WAIT        -EI_min] = JDWP_EVENT(MONITOR_WAIT);
    index2jdwp[EI_MONITOR_WAITED      -EI_min] = JDWP_EVENT(MONITOR_WAITED);
    index2jdwp[EI_VM_INIT             -EI_min] = JDWP_EVENT(VM_INIT);
    index2jdwp[EI_VM_DEATH            -EI_min] = JDWP_EVENT(VM_DEATH);
}

jdwpEvent
eventIndex2jdwp(EventIndex i)
{
    if ( i < EI_min || i > EI_max ) {
        EXIT_ERROR(AGENT_ERROR_INVALID_INDEX,"bad EventIndex");
    }
    return index2jdwp[i-EI_min];
}

jvmtiEvent
eventIndex2jvmti(EventIndex i)
{
    if ( i < EI_min || i > EI_max ) {
        EXIT_ERROR(AGENT_ERROR_INVALID_INDEX,"bad EventIndex");
    }
    return index2jvmti[i-EI_min];
}

EventIndex
jdwp2EventIndex(jdwpEvent eventType)
{
    switch ( eventType ) {
        case JDWP_EVENT(SINGLE_STEP):
            return EI_SINGLE_STEP;
        case JDWP_EVENT(BREAKPOINT):
            return EI_BREAKPOINT;
        case JDWP_EVENT(FRAME_POP):
            return EI_FRAME_POP;
        case JDWP_EVENT(EXCEPTION):
            return EI_EXCEPTION;
        case JDWP_EVENT(THREAD_START):
            return EI_THREAD_START;
        case JDWP_EVENT(THREAD_END):
            return EI_THREAD_END;
        case JDWP_EVENT(CLASS_PREPARE):
            return EI_CLASS_PREPARE;
        case JDWP_EVENT(CLASS_UNLOAD):
            return EI_GC_FINISH;
        case JDWP_EVENT(CLASS_LOAD):
            return EI_CLASS_LOAD;
        case JDWP_EVENT(FIELD_ACCESS):
            return EI_FIELD_ACCESS;
        case JDWP_EVENT(FIELD_MODIFICATION):
            return EI_FIELD_MODIFICATION;
        case JDWP_EVENT(EXCEPTION_CATCH):
            return EI_EXCEPTION_CATCH;
        case JDWP_EVENT(METHOD_ENTRY):
            return EI_METHOD_ENTRY;
        case JDWP_EVENT(METHOD_EXIT):
            return EI_METHOD_EXIT;
        case JDWP_EVENT(METHOD_EXIT_WITH_RETURN_VALUE):
            return EI_METHOD_EXIT;
        case JDWP_EVENT(MONITOR_CONTENDED_ENTER):
            return EI_MONITOR_CONTENDED_ENTER;
        case JDWP_EVENT(MONITOR_CONTENDED_ENTERED):
            return EI_MONITOR_CONTENDED_ENTERED;
        case JDWP_EVENT(MONITOR_WAIT):
            return EI_MONITOR_WAIT;
        case JDWP_EVENT(MONITOR_WAITED):
            return EI_MONITOR_WAITED;
        case JDWP_EVENT(VM_INIT):
            return EI_VM_INIT;
        case JDWP_EVENT(VM_DEATH):
            return EI_VM_DEATH;
        default:
            break;
    }

    /*
     * Event type not recognized - don't exit with error as caller
     * may wish to return error to debugger.
     */
    return (EventIndex)0;
}

EventIndex
jvmti2EventIndex(jvmtiEvent kind)
{
    switch ( kind ) {
        case JVMTI_EVENT_SINGLE_STEP:
            return EI_SINGLE_STEP;
        case JVMTI_EVENT_BREAKPOINT:
            return EI_BREAKPOINT;
        case JVMTI_EVENT_FRAME_POP:
            return EI_FRAME_POP;
        case JVMTI_EVENT_EXCEPTION:
            return EI_EXCEPTION;
        case JVMTI_EVENT_THREAD_START:
            return EI_THREAD_START;
        case JVMTI_EVENT_THREAD_END:
            return EI_THREAD_END;
        case JVMTI_EVENT_CLASS_PREPARE:
            return EI_CLASS_PREPARE;
        case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
            return EI_GC_FINISH;
        case JVMTI_EVENT_CLASS_LOAD:
            return EI_CLASS_LOAD;
        case JVMTI_EVENT_FIELD_ACCESS:
            return EI_FIELD_ACCESS;
        case JVMTI_EVENT_FIELD_MODIFICATION:
            return EI_FIELD_MODIFICATION;
        case JVMTI_EVENT_EXCEPTION_CATCH:
            return EI_EXCEPTION_CATCH;
        case JVMTI_EVENT_METHOD_ENTRY:
            return EI_METHOD_ENTRY;
        case JVMTI_EVENT_METHOD_EXIT:
            return EI_METHOD_EXIT;
        /*
         * There is no JVMTI_EVENT_METHOD_EXIT_WITH_RETURN_VALUE.
         * The normal JVMTI_EVENT_METHOD_EXIT always contains the return value.
         */
        case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
            return EI_MONITOR_CONTENDED_ENTER;
        case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
            return EI_MONITOR_CONTENDED_ENTERED;
        case JVMTI_EVENT_MONITOR_WAIT:
            return EI_MONITOR_WAIT;
        case JVMTI_EVENT_MONITOR_WAITED:
            return EI_MONITOR_WAITED;
        case JVMTI_EVENT_VM_INIT:
            return EI_VM_INIT;
        case JVMTI_EVENT_VM_DEATH:
            return EI_VM_DEATH;
        default:
            EXIT_ERROR(AGENT_ERROR_INVALID_INDEX,"JVMTI to EventIndex mapping");
            break;
    }
    return (EventIndex)0;
}

/* This routine is commonly used, maps jvmti and agent errors to the best
 *    jdwp error code we can map to.
 */
jdwpError
map2jdwpError(jvmtiError error)
{
    switch ( error ) {
        case JVMTI_ERROR_NONE:
            return JDWP_ERROR(NONE);
        case AGENT_ERROR_INVALID_THREAD:
        case JVMTI_ERROR_INVALID_THREAD:
            return JDWP_ERROR(INVALID_THREAD);
        case JVMTI_ERROR_INVALID_THREAD_GROUP:
            return JDWP_ERROR(INVALID_THREAD_GROUP);
        case JVMTI_ERROR_INVALID_PRIORITY:
            return JDWP_ERROR(INVALID_PRIORITY);
        case JVMTI_ERROR_THREAD_NOT_SUSPENDED:
            return JDWP_ERROR(THREAD_NOT_SUSPENDED);
        case JVMTI_ERROR_THREAD_SUSPENDED:
            return JDWP_ERROR(THREAD_SUSPENDED);
        case JVMTI_ERROR_THREAD_NOT_ALIVE:
            return JDWP_ERROR(INVALID_THREAD);
        case AGENT_ERROR_INVALID_OBJECT:
        case JVMTI_ERROR_INVALID_OBJECT:
            return JDWP_ERROR(INVALID_OBJECT);
        case JVMTI_ERROR_INVALID_CLASS:
            return JDWP_ERROR(INVALID_CLASS);
        case JVMTI_ERROR_CLASS_NOT_PREPARED:
            return JDWP_ERROR(CLASS_NOT_PREPARED);
        case JVMTI_ERROR_INVALID_METHODID:
            return JDWP_ERROR(INVALID_METHODID);
        case JVMTI_ERROR_INVALID_LOCATION:
            return JDWP_ERROR(INVALID_LOCATION);
        case JVMTI_ERROR_INVALID_FIELDID:
            return JDWP_ERROR(INVALID_FIELDID);
        case AGENT_ERROR_NO_MORE_FRAMES:
        case JVMTI_ERROR_NO_MORE_FRAMES:
            return JDWP_ERROR(NO_MORE_FRAMES);
        case JVMTI_ERROR_OPAQUE_FRAME:
            return JDWP_ERROR(OPAQUE_FRAME);
        case JVMTI_ERROR_TYPE_MISMATCH:
            return JDWP_ERROR(TYPE_MISMATCH);
        case JVMTI_ERROR_INVALID_SLOT:
            return JDWP_ERROR(INVALID_SLOT);
        case JVMTI_ERROR_DUPLICATE:
            return JDWP_ERROR(DUPLICATE);
        case JVMTI_ERROR_NOT_FOUND:
            return JDWP_ERROR(NOT_FOUND);
        case JVMTI_ERROR_INVALID_MONITOR:
            return JDWP_ERROR(INVALID_MONITOR);
        case JVMTI_ERROR_NOT_MONITOR_OWNER:
            return JDWP_ERROR(NOT_MONITOR_OWNER);
        case JVMTI_ERROR_INTERRUPT:
            return JDWP_ERROR(INTERRUPT);
        case JVMTI_ERROR_INVALID_CLASS_FORMAT:
            return JDWP_ERROR(INVALID_CLASS_FORMAT);
        case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:
            return JDWP_ERROR(CIRCULAR_CLASS_DEFINITION);
        case JVMTI_ERROR_FAILS_VERIFICATION:
            return JDWP_ERROR(FAILS_VERIFICATION);
        case JVMTI_ERROR_INVALID_TYPESTATE:
            return JDWP_ERROR(INVALID_TYPESTATE);
        case JVMTI_ERROR_UNSUPPORTED_VERSION:
            return JDWP_ERROR(UNSUPPORTED_VERSION);
        case JVMTI_ERROR_NAMES_DONT_MATCH:
            return JDWP_ERROR(NAMES_DONT_MATCH);
        case AGENT_ERROR_NULL_POINTER:
        case JVMTI_ERROR_NULL_POINTER:
            return JDWP_ERROR(NULL_POINTER);
        case JVMTI_ERROR_ABSENT_INFORMATION:
            return JDWP_ERROR(ABSENT_INFORMATION);
        case AGENT_ERROR_INVALID_EVENT_TYPE:
        case JVMTI_ERROR_INVALID_EVENT_TYPE:
            return JDWP_ERROR(INVALID_EVENT_TYPE);
        case AGENT_ERROR_ILLEGAL_ARGUMENT:
        case JVMTI_ERROR_ILLEGAL_ARGUMENT:
            return JDWP_ERROR(ILLEGAL_ARGUMENT);
        case JVMTI_ERROR_OUT_OF_MEMORY:
        case AGENT_ERROR_OUT_OF_MEMORY:
            return JDWP_ERROR(OUT_OF_MEMORY);
        case JVMTI_ERROR_ACCESS_DENIED:
            return JDWP_ERROR(ACCESS_DENIED);
        case JVMTI_ERROR_WRONG_PHASE:
        case AGENT_ERROR_VM_DEAD:
        case AGENT_ERROR_NO_JNI_ENV:
            return JDWP_ERROR(VM_DEAD);
        case AGENT_ERROR_JNI_EXCEPTION:
        case JVMTI_ERROR_UNATTACHED_THREAD:
            return JDWP_ERROR(UNATTACHED_THREAD);
        case JVMTI_ERROR_NOT_AVAILABLE:
        case JVMTI_ERROR_MUST_POSSESS_CAPABILITY:
            return JDWP_ERROR(NOT_IMPLEMENTED);
        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:
            return JDWP_ERROR(HIERARCHY_CHANGE_NOT_IMPLEMENTED);
        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:
            return JDWP_ERROR(DELETE_METHOD_NOT_IMPLEMENTED);
        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:
            return JDWP_ERROR(ADD_METHOD_NOT_IMPLEMENTED);
        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:
            return JDWP_ERROR(SCHEMA_CHANGE_NOT_IMPLEMENTED);
        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:
            return JDWP_ERROR(CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED);
        case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
            return JDWP_ERROR(METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED);
        case AGENT_ERROR_NOT_CURRENT_FRAME:
            return JDWP_ERROR(NOT_CURRENT_FRAME);
        case AGENT_ERROR_INVALID_TAG:
            return JDWP_ERROR(INVALID_TAG);
        case AGENT_ERROR_ALREADY_INVOKING:
            return JDWP_ERROR(ALREADY_INVOKING);
        case AGENT_ERROR_INVALID_INDEX:
            return JDWP_ERROR(INVALID_INDEX);
        case AGENT_ERROR_INVALID_LENGTH:
            return JDWP_ERROR(INVALID_LENGTH);
        case AGENT_ERROR_INVALID_STRING:
            return JDWP_ERROR(INVALID_STRING);
        case AGENT_ERROR_INVALID_CLASS_LOADER:
            return JDWP_ERROR(INVALID_CLASS_LOADER);
        case AGENT_ERROR_INVALID_ARRAY:
            return JDWP_ERROR(INVALID_ARRAY);
        case AGENT_ERROR_TRANSPORT_LOAD:
            return JDWP_ERROR(TRANSPORT_LOAD);
        case AGENT_ERROR_TRANSPORT_INIT:
            return JDWP_ERROR(TRANSPORT_INIT);
        case AGENT_ERROR_NATIVE_METHOD:
            return JDWP_ERROR(NATIVE_METHOD);
        case AGENT_ERROR_INVALID_COUNT:
            return JDWP_ERROR(INVALID_COUNT);
        case AGENT_ERROR_INVALID_FRAMEID:
            return JDWP_ERROR(INVALID_FRAMEID);
        case JVMTI_ERROR_INTERNAL:
        case JVMTI_ERROR_INVALID_ENVIRONMENT:
        case AGENT_ERROR_INTERNAL:
        case AGENT_ERROR_JVMTI_INTERNAL:
        case AGENT_ERROR_JDWP_INTERNAL:
            return JDWP_ERROR(INTERNAL);
        default:
            break;
    }
    return JDWP_ERROR(INTERNAL);
}

jint
map2jdwpSuspendStatus(jint state)
{
    jint status = 0;
    if ( ( state & JVMTI_THREAD_STATE_SUSPENDED ) != 0 )  {
        status = JDWP_SUSPEND_STATUS(SUSPENDED);
    }
    return status;
}

jdwpThreadStatus
map2jdwpThreadStatus(jint state)
{
    jdwpThreadStatus status;

    status = (jdwpThreadStatus)(-1);

    if ( ! ( state & JVMTI_THREAD_STATE_ALIVE ) ) {
        if ( state & JVMTI_THREAD_STATE_TERMINATED ) {
            status = JDWP_THREAD_STATUS(ZOMBIE);
        } else {
            /* FIXUP? New JDWP #define for not started? */
            status = (jdwpThreadStatus)(-1);
        }
    } else {
        if ( state & JVMTI_THREAD_STATE_SLEEPING ) {
            status = JDWP_THREAD_STATUS(SLEEPING);
        } else if ( state & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER ) {
            status = JDWP_THREAD_STATUS(MONITOR);
        } else if ( state & JVMTI_THREAD_STATE_WAITING ) {
            status = JDWP_THREAD_STATUS(WAIT);
        } else if ( state & JVMTI_THREAD_STATE_RUNNABLE ) {
            status = JDWP_THREAD_STATUS(RUNNING);
        }
    }
    return status;
}

jint
map2jdwpClassStatus(jint classStatus)
{
    jint status = 0;
    if ( ( classStatus & JVMTI_CLASS_STATUS_VERIFIED ) != 0 ) {
        status |= JDWP_CLASS_STATUS(VERIFIED);
    }
    if ( ( classStatus & JVMTI_CLASS_STATUS_PREPARED ) != 0 ) {
        status |= JDWP_CLASS_STATUS(PREPARED);
    }
    if ( ( classStatus & JVMTI_CLASS_STATUS_INITIALIZED ) != 0 ) {
        status |= JDWP_CLASS_STATUS(INITIALIZED);
    }
    if ( ( classStatus & JVMTI_CLASS_STATUS_ERROR ) != 0 ) {
        status |= JDWP_CLASS_STATUS(ERROR);
    }
    return status;
}

void
log_debugee_location(const char *func,
        jthread thread, jmethodID method, jlocation location)
{
    int logging_locations = LOG_TEST(JDWP_LOG_LOC);

    if ( logging_locations ) {
        char *method_name;
        char *class_sig;
        jvmtiError error;
        jvmtiThreadInfo info;
        jint state;

        /* Get thread information */
        info.name = NULL;
        error = FUNC_PTR(gdata->jvmti,GetThreadInfo)
                                (gdata->jvmti, thread, &info);
        if ( error != JVMTI_ERROR_NONE) {
            info.name = NULL;
        }
        error = FUNC_PTR(gdata->jvmti,GetThreadState)
                                (gdata->jvmti, thread, &state);
        if ( error != JVMTI_ERROR_NONE) {
            state = 0;
        }

        /* Get method if necessary */
        if ( method==NULL ) {
            error = FUNC_PTR(gdata->jvmti,GetFrameLocation)
                        (gdata->jvmti, thread, 0, &method, &location);
            if ( error != JVMTI_ERROR_NONE ) {
                method = NULL;
                location = 0;
            }
        }

        /* Get method name */
        method_name = NULL;
        if ( method != NULL ) {
            error = methodSignature(method, &method_name, NULL, NULL);
            if ( error != JVMTI_ERROR_NONE ) {
                method_name = NULL;
            }
        }

        /* Get class signature */
        class_sig = NULL;
        if ( method != NULL ) {
            jclass clazz;

            error = methodClass(method, &clazz);
            if ( error == JVMTI_ERROR_NONE ) {
                error = classSignature(clazz, &class_sig, NULL);
                if ( error != JVMTI_ERROR_NONE ) {
                    class_sig = NULL;
                }
            }
        }

        /* Issue log message */
        LOG_LOC(("%s: debugee: thread=%p(%s:0x%x),method=%p(%s@%d;%s)",
                func,
                thread, info.name==NULL ? "?" : info.name, state,
                method, method_name==NULL ? "?" : method_name,
                (int)location, class_sig==NULL ? "?" : class_sig));

        /* Free memory */
        if ( class_sig != NULL ) {
            jvmtiDeallocate(class_sig);
        }
        if ( method_name != NULL ) {
            jvmtiDeallocate(method_name);
        }
        if ( info.name != NULL ) {
            jvmtiDeallocate(info.name);
        }
    }
}

/* ********************************************************************* */
/* JDK 6.0: Use of new Heap Iteration functions */
/* ********************************************************************* */

/* ********************************************************************* */
/* Instances */

/* Structure to hold class instances heap iteration data (arg user_data) */
typedef struct ClassInstancesData {
    jint         instCount;
    jint         maxInstances;
    jlong        objTag;
    jvmtiError   error;
} ClassInstancesData;

/* Callback for instance object tagging (heap_reference_callback). */
static jint JNICALL
cbObjectTagInstance(jvmtiHeapReferenceKind reference_kind,
     const jvmtiHeapReferenceInfo* reference_info, jlong class_tag,
     jlong referrer_class_tag, jlong size,
     jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data)
{
    ClassInstancesData  *data;

    /* Check data structure */
    data = (ClassInstancesData*)user_data;
    if (data == NULL) {
        data->error = AGENT_ERROR_ILLEGAL_ARGUMENT;
        return JVMTI_VISIT_ABORT;
    }

    /* If we have tagged enough objects, just abort */
    if ( data->maxInstances != 0 && data->instCount >= data->maxInstances ) {
        return JVMTI_VISIT_ABORT;
    }

    /* If tagged already, just continue */
    if ( (*tag_ptr) != (jlong)0 ) {
        return JVMTI_VISIT_OBJECTS;
    }

    /* Tag the object so we don't count it again, and so we can retrieve it */
    (*tag_ptr) = data->objTag;
    data->instCount++;
    return JVMTI_VISIT_OBJECTS;
}

/* Get instances for one class */
jvmtiError
classInstances(jclass klass, ObjectBatch *instances, int maxInstances)
{
    ClassInstancesData data;
    jvmtiHeapCallbacks heap_callbacks;
    jvmtiError         error;
    jvmtiEnv          *jvmti;

    /* Check interface assumptions */

    if (klass == NULL) {
        return AGENT_ERROR_INVALID_OBJECT;
    }

    if ( maxInstances < 0 || instances == NULL) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Initialize return information */
    instances->count   = 0;
    instances->objects = NULL;

    /* Get jvmti environment to use */
    jvmti = getSpecialJvmti();
    if ( jvmti == NULL ) {
        return AGENT_ERROR_INTERNAL;
    }

    /* Setup data to passed around the callbacks */
    data.instCount    = 0;
    data.maxInstances = maxInstances;
    data.objTag       = (jlong)1;
    data.error        = JVMTI_ERROR_NONE;

    /* Clear out callbacks structure */
    (void)memset(&heap_callbacks,0,sizeof(heap_callbacks));

    /* Set the callbacks we want */
    heap_callbacks.heap_reference_callback = &cbObjectTagInstance;

    /* Follow references, no initiating object, just this class, all objects */
    error = JVMTI_FUNC_PTR(jvmti,FollowReferences)
                 (jvmti, 0, klass, NULL, &heap_callbacks, &data);
    if ( error == JVMTI_ERROR_NONE ) {
        error = data.error;
    }

    /* Get all the instances now that they are tagged */
    if ( error == JVMTI_ERROR_NONE ) {
        error = JVMTI_FUNC_PTR(jvmti,GetObjectsWithTags)
                      (jvmti, 1, &(data.objTag), &(instances->count),
                       &(instances->objects), NULL);
        /* Verify we got the count we expected */
        if ( data.instCount != instances->count ) {
            error = AGENT_ERROR_INTERNAL;
        }
    }

    /* Dispose of any special jvmti environment */
    (void)JVMTI_FUNC_PTR(jvmti,DisposeEnvironment)(jvmti);
    return error;
}

/* ********************************************************************* */
/* Instance counts. */

/* Macros to convert a class or instance tag to an index and back again */
#define INDEX2CLASSTAG(i)      ((jlong)((i)+1))
#define CLASSTAG2INDEX(t)      (((int)(t))-1)
#define JLONG_ABS(x)           (((x)<(jlong)0)?-(x):(x))

/* Structure to hold class count heap traversal data (arg user_data) */
typedef struct ClassCountData {
    int          classCount;
    jlong       *counts;
    jlong        negObjTag;
    jvmtiError   error;
} ClassCountData;

/* Two different cbObjectCounter's, one for FollowReferences, one for
 *    IterateThroughHeap. Pick a card, any card.
 */

/* Callback for object count heap traversal (heap_reference_callback) */
static jint JNICALL
cbObjectCounterFromRef(jvmtiHeapReferenceKind reference_kind,
     const jvmtiHeapReferenceInfo* reference_info, jlong class_tag,
     jlong referrer_class_tag, jlong size,
     jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data)
{
    ClassCountData  *data;
    int              index;
    jlong            jindex;
    jlong            tag;

    /* Check data structure */
    data = (ClassCountData*)user_data;
    if (data == NULL) {
        data->error = AGENT_ERROR_ILLEGAL_ARGUMENT;
        return JVMTI_VISIT_ABORT;
    }

    /* Classes with no class_tag should have been filtered out. */
    if ( class_tag == (jlong)0 ) {
        data->error = AGENT_ERROR_INTERNAL;
        return JVMTI_VISIT_ABORT;
    }

    /* Class tag not one we really want (jclass not in supplied list) */
    if ( class_tag == data->negObjTag ) {
        return JVMTI_VISIT_OBJECTS;
    }

    /* If object tag is negative, just continue, we counted it */
    tag = (*tag_ptr);
    if ( tag < (jlong)0 ) {
        return JVMTI_VISIT_OBJECTS;
    }

    /* Tag the object with a negative value just so we don't count it again */
    if ( tag == (jlong)0 ) {
        /* This object had no tag value, so we give it the negObjTag value */
        (*tag_ptr) = data->negObjTag;
    } else {
        /* If this object had a positive tag value, it must be one of the
         *    jclass objects we tagged. We need to preserve the value of
         *    this tag for later objects that might have this as a class
         *    tag, so we just make the existing tag value negative.
         */
        (*tag_ptr) = -tag;
    }

    /* Absolute value of class tag is an index into the counts[] array */
    jindex = JLONG_ABS(class_tag);
    index = CLASSTAG2INDEX(jindex);
    if (index < 0 || index >= data->classCount) {
        data->error = AGENT_ERROR_ILLEGAL_ARGUMENT;
        return JVMTI_VISIT_ABORT;
    }

    /* Bump instance count on this class */
    data->counts[index]++;
    return JVMTI_VISIT_OBJECTS;
}

/* Callback for instance count heap traversal (heap_iteration_callback) */
static jint JNICALL
cbObjectCounter(jlong class_tag, jlong size, jlong* tag_ptr, jint length,
                        void* user_data)
{
    ClassCountData  *data;
    int              index;

    /* Check data structure */
    data = (ClassCountData*)user_data;
    if (data == NULL) {
        data->error = AGENT_ERROR_ILLEGAL_ARGUMENT;
        return JVMTI_VISIT_ABORT;
    }

    /* Classes with no tag should be filtered out. */
    if ( class_tag == (jlong)0 ) {
        data->error = AGENT_ERROR_INTERNAL;
        return JVMTI_VISIT_ABORT;
    }

    /* Class tag is actually an index into data arrays */
    index = CLASSTAG2INDEX(class_tag);
    if (index < 0 || index >= data->classCount) {
        data->error = AGENT_ERROR_ILLEGAL_ARGUMENT;
        return JVMTI_VISIT_ABORT;
    }

    /* Bump instance count on this class */
    data->counts[index]++;
    return JVMTI_VISIT_OBJECTS;
}

/* Get instance counts for a set of classes */
jvmtiError
classInstanceCounts(jint classCount, jclass *classes, jlong *counts)
{
    jvmtiHeapCallbacks heap_callbacks;
    ClassCountData     data;
    jvmtiError         error;
    jvmtiEnv          *jvmti;
    int                i;

    /* Check interface assumptions */
    if ( classes == NULL || classCount <= 0 || counts == NULL ) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Initialize return information */
    for ( i = 0 ; i < classCount ; i++ ) {
        counts[i] = (jlong)0;
    }

    /* Get jvmti environment to use */
    jvmti = getSpecialJvmti();
    if ( jvmti == NULL ) {
        return AGENT_ERROR_INTERNAL;
    }

    /* Setup class data structure */
    data.error        = JVMTI_ERROR_NONE;
    data.classCount   = classCount;
    data.counts       = counts;

    error = JVMTI_ERROR_NONE;
    /* Set tags on classes, use index in classes[] as the tag value. */
    error             = JVMTI_ERROR_NONE;
    for ( i = 0 ; i < classCount ; i++ ) {
        if (classes[i] != NULL) {
            jlong tag;

            tag = INDEX2CLASSTAG(i);
            error = JVMTI_FUNC_PTR(jvmti,SetTag) (jvmti, classes[i], tag);
            if ( error != JVMTI_ERROR_NONE ) {
                break;
            }
        }
    }

    /* Traverse heap, two ways to do this for instance counts. */
    if ( error == JVMTI_ERROR_NONE ) {

        /* Clear out callbacks structure */
        (void)memset(&heap_callbacks,0,sizeof(heap_callbacks));

        /* Check debug flags to see how to do this. */
        if ( (gdata->debugflags & USE_ITERATE_THROUGH_HEAP) == 0 ) {

            /* Using FollowReferences only gives us live objects, but we
             *   need to tag the objects to avoid counting them twice since
             *   the callback is per reference.
             *   The jclass objects have been tagged with their index in the
             *   supplied list, and that tag may flip to negative if it
             *   is also an object of interest.
             *   All other objects being counted that weren't in the
             *   supplied classes list will have a negative classCount
             *   tag value. So all objects counted will have negative tags.
             *   If the absolute tag value is an index in the supplied
             *   list, then it's one of the supplied classes.
             */
            data.negObjTag = -INDEX2CLASSTAG(classCount);

            /* Setup callbacks, only using object reference callback */
            heap_callbacks.heap_reference_callback = &cbObjectCounterFromRef;

            /* Follow references, no initiating object, tagged classes only */
            error = JVMTI_FUNC_PTR(jvmti,FollowReferences)
                          (jvmti, JVMTI_HEAP_FILTER_CLASS_UNTAGGED,
                           NULL, NULL, &heap_callbacks, &data);

        } else {

            /* Using IterateThroughHeap means that we will visit each object
             *   once, so no special tag tricks here. Just simple counting.
             *   However in this case the object might not be live, so we do
             *   a GC beforehand to make sure we minimize this.
             */

            /* FIXUP: Need some kind of trigger here to avoid excessive GC's? */
            error = JVMTI_FUNC_PTR(jvmti,ForceGarbageCollection)(jvmti);
            if ( error != JVMTI_ERROR_NONE ) {

                /* Setup callbacks, just need object callback */
                heap_callbacks.heap_iteration_callback = &cbObjectCounter;

                /* Iterate through entire heap, tagged classes only */
                error = JVMTI_FUNC_PTR(jvmti,IterateThroughHeap)
                              (jvmti, JVMTI_HEAP_FILTER_CLASS_UNTAGGED,
                               NULL, &heap_callbacks, &data);

            }
        }

        /* Use data error if needed */
        if ( error == JVMTI_ERROR_NONE ) {
            error = data.error;
        }

    }

    /* Dispose of any special jvmti environment */
    (void)JVMTI_FUNC_PTR(jvmti,DisposeEnvironment)(jvmti);
    return error;
}

/* ********************************************************************* */
/* Referrers */

/* Structure to hold object referrer heap traversal data (arg user_data) */
typedef struct ReferrerData {
  int        refCount;
  int        maxObjects;
  jlong      refTag;
  jlong      objTag;
  jboolean   selfRef;
  jvmtiError error;
} ReferrerData;

/* Callback for referrers object tagging (heap_reference_callback). */
static jint JNICALL
cbObjectTagReferrer(jvmtiHeapReferenceKind reference_kind,
     const jvmtiHeapReferenceInfo* reference_info, jlong class_tag,
     jlong referrer_class_tag, jlong size,
     jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data)
{
    ReferrerData  *data;

    /* Check data structure */
    data = (ReferrerData*)user_data;
    if (data == NULL) {
        data->error = AGENT_ERROR_ILLEGAL_ARGUMENT;
        return JVMTI_VISIT_ABORT;
    }

    /* If we have tagged enough objects, just abort */
    if ( data->maxObjects != 0 && data->refCount >= data->maxObjects ) {
        return JVMTI_VISIT_ABORT;
    }

    /* If not of interest, just continue */
    if ( (*tag_ptr) != data->objTag ) {
        return JVMTI_VISIT_OBJECTS;
    }

    /* Self reference that we haven't counted? */
    if ( tag_ptr == referrer_tag_ptr ) {
        if ( data->selfRef == JNI_FALSE ) {
            data->selfRef = JNI_TRUE;
            data->refCount++;
        }
        return JVMTI_VISIT_OBJECTS;
    }

    /* If the referrer can be tagged, and hasn't been tagged, tag it */
    if ( referrer_tag_ptr != NULL ) {
        if ( (*referrer_tag_ptr) == (jlong)0 ) {
            *referrer_tag_ptr = data->refTag;
            data->refCount++;
        }
    }
    return JVMTI_VISIT_OBJECTS;
}

/* Heap traversal to find referrers of an object */
jvmtiError
objectReferrers(jobject obj, ObjectBatch *referrers, int maxObjects)
{
    jvmtiHeapCallbacks heap_callbacks;
    ReferrerData       data;
    jvmtiError         error;
    jvmtiEnv          *jvmti;

    /* Check interface assumptions */
    if (obj == NULL) {
        return AGENT_ERROR_INVALID_OBJECT;
    }
    if (referrers == NULL || maxObjects < 0 ) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Initialize return information */
    referrers->count = 0;
    referrers->objects = NULL;

    /* Get jvmti environment to use */
    jvmti = getSpecialJvmti();
    if ( jvmti == NULL ) {
        return AGENT_ERROR_INTERNAL;
    }

    /* Fill in the data structure passed around the callbacks */
    data.refCount   = 0;
    data.maxObjects = maxObjects;
    data.objTag     = (jlong)1;
    data.refTag     = (jlong)2;
    data.selfRef    = JNI_FALSE;
    data.error      = JVMTI_ERROR_NONE;

    /* Tag the object of interest */
    error = JVMTI_FUNC_PTR(jvmti,SetTag) (jvmti, obj, data.objTag);

    /* No need to go any further if we can't tag the object */
    if ( error == JVMTI_ERROR_NONE ) {

        /* Clear out callbacks structure */
        (void)memset(&heap_callbacks,0,sizeof(heap_callbacks));

        /* Setup callbacks we want */
        heap_callbacks.heap_reference_callback = &cbObjectTagReferrer;

        /* Follow references, no initiating object, all classes, 1 tagged objs */
        error = JVMTI_FUNC_PTR(jvmti,FollowReferences)
                      (jvmti, JVMTI_HEAP_FILTER_UNTAGGED,
                       NULL, NULL, &heap_callbacks, &data);

        /* Use data error if needed */
        if ( error == JVMTI_ERROR_NONE ) {
            error = data.error;
        }

    }

    /* Watch out for self-reference */
    if ( error == JVMTI_ERROR_NONE && data.selfRef == JNI_TRUE ) {
        /* Tag itself as a referer */
        error = JVMTI_FUNC_PTR(jvmti,SetTag) (jvmti, obj, data.refTag);
    }

    /* Get the jobjects for the tagged referrer objects.  */
    if ( error == JVMTI_ERROR_NONE ) {
        error = JVMTI_FUNC_PTR(jvmti,GetObjectsWithTags)
                    (jvmti, 1, &(data.refTag), &(referrers->count),
                          &(referrers->objects), NULL);
        /* Verify we got the count we expected */
        if ( data.refCount != referrers->count ) {
            error = AGENT_ERROR_INTERNAL;
        }
    }

    /* Dispose of any special jvmti environment */
    (void)JVMTI_FUNC_PTR(jvmti,DisposeEnvironment)(jvmti);
    return error;
}

Other Java examples (source code examples)

Here is a short list of links related to this Java util.c source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.