/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include /* * 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.math.BigInteger */ jclass g_bigint_jc = 0; jmethodID g_bigint_val_jsm = 0; jmethodID g_bigint_div_jm = 0; jmethodID g_bigint_shl_jm = 0; jmethodID g_bigint_or_jm = 0; jmethodID g_bigint_setbit_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.StringBuilder */ 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.math.BigInteger */ { JCLASS, &g_bigint_jc, "java/math/BigInteger" }, { JMETHOD_STATIC, &g_bigint_val_jsm, "valueOf", "(J)Ljava/math/BigInteger;" }, { JMETHOD, &g_bigint_div_jm, "divide", "(Ljava/math/BigInteger;)Ljava/math/BigInteger;" }, { JMETHOD, &g_bigint_shl_jm, "shiftLeft", "(I)Ljava/math/BigInteger;" }, { JMETHOD, &g_bigint_or_jm, "or", "(Ljava/math/BigInteger;)Ljava/math/BigInteger;" }, { JMETHOD, &g_bigint_setbit_jm, "setBit", "(I)Ljava/math/BigInteger;" }, /* 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.StringBuilder */ { JCLASS, &g_buf_jc, "java/lang/StringBuilder" }, { JMETHOD, &g_bufinit_jm, CONSTRUCTOR, "()V" }, { JMETHOD, &g_buf_append_char_jm, "append", "(C)Ljava/lang/StringBuilder;" }, { JMETHOD, &g_buf_append_int_jm, "append", "(I)Ljava/lang/StringBuilder;" }, { JMETHOD, &g_buf_append_long_jm, "append", "(J)Ljava/lang/StringBuilder;" }, { JMETHOD, &g_buf_append_str_jm, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;" }, { JMETHOD, &g_buf_append_obj_jm, "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;" }, { 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 * 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); } jobject dtj_uint64(JNIEnv *jenv, uint64_t u) { int64_t i = (int64_t)u; jobject val64; if (i >= 0) { val64 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc, g_bigint_val_jsm, u); } else { jobject tmp; u ^= ((uint64_t)0x1 << 63); val64 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc, g_bigint_val_jsm, u); tmp = val64; val64 = (*jenv)->CallObjectMethod(jenv, tmp, g_bigint_setbit_jm, 63); (*jenv)->DeleteLocalRef(jenv, tmp); } return (val64); } jobject dtj_int128(JNIEnv *jenv, uint64_t high, uint64_t low) { jobject val128; jobject low64; jobject tmp; val128 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc, g_bigint_val_jsm, high); tmp = val128; val128 = (*jenv)->CallObjectMethod(jenv, tmp, g_bigint_shl_jm, 64); (*jenv)->DeleteLocalRef(jenv, tmp); low64 = dtj_uint64(jenv, low); tmp = val128; val128 = (*jenv)->CallObjectMethod(jenv, tmp, g_bigint_or_jm, low64); (*jenv)->DeleteLocalRef(jenv, tmp); (*jenv)->DeleteLocalRef(jenv, low64); return (val128); } 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 = (uintptr_t)p1->dple_ptr; uint64_t y = (uintptr_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); }