diff options
Diffstat (limited to 'usr/src/lib/libdtrace_jni/common/dtj_util.c')
| -rw-r--r-- | usr/src/lib/libdtrace_jni/common/dtj_util.c | 1506 |
1 files changed, 1506 insertions, 0 deletions
diff --git a/usr/src/lib/libdtrace_jni/common/dtj_util.c b/usr/src/lib/libdtrace_jni/common/dtj_util.c new file mode 100644 index 0000000000..17463e0f0e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtj_util.c @@ -0,0 +1,1506 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stddef.h> +#include <sys/types.h> +#include <pthread.h> +#include <string.h> +#include <dtj_util.h> + +/* + * dtj_util.c separates functionality that is generally useful from + * that which is specific to the Java DTrace API. If moved to a separate + * library, this functionality could be shared by other JNI wrappers. + */ + +boolean_t g_dtj_util_debug = B_FALSE; +static boolean_t g_dtj_load_common = B_FALSE; + +/* NativeException */ +jclass g_nx_jc = 0; +jmethodID g_nxinit_jm = 0; + +/* java.io.Serializable */ +jclass g_serial_jc = 0; + +/* java.lang.Number */ +jclass g_number_jc = 0; +jmethodID g_shortval_jm = 0; +jmethodID g_intval_jm = 0; +jmethodID g_longval_jm = 0; + +/* java.lang.Byte */ +jclass g_byte_jc = 0; +jmethodID g_byteinit_jm = 0; + +/* java.lang.Character */ +jclass g_char_jc = 0; +jmethodID g_charinit_jm = 0; +jmethodID g_charval_jm = 0; + +/* java.lang.Short */ +jclass g_short_jc = 0; +jmethodID g_shortinit_jm = 0; + +/* java.lang.Integer */ +jclass g_int_jc = 0; +jmethodID g_intinit_jm = 0; + +/* java.lang.Long */ +jclass g_long_jc = 0; +jmethodID g_longinit_jm = 0; + +/* java.lang.String */ +jclass g_string_jc = 0; +jmethodID g_strinit_bytes_jm = 0; +jmethodID g_strbytes_jm = 0; +jmethodID g_trim_jm = 0; + +/* java.lang.StringBuffer */ +jclass g_buf_jc = 0; +jmethodID g_bufinit_jm = 0; +jmethodID g_buf_append_char_jm = 0; +jmethodID g_buf_append_int_jm = 0; +jmethodID g_buf_append_long_jm = 0; +jmethodID g_buf_append_str_jm = 0; +jmethodID g_buf_append_obj_jm = 0; +jmethodID g_buflen_jm = 0; +jmethodID g_bufsetlen_jm = 0; + +/* java.lang.Object */ +jclass g_object_jc = 0; +jmethodID g_tostring_jm = 0; +jmethodID g_equals_jm = 0; + +/* java.lang.Enum */ +jclass g_enum_jc = 0; +jmethodID g_enumname_jm = 0; + +/* List */ +jclass g_list_jc = 0; +jmethodID g_listclear_jm = 0; +jmethodID g_listadd_jm = 0; +jmethodID g_listget_jm = 0; +jmethodID g_listsize_jm = 0; + +/* Global list pools */ +static uu_list_pool_t *g_pointer_pool = NULL; +static uu_list_pool_t *g_string_pool = NULL; + +static dtj_status_t dtj_get_jni_classes(JNIEnv *, uu_list_t *, uu_list_pool_t *, + uu_list_pool_t *, uu_list_pool_t *, const dtj_table_entry_t *); +static dtj_status_t dtj_cache_jni_methods(JNIEnv *, dtj_java_class_t *); +static dtj_status_t dtj_cache_jni_fields(JNIEnv *, dtj_java_class_t *); + +/* Constructors */ +static dtj_java_class_t *dtj_java_class_create(JNIEnv *, jclass *, char *, + uu_list_pool_t *, uu_list_pool_t *, uu_list_pool_t *); +static dtj_java_method_t *dtj_java_method_create(JNIEnv *, jmethodID *, char *, + char *, uu_list_pool_t *); +static dtj_java_method_t *dtj_java_static_method_create(JNIEnv *, jmethodID *, + char *, char *, uu_list_pool_t *); +static dtj_java_field_t *dtj_java_field_create(JNIEnv *, jfieldID *, char *, + char *, uu_list_pool_t *); +static dtj_java_field_t *dtj_java_static_field_create(JNIEnv *, jfieldID *, + char *, char *, uu_list_pool_t *); + +/* Destructors */ +static void dtj_java_class_destroy(void *, void *); +static void dtj_java_method_destroy(void *, void *); +static void dtj_java_field_destroy(void *, void *); + +/* Comparison functions, uu_compare_fn_t signature */ +static int dtj_java_class_cmp(const void *, const void *, void *); +static int dtj_java_method_cmp(const void *, const void *, void *); +static int dtj_java_field_cmp(const void *, const void *, void *); + +/* Java Throwable */ +static void dtj_throw(JNIEnv *, jclass, const char *, va_list *); + +/* Support for uu_list_t wrappers */ +static boolean_t dtj_check_pointer_pool(void); +static boolean_t dtj_check_string_pool(void); + +dtj_status_t +dtj_load_common(JNIEnv *jenv) +{ + dtj_status_t status; + + static const dtj_table_entry_t table[] = { + /* NativeException */ + { JCLASS, &g_nx_jc, + "org/opensolaris/os/dtrace/NativeException" }, + { JMETHOD, &g_nxinit_jm, CONSTRUCTOR, + "(Ljava/lang/String;ILjava/lang/Throwable;)V" }, + + /* java.io.Serializable */ + { JCLASS, &g_serial_jc, "java/io/Serializable" }, + + /* java.lang.Number */ + { JCLASS, &g_number_jc, "java/lang/Number" }, + { JMETHOD, &g_shortval_jm, "shortValue", "()S" }, + { JMETHOD, &g_intval_jm, "intValue", "()I" }, + { JMETHOD, &g_longval_jm, "longValue", "()J" }, + + /* java.lang.Byte */ + { JCLASS, &g_byte_jc, "java/lang/Byte" }, + { JMETHOD, &g_byteinit_jm, CONSTRUCTOR, "(B)V" }, + + /* java.lang.Character */ + { JCLASS, &g_char_jc, "java/lang/Character" }, + { JMETHOD, &g_charinit_jm, CONSTRUCTOR, "(C)V" }, + { JMETHOD, &g_charval_jm, "charValue", "()C" }, + + /* java.lang.Short */ + { JCLASS, &g_short_jc, "java/lang/Short" }, + { JMETHOD, &g_shortinit_jm, CONSTRUCTOR, "(S)V" }, + + /* java.lang.Integer */ + { JCLASS, &g_int_jc, "java/lang/Integer" }, + { JMETHOD, &g_intinit_jm, CONSTRUCTOR, "(I)V" }, + + /* java.lang.Long */ + { JCLASS, &g_long_jc, "java/lang/Long" }, + { JMETHOD, &g_longinit_jm, CONSTRUCTOR, "(J)V" }, + + /* java.lang.String */ + { JCLASS, &g_string_jc, "java/lang/String" }, + { JMETHOD, &g_strinit_bytes_jm, CONSTRUCTOR, "([B)V" }, + { JMETHOD, &g_strbytes_jm, "getBytes", "()[B" }, + { JMETHOD, &g_trim_jm, "trim", "()Ljava/lang/String;" }, + + /* java.lang.StringBuffer */ + { JCLASS, &g_buf_jc, "java/lang/StringBuffer" }, + { JMETHOD, &g_bufinit_jm, CONSTRUCTOR, "()V" }, + { JMETHOD, &g_buf_append_char_jm, "append", + "(C)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buf_append_int_jm, "append", + "(I)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buf_append_long_jm, "append", + "(J)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buf_append_str_jm, "append", + "(Ljava/lang/String;)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buf_append_obj_jm, "append", + "(Ljava/lang/Object;)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buflen_jm, "length", "()I" }, + { JMETHOD, &g_bufsetlen_jm, "setLength", "(I)V" }, + + /* java.lang.Object */ + { JCLASS, &g_object_jc, "java/lang/Object" }, + { JMETHOD, &g_tostring_jm, "toString", + "()Ljava/lang/String;" }, + { JMETHOD, &g_equals_jm, "equals", + "(Ljava/lang/Object;)Z" }, + + /* java.lang.Enum */ + { JCLASS, &g_enum_jc, "java/lang/Enum" }, + { JMETHOD, &g_enumname_jm, "name", + "()Ljava/lang/String;" }, + + /* List */ + { JCLASS, &g_list_jc, "java/util/List" }, + { JMETHOD, &g_listclear_jm, "clear", "()V" }, + { JMETHOD, &g_listadd_jm, "add", "(Ljava/lang/Object;)Z" }, + { JMETHOD, &g_listget_jm, "get", "(I)Ljava/lang/Object;" }, + { JMETHOD, &g_listsize_jm, "size", "()I" }, + + { DTJ_TYPE_END } + }; + + status = dtj_cache_jni_classes(jenv, table); + if (status == DTJ_OK) { + g_dtj_load_common = B_TRUE; + } + return (status); +} + +static int +/* ARGSUSED */ +dtj_java_class_cmp(const void * v1, const void * v2, void *arg) +{ + const dtj_java_class_t *c1 = v1; + const dtj_java_class_t *c2 = v2; + return (strcmp(c1->djc_name, c2->djc_name)); +} + +static int +/* ARGSUSED */ +dtj_java_method_cmp(const void *v1, const void *v2, void *arg) +{ + int cmp; + const dtj_java_method_t *m1 = v1; + const dtj_java_method_t *m2 = v2; + cmp = strcmp(m1->djm_name, m2->djm_name); + if (cmp == 0) { + cmp = strcmp(m1->djm_signature, m2->djm_signature); + } + return (cmp); +} + +static int +/* ARGSUSED */ +dtj_java_field_cmp(const void *v1, const void *v2, void *arg) +{ + const dtj_java_field_t *f1 = v1; + const dtj_java_field_t *f2 = v2; + return (strcmp(f1->djf_name, f2->djf_name)); +} + +static dtj_java_class_t * +dtj_java_class_create(JNIEnv *jenv, jclass *jc, char *name, + uu_list_pool_t *classpool, uu_list_pool_t *methodpool, + uu_list_pool_t *fieldpool) +{ + dtj_java_class_t *c = uu_zalloc(sizeof (dtj_java_class_t)); + if (c) { + uu_list_node_init(c, &c->djc_node, classpool); + c->djc_ptr = jc; + c->djc_name = name; + c->djc_methods = uu_list_create(methodpool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + if (!c->djc_methods) { + dtj_throw_out_of_memory(jenv, + "Failed method list creation"); + uu_list_node_fini(c, &c->djc_node, classpool); + free(c); + c = NULL; + } + c->djc_fields = uu_list_create(fieldpool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + if (!c->djc_fields) { + dtj_throw_out_of_memory(jenv, + "Failed field list creation"); + uu_list_destroy(c->djc_methods); + c->djc_methods = NULL; + uu_list_node_fini(c, &c->djc_node, classpool); + free(c); + c = NULL; + } + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate class description"); + } + return (c); +} + +static dtj_java_method_t * +dtj_java_method_create(JNIEnv *jenv, jmethodID *jm, char *name, char *signature, + uu_list_pool_t *methodpool) +{ + dtj_java_method_t *m = uu_zalloc(sizeof (dtj_java_method_t)); + if (m) { + uu_list_node_init(m, &m->djm_node, methodpool); + m->djm_ptr = jm; + m->djm_name = name; + m->djm_signature = signature; + m->djm_static = B_FALSE; + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate method description"); + } + return (m); +} + +static dtj_java_method_t * +dtj_java_static_method_create(JNIEnv *jenv, jmethodID *jm, char *name, + char *signature, uu_list_pool_t *methodpool) +{ + dtj_java_method_t *m = dtj_java_method_create(jenv, jm, name, signature, + methodpool); + if (m) { + m->djm_static = B_TRUE; + } + return (m); +} + +static dtj_java_field_t * +dtj_java_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type, + uu_list_pool_t *fieldpool) +{ + dtj_java_field_t *f = uu_zalloc(sizeof (dtj_java_field_t)); + if (f) { + uu_list_node_init(f, &f->djf_node, fieldpool); + f->djf_ptr = jf; + f->djf_name = name; + f->djf_type = type; + f->djf_static = B_FALSE; + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate field description"); + } + return (f); +} + +static dtj_java_field_t * +dtj_java_static_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type, + uu_list_pool_t *fieldpool) +{ + dtj_java_field_t *f = dtj_java_field_create(jenv, jf, name, type, + fieldpool); + if (f) { + f->djf_static = B_TRUE; + } + return (f); +} + +static void +/* ARGSUSED */ +dtj_java_class_destroy(void *v, void *arg) +{ + if (v) { + dtj_java_class_t *c = v; + c->djc_ptr = NULL; /* do not free user-defined storage */ + c->djc_name = NULL; /* string literal */ + dtj_list_destroy(c->djc_methods, dtj_java_method_destroy, NULL); + dtj_list_destroy(c->djc_fields, dtj_java_field_destroy, NULL); + c->djc_methods = NULL; + c->djc_fields = NULL; + uu_free(v); + } +} + +static void +/* ARGSUSED */ +dtj_java_method_destroy(void *v, void *arg) +{ + if (v) { + dtj_java_method_t *m = v; + m->djm_ptr = NULL; /* do not free user-defined space */ + m->djm_name = NULL; /* string literal */ + m->djm_signature = NULL; /* string literal */ + uu_free(v); + } +} + +static void +/* ARGSUSED */ +dtj_java_field_destroy(void *v, void *arg) +{ + if (v) { + dtj_java_field_t *f = v; + f->djf_ptr = NULL; /* do not free user-defined space */ + f->djf_name = NULL; /* string literal */ + f->djf_type = NULL; /* string literal */ + uu_free(f); + } +} + +dtj_status_t +dtj_cache_jni_classes(JNIEnv *jenv, const dtj_table_entry_t *table) +{ + dtj_java_class_t *class; + uu_list_pool_t *classpool; + uu_list_pool_t *methodpool; + uu_list_pool_t *fieldpool; + uu_list_t *classes; + uu_list_walk_t *itr; + jclass jc; + jclass gjc; + dtj_status_t status; + + classpool = uu_list_pool_create("classpool", + sizeof (dtj_java_class_t), + offsetof(dtj_java_class_t, djc_node), dtj_java_class_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (!classpool) { + dtj_throw_out_of_memory(jenv, "failed class pool creation"); + return (DTJ_ERR); + } + methodpool = uu_list_pool_create("methodpool", + sizeof (dtj_java_method_t), + offsetof(dtj_java_method_t, djm_node), dtj_java_method_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (!methodpool) { + dtj_throw_out_of_memory(jenv, "failed method pool creation"); + return (DTJ_ERR); + } + fieldpool = uu_list_pool_create("fieldpool", + sizeof (dtj_java_field_t), + offsetof(dtj_java_field_t, djf_node), dtj_java_field_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (!fieldpool) { + dtj_throw_out_of_memory(jenv, "failed field pool creation"); + return (DTJ_ERR); + } + + classes = uu_list_create(classpool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + if (!classes) { + dtj_throw_out_of_memory(jenv, "failed class list creation"); + return (DTJ_ERR); + } + + status = dtj_get_jni_classes(jenv, classes, classpool, methodpool, + fieldpool, table); + if (status != DTJ_OK) { + /* java error pending */ + return (status); + } + + itr = uu_list_walk_start(classes, 0); + while ((class = uu_list_walk_next(itr)) != NULL) { + jc = (*jenv)->FindClass(jenv, class->djc_name); + if (!jc) { + /* NoClassDefFoundError pending */ + return (DTJ_ERR); + } + gjc = (*jenv)->NewGlobalRef(jenv, jc); + (*jenv)->DeleteLocalRef(jenv, jc); + if (!gjc) { + dtj_throw_out_of_memory(jenv, + "Failed to create global class reference"); + return (DTJ_ERR); + } + *(class->djc_ptr) = gjc; + status = dtj_cache_jni_methods(jenv, class); + if (status != DTJ_OK) { + /* java error pending */ + return (status); + } + status = dtj_cache_jni_fields(jenv, class); + if (status != DTJ_OK) { + /* java error pending */ + return (status); + } + } + uu_list_walk_end(itr); + dtj_list_destroy(classes, dtj_java_class_destroy, NULL); + uu_list_pool_destroy(classpool); + uu_list_pool_destroy(methodpool); + uu_list_pool_destroy(fieldpool); + return (DTJ_OK); +} + +/* + * Converts JNI table entry desriptions into java_class_t descriptors. + */ +static dtj_status_t +dtj_get_jni_classes(JNIEnv *jenv, uu_list_t *classes, + uu_list_pool_t *classpool, uu_list_pool_t *methodpool, + uu_list_pool_t *fieldpool, const dtj_table_entry_t *table) +{ + int i; + dtj_java_class_t *c = NULL; + dtj_java_method_t *m; + dtj_java_field_t *f; + + for (i = 0; table[i].djte_type != DTJ_TYPE_END; ++i) { + /* + * Class not added until all of its method and field information + * is attached, so we defer adding a class until the next + * element with type JCLASS. + */ + switch (table[i].djte_type) { + case JCLASS: + if (c) { + /* previous class */ + if (!dtj_list_add(classes, c)) { + dtj_throw_out_of_memory(jenv, + "Failed to add class description"); + /* + * In response to an error return value, + * the caller will delete the class + * descriptions list with any + * descriptions created so far. + */ + return (DTJ_ERR); + } + } + c = dtj_java_class_create(jenv, + (jclass *)table[i].djte_addr, table[i].djte_name, + classpool, methodpool, fieldpool); + if (!c) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + break; + case JMETHOD: + if (!c) { + dtj_throw_illegal_state(jenv, + "method description not preceded " + "by class description"); + return (DTJ_ERR); + } + m = dtj_java_method_create(jenv, + (jmethodID *)table[i].djte_addr, + table[i].djte_name, table[i].djte_desc, + methodpool); + if (!m) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + if (!dtj_list_add(c->djc_methods, m)) { + dtj_throw_out_of_memory(jenv, + "Failed to add method description"); + return (DTJ_ERR); + } + break; + case JMETHOD_STATIC: + if (!c) { + dtj_throw_illegal_state(jenv, + "static method description not preceded " + "by class description"); + return (DTJ_ERR); + } + m = dtj_java_static_method_create(jenv, + (jmethodID *)table[i].djte_addr, + table[i].djte_name, table[i].djte_desc, + methodpool); + if (!m) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + if (!dtj_list_add(c->djc_methods, m)) { + dtj_throw_out_of_memory(jenv, + "Failed to add static method description"); + return (DTJ_ERR); + } + break; + case JFIELD: + if (!c) { + dtj_throw_illegal_state(jenv, + "field description not preceded " + "by class description"); + return (DTJ_ERR); + } + f = dtj_java_field_create(jenv, + (jfieldID *)table[i].djte_addr, + table[i].djte_name, table[i].djte_desc, + fieldpool); + if (!f) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + if (!dtj_list_add(c->djc_fields, f)) { + dtj_throw_out_of_memory(jenv, + "Failed to add field description"); + return (DTJ_ERR); + } + break; + case JFIELD_STATIC: + if (!c) { + dtj_throw_illegal_state(jenv, + "static field description not preceded " + "by class description"); + return (DTJ_ERR); + } + f = dtj_java_static_field_create(jenv, + (jfieldID *)table[i].djte_addr, + table[i].djte_name, table[i].djte_desc, + fieldpool); + if (!f) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + if (!dtj_list_add(c->djc_fields, f)) { + dtj_throw_out_of_memory(jenv, + "Failed to add static field description"); + return (DTJ_ERR); + } + break; + default: + dtj_throw_illegal_state(jenv, + "Unexpected jni_type_e: %d", table[i].djte_type); + return (DTJ_ERR); + } + } + if (c) { + /* last class */ + if (!dtj_list_add(classes, c)) { + dtj_throw_out_of_memory(jenv, + "Failed to add class description"); + return (DTJ_ERR); + } + } + + return (DTJ_OK); +} + +static dtj_status_t +dtj_cache_jni_methods(JNIEnv *jenv, dtj_java_class_t *c) +{ + dtj_java_method_t *method; + jmethodID jm; + uu_list_walk_t *itr; + itr = uu_list_walk_start(c->djc_methods, 0); + while ((method = uu_list_walk_next(itr)) != NULL) { + if (method->djm_static) { + jm = (*jenv)->GetStaticMethodID(jenv, *(c->djc_ptr), + method->djm_name, method->djm_signature); + } else { + jm = (*jenv)->GetMethodID(jenv, *(c->djc_ptr), + method->djm_name, method->djm_signature); + } + if (jm == 0) { + /* + * The pending NoSuchMethodError gives only the + * method name, which is not so helpful for + * overloaded methods and methods such as <init> + * that have the same name in multiple classes. + * Clear the pending error and throw one that + * includes the class name and the method + * signature. + */ + jclass jc; + char msg[DTJ_MSG_SIZE]; + (*jenv)->ExceptionClear(jenv); + (void) snprintf(msg, sizeof (msg), "%s %s %s", + c->djc_name, method->djm_name, + method->djm_signature); + + jc = (*jenv)->FindClass(jenv, + "java/lang/NoSuchMethodError"); + (*jenv)->ThrowNew(jenv, jc, msg); + (*jenv)->DeleteLocalRef(jenv, jc); + return (DTJ_ERR); + } + *(method->djm_ptr) = jm; + } + uu_list_walk_end(itr); + return (DTJ_OK); +} + +static dtj_status_t +dtj_cache_jni_fields(JNIEnv *jenv, dtj_java_class_t *c) +{ + dtj_java_field_t *field; + jfieldID jf; + uu_list_walk_t *itr; + itr = uu_list_walk_start(c->djc_fields, 0); + while ((field = uu_list_walk_next(itr)) != NULL) { + if (field->djf_static) { + jf = (*jenv)->GetStaticFieldID(jenv, *(c->djc_ptr), + field->djf_name, field->djf_type); + } else { + jf = (*jenv)->GetFieldID(jenv, *(c->djc_ptr), + field->djf_name, field->djf_type); + } + if (jf == 0) { + jclass jc; + char msg[DTJ_MSG_SIZE]; + (*jenv)->ExceptionClear(jenv); + (void) snprintf(msg, sizeof (msg), + "%s.%s signature: %s", c->djc_name, + field->djf_name, field->djf_type); + + jc = (*jenv)->FindClass(jenv, + "java/lang/NoSuchFieldError"); + (*jenv)->ThrowNew(jenv, jc, msg); + (*jenv)->DeleteLocalRef(jenv, jc); + return (DTJ_ERR); + } + *(field->djf_ptr) = jf; + } + uu_list_walk_end(itr); + return (DTJ_OK); +} + + +/* Common utilities */ + +static void +dtj_throw(JNIEnv *jenv, jclass jc, const char *fmt, va_list *ap) +{ + char msg[DTJ_MSG_SIZE]; + (void) vsnprintf(msg, sizeof (msg), fmt, *ap); + (*jenv)->ThrowNew(jenv, jc, msg); +} + +void +dtj_throw_out_of_memory(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc; + /* + * JNI documentation unclear whether NewGlobalRef() can throw + * OutOfMemoryError, so we'll make this function safe in case + * OutOfMemoryError has already been thrown + */ + if ((*jenv)->ExceptionCheck(jenv)) { + return; + } + jc = (*jenv)->FindClass(jenv, + "java/lang/OutOfMemoryError"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_null_pointer(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/NullPointerException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_illegal_state(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/IllegalStateException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_illegal_argument(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/IllegalArgumentException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_no_such_element(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/util/NoSuchElementException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_class_cast(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/ClassCastException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_assertion(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/AssertionError"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_resource_limit(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "org/opensolaris/os/dtrace/ResourceLimitException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_wrap_exception(JNIEnv *jenv, const char *file, int line) +{ + jthrowable e = NULL; + jthrowable nx = NULL; + jstring jfile = NULL; + + e = (*jenv)->ExceptionOccurred(jenv); + if (!e) { + return; + } + + if (!g_dtj_load_common) { + return; + } + + (*jenv)->ExceptionClear(jenv); + + /* Unsafe to test while exception pending */ + if ((*jenv)->IsInstanceOf(jenv, e, g_nx_jc)) { + /* Already wrapped */ + (*jenv)->Throw(jenv, e); + (*jenv)->DeleteLocalRef(jenv, e); + return; + } + + jfile = dtj_NewStringNative(jenv, file); + if ((*jenv)->ExceptionCheck(jenv)) { + /* + * Only wrap the exception if possible, otherwise just throw the + * original exception. + */ + (*jenv)->ExceptionClear(jenv); + (*jenv)->Throw(jenv, e); + (*jenv)->DeleteLocalRef(jenv, e); + return; + } + + nx = (jthrowable)(*jenv)->NewObject(jenv, g_nx_jc, g_nxinit_jm, + jfile, line, e); + (*jenv)->DeleteLocalRef(jenv, jfile); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->ExceptionClear(jenv); + (*jenv)->Throw(jenv, e); + (*jenv)->DeleteLocalRef(jenv, e); + return; + } + + (*jenv)->DeleteLocalRef(jenv, e); + (*jenv)->Throw(jenv, nx); + (*jenv)->DeleteLocalRef(jenv, nx); +} + +/* + * Calls the given java object's toString() method and prints the value to + * stdout. Useful for debugging. Guaranteed that no exception is pending when + * this function returns. + */ +void +dtj_print_object(JNIEnv *jenv, jobject jobj) +{ + jstring jstr; + const char *cstr; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + (*jenv)->ExceptionDescribe(jenv); /* clears the exception */ + return; + } + + if (!jobj) { + (void) printf("null\n"); + return; + } + + jstr = (*jenv)->CallObjectMethod(jenv, jobj, g_tostring_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->ExceptionDescribe(jenv); /* clears the exception */ + return; + } + cstr = (*jenv)->GetStringUTFChars(jenv, jstr, 0); + if (cstr) { + (void) printf("%s\n", cstr); + } else { + (*jenv)->ExceptionDescribe(jenv); /* clears the exception */ + (*jenv)->DeleteLocalRef(jenv, jstr); + return; + } + (*jenv)->ReleaseStringUTFChars(jenv, jstr, cstr); + (*jenv)->DeleteLocalRef(jenv, jstr); +} + +jstring +dtj_format_string(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + char str[DTJ_MSG_SIZE]; + + jstring jstr = NULL; + + va_start(ap, fmt); + (void) vsnprintf(str, sizeof (str), fmt, ap); + va_end(ap); + + jstr = dtj_NewStringNative(jenv, str); + /* return NULL if OutOfMemoryError pending */ + return (jstr); +} + +jstring +dtj_NewStringNative(JNIEnv *jenv, const char *str) +{ + jstring result; + jbyteArray bytes = 0; + int len; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + return (NULL); + } + + len = strlen(str); + + bytes = (*jenv)->NewByteArray(jenv, len); + if (!bytes) { + return (NULL); /* OutOfMemoryError pending */ + } + (*jenv)->SetByteArrayRegion(jenv, bytes, 0, len, + (jbyte *)str); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, bytes); + return (NULL); /* ArrayIndexOutOfBoundsException pending */ + } + result = (*jenv)->NewObject(jenv, g_string_jc, g_strinit_bytes_jm, + bytes); + (*jenv)->DeleteLocalRef(jenv, bytes); + /* return NULL result if exception pending */ + return (result); +} + +char * +dtj_GetStringNativeChars(JNIEnv *jenv, jstring jstr) +{ + jbyteArray bytes = NULL; + + jint len; + char *result = NULL; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + return (NULL); + } + + bytes = (*jenv)->CallObjectMethod(jenv, jstr, g_strbytes_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + return (NULL); /* OutOfMemoryError pending */ + } + /* Does not throw exceptions */ + len = (*jenv)->GetArrayLength(jenv, bytes); + result = malloc(len + 1); + if (!result) { + (*jenv)->DeleteLocalRef(jenv, bytes); + dtj_throw_out_of_memory(jenv, + "could not allocate native chars"); + return (NULL); + } + + /* Skip check for ArrayIndexOutOfBoundsException */ + (*jenv)->GetByteArrayRegion(jenv, bytes, 0, len, + (jbyte *)result); + (*jenv)->DeleteLocalRef(jenv, bytes); + result[len] = '\0'; /* NUL-terminate */ + + return (result); +} + +void +/* ARGSUSED */ +dtj_ReleaseStringNativeChars(JNIEnv *jenv, jstring jstr, const char *str) +{ + free((void *)str); +} + +char ** +dtj_get_argv(JNIEnv *jenv, jobjectArray args, int *argc) +{ + char **argv = NULL; /* return value */ + const char *str; + int i; + + jstring jstr = NULL; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + return (NULL); + } + + *argc = (*jenv)->GetArrayLength(jenv, args); + /* + * Initialize all string pointers to NULL so that in case of an error + * filling in the array, free_argv() will not attempt to free the + * unallocated elements. Also NULL-terminate the string array for + * functions that expect terminating NULL rather than rely on argc. + */ + argv = uu_zalloc((sizeof (char *)) * (*argc + 1)); + if (!argv) { + dtj_throw_out_of_memory(jenv, "Failed to allocate args array"); + return (NULL); + } + + for (i = 0; i < *argc; ++i) { + jstr = (*jenv)->GetObjectArrayElement(jenv, args, i); + if ((*jenv)->ExceptionCheck(jenv)) { + dtj_free_argv(argv); + return (NULL); + } + str = dtj_GetStringNativeChars(jenv, jstr); + if ((*jenv)->ExceptionCheck(jenv)) { + dtj_free_argv(argv); + (*jenv)->DeleteLocalRef(jenv, jstr); + return (NULL); + } + argv[i] = malloc(strlen(str) + 1); + if (!argv[i]) { + dtj_throw_out_of_memory(jenv, "Failed to allocate arg"); + dtj_free_argv(argv); + dtj_ReleaseStringNativeChars(jenv, jstr, str); + (*jenv)->DeleteLocalRef(jenv, jstr); + return (NULL); + } + (void) strcpy(argv[i], str); + dtj_ReleaseStringNativeChars(jenv, jstr, str); + (*jenv)->DeleteLocalRef(jenv, jstr); + jstr = NULL; + } + + return (argv); +} + +char ** +dtj_make_argv(JNIEnv *jenv, jstring command, int *argc) +{ + const char *ws = "\f\n\r\t\v "; + char **argv = NULL; /* return value */ + const char *cmd; /* native command string */ + char *s; /* writable command */ + char *tok; /* token */ + int len; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + return (NULL); + } + + if (!command) { + dtj_throw_null_pointer(jenv, "command is null"); + return (NULL); + } else if ((*jenv)->GetStringLength(jenv, command) == 0) { + dtj_throw_illegal_argument(jenv, "command is empty"); + return (NULL); + } + + cmd = dtj_GetStringNativeChars(jenv, command); + if ((*jenv)->ExceptionCheck(jenv)) { + return (NULL); + } + len = strlen(cmd); + s = malloc(len + 1); + if (!s) { + dtj_throw_out_of_memory(jenv, + "failed to allocate command string"); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (NULL); + } + (void) strcpy(s, cmd); + /* + * Initialize all string pointers to NULL so that in case of an error + * filling in the array, free_argv() will not attempt to free the + * unallocated elements. Also NULL-terminate the string array for + * functions that expect terminating NULL rather than rely on argc. + * Allow for maximum length resulting from single-character tokens + * separated by single spaces. + */ + argv = uu_zalloc(sizeof (char *) * (len / 2 + 1)); + if (!argv) { + dtj_throw_out_of_memory(jenv, "failed to allocate args array"); + free(s); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (NULL); + } + + *argc = 0; + for (tok = strtok(s, ws); tok != NULL; tok = strtok(NULL, ws)) { + argv[*argc] = malloc(strlen(tok) + 1); + if (!argv[*argc]) { + dtj_throw_out_of_memory(jenv, "Failed to allocate arg"); + dtj_free_argv(argv); + free(s); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (NULL); + } + (void) strcpy(argv[(*argc)++], tok); + } + + if (*argc == 0) { + dtj_throw_illegal_argument(jenv, "command is blank"); + dtj_free_argv(argv); + free(s); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (NULL); + } + + free(s); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (argv); +} + +void +dtj_free_argv(char **argv) +{ + if (argv) { + char **s = argv; + while (*s) { + free((void *)*s); + *s++ = NULL; + } + free((void *)argv); + } +} + + +/* Wrappers for uu_list_t */ + +int +/* ARGSUSED */ +dtj_pointer_list_entry_cmp(const void *v1, const void *v2, void *arg) +{ + const dtj_pointer_list_entry_t *p1 = v1; + const dtj_pointer_list_entry_t *p2 = v2; + + /* + * It is not valid to compare pointers using the relational operators + * unless they point to elements in the same array. + */ + uint64_t x = (uint64_t)p1->dple_ptr; + uint64_t y = (uint64_t)p2->dple_ptr; + int rc; + rc = ((x > y) ? 1 : ((x < y) ? -1 : 0)); + return (rc); +} + +int +/* ARGSUSED */ +dtj_string_list_entry_cmp(const void *v1, const void *v2, void *arg) +{ + const dtj_string_list_entry_t *p1 = v1; + const dtj_string_list_entry_t *p2 = v2; + const char *s1 = p1->dsle_value; + const char *s2 = p2->dsle_value; + if (s1 == NULL) { + return (s2 == NULL ? 0 : -1); + } + if (s2 == NULL) { + return (1); + } + return (strcmp(s1, s2)); +} + +static boolean_t +dtj_check_pointer_pool(void) +{ + if (g_pointer_pool == NULL) { + g_pointer_pool = uu_list_pool_create("g_pointer_pool", + sizeof (dtj_pointer_list_entry_t), + offsetof(dtj_pointer_list_entry_t, dple_node), + dtj_pointer_list_entry_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (g_pointer_pool == NULL) { + return (B_FALSE); + } + } + return (B_TRUE); +} + +uu_list_t * +dtj_pointer_list_create(void) +{ + uu_list_t *list; + + if (!dtj_check_pointer_pool()) { + return (NULL); + } + + list = uu_list_create(g_pointer_pool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + return (list); +} + +dtj_pointer_list_entry_t * +dtj_pointer_list_entry_create(void *p) +{ + dtj_pointer_list_entry_t *e; + + if (!dtj_check_pointer_pool()) { + return (NULL); + } + + e = uu_zalloc(sizeof (dtj_pointer_list_entry_t)); + if (e) { + uu_list_node_init(e, &e->dple_node, g_pointer_pool); + e->dple_ptr = p; + } + return (e); +} + +static boolean_t +dtj_check_string_pool(void) +{ + if (g_string_pool == NULL) { + g_string_pool = uu_list_pool_create("g_string_pool", + sizeof (dtj_string_list_entry_t), + offsetof(dtj_string_list_entry_t, dsle_node), + dtj_string_list_entry_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (g_string_pool == NULL) { + return (B_FALSE); + } + } + return (B_TRUE); +} + +uu_list_t * +dtj_string_list_create(void) +{ + uu_list_t *list; + + if (!dtj_check_string_pool()) { + return (NULL); + } + + list = uu_list_create(g_string_pool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + return (list); +} + +dtj_string_list_entry_t * +dtj_string_list_entry_create(const char *s) +{ + dtj_string_list_entry_t *e; + + if (!dtj_check_string_pool()) { + return (NULL); + } + + e = uu_zalloc(sizeof (dtj_string_list_entry_t)); + if (e) { + uu_list_node_init(e, &e->dsle_node, g_string_pool); + if (s) { + e->dsle_value = malloc(strlen(s) + 1); + if (e->dsle_value) { + (void) strcpy(e->dsle_value, s); + } else { + uu_list_node_fini(e, &e->dsle_node, + g_string_pool); + uu_free(e); + e = NULL; + } + } + } + return (e); +} + +void +dtj_pointer_list_entry_destroy(void *v, + dtj_value_destroy_f *value_destroy, void *arg) +{ + if (v) { + dtj_pointer_list_entry_t *e = v; + if (value_destroy) { + value_destroy(e->dple_ptr, arg); + } + uu_list_node_fini(e, &e->dple_node, g_pointer_pool); + e->dple_ptr = NULL; + uu_free(v); + } +} + +void +/* ARGSUSED */ +dtj_string_list_entry_destroy(void *v, void *arg) +{ + if (v) { + dtj_string_list_entry_t *e = v; + free(e->dsle_value); + uu_list_node_fini(e, &e->dsle_node, g_string_pool); + e->dsle_value = NULL; + uu_free(v); + } +} + +void +dtj_list_clear(uu_list_t *list, dtj_value_destroy_f *value_destroy, + void *arg) +{ + void *cookie; /* needed for uu_list_teardown */ + void *value; + + if (!list) { + return; + } + + cookie = NULL; + if (value_destroy) { + while ((value = uu_list_teardown(list, &cookie)) != NULL) { + value_destroy(value, arg); + } + } else { + while ((value = uu_list_teardown(list, &cookie)) != NULL) { + } + } +} + +void +dtj_list_destroy(uu_list_t *list, + dtj_value_destroy_f *value_destroy, void *arg) +{ + dtj_list_clear(list, value_destroy, arg); + uu_list_destroy(list); +} + +void +dtj_pointer_list_clear(uu_list_t *list, + dtj_value_destroy_f *value_destroy, void *arg) +{ + void *cookie; /* needed for uu_list_teardown */ + dtj_pointer_list_entry_t *e; + + if (!list) { + return; + } + + cookie = NULL; + while ((e = uu_list_teardown(list, &cookie)) != NULL) { + dtj_pointer_list_entry_destroy(e, value_destroy, arg); + } +} + +void +dtj_pointer_list_destroy(uu_list_t *list, + dtj_value_destroy_f *value_destroy, void *arg) +{ + dtj_pointer_list_clear(list, value_destroy, arg); + uu_list_destroy(list); +} + +void +dtj_string_list_clear(uu_list_t *list) +{ + dtj_list_clear(list, dtj_string_list_entry_destroy, NULL); +} + +void +dtj_string_list_destroy(uu_list_t *list) +{ + dtj_list_destroy(list, dtj_string_list_entry_destroy, NULL); +} + +boolean_t +dtj_list_empty(uu_list_t *list) +{ + return (uu_list_numnodes(list) == 0); +} + +boolean_t +dtj_list_add(uu_list_t *list, void *value) +{ + return (uu_list_insert_before(list, NULL, value) == 0); +} + +boolean_t +dtj_pointer_list_add(uu_list_t *list, void *p) +{ + dtj_pointer_list_entry_t *e = dtj_pointer_list_entry_create(p); + if (!e) { + return (B_FALSE); + } + return (dtj_list_add(list, e)); +} + +void * +dtj_pointer_list_walk_next(uu_list_walk_t *itr) +{ + dtj_pointer_list_entry_t *e = uu_list_walk_next(itr); + if (!e) { + return (DTJ_INVALID_PTR); + } + return (e->dple_ptr); +} + +void * +dtj_pointer_list_first(uu_list_t *list) +{ + dtj_pointer_list_entry_t *e = uu_list_first(list); + if (!e) { + /* NULL is a valid value; use -1 for invalid */ + return (DTJ_INVALID_PTR); + } + return (e->dple_ptr); +} + +void * +dtj_pointer_list_last(uu_list_t *list) +{ + dtj_pointer_list_entry_t *e = uu_list_last(list); + if (!e) { + /* NULL is a valid value; use -1 for invalid */ + return (DTJ_INVALID_PTR); + } + return (e->dple_ptr); +} + +boolean_t +dtj_string_list_add(uu_list_t *list, const char *s) +{ + dtj_string_list_entry_t *e = dtj_string_list_entry_create(s); + if (!e) { + return (B_FALSE); + } + return (dtj_list_add(list, e)); +} + +const char * +dtj_string_list_walk_next(uu_list_walk_t *itr) +{ + dtj_string_list_entry_t *e = uu_list_walk_next(itr); + if (!e) { + return (DTJ_INVALID_STR); + } + return (e->dsle_value); +} + +const char * +dtj_string_list_first(uu_list_t *list) +{ + dtj_string_list_entry_t *e = uu_list_first(list); + if (!e) { + /* NULL is a valid string value; use -1 for invalid */ + return (DTJ_INVALID_STR); + } + return (e->dsle_value); +} + +const char * +dtj_string_list_last(uu_list_t *list) +{ + dtj_string_list_entry_t *e = uu_list_last(list); + if (!e) { + /* NULL is a valid string value; use -1 for invalid */ + return (DTJ_INVALID_STR); + } + return (e->dsle_value); +} |
