| /* |
| * Copyright 2003-2005 Sun Microsystems, Inc. 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 Sun Microsystems 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. |
| */ |
| |
| /* 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); |
| HPROF_ASSERT(mnum < info->method_count); |
| method = info->method[mnum].method_id; |
| if ( method == NULL ) { |
| char * name; |
| char * sig; |
| jclass clazz; |
| |
| name = (char *)string_get(info->method[mnum].name_index); |
| HPROF_ASSERT(name!=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; |
| } |