/* * @(#)hprof_class.c 1.37 10/03/23 * * Copyright (c) 2006, 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: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution 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 or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ /* 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; }