jdk/src/demo/share/jvmti/hprof/hprof_reference.c
author chegar
Sun, 17 Aug 2014 15:54:13 +0100
changeset 25859 3317bb8137f4
parent 14342 jdk/src/share/demo/jvmti/hprof/hprof_reference.c@8435a30053c1
permissions -rw-r--r--
8054834: Modular Source Code Reviewed-by: alanb, chegar, ihse, mduigou Contributed-by: alan.bateman@oracle.com, alex.buckley@oracle.com, chris.hegarty@oracle.com, erik.joelsson@oracle.com, jonathan.gibbons@oracle.com, karen.kinnear@oracle.com, magnus.ihse.bursie@oracle.com, mandy.chung@oracle.com, mark.reinhold@oracle.com, paul.sandoz@oracle.com

/*
 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This source code is provided to illustrate the usage of a given feature
 * or technique and has been deliberately simplified. Additional steps
 * required for a production-quality application, such as security checks,
 * input validation and proper error handling, might not be present in
 * this sample code.
 */


/* Object references table (used in hprof_object.c). */

/*
 * This table is used by the object table to store object reference
 *   and primitive data information obtained from iterations over the
 *   heap (see hprof_site.c).
 *
 * Most of these table entries have no Key, but the key is used to store
 *   the primitive array and primitive field jvalue. None of these entries
 *   are ever looked up, there will be no hash table, use of the
 *   LookupTable was just an easy way to handle a unbounded table of
 *   entries. The object table (see hprof_object.c) will completely
 *   free this reference table after each heap dump or after processing the
 *   references and primitive data.
 *
 * The hprof format required this accumulation of all heap iteration
 *   references and primitive data from objects in order to compose an
 *   hprof records for it.
 *
 * This file contains detailed understandings of how an hprof CLASS
 *   and INSTANCE dump is constructed, most of this is derived from the
 *   original hprof code, but some has been derived by reading the HAT
 *   code that accepts this format.
 *
 */

#include "hprof.h"

/* The flavor of data being saved in the RefInfo */
enum {
    INFO_OBJECT_REF_DATA    = 1,
    INFO_PRIM_FIELD_DATA    = 2,
    INFO_PRIM_ARRAY_DATA    = 3
};

/* Reference information, object reference or primitive data information */
typedef struct RefInfo {
    ObjectIndex object_index; /* If an object reference, the referree index */
    jint        index;        /* If array or field, array or field index */
    jint        length;       /* If array the element count, if not -1 */
    RefIndex    next;         /* The next table element */
    unsigned    flavor   : 8; /* INFO_*, flavor of RefInfo */
    unsigned    refKind  : 8; /* The kind of reference */
    unsigned    primType : 8; /* If primitive data involved, it's type */
} RefInfo;

/* Private internal functions. */

/* Get the RefInfo structure from an entry */
static RefInfo *
get_info(RefIndex index)
{
    RefInfo *info;

    info = (RefInfo*)table_get_info(gdata->reference_table, index);
    return info;
}

/* Get a jvalue that was stored as the key. */
static jvalue
get_key_value(RefIndex index)
{
    void  *key;
    int    len;
    jvalue value;
    static jvalue empty_value;

    key = NULL;
    table_get_key(gdata->reference_table, index, &key, &len);
    HPROF_ASSERT(key!=NULL);
    HPROF_ASSERT(len==(int)sizeof(jvalue));
    if ( key != NULL ) {
        (void)memcpy(&value, key, (int)sizeof(jvalue));
    } else {
        value = empty_value;
    }
    return value;
}

/* Get size of a primitive type */
static jint
get_prim_size(jvmtiPrimitiveType primType)
{
    jint size;

    switch ( primType ) {
        case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
            size = (jint)sizeof(jboolean);
            break;
        case JVMTI_PRIMITIVE_TYPE_BYTE:
            size = (jint)sizeof(jbyte);
            break;
        case JVMTI_PRIMITIVE_TYPE_CHAR:
            size = (jint)sizeof(jchar);
            break;
        case JVMTI_PRIMITIVE_TYPE_SHORT:
            size = (jint)sizeof(jshort);
            break;
        case JVMTI_PRIMITIVE_TYPE_INT:
            size = (jint)sizeof(jint);
            break;
        case JVMTI_PRIMITIVE_TYPE_FLOAT:
            size = (jint)sizeof(jfloat);
            break;
        case JVMTI_PRIMITIVE_TYPE_LONG:
            size = (jint)sizeof(jlong);
            break;
        case JVMTI_PRIMITIVE_TYPE_DOUBLE:
            size = (jint)sizeof(jdouble);
            break;
        default:
            HPROF_ASSERT(0);
            size = 1;
            break;
    }
    return size;
}

/* Get a void* elements array that was stored as the key. */
static void *
get_key_elements(RefIndex index, jvmtiPrimitiveType primType,
                 jint *nelements, jint *nbytes)
{
    void  *key;
    jint   byteLen;

    HPROF_ASSERT(nelements!=NULL);
    HPROF_ASSERT(nbytes!=NULL);

    table_get_key(gdata->reference_table, index, &key, &byteLen);
    HPROF_ASSERT(byteLen>=0);
    HPROF_ASSERT(byteLen!=0?key!=NULL:key==NULL);
    *nbytes      = byteLen;
    *nelements   = byteLen / get_prim_size(primType);
    return key;
}

/* Dump a RefInfo* structure */
static void
dump_ref_info(RefInfo *info)
{
    debug_message("[%d]: flavor=%d"
                          ", refKind=%d"
                          ", primType=%d"
                          ", object_index=0x%x"
                          ", length=%d"
                          ", next=0x%x"
                          "\n",
            info->index,
            info->flavor,
            info->refKind,
            info->primType,
            info->object_index,
            info->length,
            info->next);
}

/* Dump a RefIndex list */
static void
dump_ref_list(RefIndex list)
{
    RefInfo *info;
    RefIndex index;

    debug_message("\nFOLLOW REFERENCES RETURNED:\n");
    index = list;
    while ( index != 0 ) {
        info = get_info(index);
        dump_ref_info(info);
        index = info->next;
    }
}

/* Dump information about a field and what ref data we had on it */
static void
dump_field(FieldInfo *fields, jvalue *fvalues, int n_fields,
                jint index, jvalue value, jvmtiPrimitiveType primType)
{
    ClassIndex  cnum;
    StringIndex name;
    StringIndex sig;

    cnum = fields[index].cnum;
    name = fields[index].name_index;
    sig  = fields[index].sig_index;
    debug_message("[%d] %s \"%s\" \"%s\"",
          index,
          cnum!=0?string_get(class_get_signature(cnum)):"?",
          name!=0?string_get(name):"?",
          sig!=0?string_get(sig):"?");
    if ( fields[index].primType!=0 || fields[index].primType!=primType ) {
        debug_message(" (primType=%d(%c)",
          fields[index].primType,
          primTypeToSigChar(fields[index].primType));
        if ( primType != fields[index].primType ) {
            debug_message(", got %d(%c)",
              primType,
              primTypeToSigChar(primType));
        }
        debug_message(")");
    } else {
        debug_message("(ty=OBJ)");
    }
    if ( value.j != (jlong)0 || fvalues[index].j != (jlong)0 ) {
        debug_message(" val=[0x%08x,0x%08x] or [0x%08x,0x%08x]",
            jlong_high(value.j), jlong_low(value.j),
            jlong_high(fvalues[index].j), jlong_low(fvalues[index].j));
    }
    debug_message("\n");
}

/* Dump all the fields of interest */
static void
dump_fields(RefIndex list, FieldInfo *fields, jvalue *fvalues, int n_fields)
{
    int i;

    debug_message("\nHPROF LIST OF ALL FIELDS:\n");
    for ( i = 0 ; i < n_fields ; i++ ) {
        if ( fields[i].name_index != 0 ) {
            dump_field(fields, fvalues, n_fields, i, fvalues[i], fields[i].primType);
        }
    }
    dump_ref_list(list);
}

/* Verify field data */
static void
verify_field(RefIndex list, FieldInfo *fields, jvalue *fvalues, int n_fields,
                jint index, jvalue value, jvmtiPrimitiveType primType)
{
    HPROF_ASSERT(fvalues != NULL);
    HPROF_ASSERT(n_fields > 0);
    HPROF_ASSERT(index < n_fields);
    HPROF_ASSERT(index >= 0 );
    if ( primType!=fields[index].primType ) {
        dump_fields(list, fields, fvalues, n_fields);
        debug_message("\nPROBLEM WITH:\n");
        dump_field(fields, fvalues, n_fields, index, value, primType);
        debug_message("\n");
        HPROF_ERROR(JNI_FALSE, "Trouble with fields and heap data");
    }
    if ( primType == JVMTI_PRIMITIVE_TYPE_BOOLEAN &&
         ( value.b != 1 && value.b != 0 ) ) {
        dump_fields(list, fields, fvalues, n_fields);
        debug_message("\nPROBLEM WITH:\n");
        dump_field(fields, fvalues, n_fields, index, value, primType);
        debug_message("\n");
        HPROF_ERROR(JNI_FALSE, "Trouble with fields and heap data");
    }
}

/* Fill in a field value, making sure the index is safe */
static void
fill_in_field_value(RefIndex list, FieldInfo *fields, jvalue *fvalues,
                    int n_fields, jint index, jvalue value,
                    jvmtiPrimitiveType primType)
{
    HPROF_ASSERT(fvalues != NULL);
    HPROF_ASSERT(n_fields > 0);
    HPROF_ASSERT(index < n_fields);
    HPROF_ASSERT(index >= 0 );
    HPROF_ASSERT(fvalues[index].j==(jlong)0);
    verify_field(list, fields, fvalues, n_fields, index, value, primType);
    if (index >= 0 && index < n_fields) {
        fvalues[index] = value;
    }
}

/* Walk all references for an ObjectIndex and construct the hprof CLASS dump. */
static void
dump_class_and_supers(JNIEnv *env, ObjectIndex object_index, RefIndex list)
{
    SiteIndex    site_index;
    SerialNumber trace_serial_num;
    RefIndex     index;
    ClassIndex   super_cnum;
    ObjectIndex  super_index;
    LoaderIndex  loader_index;
    ObjectIndex  signers_index;
    ObjectIndex  domain_index;
    FieldInfo   *fields;
    jvalue      *fvalues;
    jint         n_fields;
    jboolean     skip_fields;
    jint         n_fields_set;
    jlong        size;
    ClassIndex   cnum;
    char        *sig;
    ObjectKind   kind;
    TraceIndex   trace_index;
    Stack       *cpool_values;
    ConstantPoolValue *cpool;
    jint         cpool_count;

    HPROF_ASSERT(object_index!=0);
    kind        = object_get_kind(object_index);
    if ( kind != OBJECT_CLASS ) {
        return;
    }
    site_index         = object_get_site(object_index);
    HPROF_ASSERT(site_index!=0);
    cnum        = site_get_class_index(site_index);
    HPROF_ASSERT(cnum!=0);
    if ( class_get_status(cnum) & CLASS_DUMPED ) {
        return;
    }
    class_add_status(cnum, CLASS_DUMPED);
    size        = (jlong)object_get_size(object_index);

    super_index = 0;
    super_cnum  = class_get_super(cnum);
    if ( super_cnum != 0 ) {
        super_index  = class_get_object_index(super_cnum);
        if ( super_index != 0 ) {
            dump_class_and_supers(env, super_index,
                        object_get_references(super_index));
        }
    }

    trace_index      = site_get_trace_index(site_index);
    HPROF_ASSERT(trace_index!=0);
    trace_serial_num = trace_get_serial_number(trace_index);
    sig              = string_get(class_get_signature(cnum));
    loader_index     = class_get_loader(cnum);
    signers_index    = 0;
    domain_index     = 0;

    /* Get field information */
    n_fields     = 0;
    skip_fields  = JNI_FALSE;
    n_fields_set = 0;
    fields       = NULL;
    fvalues      = NULL;
    if ( class_get_all_fields(env, cnum, &n_fields, &fields) == 1 ) {
        /* Problems getting all the fields, can't trust field index values */
        skip_fields = JNI_TRUE;
        /* Class with no references at all? (ok to be unprepared if list==0?) */
        if ( list != 0 ) {
            /* It is assumed that the reason why we didn't get the fields
             *     was because the class is not prepared.
             */
            if ( gdata->debugflags & DEBUGFLAG_UNPREPARED_CLASSES ) {
                dump_ref_list(list);
                debug_message("Unprepared class with references: %s\n",
                               sig);
            }
            HPROF_ERROR(JNI_FALSE, "Trouble with unprepared classes");
        }
        /* Why would an unprepared class contain references? */
    }
    if ( n_fields > 0 ) {
        fvalues      = (jvalue*)HPROF_MALLOC(n_fields*(int)sizeof(jvalue));
        (void)memset(fvalues, 0, n_fields*(int)sizeof(jvalue));
    }

    /* We use a Stack just because it will automatically expand as needed */
    cpool_values = stack_init(16, 16, sizeof(ConstantPoolValue));
    cpool = NULL;
    cpool_count = 0;

    index      = list;
    while ( index != 0 ) {
        RefInfo    *info;
        jvalue      ovalue;
        static jvalue empty_value;

        info = get_info(index);

        switch ( info->flavor ) {
            case INFO_OBJECT_REF_DATA:
                switch ( info->refKind ) {
                    case JVMTI_HEAP_REFERENCE_FIELD:
                    case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT:
                        /* Should never be seen on a class dump */
                        HPROF_ASSERT(0);
                        break;
                    case JVMTI_HEAP_REFERENCE_STATIC_FIELD:
                        if ( skip_fields == JNI_TRUE ) {
                            break;
                        }
                        ovalue   = empty_value;
                        ovalue.i = info->object_index;
                        fill_in_field_value(list, fields, fvalues, n_fields,
                                        info->index, ovalue, 0);
                        n_fields_set++;
                        HPROF_ASSERT(n_fields_set <= n_fields);
                        break;
                    case JVMTI_HEAP_REFERENCE_CONSTANT_POOL: {
                        ConstantPoolValue cpv;
                        ObjectIndex       cp_object_index;
                        SiteIndex         cp_site_index;
                        ClassIndex        cp_cnum;

                        cp_object_index = info->object_index;
                        HPROF_ASSERT(cp_object_index!=0);
                        cp_site_index = object_get_site(cp_object_index);
                        HPROF_ASSERT(cp_site_index!=0);
                        cp_cnum = site_get_class_index(cp_site_index);
                        cpv.constant_pool_index = info->index;
                        cpv.sig_index = class_get_signature(cp_cnum);
                        cpv.value.i = cp_object_index;
                        stack_push(cpool_values, (void*)&cpv);
                        cpool_count++;
                        break;
                        }
                    case JVMTI_HEAP_REFERENCE_SIGNERS:
                        signers_index = info->object_index;
                        break;
                    case JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN:
                        domain_index = info->object_index;
                        break;
                    case JVMTI_HEAP_REFERENCE_CLASS_LOADER:
                    case JVMTI_HEAP_REFERENCE_INTERFACE:
                    default:
                        /* Ignore, not needed */
                        break;
                }
                break;
            case INFO_PRIM_FIELD_DATA:
                if ( skip_fields == JNI_TRUE ) {
                    break;
                }
                HPROF_ASSERT(info->primType!=0);
                HPROF_ASSERT(info->length==-1);
                HPROF_ASSERT(info->refKind==JVMTI_HEAP_REFERENCE_STATIC_FIELD);
                ovalue = get_key_value(index);
                fill_in_field_value(list, fields, fvalues, n_fields,
                                    info->index, ovalue, info->primType);
                n_fields_set++;
                HPROF_ASSERT(n_fields_set <= n_fields);
                break;
            case INFO_PRIM_ARRAY_DATA:
            default:
                /* Should never see these */
                HPROF_ASSERT(0);
                break;
        }

        index = info->next;
    }

    /* Get constant pool data if we have any */
    HPROF_ASSERT(cpool_count==stack_depth(cpool_values));
    if ( cpool_count > 0 ) {
        cpool = (ConstantPoolValue*)stack_element(cpool_values, 0);
    }
    io_heap_class_dump(cnum, sig, object_index, trace_serial_num,
            super_index,
            loader_object_index(env, loader_index),
            signers_index, domain_index,
            (jint)size, cpool_count, cpool, n_fields, fields, fvalues);

    stack_term(cpool_values);
    if ( fvalues != NULL ) {
        HPROF_FREE(fvalues);
    }
}

/* Walk all references for an ObjectIndex and construct the hprof INST dump. */
static void
dump_instance(JNIEnv *env, ObjectIndex object_index, RefIndex list)
{
    jvmtiPrimitiveType primType;
    SiteIndex    site_index;
    SerialNumber trace_serial_num;
    RefIndex     index;
    ObjectIndex  class_index;
    jlong        size;
    ClassIndex   cnum;
    char        *sig;
    void        *elements;
    jint         num_elements;
    jint         num_bytes;
    ObjectIndex *values;
    FieldInfo   *fields;
    jvalue      *fvalues;
    jint         n_fields;
    jboolean     skip_fields;
    jint         n_fields_set;
    ObjectKind   kind;
    TraceIndex   trace_index;
    jboolean     is_array;
    jboolean     is_prim_array;

    HPROF_ASSERT(object_index!=0);
    kind        = object_get_kind(object_index);
    if ( kind == OBJECT_CLASS ) {
        return;
    }
    site_index       = object_get_site(object_index);
    HPROF_ASSERT(site_index!=0);
    cnum             = site_get_class_index(site_index);
    HPROF_ASSERT(cnum!=0);
    size             = (jlong)object_get_size(object_index);
    trace_index      = site_get_trace_index(site_index);
    HPROF_ASSERT(trace_index!=0);
    trace_serial_num = trace_get_serial_number(trace_index);
    sig              = string_get(class_get_signature(cnum));
    class_index      = class_get_object_index(cnum);

    values       = NULL;
    elements     = NULL;
    num_elements = 0;
    num_bytes    = 0;

    n_fields     = 0;
    skip_fields  = JNI_FALSE;
    n_fields_set = 0;
    fields       = NULL;
    fvalues      = NULL;

    index      = list;

    is_array      = JNI_FALSE;
    is_prim_array = JNI_FALSE;

    if ( sig[0] != JVM_SIGNATURE_ARRAY ) {
        if ( class_get_all_fields(env, cnum, &n_fields, &fields) == 1 ) {
            /* Trouble getting all the fields, can't trust field index values */
            skip_fields = JNI_TRUE;
            /* It is assumed that the reason why we didn't get the fields
             *     was because the class is not prepared.
             */
            if ( gdata->debugflags & DEBUGFLAG_UNPREPARED_CLASSES ) {
                if ( list != 0 ) {
                    dump_ref_list(list);
                    debug_message("Instance of unprepared class with refs: %s\n",
                                   sig);
                } else {
                    debug_message("Instance of unprepared class without refs: %s\n",
                                   sig);
                }
                HPROF_ERROR(JNI_FALSE, "Big Trouble with unprepared class instances");
            }
        }
        if ( n_fields > 0 ) {
            fvalues = (jvalue*)HPROF_MALLOC(n_fields*(int)sizeof(jvalue));
            (void)memset(fvalues, 0, n_fields*(int)sizeof(jvalue));
        }
    } else {
        is_array = JNI_TRUE;
        if ( sig[0] != 0 && sigToPrimSize(sig+1) != 0 ) {
            is_prim_array = JNI_TRUE;
        }
    }

    while ( index != 0 ) {
        RefInfo *info;
        jvalue   ovalue;
        static jvalue empty_value;

        info = get_info(index);

        /* Process reference objects, many not used right now. */
        switch ( info->flavor ) {
            case INFO_OBJECT_REF_DATA:
                switch ( info->refKind ) {
                    case JVMTI_HEAP_REFERENCE_SIGNERS:
                    case JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN:
                    case JVMTI_HEAP_REFERENCE_CLASS_LOADER:
                    case JVMTI_HEAP_REFERENCE_INTERFACE:
                    case JVMTI_HEAP_REFERENCE_STATIC_FIELD:
                    case JVMTI_HEAP_REFERENCE_CONSTANT_POOL:
                        /* Should never be seen on an instance dump */
                        HPROF_ASSERT(0);
                        break;
                    case JVMTI_HEAP_REFERENCE_FIELD:
                        if ( skip_fields == JNI_TRUE ) {
                            break;
                        }
                        HPROF_ASSERT(is_array!=JNI_TRUE);
                        ovalue   = empty_value;
                        ovalue.i = info->object_index;
                        fill_in_field_value(list, fields, fvalues, n_fields,
                                        info->index, ovalue, 0);
                        n_fields_set++;
                        HPROF_ASSERT(n_fields_set <= n_fields);
                        break;
                    case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT:
                        /* We get each object element one at a time.  */
                        HPROF_ASSERT(is_array==JNI_TRUE);
                        HPROF_ASSERT(is_prim_array!=JNI_TRUE);
                        if ( num_elements <= info->index ) {
                            int nbytes;

                            if ( values == NULL ) {
                                num_elements = info->index + 1;
                                nbytes = num_elements*(int)sizeof(ObjectIndex);
                                values = (ObjectIndex*)HPROF_MALLOC(nbytes);
                                (void)memset(values, 0, nbytes);
                            } else {
                                void *new_values;
                                int   new_size;
                                int   obytes;

                                obytes = num_elements*(int)sizeof(ObjectIndex);
                                new_size = info->index + 1;
                                nbytes = new_size*(int)sizeof(ObjectIndex);
                                new_values = (void*)HPROF_MALLOC(nbytes);
                                (void)memcpy(new_values, values, obytes);
                                (void)memset(((char*)new_values)+obytes, 0,
                                                        nbytes-obytes);
                                HPROF_FREE(values);
                                num_elements = new_size;
                                values =  new_values;
                            }
                        }
                        HPROF_ASSERT(values[info->index]==0);
                        values[info->index] = info->object_index;
                        break;
                    default:
                        /* Ignore, not needed */
                        break;
                }
                break;
            case INFO_PRIM_FIELD_DATA:
                if ( skip_fields == JNI_TRUE ) {
                    break;
                }
                HPROF_ASSERT(info->primType!=0);
                HPROF_ASSERT(info->length==-1);
                HPROF_ASSERT(info->refKind==JVMTI_HEAP_REFERENCE_FIELD);
                HPROF_ASSERT(is_array!=JNI_TRUE);
                ovalue = get_key_value(index);
                fill_in_field_value(list, fields, fvalues, n_fields,
                                    info->index, ovalue, info->primType);
                n_fields_set++;
                HPROF_ASSERT(n_fields_set <= n_fields);
                break;
            case INFO_PRIM_ARRAY_DATA:
                /* Should only be one, and it's handled below */
                HPROF_ASSERT(info->refKind==0);
                /* We assert that nothing else was saved with this array */
                HPROF_ASSERT(index==list&&info->next==0);
                HPROF_ASSERT(is_array==JNI_TRUE);
                HPROF_ASSERT(is_prim_array==JNI_TRUE);
                primType = info->primType;
                elements = get_key_elements(index, primType,
                                            &num_elements, &num_bytes);
                HPROF_ASSERT(info->length==num_elements);
                size = num_bytes;
                break;
            default:
                HPROF_ASSERT(0);
                break;
        }
        index = info->next;
    }

    if ( is_array == JNI_TRUE ) {
        if ( is_prim_array == JNI_TRUE ) {
            HPROF_ASSERT(values==NULL);
            io_heap_prim_array(object_index, trace_serial_num,
                    (jint)size, num_elements, sig, elements);
        } else {
            HPROF_ASSERT(elements==NULL);
            io_heap_object_array(object_index, trace_serial_num,
                    (jint)size, num_elements, sig, values, class_index);
        }
    } else {
        io_heap_instance_dump(cnum, object_index, trace_serial_num,
                    class_index, (jint)size, sig, fields, fvalues, n_fields);
    }
    if ( values != NULL ) {
        HPROF_FREE(values);
    }
    if ( fvalues != NULL ) {
        HPROF_FREE(fvalues);
    }
    if ( elements != NULL ) {
        /* Do NOT free elements, it's a key in the table, leave it be */
    }
}

/* External interfaces. */

void
reference_init(void)
{
    HPROF_ASSERT(gdata->reference_table==NULL);
    gdata->reference_table = table_initialize("Ref", 2048, 4096, 0,
                            (int)sizeof(RefInfo));
}

/* Save away a reference to an object */
RefIndex
reference_obj(RefIndex next, jvmtiHeapReferenceKind refKind,
              ObjectIndex object_index, jint index, jint length)
{
    static RefInfo  empty_info;
    RefIndex        entry;
    RefInfo         info;

    info                = empty_info;
    info.flavor         = INFO_OBJECT_REF_DATA;
    info.refKind        = refKind;
    info.object_index   = object_index;
    info.index          = index;
    info.length         = length;
    info.next           = next;
    entry = table_create_entry(gdata->reference_table, NULL, 0, (void*)&info);
    return entry;
}

/* Save away some primitive field data */
RefIndex
reference_prim_field(RefIndex next, jvmtiHeapReferenceKind refKind,
              jvmtiPrimitiveType primType, jvalue field_value, jint field_index)
{
    static RefInfo  empty_info;
    RefIndex        entry;
    RefInfo         info;

    HPROF_ASSERT(primType==JVMTI_PRIMITIVE_TYPE_BOOLEAN?(field_value.b==1||field_value.b==0):1);

    info                = empty_info;
    info.flavor         = INFO_PRIM_FIELD_DATA;
    info.refKind        = refKind;
    info.primType       = primType;
    info.index          = field_index;
    info.length         = -1;
    info.next           = next;
    entry = table_create_entry(gdata->reference_table,
                (void*)&field_value, (int)sizeof(jvalue), (void*)&info);
    return entry;
}

/* Save away some primitive array data */
RefIndex
reference_prim_array(RefIndex next, jvmtiPrimitiveType primType,
              const void *elements, jint elementCount)
{
    static RefInfo  empty_info;
    RefIndex        entry;
    RefInfo         info;

    HPROF_ASSERT(next == 0);
    HPROF_ASSERT(elementCount >= 0);
    HPROF_ASSERT(elements != NULL);

    info                = empty_info;
    info.flavor         = INFO_PRIM_ARRAY_DATA;
    info.refKind        = 0;
    info.primType       = primType;
    info.index          = 0;
    info.length         = elementCount;
    info.next           = next;
    entry = table_create_entry(gdata->reference_table, (void*)elements,
                         elementCount * get_prim_size(primType), (void*)&info);
    return entry;
}

void
reference_cleanup(void)
{
    if ( gdata->reference_table == NULL ) {
        return;
    }
    table_cleanup(gdata->reference_table, NULL, NULL);
    gdata->reference_table = NULL;
}

void
reference_dump_instance(JNIEnv *env, ObjectIndex object_index, RefIndex list)
{
    dump_instance(env, object_index, list);
}

void
reference_dump_class(JNIEnv *env, ObjectIndex object_index, RefIndex list)
{
    dump_class_and_supers(env, object_index, list);
}