/* * 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 2007 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 #include #include #include #include #include /* generated by javah */ #include /* * dtrace_jni.c defines all the native methods of the Java DTrace API. Every * native method is declared in a single class, LocalConsumer.java. * * Notes: * * The data generating loop must explicitly release every object reference it * obtains in order to avoid a memory leak. A local JNI object reference is not * automatically released until control returns to java, which never happens as * long as the data generating loop runs. This applies to any JNI function that * obtains an object reference (such as CallObjectMethod() or NewObject()). A * local reference is released by calling DeleteLocalRef(), which is safe to * call with an exception pending. * * It is important to check for an exception after calling java code from native * C, such as after notifying the java consumer of new data. Failure to do this * makes it possible for users of the interface to crash the JVM by throwing an * exception in java code. * * Some JNI functions, like GetIntField() or ReleaseStringUTFChars(), do not * need to be checked for exceptions. * * GetStringUTFChars() returns NULL if and only if an exception was thrown. * * It is important to stop a DTrace consumer and remove it if an exception * occurs. This API guarantees that a consumer is stopped automatically if it * throws an exception. An application running multiple DTrace consumers * simultaneously should be able to continue running the others normally if any * fail. * * Calls to libdtrace are not guaranteed to be MT-safe. Even if they are * currently MT-safe, they are not guaranteed to remain that way. To address * this, a global lock (the LocalConsumer.class reference) is used around calls * to libdtrace. In many cases, the locking is done in java, which should be * indicated in this file by a comment above the function that assumes prior * locking. To access the same global lock from native C code, the JNI function * MonitorEnter() is used. Each MonitorEnter() must have a matching * MonitorExit() or the application will hang (all consumer threads). The * consumer loop and the getAggregate() method require a per-consumer lock * rather than a global lock; in that case the argument to MonitorEnter() and * MonitorExit() is the consumerLock member of the LocalConsumer, not the * LocalConsumer itself. */ /* * Increment the version whenever there is a change in the interface between * Java and native code, whether from Java into native code: * - LocalConsumer.h (generated by javah) * or from native code back into Java: * - dtj_table_load() in dtj_jnitab.c * Changes to dtj_load_common() in dtj_util.c should not normally require a * version update, since dtj_util.c defines classes in the JDK, not classes in * the Java DTrace API. * * This version needs to match the version in LocalConsumer.java */ #define DTRACE_JNI_VERSION 2 #define FIRST_HANDLE 0 /* sequence-generated consumer ID */ #define NO_HANDLE -1 #define INITIAL_CAPACITY 8 /* initial size of consumer array */ #define MAX_CAPACITY_INCREMENT 1024 static boolean_t g_dtj_load = B_FALSE; static int g_handle_seq = NO_HANDLE; /* * key: caller's consumer handle (int) * value: per-consumer data includes dtrace handle (consumer_t *) */ static dtj_consumer_t **g_consumer_table = NULL; static size_t g_consumer_capacity = 0; static size_t g_consumer_count = 0; static size_t g_max_capacity_increment = MAX_CAPACITY_INCREMENT; static size_t g_max_consumers = 0; /* no maximum */ static boolean_t g_init = B_FALSE; static pthread_mutex_t g_table_lock; static pthread_mutexattr_t g_table_lock_attr; pthread_key_t g_dtj_consumer_key; static int dtj_get_handle(JNIEnv *, jobject); static dtj_status_t dtj_get_java_consumer(JNIEnv *, jobject, dtj_java_consumer_t *); static const char *dtj_getexecname(void); static jobject dtj_get_program_info(dtj_java_consumer_t *, dtrace_proginfo_t *); static jobject dtj_add_program(dtj_java_consumer_t *, dtj_program_t *); static void dtj_flag(uint_t *, uint_t, boolean_t *, boolean_t *); static boolean_t dtj_cflag(dtj_java_consumer_t *, const char *, boolean_t *, boolean_t *); static void dtj_list_probes(JNIEnv *, jobject, jobject, jobject, dtrace_probe_f *); static void dtj_list_compiled_probes(JNIEnv *, jobject, jobject, jobject, dtrace_probe_f *); static int dtj_list_probe(dtrace_hdl_t *, const dtrace_probedesc_t *, void *); static int dtj_list_probe_detail(dtrace_hdl_t *, const dtrace_probedesc_t *, void *); static int dtj_list_stmt(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *, void *); static boolean_t dtj_add_consumer(JNIEnv *, dtj_consumer_t *, int *); static dtj_consumer_t *dtj_remove_consumer(JNIEnv *, jobject); static dtj_consumer_t *dtj_remove_consumer_at(int); /* * Gets a sequence-generated consumer ID, or NO_HANDLE if exception pending */ static int dtj_get_handle(JNIEnv *jenv, jobject caller) { int handle; if (!g_dtj_load) { dtj_throw_illegal_state(jenv, "JNI table not loaded"); return (NO_HANDLE); } handle = (*jenv)->CallIntMethod(jenv, caller, g_gethandle_jm); if ((*jenv)->ExceptionCheck(jenv)) { return (NO_HANDLE); } if (handle == NO_HANDLE) { dtj_throw_illegal_state(jenv, "no consumer handle"); } return (handle); } /* * Populates the given java consumer created for use in the current native * method call. If the return value is DTJ_ERR, a java exception is pending. * Throws IllegalStateException if the caller does not have a valid handle. * Throws NoSuchElementException if the caller's handle is not in the global * caller table. */ static dtj_status_t dtj_get_java_consumer(JNIEnv *jenv, jobject caller, dtj_java_consumer_t *jc) { dtj_consumer_t *consumer; int handle = dtj_get_handle(jenv, caller); if (handle == NO_HANDLE) { return (DTJ_ERR); /* java exception pending */ } (void) pthread_mutex_lock(&g_table_lock); if (g_consumer_table) { if ((handle >= 0) && (handle < g_consumer_capacity)) { consumer = g_consumer_table[handle]; } else { consumer = NULL; } } else { consumer = NULL; } (void) pthread_mutex_unlock(&g_table_lock); if (consumer == NULL) { dtj_throw_no_such_element(jenv, "consumer handle %d", handle); return (DTJ_ERR); } /* Initialize java consumer */ bzero(jc, sizeof (dtj_java_consumer_t)); /* Attach per-consumer data */ jc->dtjj_consumer = consumer; /* Attach per-JNI-invocation data */ jc->dtjj_caller = caller; jc->dtjj_jenv = jenv; return (DTJ_OK); } /* * Adds a consumer to the global consumer table. * Returns B_TRUE if successful; a java exception is pending otherwise. * Postcondition: if successful, g_handle_seq is the handle of the consumer just * added. */ static boolean_t dtj_add_consumer(JNIEnv *jenv, dtj_consumer_t *c, int *seq) { int start; if (!g_init) { (void) pthread_key_create(&g_dtj_consumer_key, NULL); (void) pthread_mutexattr_init(&g_table_lock_attr); (void) pthread_mutexattr_settype(&g_table_lock_attr, PTHREAD_MUTEX_RECURSIVE); (void) pthread_mutex_init(&g_table_lock, &g_table_lock_attr); g_init = B_TRUE; } *seq = NO_HANDLE; (void) pthread_mutex_lock(&g_table_lock); if (g_consumer_table == NULL) { g_consumer_table = malloc(INITIAL_CAPACITY * sizeof (dtj_consumer_t *)); if (!g_consumer_table) { g_handle_seq = NO_HANDLE; dtj_throw_out_of_memory(jenv, "could not allocate consumer table"); (void) pthread_mutex_unlock(&g_table_lock); return (B_FALSE); } bzero(g_consumer_table, (INITIAL_CAPACITY * sizeof (dtj_consumer_t *))); g_consumer_capacity = INITIAL_CAPACITY; } else if ((g_max_consumers > 0) && (g_consumer_count >= g_max_consumers)) { dtj_throw_resource_limit(jenv, "Too many consumers"); (void) pthread_mutex_unlock(&g_table_lock); return (B_FALSE); } else if (g_consumer_count >= g_consumer_capacity) { dtj_consumer_t **t; size_t new_capacity; if (g_consumer_capacity <= g_max_capacity_increment) { new_capacity = (g_consumer_capacity * 2); } else { new_capacity = (g_consumer_capacity + g_max_capacity_increment); } if ((g_max_consumers > 0) && (new_capacity > g_max_consumers)) { new_capacity = g_max_consumers; } t = realloc(g_consumer_table, new_capacity * sizeof (dtj_consumer_t *)); if (!t) { dtj_throw_out_of_memory(jenv, "could not reallocate consumer table"); (void) pthread_mutex_unlock(&g_table_lock); return (B_FALSE); } g_consumer_table = t; bzero(g_consumer_table + g_consumer_capacity, ((new_capacity - g_consumer_capacity) * sizeof (dtj_consumer_t *))); g_consumer_capacity = new_capacity; } /* Look for an empty slot in the table */ g_handle_seq = (g_handle_seq == NO_HANDLE ? FIRST_HANDLE : g_handle_seq + 1); if (g_handle_seq >= g_consumer_capacity) { g_handle_seq = FIRST_HANDLE; } start = g_handle_seq; /* guard against infinite loop */ while (g_consumer_table[g_handle_seq] != NULL) { ++g_handle_seq; if (g_handle_seq == start) { dtj_throw_illegal_state(jenv, "consumer table full," " but count %d < capacity %d", g_consumer_count, g_consumer_capacity); (void) pthread_mutex_unlock(&g_table_lock); return (B_FALSE); } else if (g_handle_seq >= g_consumer_capacity) { g_handle_seq = FIRST_HANDLE; } } g_consumer_table[g_handle_seq] = c; *seq = g_handle_seq; ++g_consumer_count; (void) pthread_mutex_unlock(&g_table_lock); return (B_TRUE); } /* * Removes a consumer from the global consumer table. The call may be initiated * from Java code or from native code (because an exception has occurred). * Precondition: no exception pending (any pending exception must be temporarily * cleared) * Returns NULL if the caller is not in the table or if this function throws an * exception; either case leaves the global consumer table unchanged. * Throws IllegalStateException if the caller does not have a valid handle. */ static dtj_consumer_t * dtj_remove_consumer(JNIEnv *jenv, jobject caller) { dtj_consumer_t *consumer; int handle = dtj_get_handle(jenv, caller); if (handle == NO_HANDLE) { return (NULL); /* java exception pending */ } consumer = dtj_remove_consumer_at(handle); return (consumer); } /* * Returns NULL if there is no consumer with the given handle. Does not throw * exceptions. */ static dtj_consumer_t * dtj_remove_consumer_at(int handle) { dtj_consumer_t *consumer; (void) pthread_mutex_lock(&g_table_lock); if (g_consumer_table) { if ((handle >= 0) && (handle < g_consumer_capacity)) { consumer = g_consumer_table[handle]; if (consumer != NULL) { g_consumer_table[handle] = NULL; --g_consumer_count; if (g_consumer_count == 0) { free(g_consumer_table); g_consumer_table = NULL; g_consumer_capacity = 0; g_handle_seq = NO_HANDLE; } } } else { consumer = NULL; } } else { consumer = NULL; } (void) pthread_mutex_unlock(&g_table_lock); return (consumer); } /* * Gets the name of the executable in case it is an application with an embedded * JVM and not "java". Implementation is copied from lib/mpss/common/mpss.c. * The use of static auxv_t makes the MT-level unsafe. The caller is expected * to use the global lock (LocalConsumer.class). */ static const char * dtj_getexecname(void) { const char *execname = NULL; static auxv_t auxb; /* * The first time through, read the initial aux vector that was * passed to the process at exec(2). Only do this once. */ int fd = open("/proc/self/auxv", O_RDONLY); if (fd >= 0) { while (read(fd, &auxb, sizeof (auxv_t)) == sizeof (auxv_t)) { if (auxb.a_type == AT_SUN_EXECNAME) { execname = auxb.a_un.a_ptr; break; } } (void) close(fd); } return (execname); } /* * Add the compiled program to a list of programs the API expects to enable. * Returns the Program instance identifying the listed program, or NULL if the * Program constructor fails (exception pending in that case). */ static jobject dtj_add_program(dtj_java_consumer_t *jc, dtj_program_t *p) { JNIEnv *jenv = jc->dtjj_jenv; jobject jprogram = NULL; switch (p->dtjp_type) { case DTJ_PROGRAM_STRING: jprogram = (*jenv)->NewObject(jenv, g_program_jc, g_proginit_jm); break; case DTJ_PROGRAM_FILE: jprogram = (*jenv)->NewObject(jenv, g_programfile_jc, g_fproginit_jm); break; default: dtj_throw_illegal_argument(jenv, "unexpected program type %d\n", p->dtjp_type); } if ((*jenv)->ExceptionCheck(jenv)) { return (NULL); } /* Does not throw exceptions */ (*jenv)->SetIntField(jenv, jprogram, g_progid_jf, uu_list_numnodes(jc->dtjj_consumer->dtjc_program_list)); if (!dtj_list_add(jc->dtjj_consumer->dtjc_program_list, p)) { (*jenv)->DeleteLocalRef(jenv, jprogram); dtj_throw_out_of_memory(jenv, "could not add program"); return (NULL); } return (jprogram); } /* * Returns a new ProgramInfo, or NULL if the constructor fails (java exception * pending in that case). */ static jobject dtj_get_program_info(dtj_java_consumer_t *jc, dtrace_proginfo_t *pinfo) { JNIEnv *jenv = jc->dtjj_jenv; jobject minProbeAttributes = NULL; jobject minStatementAttributes = NULL; jobject programInfo = NULL; /* return value */ minProbeAttributes = dtj_new_attribute(jc, &pinfo->dpi_descattr); if (!minProbeAttributes) { return (NULL); /* java exception pending */ } minStatementAttributes = dtj_new_attribute(jc, &pinfo->dpi_stmtattr); if (!minStatementAttributes) { (*jenv)->DeleteLocalRef(jenv, minProbeAttributes); return (NULL); /* java exception pending */ } programInfo = (*jenv)->NewObject(jenv, g_proginfo_jc, g_proginfoinit_jm, minProbeAttributes, minStatementAttributes, pinfo->dpi_matches); (*jenv)->DeleteLocalRef(jenv, minProbeAttributes); (*jenv)->DeleteLocalRef(jenv, minStatementAttributes); return (programInfo); } /* * Called by LocalConsumer static initializer. */ JNIEXPORT void JNICALL /* ARGSUSED */ Java_org_opensolaris_os_dtrace_LocalConsumer__1checkVersion(JNIEnv *env, jclass class, jint version) { if (version != DTRACE_JNI_VERSION) { dtj_throw_illegal_state(env, "LocalConsumer version %d incompatible with native " "implementation version %d", version, DTRACE_JNI_VERSION); } } /* * Called by LocalConsumer static initializer. */ JNIEXPORT void JNICALL /* ARGSUSED */ Java_org_opensolaris_os_dtrace_LocalConsumer__1loadJniTable(JNIEnv *env, jclass class) { if (g_dtj_load) { /* * JNI table includes a global reference to the LocalConsumer * class, preventing the class from being unloaded. The * LocalConsumer static initializer should never execute more * than once. */ dtj_throw_illegal_state(env, "JNI table already loaded"); return; } /* If this fails, a Java Error (e.g. NoSuchMethodError) is pending */ if (dtj_load(env) == DTJ_OK) { g_dtj_load = B_TRUE; } } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1open(JNIEnv *env, jobject obj, jobjectArray flags) { dtrace_hdl_t *dtp; dtj_consumer_t *c; const char *f; /* flag name */ int oflags = 0; int i, len; int id; int err; jobject flag = NULL; jstring flagname = NULL; if (!g_dtj_load) { dtj_throw_illegal_state(env, "JNI table not loaded"); return; } c = dtj_consumer_create(env); if (!c) { return; /* java exception pending */ } /* Get open flags */ len = (flags ? (*env)->GetArrayLength(env, flags) : 0); for (i = 0; i < len; ++i) { flag = (*env)->GetObjectArrayElement(env, flags, i); if ((*env)->ExceptionCheck(env)) { dtj_consumer_destroy(c); return; } flagname = (*env)->CallObjectMethod(env, flag, g_enumname_jm); (*env)->DeleteLocalRef(env, flag); if ((*env)->ExceptionCheck(env)) { dtj_consumer_destroy(c); return; } f = (*env)->GetStringUTFChars(env, flagname, NULL); if ((*env)->ExceptionCheck(env)) { (*env)->DeleteLocalRef(env, flagname); dtj_consumer_destroy(c); return; } if (strcmp(f, "ILP32") == 0) { oflags |= DTRACE_O_ILP32; } else if (strcmp(f, "LP64") == 0) { oflags |= DTRACE_O_LP64; } (*env)->ReleaseStringUTFChars(env, flagname, f); (*env)->DeleteLocalRef(env, flagname); } /* Check for mutually exclusive flags */ if ((oflags & DTRACE_O_ILP32) && (oflags & DTRACE_O_LP64)) { dtj_throw_illegal_argument(env, "Cannot set both ILP32 and LP64"); dtj_consumer_destroy(c); return; } /* * Make sure we can add the consumer before calling dtrace_open(). * Repeated calls to open() when the consumer table is maxed out should * avoid calling dtrace_open(). (Normally there is no limit to the size * of the consumer table, but the undocumented JAVA_DTRACE_MAX_CONSUMERS * system property lets you set a limit after which * ResourceLimitException is thrown.) */ if (!dtj_add_consumer(env, c, &id)) { dtj_consumer_destroy(c); return; /* java exception pending */ } (*env)->CallVoidMethod(env, obj, g_sethandle_jm, id); if ((*env)->ExceptionCheck(env)) { (void) dtj_remove_consumer_at(id); dtj_consumer_destroy(c); return; } if ((dtp = dtrace_open(DTRACE_VERSION, oflags, &err)) == NULL) { dtj_java_consumer_t jc; jc.dtjj_jenv = env; dtj_throw_dtrace_exception(&jc, dtrace_errmsg(NULL, err)); (void) dtj_remove_consumer_at(id); dtj_consumer_destroy(c); return; } c->dtjc_dtp = dtp; /* set consumer handle to native DTrace library */ } static void dtj_flag(uint_t *flags, uint_t flag, boolean_t *get, boolean_t *set) { assert((get && !set) || (set && !get)); if (get) { *get = (*flags & flag); } else { if (*set) { *flags |= flag; } else { *flags &= ~flag; } } } /* * Returns B_TRUE if opt is a recognized compile flag, B_FALSE otherwise. */ static boolean_t dtj_cflag(dtj_java_consumer_t *jc, const char *opt, boolean_t *get, boolean_t *set) { boolean_t is_cflag = B_TRUE; uint_t *flags = &jc->dtjj_consumer->dtjc_cflags; /* see lib/libdtrace/common/dt_option.c */ if (strcmp(opt, "argref") == 0) { dtj_flag(flags, DTRACE_C_ARGREF, get, set); } else if (strcmp(opt, "cpp") == 0) { dtj_flag(flags, DTRACE_C_CPP, get, set); } else if (strcmp(opt, "defaultargs") == 0) { dtj_flag(flags, DTRACE_C_DEFARG, get, set); } else if (strcmp(opt, "empty") == 0) { dtj_flag(flags, DTRACE_C_EMPTY, get, set); } else if (strcmp(opt, "errtags") == 0) { dtj_flag(flags, DTRACE_C_ETAGS, get, set); } else if (strcmp(opt, "knodefs") == 0) { dtj_flag(flags, DTRACE_C_KNODEF, get, set); } else if (strcmp(opt, "nolibs") == 0) { dtj_flag(flags, DTRACE_C_NOLIBS, get, set); } else if (strcmp(opt, "pspec") == 0) { dtj_flag(flags, DTRACE_C_PSPEC, get, set); } else if (strcmp(opt, "unodefs") == 0) { dtj_flag(flags, DTRACE_C_UNODEF, get, set); } else if (strcmp(opt, "verbose") == 0) { dtj_flag(flags, DTRACE_C_DIFV, get, set); } else if (strcmp(opt, "zdefs") == 0) { dtj_flag(flags, DTRACE_C_ZDEFS, get, set); } else { is_cflag = B_FALSE; } if (is_cflag && set && (jc->dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT)) { dtj_throw_illegal_state(jc->dtjj_jenv, "cannot set compile time option \"%s\" after calling go()", opt); return (is_cflag); } return (is_cflag); } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT jobject JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1compileString(JNIEnv *env, jobject obj, jstring program, jobjectArray args) { const char *prog; dtj_java_consumer_t jc; dtrace_hdl_t *dtp; dtj_program_t *p; int argc = 0; char **argv = NULL; jstring jprogram = NULL; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return (NULL); /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; prog = (*env)->GetStringUTFChars(env, program, 0); if ((*env)->ExceptionCheck(env)) { return (NULL); } p = dtj_program_create(env, DTJ_PROGRAM_STRING, prog); if (!p) { (*env)->ReleaseStringUTFChars(env, program, prog); return (NULL); /* java exception pending */ } if (args) { argv = dtj_get_argv(env, args, &argc); if ((*env)->ExceptionCheck(env)) { (*env)->ReleaseStringUTFChars(env, program, prog); dtj_program_destroy(p, NULL); return (NULL); } } if ((p->dtjp_program = dtrace_program_strcompile(dtp, prog, DTRACE_PROBESPEC_NAME, jc.dtjj_consumer->dtjc_cflags, argc, argv)) == NULL) { dtj_throw_dtrace_exception(&jc, "invalid probe specifier %s: %s", prog, dtrace_errmsg(dtp, dtrace_errno(dtp))); (*env)->ReleaseStringUTFChars(env, program, prog); dtj_program_destroy(p, NULL); dtj_free_argv(argv); return (NULL); } (*env)->ReleaseStringUTFChars(env, program, prog); dtj_free_argv(argv); jprogram = dtj_add_program(&jc, p); return (jprogram); /* NULL if exception pending */ } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT jobject JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1compileFile(JNIEnv *env, jobject obj, jstring filename, jobjectArray args) { FILE *fp; const char *file; dtj_java_consumer_t jc; dtrace_hdl_t *dtp; dtj_program_t *p; int argc = 0; char **argv = NULL; jstring jprogram = NULL; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return (NULL); /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; file = dtj_GetStringNativeChars(env, filename); if ((fp = fopen(file, "r")) == NULL) { dtj_throw_dtrace_exception(&jc, "failed to open %s", file); dtj_ReleaseStringNativeChars(env, filename, file); return (NULL); } p = dtj_program_create(env, DTJ_PROGRAM_FILE, file); if (!p) { (void) fclose(fp); dtj_ReleaseStringNativeChars(env, filename, file); return (NULL); /* java exception pending */ } if (args) { argv = dtj_get_argv(env, args, &argc); if ((*env)->ExceptionCheck(env)) { (void) fclose(fp); dtj_ReleaseStringNativeChars(env, filename, file); dtj_program_destroy(p, NULL); return (NULL); } } if ((p->dtjp_program = dtrace_program_fcompile(dtp, fp, jc.dtjj_consumer->dtjc_cflags, argc, argv)) == NULL) { dtj_throw_dtrace_exception(&jc, "failed to compile script %s: %s", file, dtrace_errmsg(dtp, dtrace_errno(dtp))); (void) fclose(fp); dtj_ReleaseStringNativeChars(env, filename, file); dtj_program_destroy(p, NULL); dtj_free_argv(argv); return (NULL); } (void) fclose(fp); dtj_ReleaseStringNativeChars(env, filename, file); dtj_free_argv(argv); jprogram = dtj_add_program(&jc, p); return (jprogram); /* NULL if exception pending */ } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1exec(JNIEnv *env, jobject obj, jobject program) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; int progid = -1; uu_list_walk_t *itr; dtj_program_t *p; dtrace_proginfo_t *pinfo = NULL; int i; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; if (program) { progid = (*env)->GetIntField(env, program, g_progid_jf); } if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { dtj_throw_illegal_state(env, "no program compiled"); return; } itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) { /* enable all probes or those of given program only */ if ((progid != -1) && (progid != i)) { continue; } if (p->dtjp_enabled) { dtj_throw_illegal_state(env, "program already enabled"); uu_list_walk_end(itr); return; } pinfo = &p->dtjp_info; if (dtrace_program_exec(dtp, p->dtjp_program, pinfo) == -1) { dtj_throw_dtrace_exception(&jc, "failed to enable %s: %s", p->dtjp_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); uu_list_walk_end(itr); return; } p->dtjp_enabled = B_TRUE; } uu_list_walk_end(itr); if (program) { jobject programInfo = NULL; if (!pinfo) { /* * Consumer.enable() has already checked that the * program was compiled by this consumer. This is an * implementation error, not a user error. */ dtj_throw_illegal_state(env, "program not found"); return; } programInfo = dtj_get_program_info(&jc, pinfo); if (!programInfo) { return; /* java exception pending */ } (*env)->SetObjectField(env, program, g_proginfo_jf, programInfo); (*env)->DeleteLocalRef(env, programInfo); } } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1getProgramInfo(JNIEnv *env, jobject obj, jobject program) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; int progid; uu_list_walk_t *itr; dtj_program_t *p; dtrace_proginfo_t *pinfo = NULL; int i; jobject programInfo = NULL; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; progid = (*env)->GetIntField(env, program, g_progid_jf); if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { dtj_throw_illegal_state(env, "no program compiled"); return; } itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); for (i = 0; ((p = uu_list_walk_next(itr)) != NULL) && !pinfo; ++i) { if (progid != i) { /* get info of given program only */ continue; } pinfo = &p->dtjp_info; dtrace_program_info(dtp, p->dtjp_program, pinfo); } uu_list_walk_end(itr); programInfo = dtj_get_program_info(&jc, pinfo); if (!programInfo) { return; /* java exception pending */ } (*env)->SetObjectField(env, program, g_proginfo_jf, programInfo); (*env)->DeleteLocalRef(env, programInfo); } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1setOption(JNIEnv *env, jobject obj, jstring option, jstring value) { dtj_java_consumer_t jc; const char *opt; const char *val; boolean_t on; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } opt = (*env)->GetStringUTFChars(env, option, 0); if (!opt) { dtj_throw_out_of_memory(env, "could not allocate option string"); return; } if (value) { val = (*env)->GetStringUTFChars(env, value, 0); if (!val) { dtj_throw_out_of_memory(env, "could not allocate option value string"); (*env)->ReleaseStringUTFChars(env, option, opt); return; } } else { /* * dtrace_setopt() sets option to 0 if value is NULL. That's * not the same thing as unsetting a boolean option, since * libdtrace uses -2 to mean unset. We'll leave it to * LocalConsumer.java to disallow null or not. */ val = NULL; } /* * Check for boolean compile-time options not handled by * dtrace_setopt(). */ on = (!val || (strcmp(val, "unset") != 0)); if (dtj_cflag(&jc, opt, NULL, &on)) { (*env)->ReleaseStringUTFChars(env, option, opt); if (value) { (*env)->ReleaseStringUTFChars(env, value, val); } return; } /* * The transition from INIT to GO is protected by synchronization * (a per-consumer lock) in LocalConsumer.java, ensuring that go() and * setOption() execute sequentially. */ if (jc.dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT) { /* * If the consumer is already running, defer setting the option * until we wake up from dtrace_sleep. */ dtj_request_t *request; (void) pthread_mutex_lock( &jc.dtjj_consumer->dtjc_request_list_lock); request = dtj_request_create(env, DTJ_REQUEST_OPTION, opt, val); if (request) { if (!dtj_list_add(jc.dtjj_consumer->dtjc_request_list, request)) { dtj_throw_out_of_memory(env, "Failed to add setOption request"); } } /* else java exception pending */ (void) pthread_mutex_unlock( &jc.dtjj_consumer->dtjc_request_list_lock); } else { dtrace_hdl_t *dtp = jc.dtjj_consumer->dtjc_dtp; if (dtrace_setopt(dtp, opt, val) == -1) { dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp, dtrace_errno(dtp))); } } (*env)->ReleaseStringUTFChars(env, option, opt); if (value) { (*env)->ReleaseStringUTFChars(env, value, val); } } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT jlong JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1getOption(JNIEnv *env, jobject obj, jstring option) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; const char *opt; dtrace_optval_t optval; boolean_t cflag; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return (0); /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; opt = (*env)->GetStringUTFChars(env, option, 0); if (!opt) { dtj_throw_out_of_memory(env, "could not allocate option string"); return (0); } /* * Check for boolean compile-time options not handled by * dtrace_setopt(). */ if (dtj_cflag(&jc, opt, &cflag, NULL)) { (*env)->ReleaseStringUTFChars(env, option, opt); return (cflag ? 1 : DTRACEOPT_UNSET); } if (dtrace_getopt(dtp, opt, &optval) == -1) { dtj_throw_dtrace_exception(&jc, "couldn't get option %s: %s", opt, dtrace_errmsg(dtp, dtrace_errno(dtp))); (*env)->ReleaseStringUTFChars(env, option, opt); return (0); } (*env)->ReleaseStringUTFChars(env, option, opt); return (optval); } /* * Throws IllegalStateException if not all compiled programs are enabled. */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1checkProgramEnabling(JNIEnv *env, jobject obj) { dtj_java_consumer_t jc; dtj_program_t *p; uu_list_walk_t *itr; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { dtj_throw_illegal_state(env, "no program compiled"); return; } itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); while ((p = uu_list_walk_next(itr)) != NULL) { if (!p->dtjp_enabled) { const char *type; switch (p->dtjp_type) { case DTJ_PROGRAM_STRING: type = "description"; break; case DTJ_PROGRAM_FILE: type = "script"; break; default: assert(p->dtjp_type == DTJ_PROGRAM_STRING || p->dtjp_type == DTJ_PROGRAM_FILE); } dtj_throw_illegal_state(env, "Not all compiled probes are enabled. " "Compiled %s %s not enabled.", type, p->dtjp_name); uu_list_walk_end(itr); return; } } uu_list_walk_end(itr); } JNIEXPORT jboolean JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1isEnabled(JNIEnv *env, jobject obj) { dtj_java_consumer_t jc; dtj_program_t *p; uu_list_walk_t *itr; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return (JNI_FALSE); /* java exception pending */ } if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { return (JNI_FALSE); /* no program compiled */ } itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); while ((p = uu_list_walk_next(itr)) != NULL) { if (!p->dtjp_enabled) { uu_list_walk_end(itr); return (JNI_FALSE); } } uu_list_walk_end(itr); return (JNI_TRUE); } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1go(JNIEnv *env, jobject obj) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; if (dtj_set_callback_handlers(&jc) != DTJ_OK) { return; /* java exception pending */ } if (dtrace_go(dtp) != 0) { dtj_throw_dtrace_exception(&jc, "could not enable tracing: %s", dtrace_errmsg(dtp, dtrace_errno(dtp))); return; } jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_GO; } /* * Protected by global lock (LocalConsumer.class). Called when aborting the * consumer loop before it starts. */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1stop(JNIEnv *env, jobject obj) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; if (dtrace_stop(dtp) == -1) { dtj_throw_dtrace_exception(&jc, "couldn't stop tracing: %s", dtrace_errmsg(jc.dtjj_consumer->dtjc_dtp, dtrace_errno(jc.dtjj_consumer->dtjc_dtp))); } else { jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP; } } JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1consume(JNIEnv *env, jobject obj) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_START; if (dtj_java_consumer_init(env, &jc) != DTJ_OK) { return; /* java exception pending */ } /* * Must set the thread-specific java consumer before starting the * dtrace_work() loop because the bufhandler can also be invoked by * getAggregate() from another thread. The bufhandler needs access to * the correct JNI state specific to either the consumer loop or the * getAggregate() call. */ (void) pthread_setspecific(g_dtj_consumer_key, &jc); if (jc.dtjj_consumer->dtjc_process_list != NULL) { struct ps_prochandle *P; uu_list_walk_t *itr; /* Must not call MonitorEnter with a pending exception */ if ((*env)->ExceptionCheck(env)) { dtj_java_consumer_fini(env, &jc); return; /* java exception pending */ } (*env)->MonitorEnter(env, g_caller_jc); if ((*env)->ExceptionCheck(env)) { dtj_java_consumer_fini(env, &jc); return; /* java exception pending */ } itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list, 0); while ((P = dtj_pointer_list_walk_next(itr)) != DTJ_INVALID_PTR) { dtrace_proc_continue(dtp, P); } uu_list_walk_end(itr); (*env)->MonitorExit(env, g_caller_jc); if ((*env)->ExceptionCheck(env)) { dtj_java_consumer_fini(env, &jc); return; /* java exception pending */ } } /* * Blocking call starts consumer loop. */ (void) dtj_consume(&jc); dtj_java_consumer_fini(env, &jc); /* * Stop the consumer after the consumer loop terminates, whether * normally because of the exit() action or LocalConsumer.stop(), or * abnormally because of an exception. The call is ignored if the * consumer is already stopped. It is safe to call dtj_stop() with a * pending exception. */ dtj_stop(&jc); } /* * Interrupts a running consumer. May be called from any thread. */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1interrupt(JNIEnv *env, jobject obj) { dtj_java_consumer_t jc; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } jc.dtjj_consumer->dtjc_interrupt = B_TRUE; } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1close(JNIEnv *env, jobject obj) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; /* * Need to release any created procs here, since the consumer_t * destructor (called by _destroy) will not have access to the dtrace * handle needed to release them (this function closes the dtrace * handle). */ if (jc.dtjj_consumer->dtjc_process_list != NULL) { struct ps_prochandle *P; uu_list_walk_t *itr; itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list, 0); while ((P = dtj_pointer_list_walk_next(itr)) != DTJ_INVALID_PTR) { dtrace_proc_release(dtp, P); } uu_list_walk_end(itr); } dtrace_close(dtp); } /* * Static Consumer.java method */ JNIEXPORT jint JNICALL /* ARGSUSED */ Java_org_opensolaris_os_dtrace_LocalConsumer__1openCount(JNIEnv *env, jclass c) { int open; (void) pthread_mutex_lock(&g_table_lock); if (g_consumer_table == NULL) { open = 0; } else { open = g_consumer_count; } (void) pthread_mutex_unlock(&g_table_lock); return (open); } /* * Static Consumer.java method */ JNIEXPORT jlong JNICALL /* ARGSUSED */ Java_org_opensolaris_os_dtrace_LocalConsumer__1quantizeBucket(JNIEnv *env, jclass c, jint bucket) { return (DTRACE_QUANTIZE_BUCKETVAL(bucket)); } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT jstring JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupKernelFunction( JNIEnv *jenv, jobject caller, jobject address) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; jstring jfunc; /* return value */ GElf_Addr addr; char dummy; char *s; int rc; if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { return (NULL); /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; /* Does not throw exceptions */ if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) { /* intValue() of class Number does not throw exceptions */ addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv, address, g_intval_jm); } else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) { /* longValue() of class Number does not throw exceptions */ addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv, address, g_longval_jm); } else { dtj_throw_class_cast(jenv, "Expected Number address"); return (NULL); } rc = dtrace_addr2str(dtp, addr, &dummy, 1); s = malloc(rc + 1); if (!s) { dtj_throw_out_of_memory(jenv, "Failed to allocate kernel function name"); return (NULL); } (void) dtrace_addr2str(dtp, addr, s, rc + 1); jfunc = (*jenv)->NewStringUTF(jenv, s); free(s); return (jfunc); } /* * Protected by global lock in Consumer.java */ JNIEXPORT jstring JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupUserFunction(JNIEnv *jenv, jobject caller, jint pid, jobject address) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; jstring jfunc; /* return value */ GElf_Addr addr; char dummy; char *s; int rc; if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { return (NULL); /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; /* Does not throw exceptions */ if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) { /* intValue() of class Number does not throw exceptions */ addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv, address, g_intval_jm); } else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) { /* longValue() of class Number does not throw exceptions */ addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv, address, g_longval_jm); } else { dtj_throw_class_cast(jenv, "Expected Number address"); return (NULL); } rc = dtrace_uaddr2str(dtp, pid, addr, &dummy, 1); s = malloc(rc + 1); if (!s) { dtj_throw_out_of_memory(jenv, "Failed to allocate user function name"); return (NULL); } (void) dtrace_uaddr2str(dtp, pid, addr, s, rc + 1); jfunc = (*jenv)->NewStringUTF(jenv, s); free(s); return (jfunc); } JNIEXPORT jobject JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1getAggregate(JNIEnv *env, jobject obj, jobject spec) { dtj_java_consumer_t jc; jobject aggregate = NULL; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return (NULL); /* java exception pending */ } if (dtj_java_consumer_init(env, &jc) != DTJ_OK) { return (NULL); /* java exception pending */ } jc.dtjj_aggregate_spec = spec; /* * Must set the thread-specific java consumer before calling any * function that triggers callbacks to the bufhandler set by * dtrace_handle_buffered(). The bufhandler needs access to the correct * JNI state specific to either the consumer loop or the * getAggregate() call. */ (void) pthread_setspecific(g_dtj_consumer_key, &jc); aggregate = dtj_get_aggregate(&jc); if (!aggregate) { /* java exception pending */ jc.dtjj_consumer->dtjc_interrupt = B_TRUE; } /* * Cleans up only references created by this JNI invocation. Leaves * cached per-consumer state untouched. */ dtj_java_consumer_fini(env, &jc); return (aggregate); } /* * Returns the pid of the created process, or -1 in case of an error (java * exception pending). * Protected by global lock (LocalConsumer.class) */ JNIEXPORT int JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1createProcess(JNIEnv *jenv, jobject caller, jstring command) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; struct ps_prochandle *P; char **argv; int argc; if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { return (-1); /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; if (jc.dtjj_consumer->dtjc_process_list == NULL) { jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create(); if (!jc.dtjj_consumer->dtjc_process_list) { dtj_throw_out_of_memory(jenv, "Could not allocate process list"); return (-1); } } argv = dtj_make_argv(jenv, command, &argc); if ((*jenv)->ExceptionCheck(jenv)) { return (-1); } P = dtrace_proc_create(dtp, argv[0], argv); dtj_free_argv(argv); if (!P) { dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp, dtrace_errno(dtp))); return (-1); } if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) { dtj_throw_out_of_memory(jenv, "Failed to add process to process list"); dtrace_proc_release(dtp, P); return (-1); } return (Pstatus(P)->pr_pid); } /* * Protected by global lock (LocalConsumer.class) */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1grabProcess(JNIEnv *jenv, jobject caller, jint pid) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; struct ps_prochandle *P; if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { return; /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; if (jc.dtjj_consumer->dtjc_process_list == NULL) { jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create(); if (jc.dtjj_consumer->dtjc_process_list == NULL) { dtj_throw_out_of_memory(jenv, "Could not allocate process list"); return; } } P = dtrace_proc_grab(dtp, (pid_t)pid, 0); if (!P) { dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp, dtrace_errno(dtp))); return; } if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) { dtj_throw_out_of_memory(jenv, "Failed to add process to process list"); dtrace_proc_release(dtp, P); return; } } /* * Lists all probes, or lists matching probes (using the matching rules from * Table 4-1 of the DTrace manual). * * In the future it may be desirable to support an array of probe filters rather * than a single filter. It could be that if a probe matched any of the given * filters, it would be included (implied logical OR). * * Protected by global lock (LocalConsumer.class) * param list: an empty list to populate (this function empties the list if it * is not empty already) * param filter: a ProbeDescription instance; the list will include only probes * that match the filter (match all probes if filter is null) */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbes(JNIEnv *env, jobject obj, jobject list, jobject filter) { dtj_list_probes(env, obj, list, filter, dtj_list_probe); } JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbeDetail(JNIEnv *env, jobject obj, jobject list, jobject filter) { dtj_list_probes(env, obj, list, filter, dtj_list_probe_detail); } static void dtj_list_probes(JNIEnv *env, jobject obj, jobject list, jobject filter, dtrace_probe_f *func) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; dtrace_probedesc_t probe; dtrace_probedesc_t *pdp = NULL; const char *probestr; int rc; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; jc.dtjj_probelist = list; /* clear in-out list parameter */ (*env)->CallVoidMethod(env, list, g_listclear_jm); if ((*env)->ExceptionCheck(env)) { return; } if (filter) { jstring jprobestr = NULL; jprobestr = (*env)->CallObjectMethod(env, filter, g_tostring_jm); if ((*env)->ExceptionCheck(env)) { return; } probestr = (*env)->GetStringUTFChars(env, jprobestr, NULL); if (!probestr) { (*env)->DeleteLocalRef(env, jprobestr); return; /* java exception pending */ } bzero(&probe, sizeof (probe)); rc = dtrace_str2desc(dtp, DTRACE_PROBESPEC_NAME, probestr, &probe); (*env)->ReleaseStringUTFChars(env, jprobestr, probestr); (*env)->DeleteLocalRef(env, jprobestr); if (rc == -1) { dtj_throw_dtrace_exception(&jc, "%s is not a valid probe description: %s", probestr, dtrace_errmsg(dtp, dtrace_errno(dtp))); return; } pdp = &probe; } (void) dtrace_probe_iter(dtp, pdp, func, &jc); } /* * Returns 0 to indicate success, or -1 to cause dtrace_probe_iter() to return a * negative value prematurely (indicating no match or failure). */ static int /* ARGSUSED */ dtj_list_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg) { dtj_java_consumer_t *jc = arg; JNIEnv *jenv = jc->dtjj_jenv; jobject jprobedesc = NULL; jprobedesc = dtj_new_probedesc(jc, pdp); if (!jprobedesc) { return (-1); /* java exception pending */ } /* add probe to list */ (*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm, jprobedesc); (*jenv)->DeleteLocalRef(jenv, jprobedesc); if ((*jenv)->ExceptionCheck(jenv)) { return (-1); } return (0); } /*ARGSUSED*/ static int dtj_list_probe_detail(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg) { dtj_java_consumer_t *jc = arg; JNIEnv *jenv = jc->dtjj_jenv; dtrace_probeinfo_t p; jobject jprobedesc = NULL; jobject jprobeinfo = NULL; jobject jprobe = NULL; jprobedesc = dtj_new_probedesc(jc, pdp); if (!jprobedesc) { return (-1); /* java exception pending */ } /* * If dtrace_probe_info() returns a non-zero value, dtrace_errno is set * for us. In that case, ignore the dtrace error and simply omit probe * info. That error is implicitly cleared the next time a call is made * using the same dtrace handle. */ if (dtrace_probe_info(dtp, pdp, &p) == 0) { /* create probe info instance */ jprobeinfo = dtj_new_probeinfo(jc, &p); if (!jprobeinfo) { (*jenv)->DeleteLocalRef(jenv, jprobedesc); return (-1); /* java exception pending */ } } /* create listed probe instance */ jprobe = (*jenv)->NewObject(jenv, g_probe_jc, g_probeinit_jm, jprobedesc, jprobeinfo); (*jenv)->DeleteLocalRef(jenv, jprobedesc); (*jenv)->DeleteLocalRef(jenv, jprobeinfo); if (!jprobe) { return (-1); /* java exception pending */ } /* add probe to list */ (*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm, jprobe); (*jenv)->DeleteLocalRef(jenv, jprobe); if ((*jenv)->ExceptionCheck(jenv)) { return (-1); } return (0); } /*ARGSUSED*/ static int dtj_list_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *stp, void *arg) { dtj_java_consumer_t *jc = arg; dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; if (edp == jc->dtjj_consumer->dtjc_last_probe) { return (0); } if (dtrace_probe_iter(dtp, &edp->dted_probe, jc->dtjj_consumer->dtjc_plistfunc, arg) != 0) { dtj_throw_dtrace_exception(jc, "failed to match %s:%s:%s:%s: %s", edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod, edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); return (1); } jc->dtjj_consumer->dtjc_last_probe = edp; return (0); } /* * Protected by global lock in Consumer.java */ JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbes(JNIEnv *env, jobject obj, jobject list, jobject program) { dtj_list_compiled_probes(env, obj, list, program, dtj_list_probe); } JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbeDetail( JNIEnv *env, jobject obj, jobject list, jobject program) { dtj_list_compiled_probes(env, obj, list, program, dtj_list_probe_detail); } static void dtj_list_compiled_probes(JNIEnv *env, jobject obj, jobject list, jobject program, dtrace_probe_f *func) { dtj_java_consumer_t jc; dtrace_hdl_t *dtp; uu_list_walk_t *itr; dtj_program_t *p; boolean_t found; int progid = -1; int i; if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { return; /* java exception pending */ } dtp = jc.dtjj_consumer->dtjc_dtp; jc.dtjj_probelist = list; (*env)->CallVoidMethod(env, list, g_listclear_jm); if ((*env)->ExceptionCheck(env)) { return; } if (program) { if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { dtj_throw_no_such_element(env, "no compiled program"); return; } progid = (*env)->GetIntField(env, program, g_progid_jf); if (progid == -1) { dtj_throw_illegal_argument(env, "invalid program"); return; } } jc.dtjj_consumer->dtjc_plistfunc = func; found = B_FALSE; itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) { if ((progid != -1) && (progid != i)) { continue; } found = B_TRUE; (void) dtrace_stmt_iter(dtp, p->dtjp_program, (dtrace_stmt_f *)dtj_list_stmt, &jc); } uu_list_walk_end(itr); if (program && !found) { dtj_throw_no_such_element(env, "program not found"); } } /* * Static LocalConsumer.java method * Protected by global lock (LocalConsumer.class) */ JNIEXPORT jstring JNICALL /* ARGSUSED */ Java_org_opensolaris_os_dtrace_LocalConsumer__1getVersion(JNIEnv *env, jclass class) { /* * Handles the case of locale-specific encoding of the user-visible * version string containing non-ASCII characters. */ return (dtj_NewStringNative(env, _dtrace_version)); } /* * Static LocalConsumer.java method * Protected by global lock (LocalConsumer.class) */ JNIEXPORT jstring JNICALL /* ARGSUSED */ Java_org_opensolaris_os_dtrace_LocalConsumer__1getExecutableName(JNIEnv *env, jclass class) { jstring jname = NULL; const char *name = NULL; char *s; int len; name = dtj_getexecname(); len = strlen(name); s = malloc(len + 1); if (!s) { dtj_throw_out_of_memory(env, "Failed to allocate execname"); return (NULL); } (void) strcpy(s, name); name = basename(s); free(s); jname = (*env)->NewStringUTF(env, name); return (jname); } /* * Static LocalConsumer.java method */ JNIEXPORT void JNICALL /* ARGSUSED */ Java_org_opensolaris_os_dtrace_LocalConsumer__1setMaximumConsumers(JNIEnv *env, jclass class, jint max) { g_max_consumers = max; } /* * Static LocalConsumer.java method */ JNIEXPORT void JNICALL /* ARGSUSED */ Java_org_opensolaris_os_dtrace_LocalConsumer__1setDebug(JNIEnv *env, jclass class, jboolean debug) { g_dtj_util_debug = debug; } JNIEXPORT void JNICALL Java_org_opensolaris_os_dtrace_LocalConsumer__1destroy(JNIEnv *env, jobject obj) { dtj_consumer_t *c; c = dtj_remove_consumer(env, obj); if (c == NULL) { return; /* java exception pending */ } dtj_consumer_destroy(c); }