169N/A/*
2362N/A * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
0N/A *
0N/A * Redistribution and use in source and binary forms, with or without
0N/A * modification, are permitted provided that the following conditions
0N/A * are met:
0N/A *
0N/A * - Redistributions of source code must retain the above copyright
0N/A * notice, this list of conditions and the following disclaimer.
0N/A *
0N/A * - Redistributions in binary form must reproduce the above copyright
0N/A * notice, this list of conditions and the following disclaimer in the
0N/A * documentation and/or other materials provided with the distribution.
0N/A *
0N/A * - Neither the name of Oracle nor the names of its
0N/A * contributors may be used to endorse or promote products derived
0N/A * from this software without specific prior written permission.
0N/A *
2362N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
2362N/A * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
2362N/A * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0N/A * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0N/A * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0N/A * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0N/A * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0N/A * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0N/A * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0N/A * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0N/A * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0N/A */
0N/A
169N/A/*
0N/A * This source code is provided to illustrate the usage of a given feature
0N/A * or technique and has been deliberately simplified. Additional steps
169N/A * required for a production-quality application, such as security checks,
0N/A * input validation and proper error handling, might not be present in
169N/A * this sample code.
0N/A */
/* 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;
}