jdk/src/demo/share/jvmti/hprof/hprof_class.c
changeset 25859 3317bb8137f4
parent 23010 6dadb192ad81
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/demo/share/jvmti/hprof/hprof_class.c	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 2003, 2013, 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.
+ */
+
+
+/* Table of class information.
+ *
+ *   Each element in this table is identified with a ClassIndex.
+ *   Each element is uniquely identified by it's signature and loader.
+ *   Every class load has a unique class serial number.
+ *   While loaded, each element will have a cache of a global reference
+ *     to it's jclass object, plus jmethodID's as needed.
+ *   Method signatures and names are obtained via BCI.
+ *   Methods can be identified with a ClassIndex and MethodIndex pair,
+ *     where the MethodIndex matches the index of the method name and
+ *     signature arrays obtained from the BCI pass.
+ *   Strings are stored in the string table and a StringIndex is used.
+ *   Class Loaders are stored in the loader table and a LoaderIndex is used.
+ *   Since the jclass object is an object, at some point an object table
+ *      entry may be allocated for the jclass as an ObjectIndex.
+ */
+
+#include "hprof.h"
+
+/* Effectively represents a jclass object. */
+
+/* These table elements are made unique by and sorted by signature name. */
+
+typedef struct ClassKey {
+    StringIndex    sig_string_index;    /* Signature of class */
+    LoaderIndex    loader_index;        /* Index for class loader */
+} ClassKey;
+
+/* Each class could contain method information, gotten from BCI callback */
+
+typedef struct MethodInfo {
+    StringIndex  name_index;    /* Method name, index into string table */
+    StringIndex  sig_index;     /* Method signature, index into string table */
+    jmethodID    method_id;     /* Method ID, possibly NULL at first */
+} MethodInfo;
+
+/* The basic class information we save */
+
+typedef struct ClassInfo {
+    jclass         classref;            /* Global ref to jclass */
+    MethodInfo    *method;              /* Array of method data */
+    int            method_count;        /* Count of methods */
+    ObjectIndex    object_index;        /* Optional object index for jclass */
+    SerialNumber   serial_num;          /* Unique to the actual class load */
+    ClassStatus    status;              /* Current class status (bit mask) */
+    ClassIndex     super;               /* Super class in this table */
+    StringIndex    name;                /* Name of class */
+    jint           inst_size;           /* #bytes needed for instance fields */
+    jint           field_count;         /* Number of all fields */
+    FieldInfo     *field;               /* Pointer to all FieldInfo's */
+} ClassInfo;
+
+/* Private interfaces */
+
+static ClassKey*
+get_pkey(ClassIndex index)
+{
+    void *key_ptr;
+    int   key_len;
+
+    table_get_key(gdata->class_table, index, (void*)&key_ptr, &key_len);
+    HPROF_ASSERT(key_len==sizeof(ClassKey));
+    HPROF_ASSERT(key_ptr!=NULL);
+    return (ClassKey*)key_ptr;
+}
+
+static void
+fillin_pkey(const char *sig, LoaderIndex loader_index, ClassKey *pkey)
+{
+    static ClassKey empty_key;
+
+    HPROF_ASSERT(loader_index!=0);
+    *pkey                  = empty_key;
+    pkey->sig_string_index = string_find_or_create(sig);
+    pkey->loader_index     = loader_index;
+}
+
+static ClassInfo *
+get_info(ClassIndex index)
+{
+    ClassInfo *info;
+
+    info = (ClassInfo*)table_get_info(gdata->class_table, index);
+    return info;
+}
+
+static void
+fill_info(TableIndex index, ClassKey *pkey)
+{
+    ClassInfo *info;
+    char      *sig;
+
+    info = get_info(index);
+    info->serial_num = gdata->class_serial_number_counter++;
+    info->method_count = 0;
+    info->inst_size = -1;
+    info->field_count = -1;
+    info->field = NULL;
+    sig = string_get(pkey->sig_string_index);
+    if ( sig[0] != JVM_SIGNATURE_CLASS ) {
+        info->name = pkey->sig_string_index;
+    } else {
+        int        len;
+
+        len = string_get_len(pkey->sig_string_index);
+        if ( len > 2  ) {
+            char      *name;
+
+            /* Class signature looks like "Lname;", we want "name" here. */
+            name = HPROF_MALLOC(len-1);
+            (void)memcpy(name, sig+1, len-2);
+            name[len-2] = 0;
+            info->name = string_find_or_create(name);
+            HPROF_FREE(name);
+        } else {
+            /* This would be strange, a class signature not in "Lname;" form? */
+            info->name = pkey->sig_string_index;
+        }
+   }
+}
+
+static ClassIndex
+find_entry(ClassKey *pkey)
+{
+    ClassIndex index;
+
+    index = table_find_entry(gdata->class_table,
+                                (void*)pkey, (int)sizeof(ClassKey));
+    return index;
+}
+
+static ClassIndex
+create_entry(ClassKey *pkey)
+{
+    ClassIndex index;
+
+    index = table_create_entry(gdata->class_table,
+                                (void*)pkey, (int)sizeof(ClassKey), NULL);
+    fill_info(index, pkey);
+    return index;
+}
+
+static ClassIndex
+find_or_create_entry(ClassKey *pkey)
+{
+    ClassIndex      index;
+
+    HPROF_ASSERT(pkey!=NULL);
+    HPROF_ASSERT(pkey->loader_index!=0);
+    index = find_entry(pkey);
+    if ( index == 0 ) {
+        index = create_entry(pkey);
+    }
+    return index;
+}
+
+static void
+delete_classref(JNIEnv *env, ClassInfo *info, jclass klass)
+{
+    jclass ref;
+    int    i;
+
+    HPROF_ASSERT(env!=NULL);
+    HPROF_ASSERT(info!=NULL);
+
+    for ( i = 0 ; i < info->method_count ; i++ ) {
+        info->method[i].method_id  = NULL;
+    }
+    ref = info->classref;
+    if ( klass != NULL ) {
+        info->classref = newGlobalReference(env, klass);
+    } else {
+        info->classref = NULL;
+    }
+    if ( ref != NULL ) {
+        deleteGlobalReference(env, ref);
+    }
+}
+
+static void
+cleanup_item(TableIndex index, void *key_ptr, int key_len,
+                                void *info_ptr, void *arg)
+{
+    ClassInfo *info;
+
+    /* Cleanup any information in this ClassInfo structure. */
+    HPROF_ASSERT(key_ptr!=NULL);
+    HPROF_ASSERT(key_len==sizeof(ClassKey));
+    HPROF_ASSERT(info_ptr!=NULL);
+    info = (ClassInfo *)info_ptr;
+    if ( info->method_count > 0 ) {
+        HPROF_FREE((void*)info->method);
+        info->method_count = 0;
+        info->method       = NULL;
+    }
+    if ( info->field != NULL ) {
+        HPROF_FREE((void*)info->field);
+        info->field_count = 0;
+        info->field      = NULL;
+    }
+}
+
+static void
+delete_ref_item(TableIndex index, void *key_ptr, int key_len,
+                                void *info_ptr, void *arg)
+{
+    delete_classref((JNIEnv*)arg, (ClassInfo*)info_ptr, NULL);
+}
+
+static void
+list_item(TableIndex index, void *key_ptr, int key_len,
+                                void *info_ptr, void *arg)
+{
+    ClassInfo *info;
+    ClassKey   key;
+    char      *sig;
+    int        i;
+
+    HPROF_ASSERT(key_ptr!=NULL);
+    HPROF_ASSERT(key_len==sizeof(ClassKey));
+    HPROF_ASSERT(info_ptr!=NULL);
+    key = *((ClassKey*)key_ptr);
+    sig = string_get(key.sig_string_index);
+    info = (ClassInfo *)info_ptr;
+    debug_message(
+             "0x%08x: Class %s, SN=%u, status=0x%08x, ref=%p,"
+             " method_count=%d\n",
+             index,
+             (const char *)sig,
+             info->serial_num,
+             info->status,
+             (void*)info->classref,
+             info->method_count);
+    if ( info->method_count > 0 ) {
+        for ( i = 0 ; i < info->method_count ; i++ ) {
+            debug_message(
+                "    Method %d: \"%s\", sig=\"%s\", method=%p\n",
+                i,
+                string_get(info->method[i].name_index),
+                string_get(info->method[i].sig_index),
+                (void*)info->method[i].method_id);
+        }
+    }
+}
+
+static void
+all_status_remove(TableIndex index, void *key_ptr, int key_len,
+                                void *info_ptr, void *arg)
+{
+    ClassInfo   *info;
+    ClassStatus  status;
+
+    HPROF_ASSERT(info_ptr!=NULL);
+    /*LINTED*/
+    status = (ClassStatus)(long)(ptrdiff_t)arg;
+    info = (ClassInfo *)info_ptr;
+    info->status &= (~status);
+}
+
+static void
+unload_walker(TableIndex index, void *key_ptr, int key_len,
+                                void *info_ptr, void *arg)
+{
+    ClassInfo        *info;
+
+    HPROF_ASSERT(info_ptr!=NULL);
+    info = (ClassInfo *)info_ptr;
+    if ( ! ( info->status & CLASS_IN_LOAD_LIST ) ) {
+        if ( ! (info->status & (CLASS_SPECIAL|CLASS_SYSTEM|CLASS_UNLOADED)) ) {
+            io_write_class_unload(info->serial_num, info->object_index);
+            info->status |= CLASS_UNLOADED;
+            delete_classref((JNIEnv*)arg, info, NULL);
+        }
+    }
+}
+
+/* External interfaces */
+
+void
+class_init(void)
+{
+    HPROF_ASSERT(gdata->class_table==NULL);
+    gdata->class_table = table_initialize("Class", 512, 512, 511,
+                                    (int)sizeof(ClassInfo));
+}
+
+ClassIndex
+class_find_or_create(const char *sig, LoaderIndex loader_index)
+{
+    ClassKey key;
+
+    fillin_pkey(sig, loader_index, &key);
+    return find_or_create_entry(&key);
+}
+
+ClassIndex
+class_create(const char *sig, LoaderIndex loader_index)
+{
+    ClassKey key;
+
+    fillin_pkey(sig, loader_index, &key);
+    return create_entry(&key);
+}
+
+void
+class_prime_system_classes(void)
+{
+    /* Prime System classes? Anything before VM_START is System class.
+     *   Or classes loaded before env arg is non-NULL.
+     *   Or any of the classes listed below.
+     */
+    static const char * signatures[] =
+        {
+            "Ljava/lang/Object;",
+            "Ljava/io/Serializable;",
+            "Ljava/lang/String;",
+            "Ljava/lang/Class;",
+            "Ljava/lang/ClassLoader;",
+            "Ljava/lang/System;",
+            "Ljava/lang/Thread;",
+            "Ljava/lang/ThreadGroup;",
+        };
+    int n_signatures;
+    int i;
+    LoaderIndex loader_index;
+
+    n_signatures = (int)sizeof(signatures)/(int)sizeof(signatures[0]);
+    loader_index = loader_find_or_create(NULL, NULL);
+    for ( i = 0 ; i < n_signatures ; i++ ) {
+        ClassInfo  *info;
+        ClassIndex  index;
+        ClassKey    key;
+
+        fillin_pkey(signatures[i], loader_index, &key);
+        index = find_or_create_entry(&key);
+        info = get_info(index);
+        info->status |= CLASS_SYSTEM;
+    }
+}
+
+void
+class_add_status(ClassIndex index, ClassStatus status)
+{
+    ClassInfo *info;
+
+    info = get_info(index);
+    info->status |= status;
+}
+
+ClassStatus
+class_get_status(ClassIndex index)
+{
+    ClassInfo *info;
+
+    info = get_info(index);
+    return info->status;
+}
+
+StringIndex
+class_get_signature(ClassIndex index)
+{
+    ClassKey *pkey;
+
+    pkey = get_pkey(index);
+    return pkey->sig_string_index;
+}
+
+SerialNumber
+class_get_serial_number(ClassIndex index)
+{
+    ClassInfo *info;
+
+    if ( index == 0 ) {
+        return 0;
+    }
+    info = get_info(index);
+    return info->serial_num;
+}
+
+void
+class_all_status_remove(ClassStatus status)
+{
+    table_walk_items(gdata->class_table, &all_status_remove,
+                (void*)(ptrdiff_t)(long)status);
+}
+
+void
+class_do_unloads(JNIEnv *env)
+{
+    table_walk_items(gdata->class_table, &unload_walker, (void*)env);
+}
+
+void
+class_list(void)
+{
+    debug_message(
+        "--------------------- Class Table ------------------------\n");
+    table_walk_items(gdata->class_table, &list_item, NULL);
+    debug_message(
+        "----------------------------------------------------------\n");
+}
+
+void
+class_cleanup(void)
+{
+    table_cleanup(gdata->class_table, &cleanup_item, NULL);
+    gdata->class_table = NULL;
+}
+
+void
+class_delete_global_references(JNIEnv* env)
+{
+    table_walk_items(gdata->class_table, &delete_ref_item, (void*)env);
+}
+
+void
+class_set_methods(ClassIndex index, const char **name, const char **sig,
+                        int count)
+{
+    ClassInfo *info;
+    int        i;
+
+    info               = get_info(index);
+    if ( info->method_count > 0 ) {
+        HPROF_FREE((void*)info->method);
+        info->method_count = 0;
+        info->method       = NULL;
+    }
+    info->method_count = count;
+    if ( count > 0 ) {
+        info->method = (MethodInfo *)HPROF_MALLOC(count*(int)sizeof(MethodInfo));
+        for ( i = 0 ; i < count ; i++ ) {
+            info->method[i].name_index = string_find_or_create(name[i]);
+            info->method[i].sig_index  = string_find_or_create(sig[i]);
+            info->method[i].method_id  = NULL;
+        }
+    }
+}
+
+jclass
+class_new_classref(JNIEnv *env, ClassIndex index, jclass classref)
+{
+    ClassInfo *info;
+
+    HPROF_ASSERT(classref!=NULL);
+    info = get_info(index);
+    if ( ! isSameObject(env, classref, info->classref) ) {
+        delete_classref(env, info, classref);
+    }
+    return info->classref;
+}
+
+jclass
+class_get_class(JNIEnv *env, ClassIndex index)
+{
+    ClassInfo *info;
+    jclass     clazz;
+
+    info        = get_info(index);
+    clazz       = info->classref;
+    if ( env != NULL && clazz == NULL ) {
+        WITH_LOCAL_REFS(env, 1) {
+            jclass   new_clazz;
+            char    *class_name;
+
+            class_name = string_get(info->name);
+            /* This really only makes sense for the bootclass classes,
+             *   since FindClass doesn't provide a way to load a class in
+             *   a specific class loader.
+             */
+            new_clazz = findClass(env, class_name);
+            if ( new_clazz == NULL ) {
+                HPROF_ERROR(JNI_TRUE, "Cannot load class with findClass");
+            }
+            HPROF_ASSERT(new_clazz!=NULL);
+            clazz = class_new_classref(env, index, new_clazz);
+        } END_WITH_LOCAL_REFS;
+        HPROF_ASSERT(clazz!=NULL);
+    }
+    return clazz;
+}
+
+jmethodID
+class_get_methodID(JNIEnv *env, ClassIndex index, MethodIndex mnum)
+{
+    ClassInfo *info;
+    jmethodID  method;
+
+    info = get_info(index);
+    if (mnum >= info->method_count) {
+        jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
+        (*env)->ThrowNew(env, newExcCls, "Illegal mnum");
+
+        return NULL;
+    }
+    method = info->method[mnum].method_id;
+    if ( method == NULL ) {
+        char * name;
+        char * sig;
+        jclass clazz;
+
+        name  = (char *)string_get(info->method[mnum].name_index);
+        if (name==NULL) {
+            jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
+            (*env)->ThrowNew(env, newExcCls, "Name not found");
+
+            return NULL;
+        }
+        sig   = (char *)string_get(info->method[mnum].sig_index);
+        HPROF_ASSERT(sig!=NULL);
+        clazz = class_get_class(env, index);
+        if ( clazz != NULL ) {
+            method = getMethodID(env, clazz, name, sig);
+            HPROF_ASSERT(method!=NULL);
+            info = get_info(index);
+            info->method[mnum].method_id = method;
+        }
+    }
+    return method;
+}
+
+void
+class_set_inst_size(ClassIndex index, jint inst_size)
+{
+    ClassInfo *info;
+
+    info = get_info(index);
+    info->inst_size = inst_size;
+}
+
+jint
+class_get_inst_size(ClassIndex index)
+{
+    ClassInfo *info;
+
+    info = get_info(index);
+    return info->inst_size;
+}
+
+void
+class_set_object_index(ClassIndex index, ObjectIndex object_index)
+{
+    ClassInfo *info;
+
+    info = get_info(index);
+    info->object_index = object_index;
+}
+
+ObjectIndex
+class_get_object_index(ClassIndex index)
+{
+    ClassInfo *info;
+
+    info = get_info(index);
+    return info->object_index;
+}
+
+ClassIndex
+class_get_super(ClassIndex index)
+{
+    ClassInfo *info;
+
+    info = get_info(index);
+    return info->super;
+}
+
+void
+class_set_super(ClassIndex index, ClassIndex super)
+{
+    ClassInfo *info;
+
+    info = get_info(index);
+    info->super = super;
+}
+
+LoaderIndex
+class_get_loader(ClassIndex index)
+{
+    ClassKey *pkey;
+
+    pkey = get_pkey(index);
+    HPROF_ASSERT(pkey->loader_index!=0);
+    return pkey->loader_index;
+}
+
+/* Get ALL class fields (supers too), return 1 on error, 0 if ok */
+jint
+class_get_all_fields(JNIEnv *env, ClassIndex index,
+                jint *pfield_count, FieldInfo **pfield)
+{
+    ClassInfo  *info;
+    FieldInfo  *finfo;
+    jint        count;
+    jint        ret;
+
+    count = 0;
+    finfo = NULL;
+    ret   = 1;       /* Default is to return an error condition */
+
+    info = get_info(index);
+    if ( info != NULL ) {
+        if ( info->field_count >= 0 ) {
+            /* Get cache */
+            count = info->field_count;
+            finfo = info->field;
+            ret   = 0;                 /* Return of cache data, no error */
+        } else {
+            jclass     klass;
+
+            klass = info->classref;
+            if ( klass == NULL || isSameObject(env, klass, NULL) ) {
+                /* This is probably an error because this will cause the field
+                 *    index values to be off, but I'm hesitant to generate a
+                 *    fatal error here, so I will issue something and continue.
+                 *    I should have been holding a global reference to all the
+                 *    jclass, so I'm not sure how this could happen.
+                 *    Issuing a FindClass() here is just asking for trouble
+                 *    because if the class went away, we aren't even sure
+                 *    what ClassLoader to use.
+                 */
+                HPROF_ERROR(JNI_FALSE, "Missing jclass when fields needed");
+            } else {
+                jint status;
+
+                status = getClassStatus(klass);
+                if ( status &
+                    (JVMTI_CLASS_STATUS_PRIMITIVE|JVMTI_CLASS_STATUS_ARRAY) ) {
+                    /* Set cache */
+                    info->field_count = count;
+                    info->field       = finfo;
+                    ret               = 0;      /* Primitive or array ok */
+                } else if ( status & JVMTI_CLASS_STATUS_PREPARED ) {
+                    /* Call JVMTI to get them */
+                    getAllClassFieldInfo(env, klass, &count, &finfo);
+                    /* Set cache */
+                    info->field_count = count;
+                    info->field       = finfo;
+                    ret               = 0;
+                }
+            }
+        }
+    }
+    *pfield_count = count;
+    *pfield       = finfo;
+    return ret;
+}