diff -r 836adbf7a2cd -r 3317bb8137f4 jdk/src/demo/share/jvmti/hprof/hprof_class.c --- /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; +}