jdk/src/jdk.hprof.agent/share/native/libhprof/hprof_reference.c
changeset 26201 40a873d21081
parent 25859 3317bb8137f4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.hprof.agent/share/native/libhprof/hprof_reference.c	Tue Aug 26 07:55:08 2014 +0200
@@ -0,0 +1,814 @@
+/*
+ * 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);
+}