11449Stomee /* 21449Stomee * CDDL HEADER START 31449Stomee * 41449Stomee * The contents of this file are subject to the terms of the 51449Stomee * Common Development and Distribution License (the "License"). 61449Stomee * You may not use this file except in compliance with the License. 71449Stomee * 81449Stomee * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91449Stomee * or http://www.opensolaris.org/os/licensing. 101449Stomee * See the License for the specific language governing permissions 111449Stomee * and limitations under the License. 121449Stomee * 131449Stomee * When distributing Covered Code, include this CDDL HEADER in each 141449Stomee * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151449Stomee * If applicable, add the following below this CDDL HEADER, with the 161449Stomee * fields enclosed by brackets "[]" replaced with your own identifying 171449Stomee * information: Portions Copyright [yyyy] [name of copyright owner] 181449Stomee * 191449Stomee * CDDL HEADER END 201449Stomee */ 211449Stomee 221449Stomee /* 23*6136Stomee * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 241449Stomee * Use is subject to license terms. 251449Stomee */ 261449Stomee 271449Stomee #pragma ident "%Z%%M% %I% %E% SMI" 281449Stomee 291449Stomee #include <stdio.h> 301449Stomee #include <errno.h> 311449Stomee #include <string.h> 321449Stomee #include <stdlib.h> 331449Stomee #include <unistd.h> 341449Stomee #include <libgen.h> 351449Stomee #include <assert.h> 361449Stomee #include <strings.h> 371449Stomee #include <libproc.h> 381449Stomee #include <pthread.h> 391449Stomee #include <dtrace_jni.h> 401449Stomee /* generated by javah */ 411449Stomee #include <LocalConsumer.h> 421449Stomee 431449Stomee /* 441449Stomee * dtrace_jni.c defines all the native methods of the Java DTrace API. Every 451449Stomee * native method is declared in a single class, LocalConsumer.java. 461449Stomee * 471449Stomee * Notes: 481449Stomee * 491449Stomee * The data generating loop must explicitly release every object reference it 501449Stomee * obtains in order to avoid a memory leak. A local JNI object reference is not 511449Stomee * automatically released until control returns to java, which never happens as 521449Stomee * long as the data generating loop runs. This applies to any JNI function that 531449Stomee * obtains an object reference (such as CallObjectMethod() or NewObject()). A 541449Stomee * local reference is released by calling DeleteLocalRef(), which is safe to 551449Stomee * call with an exception pending. 561449Stomee * 571449Stomee * It is important to check for an exception after calling java code from native 581449Stomee * C, such as after notifying the java consumer of new data. Failure to do this 591449Stomee * makes it possible for users of the interface to crash the JVM by throwing an 601449Stomee * exception in java code. 611449Stomee * 621449Stomee * Some JNI functions, like GetIntField() or ReleaseStringUTFChars(), do not 631449Stomee * need to be checked for exceptions. 641449Stomee * 651449Stomee * GetStringUTFChars() returns NULL if and only if an exception was thrown. 661449Stomee * 671449Stomee * It is important to stop a DTrace consumer and remove it if an exception 681449Stomee * occurs. This API guarantees that a consumer is stopped automatically if it 691449Stomee * throws an exception. An application running multiple DTrace consumers 701449Stomee * simultaneously should be able to continue running the others normally if any 711449Stomee * fail. 721449Stomee * 731449Stomee * Calls to libdtrace are not guaranteed to be MT-safe. Even if they are 741449Stomee * currently MT-safe, they are not guaranteed to remain that way. To address 751449Stomee * this, a global lock (the LocalConsumer.class reference) is used around calls 761449Stomee * to libdtrace. In many cases, the locking is done in java, which should be 771449Stomee * indicated in this file by a comment above the function that assumes prior 781449Stomee * locking. To access the same global lock from native C code, the JNI function 791449Stomee * MonitorEnter() is used. Each MonitorEnter() must have a matching 801449Stomee * MonitorExit() or the application will hang (all consumer threads). The 811449Stomee * consumer loop and the getAggregate() method require a per-consumer lock 821449Stomee * rather than a global lock; in that case the argument to MonitorEnter() and 831449Stomee * MonitorExit() is the consumerLock member of the LocalConsumer, not the 841449Stomee * LocalConsumer itself. 851449Stomee */ 861449Stomee 873645Stomee /* 883645Stomee * Increment the version whenever there is a change in the interface between 893645Stomee * Java and native code, whether from Java into native code: 903645Stomee * - LocalConsumer.h (generated by javah) 913645Stomee * or from native code back into Java: 923645Stomee * - dtj_table_load() in dtj_jnitab.c 933645Stomee * Changes to dtj_load_common() in dtj_util.c should not normally require a 943645Stomee * version update, since dtj_util.c defines classes in the JDK, not classes in 953645Stomee * the Java DTrace API. 963645Stomee * 973645Stomee * This version needs to match the version in LocalConsumer.java 983645Stomee */ 99*6136Stomee #define DTRACE_JNI_VERSION 3 1001449Stomee 1011449Stomee #define FIRST_HANDLE 0 /* sequence-generated consumer ID */ 1021449Stomee #define NO_HANDLE -1 1031449Stomee #define INITIAL_CAPACITY 8 /* initial size of consumer array */ 1041449Stomee #define MAX_CAPACITY_INCREMENT 1024 1051449Stomee 1061449Stomee static boolean_t g_dtj_load = B_FALSE; 1071449Stomee static int g_handle_seq = NO_HANDLE; 1081449Stomee /* 1091449Stomee * key: caller's consumer handle (int) 1101449Stomee * value: per-consumer data includes dtrace handle (consumer_t *) 1111449Stomee */ 1121449Stomee static dtj_consumer_t **g_consumer_table = NULL; 1131449Stomee static size_t g_consumer_capacity = 0; 1141449Stomee static size_t g_consumer_count = 0; 1151449Stomee static size_t g_max_capacity_increment = MAX_CAPACITY_INCREMENT; 1161449Stomee static size_t g_max_consumers = 0; /* no maximum */ 1171449Stomee static boolean_t g_init = B_FALSE; 1181449Stomee static pthread_mutex_t g_table_lock; 1191449Stomee static pthread_mutexattr_t g_table_lock_attr; 1201449Stomee pthread_key_t g_dtj_consumer_key; 1211449Stomee 1221449Stomee static int dtj_get_handle(JNIEnv *, jobject); 1231449Stomee static dtj_status_t dtj_get_java_consumer(JNIEnv *, jobject, 1241449Stomee dtj_java_consumer_t *); 1251449Stomee static const char *dtj_getexecname(void); 1261449Stomee static jobject dtj_get_program_info(dtj_java_consumer_t *, dtrace_proginfo_t *); 1271449Stomee static jobject dtj_add_program(dtj_java_consumer_t *, dtj_program_t *); 1281449Stomee static void dtj_flag(uint_t *, uint_t, boolean_t *, boolean_t *); 1291449Stomee static boolean_t dtj_cflag(dtj_java_consumer_t *, const char *, boolean_t *, 1301449Stomee boolean_t *); 1311449Stomee static void dtj_list_probes(JNIEnv *, jobject, jobject, jobject, 1321449Stomee dtrace_probe_f *); 1331449Stomee static void dtj_list_compiled_probes(JNIEnv *, jobject, jobject, jobject, 1341449Stomee dtrace_probe_f *); 1351449Stomee static int dtj_list_probe(dtrace_hdl_t *, const dtrace_probedesc_t *, void *); 1361449Stomee static int dtj_list_probe_detail(dtrace_hdl_t *, const dtrace_probedesc_t *, 1371449Stomee void *); 1381449Stomee static int dtj_list_stmt(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *, 1391449Stomee void *); 1401449Stomee static boolean_t dtj_add_consumer(JNIEnv *, dtj_consumer_t *, int *); 1411449Stomee static dtj_consumer_t *dtj_remove_consumer(JNIEnv *, jobject); 1421449Stomee static dtj_consumer_t *dtj_remove_consumer_at(int); 1431449Stomee 1441449Stomee /* 1451449Stomee * Gets a sequence-generated consumer ID, or NO_HANDLE if exception pending 1461449Stomee */ 1471449Stomee static int 1481449Stomee dtj_get_handle(JNIEnv *jenv, jobject caller) 1491449Stomee { 1501449Stomee int handle; 1511449Stomee 1521449Stomee if (!g_dtj_load) { 1531449Stomee dtj_throw_illegal_state(jenv, "JNI table not loaded"); 1541449Stomee return (NO_HANDLE); 1551449Stomee } 1561449Stomee handle = (*jenv)->CallIntMethod(jenv, caller, g_gethandle_jm); 1571449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 1581449Stomee return (NO_HANDLE); 1591449Stomee } 1601449Stomee if (handle == NO_HANDLE) { 1611449Stomee dtj_throw_illegal_state(jenv, "no consumer handle"); 1621449Stomee } 1631449Stomee return (handle); 1641449Stomee } 1651449Stomee 1661449Stomee /* 1671449Stomee * Populates the given java consumer created for use in the current native 1681449Stomee * method call. If the return value is DTJ_ERR, a java exception is pending. 1691449Stomee * Throws IllegalStateException if the caller does not have a valid handle. 1701449Stomee * Throws NoSuchElementException if the caller's handle is not in the global 1711449Stomee * caller table. 1721449Stomee */ 1731449Stomee static dtj_status_t 1741449Stomee dtj_get_java_consumer(JNIEnv *jenv, jobject caller, dtj_java_consumer_t *jc) 1751449Stomee { 1761449Stomee dtj_consumer_t *consumer; 1771449Stomee int handle = dtj_get_handle(jenv, caller); 1781449Stomee if (handle == NO_HANDLE) { 1791449Stomee return (DTJ_ERR); /* java exception pending */ 1801449Stomee } 1811449Stomee (void) pthread_mutex_lock(&g_table_lock); 1821449Stomee if (g_consumer_table) { 1831449Stomee if ((handle >= 0) && (handle < g_consumer_capacity)) { 1841449Stomee consumer = g_consumer_table[handle]; 1851449Stomee } else { 1861449Stomee consumer = NULL; 1871449Stomee } 1881449Stomee } else { 1891449Stomee consumer = NULL; 1901449Stomee } 1911449Stomee (void) pthread_mutex_unlock(&g_table_lock); 1921449Stomee if (consumer == NULL) { 1931449Stomee dtj_throw_no_such_element(jenv, "consumer handle %d", handle); 1941449Stomee return (DTJ_ERR); 1951449Stomee } 1961449Stomee 1971449Stomee /* Initialize java consumer */ 1981449Stomee bzero(jc, sizeof (dtj_java_consumer_t)); 1991449Stomee 2001449Stomee /* Attach per-consumer data */ 2011449Stomee jc->dtjj_consumer = consumer; 2021449Stomee 2031449Stomee /* Attach per-JNI-invocation data */ 2041449Stomee jc->dtjj_caller = caller; 2051449Stomee jc->dtjj_jenv = jenv; 2061449Stomee 2071449Stomee return (DTJ_OK); 2081449Stomee } 2091449Stomee 2101449Stomee /* 2111449Stomee * Adds a consumer to the global consumer table. 2121449Stomee * Returns B_TRUE if successful; a java exception is pending otherwise. 2131449Stomee * Postcondition: if successful, g_handle_seq is the handle of the consumer just 2141449Stomee * added. 2151449Stomee */ 2161449Stomee static boolean_t 2171449Stomee dtj_add_consumer(JNIEnv *jenv, dtj_consumer_t *c, int *seq) 2181449Stomee { 2191449Stomee int start; 2201449Stomee 2211449Stomee if (!g_init) { 2221449Stomee (void) pthread_key_create(&g_dtj_consumer_key, NULL); 2231449Stomee (void) pthread_mutexattr_init(&g_table_lock_attr); 2241449Stomee (void) pthread_mutexattr_settype(&g_table_lock_attr, 2251449Stomee PTHREAD_MUTEX_RECURSIVE); 2261449Stomee (void) pthread_mutex_init(&g_table_lock, 2271449Stomee &g_table_lock_attr); 2281449Stomee g_init = B_TRUE; 2291449Stomee } 2301449Stomee 2311449Stomee *seq = NO_HANDLE; 2321449Stomee (void) pthread_mutex_lock(&g_table_lock); 2331449Stomee if (g_consumer_table == NULL) { 2341449Stomee g_consumer_table = malloc(INITIAL_CAPACITY * 2351449Stomee sizeof (dtj_consumer_t *)); 2361449Stomee if (!g_consumer_table) { 2371449Stomee g_handle_seq = NO_HANDLE; 2381449Stomee dtj_throw_out_of_memory(jenv, 2391449Stomee "could not allocate consumer table"); 2401449Stomee (void) pthread_mutex_unlock(&g_table_lock); 2411449Stomee return (B_FALSE); 2421449Stomee } 2431449Stomee bzero(g_consumer_table, (INITIAL_CAPACITY * 2441449Stomee sizeof (dtj_consumer_t *))); 2451449Stomee g_consumer_capacity = INITIAL_CAPACITY; 2463350Stomee } else if ((g_max_consumers > 0) && (g_consumer_count >= 2473350Stomee g_max_consumers)) { 2483350Stomee dtj_throw_resource_limit(jenv, "Too many consumers"); 2493350Stomee (void) pthread_mutex_unlock(&g_table_lock); 2503350Stomee return (B_FALSE); 2511449Stomee } else if (g_consumer_count >= g_consumer_capacity) { 2521449Stomee dtj_consumer_t **t; 2531449Stomee size_t new_capacity; 2541449Stomee 2551449Stomee if (g_consumer_capacity <= g_max_capacity_increment) { 2561449Stomee new_capacity = (g_consumer_capacity * 2); 2571449Stomee } else { 2581449Stomee new_capacity = (g_consumer_capacity + 2591449Stomee g_max_capacity_increment); 2601449Stomee } 2611449Stomee 2621449Stomee if ((g_max_consumers > 0) && (new_capacity > g_max_consumers)) { 2631449Stomee new_capacity = g_max_consumers; 2641449Stomee } 2651449Stomee 2661449Stomee t = realloc(g_consumer_table, 2671449Stomee new_capacity * sizeof (dtj_consumer_t *)); 2681449Stomee if (!t) { 2691449Stomee dtj_throw_out_of_memory(jenv, 2701449Stomee "could not reallocate consumer table"); 2711449Stomee (void) pthread_mutex_unlock(&g_table_lock); 2721449Stomee return (B_FALSE); 2731449Stomee } 2741449Stomee 2751449Stomee g_consumer_table = t; 2761449Stomee bzero(g_consumer_table + g_consumer_capacity, ((new_capacity - 2771449Stomee g_consumer_capacity) * sizeof (dtj_consumer_t *))); 2781449Stomee g_consumer_capacity = new_capacity; 2791449Stomee } 2801449Stomee 2811449Stomee /* Look for an empty slot in the table */ 2821449Stomee g_handle_seq = (g_handle_seq == NO_HANDLE 2831449Stomee ? FIRST_HANDLE : g_handle_seq + 1); 2841449Stomee if (g_handle_seq >= g_consumer_capacity) { 2851449Stomee g_handle_seq = FIRST_HANDLE; 2861449Stomee } 2871449Stomee start = g_handle_seq; /* guard against infinite loop */ 2881449Stomee while (g_consumer_table[g_handle_seq] != NULL) { 2891449Stomee ++g_handle_seq; 2901449Stomee if (g_handle_seq == start) { 2911449Stomee dtj_throw_illegal_state(jenv, "consumer table full," 2921449Stomee " but count %d < capacity %d", 2931449Stomee g_consumer_count, g_consumer_capacity); 2941449Stomee (void) pthread_mutex_unlock(&g_table_lock); 2951449Stomee return (B_FALSE); 2961449Stomee } else if (g_handle_seq >= g_consumer_capacity) { 2971449Stomee g_handle_seq = FIRST_HANDLE; 2981449Stomee } 2991449Stomee } 3001449Stomee g_consumer_table[g_handle_seq] = c; 3011449Stomee *seq = g_handle_seq; 3021449Stomee ++g_consumer_count; 3031449Stomee (void) pthread_mutex_unlock(&g_table_lock); 3041449Stomee return (B_TRUE); 3051449Stomee } 3061449Stomee 3071449Stomee /* 3081449Stomee * Removes a consumer from the global consumer table. The call may be initiated 3091449Stomee * from Java code or from native code (because an exception has occurred). 3101449Stomee * Precondition: no exception pending (any pending exception must be temporarily 3111449Stomee * cleared) 3121449Stomee * Returns NULL if the caller is not in the table or if this function throws an 3131449Stomee * exception; either case leaves the global consumer table unchanged. 3141449Stomee * Throws IllegalStateException if the caller does not have a valid handle. 3151449Stomee */ 3161449Stomee static dtj_consumer_t * 3171449Stomee dtj_remove_consumer(JNIEnv *jenv, jobject caller) 3181449Stomee { 3191449Stomee dtj_consumer_t *consumer; 3201449Stomee int handle = dtj_get_handle(jenv, caller); 3211449Stomee if (handle == NO_HANDLE) { 3221449Stomee return (NULL); /* java exception pending */ 3231449Stomee } 3241449Stomee consumer = dtj_remove_consumer_at(handle); 3251449Stomee return (consumer); 3261449Stomee } 3271449Stomee 3281449Stomee /* 3291449Stomee * Returns NULL if there is no consumer with the given handle. Does not throw 3301449Stomee * exceptions. 3311449Stomee */ 3321449Stomee static dtj_consumer_t * 3331449Stomee dtj_remove_consumer_at(int handle) 3341449Stomee { 3351449Stomee dtj_consumer_t *consumer; 3361449Stomee (void) pthread_mutex_lock(&g_table_lock); 3371449Stomee if (g_consumer_table) { 3381449Stomee if ((handle >= 0) && (handle < g_consumer_capacity)) { 3391449Stomee consumer = g_consumer_table[handle]; 3401449Stomee if (consumer != NULL) { 3411449Stomee g_consumer_table[handle] = NULL; 3421449Stomee --g_consumer_count; 3431449Stomee if (g_consumer_count == 0) { 3441449Stomee free(g_consumer_table); 3451449Stomee g_consumer_table = NULL; 3461449Stomee g_consumer_capacity = 0; 3471449Stomee g_handle_seq = NO_HANDLE; 3481449Stomee } 3491449Stomee } 3501449Stomee } else { 3511449Stomee consumer = NULL; 3521449Stomee } 3531449Stomee } else { 3541449Stomee consumer = NULL; 3551449Stomee } 3561449Stomee (void) pthread_mutex_unlock(&g_table_lock); 3571449Stomee return (consumer); 3581449Stomee } 3591449Stomee 3601449Stomee /* 3611449Stomee * Gets the name of the executable in case it is an application with an embedded 3621449Stomee * JVM and not "java". Implementation is copied from lib/mpss/common/mpss.c. 3631449Stomee * The use of static auxv_t makes the MT-level unsafe. The caller is expected 3641449Stomee * to use the global lock (LocalConsumer.class). 3651449Stomee */ 3661449Stomee static const char * 3671449Stomee dtj_getexecname(void) 3681449Stomee { 3691449Stomee const char *execname = NULL; 3701449Stomee static auxv_t auxb; 3711449Stomee 3721449Stomee /* 3731449Stomee * The first time through, read the initial aux vector that was 3741449Stomee * passed to the process at exec(2). Only do this once. 3751449Stomee */ 3761449Stomee int fd = open("/proc/self/auxv", O_RDONLY); 3771449Stomee 3781449Stomee if (fd >= 0) { 3791449Stomee while (read(fd, &auxb, sizeof (auxv_t)) == sizeof (auxv_t)) { 3801449Stomee if (auxb.a_type == AT_SUN_EXECNAME) { 3811449Stomee execname = auxb.a_un.a_ptr; 3821449Stomee break; 3831449Stomee } 3841449Stomee } 3851449Stomee (void) close(fd); 3861449Stomee } 3871449Stomee return (execname); 3881449Stomee } 3891449Stomee 3901449Stomee /* 3911449Stomee * Add the compiled program to a list of programs the API expects to enable. 3921449Stomee * Returns the Program instance identifying the listed program, or NULL if the 3931449Stomee * Program constructor fails (exception pending in that case). 3941449Stomee */ 3951449Stomee static jobject 3961449Stomee dtj_add_program(dtj_java_consumer_t *jc, dtj_program_t *p) 3971449Stomee { 3981449Stomee JNIEnv *jenv = jc->dtjj_jenv; 3991449Stomee 4001449Stomee jobject jprogram = NULL; 4011449Stomee 4021449Stomee switch (p->dtjp_type) { 403*6136Stomee case DTJ_PROGRAM_STRING: 4041449Stomee jprogram = (*jenv)->NewObject(jenv, g_program_jc, 4051449Stomee g_proginit_jm); 4061449Stomee break; 407*6136Stomee case DTJ_PROGRAM_FILE: 4081449Stomee jprogram = (*jenv)->NewObject(jenv, g_programfile_jc, 4091449Stomee g_fproginit_jm); 4101449Stomee break; 411*6136Stomee default: 4121449Stomee dtj_throw_illegal_argument(jenv, "unexpected program type %d\n", 4131449Stomee p->dtjp_type); 4141449Stomee } 4151449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 4161449Stomee return (NULL); 4171449Stomee } 4181449Stomee 4191449Stomee /* Does not throw exceptions */ 4201449Stomee (*jenv)->SetIntField(jenv, jprogram, g_progid_jf, 4211449Stomee uu_list_numnodes(jc->dtjj_consumer->dtjc_program_list)); 4221449Stomee 4231449Stomee if (!dtj_list_add(jc->dtjj_consumer->dtjc_program_list, p)) { 4241449Stomee (*jenv)->DeleteLocalRef(jenv, jprogram); 4251449Stomee dtj_throw_out_of_memory(jenv, 4261449Stomee "could not add program"); 4271449Stomee return (NULL); 4281449Stomee } 4291449Stomee 4301449Stomee return (jprogram); 4311449Stomee } 4321449Stomee 4331449Stomee /* 4341449Stomee * Returns a new ProgramInfo, or NULL if the constructor fails (java exception 4351449Stomee * pending in that case). 4361449Stomee */ 4371449Stomee static jobject 4381449Stomee dtj_get_program_info(dtj_java_consumer_t *jc, dtrace_proginfo_t *pinfo) 4391449Stomee { 4401449Stomee JNIEnv *jenv = jc->dtjj_jenv; 4411449Stomee 4421449Stomee jobject minProbeAttributes = NULL; 4431449Stomee jobject minStatementAttributes = NULL; 4441449Stomee jobject programInfo = NULL; /* return value */ 4451449Stomee 4461449Stomee minProbeAttributes = dtj_new_attribute(jc, &pinfo->dpi_descattr); 4471449Stomee if (!minProbeAttributes) { 4481449Stomee return (NULL); /* java exception pending */ 4491449Stomee } 4501449Stomee minStatementAttributes = dtj_new_attribute(jc, &pinfo->dpi_stmtattr); 4511449Stomee if (!minStatementAttributes) { 4521449Stomee (*jenv)->DeleteLocalRef(jenv, minProbeAttributes); 4531449Stomee return (NULL); /* java exception pending */ 4541449Stomee } 4551449Stomee 4561449Stomee programInfo = (*jenv)->NewObject(jenv, g_proginfo_jc, 4571449Stomee g_proginfoinit_jm, minProbeAttributes, minStatementAttributes, 4581449Stomee pinfo->dpi_matches); 4591449Stomee (*jenv)->DeleteLocalRef(jenv, minProbeAttributes); 4601449Stomee (*jenv)->DeleteLocalRef(jenv, minStatementAttributes); 4611449Stomee 4621449Stomee return (programInfo); 4631449Stomee } 4641449Stomee 4651449Stomee /* 4661449Stomee * Called by LocalConsumer static initializer. 4671449Stomee */ 4681449Stomee JNIEXPORT void JNICALL 4691449Stomee /* ARGSUSED */ 4701449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1checkVersion(JNIEnv *env, 4711449Stomee jclass class, jint version) 4721449Stomee { 4731449Stomee if (version != DTRACE_JNI_VERSION) { 4741449Stomee dtj_throw_illegal_state(env, 4751449Stomee "LocalConsumer version %d incompatible with native " 4761449Stomee "implementation version %d", version, DTRACE_JNI_VERSION); 4771449Stomee } 4781449Stomee } 4791449Stomee 4801449Stomee /* 4811449Stomee * Called by LocalConsumer static initializer. 4821449Stomee */ 4831449Stomee JNIEXPORT void JNICALL 4841449Stomee /* ARGSUSED */ 4851449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1loadJniTable(JNIEnv *env, 4861449Stomee jclass class) 4871449Stomee { 4881449Stomee if (g_dtj_load) { 4891449Stomee /* 4901449Stomee * JNI table includes a global reference to the LocalConsumer 4911449Stomee * class, preventing the class from being unloaded. The 4921449Stomee * LocalConsumer static initializer should never execute more 4931449Stomee * than once. 4941449Stomee */ 4951449Stomee dtj_throw_illegal_state(env, "JNI table already loaded"); 4961449Stomee return; 4971449Stomee } 4981449Stomee 4991449Stomee /* If this fails, a Java Error (e.g. NoSuchMethodError) is pending */ 5001449Stomee if (dtj_load(env) == DTJ_OK) { 5011449Stomee g_dtj_load = B_TRUE; 5021449Stomee } 5031449Stomee } 5041449Stomee 5051449Stomee /* 5061449Stomee * Protected by global lock (LocalConsumer.class) 5071449Stomee */ 5081449Stomee JNIEXPORT void JNICALL 5091449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1open(JNIEnv *env, jobject obj, 5101449Stomee jobjectArray flags) 5111449Stomee { 5121449Stomee dtrace_hdl_t *dtp; 5131449Stomee dtj_consumer_t *c; 5141449Stomee const char *f; /* flag name */ 5151449Stomee int oflags = 0; 5161449Stomee int i, len; 5171449Stomee int id; 5181449Stomee int err; 5191449Stomee 5201449Stomee jobject flag = NULL; 5211449Stomee jstring flagname = NULL; 5221449Stomee 5231449Stomee if (!g_dtj_load) { 5241449Stomee dtj_throw_illegal_state(env, "JNI table not loaded"); 5251449Stomee return; 5261449Stomee } 5271449Stomee 5281449Stomee c = dtj_consumer_create(env); 5291449Stomee if (!c) { 5301449Stomee return; /* java exception pending */ 5311449Stomee } 5321449Stomee 5331449Stomee /* Get open flags */ 5341449Stomee len = (flags ? (*env)->GetArrayLength(env, flags) : 0); 5351449Stomee for (i = 0; i < len; ++i) { 5361449Stomee flag = (*env)->GetObjectArrayElement(env, flags, i); 5371449Stomee if ((*env)->ExceptionCheck(env)) { 5381449Stomee dtj_consumer_destroy(c); 5391449Stomee return; 5401449Stomee } 5411449Stomee 5421449Stomee flagname = (*env)->CallObjectMethod(env, flag, g_enumname_jm); 5431449Stomee (*env)->DeleteLocalRef(env, flag); 5441449Stomee if ((*env)->ExceptionCheck(env)) { 5451449Stomee dtj_consumer_destroy(c); 5461449Stomee return; 5471449Stomee } 5481449Stomee f = (*env)->GetStringUTFChars(env, flagname, NULL); 5491449Stomee if ((*env)->ExceptionCheck(env)) { 5501449Stomee (*env)->DeleteLocalRef(env, flagname); 5511449Stomee dtj_consumer_destroy(c); 5521449Stomee return; 5531449Stomee } 5541449Stomee if (strcmp(f, "ILP32") == 0) { 5551449Stomee oflags |= DTRACE_O_ILP32; 5561449Stomee } else if (strcmp(f, "LP64") == 0) { 5571449Stomee oflags |= DTRACE_O_LP64; 5581449Stomee } 5591449Stomee (*env)->ReleaseStringUTFChars(env, flagname, f); 5601449Stomee (*env)->DeleteLocalRef(env, flagname); 5611449Stomee } 5621449Stomee 5631449Stomee /* Check for mutually exclusive flags */ 5641449Stomee if ((oflags & DTRACE_O_ILP32) && (oflags & DTRACE_O_LP64)) { 5651449Stomee dtj_throw_illegal_argument(env, 5661449Stomee "Cannot set both ILP32 and LP64"); 5671449Stomee dtj_consumer_destroy(c); 5681449Stomee return; 5691449Stomee } 5701449Stomee 5711449Stomee /* 5721449Stomee * Make sure we can add the consumer before calling dtrace_open(). 5731449Stomee * Repeated calls to open() when the consumer table is maxed out should 5741449Stomee * avoid calling dtrace_open(). (Normally there is no limit to the size 5751449Stomee * of the consumer table, but the undocumented JAVA_DTRACE_MAX_CONSUMERS 5761449Stomee * system property lets you set a limit after which 5771449Stomee * ResourceLimitException is thrown.) 5781449Stomee */ 5791449Stomee if (!dtj_add_consumer(env, c, &id)) { 5801449Stomee dtj_consumer_destroy(c); 5811449Stomee return; /* java exception pending */ 5821449Stomee } 5831449Stomee 5841449Stomee (*env)->CallVoidMethod(env, obj, g_sethandle_jm, id); 5851449Stomee if ((*env)->ExceptionCheck(env)) { 5861449Stomee (void) dtj_remove_consumer_at(id); 5871449Stomee dtj_consumer_destroy(c); 5881449Stomee return; 5891449Stomee } 5901449Stomee 5911449Stomee if ((dtp = dtrace_open(DTRACE_VERSION, oflags, &err)) == NULL) { 5921449Stomee dtj_java_consumer_t jc; 5931449Stomee jc.dtjj_jenv = env; 5941449Stomee dtj_throw_dtrace_exception(&jc, dtrace_errmsg(NULL, err)); 5951449Stomee (void) dtj_remove_consumer_at(id); 5961449Stomee dtj_consumer_destroy(c); 5971449Stomee return; 5981449Stomee } 5991449Stomee c->dtjc_dtp = dtp; /* set consumer handle to native DTrace library */ 6001449Stomee } 6011449Stomee 6021449Stomee static void 6031449Stomee dtj_flag(uint_t *flags, uint_t flag, boolean_t *get, boolean_t *set) 6041449Stomee { 6051449Stomee assert((get && !set) || (set && !get)); 6061449Stomee 6071449Stomee if (get) { 6081449Stomee *get = (*flags & flag); 6091449Stomee } else { 6101449Stomee if (*set) { 6111449Stomee *flags |= flag; 6121449Stomee } else { 6131449Stomee *flags &= ~flag; 6141449Stomee } 6151449Stomee } 6161449Stomee } 6171449Stomee 6181449Stomee /* 6191449Stomee * Returns B_TRUE if opt is a recognized compile flag, B_FALSE otherwise. 6201449Stomee */ 6211449Stomee static boolean_t 6221449Stomee dtj_cflag(dtj_java_consumer_t *jc, const char *opt, boolean_t *get, 6231449Stomee boolean_t *set) 6241449Stomee { 6251449Stomee boolean_t is_cflag = B_TRUE; 6261449Stomee uint_t *flags = &jc->dtjj_consumer->dtjc_cflags; 6271449Stomee 628*6136Stomee /* see lib/libdtrace/common/dt_options.c */ 6291449Stomee if (strcmp(opt, "argref") == 0) { 6301449Stomee dtj_flag(flags, DTRACE_C_ARGREF, get, set); 6311449Stomee } else if (strcmp(opt, "cpp") == 0) { 6321449Stomee dtj_flag(flags, DTRACE_C_CPP, get, set); 6331449Stomee } else if (strcmp(opt, "defaultargs") == 0) { 6341449Stomee dtj_flag(flags, DTRACE_C_DEFARG, get, set); 6351449Stomee } else if (strcmp(opt, "empty") == 0) { 6361449Stomee dtj_flag(flags, DTRACE_C_EMPTY, get, set); 6371449Stomee } else if (strcmp(opt, "errtags") == 0) { 6381449Stomee dtj_flag(flags, DTRACE_C_ETAGS, get, set); 6391449Stomee } else if (strcmp(opt, "knodefs") == 0) { 6401449Stomee dtj_flag(flags, DTRACE_C_KNODEF, get, set); 6411449Stomee } else if (strcmp(opt, "nolibs") == 0) { 6421449Stomee dtj_flag(flags, DTRACE_C_NOLIBS, get, set); 6431449Stomee } else if (strcmp(opt, "pspec") == 0) { 6441449Stomee dtj_flag(flags, DTRACE_C_PSPEC, get, set); 6451449Stomee } else if (strcmp(opt, "unodefs") == 0) { 6461449Stomee dtj_flag(flags, DTRACE_C_UNODEF, get, set); 6471449Stomee } else if (strcmp(opt, "verbose") == 0) { 6481449Stomee dtj_flag(flags, DTRACE_C_DIFV, get, set); 6491449Stomee } else if (strcmp(opt, "zdefs") == 0) { 6501449Stomee dtj_flag(flags, DTRACE_C_ZDEFS, get, set); 6511449Stomee } else { 6521449Stomee is_cflag = B_FALSE; 6531449Stomee } 6541449Stomee 6551449Stomee if (is_cflag && set && 6561449Stomee (jc->dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT)) { 6571449Stomee dtj_throw_illegal_state(jc->dtjj_jenv, 6581449Stomee "cannot set compile time option \"%s\" after calling go()", 6591449Stomee opt); 6601449Stomee return (is_cflag); 6611449Stomee } 6621449Stomee 6631449Stomee return (is_cflag); 6641449Stomee } 6651449Stomee 6661449Stomee /* 6671449Stomee * Protected by global lock (LocalConsumer.class) 6681449Stomee */ 6691449Stomee JNIEXPORT jobject JNICALL 6701449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1compileString(JNIEnv *env, 6711449Stomee jobject obj, jstring program, jobjectArray args) 6721449Stomee { 6731449Stomee const char *prog; 6741449Stomee dtj_java_consumer_t jc; 6751449Stomee dtrace_hdl_t *dtp; 6761449Stomee dtj_program_t *p; 6771449Stomee int argc = 0; 6781449Stomee char **argv = NULL; 6791449Stomee 6801449Stomee jstring jprogram = NULL; 6811449Stomee 6821449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 6831449Stomee return (NULL); /* java exception pending */ 6841449Stomee } 6851449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 6861449Stomee 6871449Stomee prog = (*env)->GetStringUTFChars(env, program, 0); 6881449Stomee if ((*env)->ExceptionCheck(env)) { 6891449Stomee return (NULL); 6901449Stomee } 6911449Stomee 6921449Stomee p = dtj_program_create(env, DTJ_PROGRAM_STRING, prog); 6931449Stomee if (!p) { 6941449Stomee (*env)->ReleaseStringUTFChars(env, program, prog); 6951449Stomee return (NULL); /* java exception pending */ 6961449Stomee } 6971449Stomee 6981449Stomee if (args) { 6991449Stomee argv = dtj_get_argv(env, args, &argc); 7001449Stomee if ((*env)->ExceptionCheck(env)) { 7011449Stomee (*env)->ReleaseStringUTFChars(env, program, prog); 7021449Stomee dtj_program_destroy(p, NULL); 7031449Stomee return (NULL); 7041449Stomee } 7051449Stomee } 7061449Stomee 7071449Stomee if ((p->dtjp_program = dtrace_program_strcompile(dtp, 7081449Stomee prog, DTRACE_PROBESPEC_NAME, jc.dtjj_consumer->dtjc_cflags, 7091449Stomee argc, argv)) == NULL) { 7101449Stomee dtj_throw_dtrace_exception(&jc, 7111449Stomee "invalid probe specifier %s: %s", 7121449Stomee prog, dtrace_errmsg(dtp, dtrace_errno(dtp))); 7131449Stomee (*env)->ReleaseStringUTFChars(env, program, prog); 7141449Stomee dtj_program_destroy(p, NULL); 7151449Stomee dtj_free_argv(argv); 7161449Stomee return (NULL); 7171449Stomee } 7181449Stomee (*env)->ReleaseStringUTFChars(env, program, prog); 7191449Stomee dtj_free_argv(argv); 7201449Stomee 7211449Stomee jprogram = dtj_add_program(&jc, p); 7221449Stomee return (jprogram); /* NULL if exception pending */ 7231449Stomee } 7241449Stomee 7251449Stomee /* 7261449Stomee * Protected by global lock (LocalConsumer.class) 7271449Stomee */ 7281449Stomee JNIEXPORT jobject JNICALL 7291449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1compileFile(JNIEnv *env, 7301449Stomee jobject obj, jstring filename, jobjectArray args) 7311449Stomee { 7321449Stomee FILE *fp; 7331449Stomee const char *file; 7341449Stomee dtj_java_consumer_t jc; 7351449Stomee dtrace_hdl_t *dtp; 7361449Stomee dtj_program_t *p; 7371449Stomee int argc = 0; 7381449Stomee char **argv = NULL; 7391449Stomee 7401449Stomee jstring jprogram = NULL; 7411449Stomee 7421449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 7431449Stomee return (NULL); /* java exception pending */ 7441449Stomee } 7451449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 7461449Stomee 7471449Stomee file = dtj_GetStringNativeChars(env, filename); 7481449Stomee if ((fp = fopen(file, "r")) == NULL) { 7491449Stomee dtj_throw_dtrace_exception(&jc, "failed to open %s", file); 7501449Stomee dtj_ReleaseStringNativeChars(env, filename, file); 7511449Stomee return (NULL); 7521449Stomee } 7531449Stomee 7541449Stomee p = dtj_program_create(env, DTJ_PROGRAM_FILE, file); 7551449Stomee if (!p) { 7561449Stomee (void) fclose(fp); 7571449Stomee dtj_ReleaseStringNativeChars(env, filename, file); 7581449Stomee return (NULL); /* java exception pending */ 7591449Stomee } 7601449Stomee 7611449Stomee if (args) { 7621449Stomee argv = dtj_get_argv(env, args, &argc); 7631449Stomee if ((*env)->ExceptionCheck(env)) { 7641449Stomee (void) fclose(fp); 7651449Stomee dtj_ReleaseStringNativeChars(env, filename, file); 7661449Stomee dtj_program_destroy(p, NULL); 7671449Stomee return (NULL); 7681449Stomee } 7691449Stomee } 7701449Stomee 7711449Stomee if ((p->dtjp_program = dtrace_program_fcompile(dtp, 7721449Stomee fp, jc.dtjj_consumer->dtjc_cflags, argc, argv)) == NULL) { 7731449Stomee dtj_throw_dtrace_exception(&jc, 7741449Stomee "failed to compile script %s: %s", file, 7751449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 7761449Stomee (void) fclose(fp); 7771449Stomee dtj_ReleaseStringNativeChars(env, filename, file); 7781449Stomee dtj_program_destroy(p, NULL); 7791449Stomee dtj_free_argv(argv); 7801449Stomee return (NULL); 7811449Stomee } 7821449Stomee (void) fclose(fp); 7831449Stomee dtj_ReleaseStringNativeChars(env, filename, file); 7841449Stomee dtj_free_argv(argv); 7851449Stomee 7861449Stomee jprogram = dtj_add_program(&jc, p); 7871449Stomee return (jprogram); /* NULL if exception pending */ 7881449Stomee } 7891449Stomee 7901449Stomee /* 7911449Stomee * Protected by global lock (LocalConsumer.class) 7921449Stomee */ 7931449Stomee JNIEXPORT void JNICALL 7941449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1exec(JNIEnv *env, jobject obj, 7951449Stomee jobject program) 7961449Stomee { 7971449Stomee dtj_java_consumer_t jc; 7981449Stomee dtrace_hdl_t *dtp; 7991449Stomee int progid = -1; 8001449Stomee uu_list_walk_t *itr; 8011449Stomee dtj_program_t *p; 8021449Stomee dtrace_proginfo_t *pinfo = NULL; 8031449Stomee int i; 8041449Stomee 8051449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 8061449Stomee return; /* java exception pending */ 8071449Stomee } 8081449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 8091449Stomee 8101449Stomee if (program) { 8111449Stomee progid = (*env)->GetIntField(env, program, g_progid_jf); 8121449Stomee } 8131449Stomee 8141449Stomee if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { 8151449Stomee dtj_throw_illegal_state(env, "no program compiled"); 8161449Stomee return; 8171449Stomee } 8181449Stomee 8191449Stomee itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); 8201449Stomee for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) { 8211449Stomee /* enable all probes or those of given program only */ 8221449Stomee if ((progid != -1) && (progid != i)) { 8231449Stomee continue; 8241449Stomee } 8251449Stomee 8261449Stomee if (p->dtjp_enabled) { 8271449Stomee dtj_throw_illegal_state(env, "program already enabled"); 8281449Stomee uu_list_walk_end(itr); 8291449Stomee return; 8301449Stomee } 8311449Stomee 8321449Stomee pinfo = &p->dtjp_info; 8331449Stomee if (dtrace_program_exec(dtp, p->dtjp_program, pinfo) == -1) { 8341449Stomee dtj_throw_dtrace_exception(&jc, 8351449Stomee "failed to enable %s: %s", p->dtjp_name, 8361449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 8371449Stomee uu_list_walk_end(itr); 8381449Stomee return; 8391449Stomee } 8401449Stomee p->dtjp_enabled = B_TRUE; 8411449Stomee } 8421449Stomee uu_list_walk_end(itr); 8431449Stomee 8441449Stomee if (program) { 8451449Stomee jobject programInfo = NULL; 8461449Stomee 8471449Stomee if (!pinfo) { 8481449Stomee /* 8491449Stomee * Consumer.enable() has already checked that the 8501449Stomee * program was compiled by this consumer. This is an 8511449Stomee * implementation error, not a user error. 8521449Stomee */ 8531449Stomee dtj_throw_illegal_state(env, "program not found"); 8541449Stomee return; 8551449Stomee } 8561449Stomee 8571449Stomee programInfo = dtj_get_program_info(&jc, pinfo); 8581449Stomee if (!programInfo) { 8591449Stomee return; /* java exception pending */ 8601449Stomee } 8611449Stomee 8621449Stomee (*env)->SetObjectField(env, program, g_proginfo_jf, 8631449Stomee programInfo); 8641449Stomee (*env)->DeleteLocalRef(env, programInfo); 8651449Stomee } 8661449Stomee } 8671449Stomee 8681449Stomee /* 8691449Stomee * Protected by global lock (LocalConsumer.class) 8701449Stomee */ 8711449Stomee JNIEXPORT void JNICALL 8721449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1getProgramInfo(JNIEnv *env, 8731449Stomee jobject obj, jobject program) 8741449Stomee { 8751449Stomee dtj_java_consumer_t jc; 8761449Stomee dtrace_hdl_t *dtp; 8771449Stomee int progid; 8781449Stomee uu_list_walk_t *itr; 8791449Stomee dtj_program_t *p; 8801449Stomee dtrace_proginfo_t *pinfo = NULL; 8811449Stomee int i; 8821449Stomee 8831449Stomee jobject programInfo = NULL; 8841449Stomee 8851449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 8861449Stomee return; /* java exception pending */ 8871449Stomee } 8881449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 8891449Stomee 8901449Stomee progid = (*env)->GetIntField(env, program, g_progid_jf); 8911449Stomee 8921449Stomee if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { 8931449Stomee dtj_throw_illegal_state(env, "no program compiled"); 8941449Stomee return; 8951449Stomee } 8961449Stomee 8971449Stomee itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); 8981449Stomee for (i = 0; ((p = uu_list_walk_next(itr)) != NULL) && !pinfo; ++i) { 8991449Stomee if (progid != i) { 9001449Stomee /* get info of given program only */ 9011449Stomee continue; 9021449Stomee } 9031449Stomee 9041449Stomee pinfo = &p->dtjp_info; 9051449Stomee dtrace_program_info(dtp, p->dtjp_program, pinfo); 9061449Stomee } 9071449Stomee uu_list_walk_end(itr); 9081449Stomee 9091449Stomee programInfo = dtj_get_program_info(&jc, pinfo); 9101449Stomee if (!programInfo) { 9111449Stomee return; /* java exception pending */ 9121449Stomee } 9131449Stomee 9141449Stomee (*env)->SetObjectField(env, program, g_proginfo_jf, 9151449Stomee programInfo); 9161449Stomee (*env)->DeleteLocalRef(env, programInfo); 9171449Stomee } 9181449Stomee 9191449Stomee /* 9201449Stomee * Protected by global lock (LocalConsumer.class) 9211449Stomee */ 9221449Stomee JNIEXPORT void JNICALL 9231449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1setOption(JNIEnv *env, 9241449Stomee jobject obj, jstring option, jstring value) 9251449Stomee { 9261449Stomee dtj_java_consumer_t jc; 9271449Stomee const char *opt; 9281449Stomee const char *val; 9291449Stomee boolean_t on; 9301449Stomee 9311449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 9321449Stomee return; /* java exception pending */ 9331449Stomee } 9341449Stomee 9351449Stomee opt = (*env)->GetStringUTFChars(env, option, 0); 9361449Stomee if (!opt) { 9371449Stomee dtj_throw_out_of_memory(env, 9381449Stomee "could not allocate option string"); 9391449Stomee return; 9401449Stomee } 9411449Stomee 9421449Stomee if (value) { 9431449Stomee val = (*env)->GetStringUTFChars(env, value, 0); 9441449Stomee if (!val) { 9451449Stomee dtj_throw_out_of_memory(env, 9461449Stomee "could not allocate option value string"); 9471449Stomee (*env)->ReleaseStringUTFChars(env, option, opt); 9481449Stomee return; 9491449Stomee } 9501449Stomee } else { 9511449Stomee /* 9521449Stomee * dtrace_setopt() sets option to 0 if value is NULL. That's 9531449Stomee * not the same thing as unsetting a boolean option, since 9541449Stomee * libdtrace uses -2 to mean unset. We'll leave it to 9551449Stomee * LocalConsumer.java to disallow null or not. 9561449Stomee */ 9571449Stomee val = NULL; 9581449Stomee } 9591449Stomee 9601449Stomee /* 9611449Stomee * Check for boolean compile-time options not handled by 9621449Stomee * dtrace_setopt(). 9631449Stomee */ 9641449Stomee on = (!val || (strcmp(val, "unset") != 0)); 9651449Stomee if (dtj_cflag(&jc, opt, NULL, &on)) { 9661449Stomee (*env)->ReleaseStringUTFChars(env, option, opt); 9671449Stomee if (value) { 9681449Stomee (*env)->ReleaseStringUTFChars(env, value, val); 9691449Stomee } 9701449Stomee return; 9711449Stomee } 9721449Stomee 9731449Stomee /* 9741449Stomee * The transition from INIT to GO is protected by synchronization 9751449Stomee * (a per-consumer lock) in LocalConsumer.java, ensuring that go() and 9761449Stomee * setOption() execute sequentially. 9771449Stomee */ 9781449Stomee if (jc.dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT) { 9791449Stomee /* 9801449Stomee * If the consumer is already running, defer setting the option 9811449Stomee * until we wake up from dtrace_sleep. 9821449Stomee */ 9831449Stomee dtj_request_t *request; 9841449Stomee 9851449Stomee 9861449Stomee (void) pthread_mutex_lock( 9871449Stomee &jc.dtjj_consumer->dtjc_request_list_lock); 9881449Stomee request = dtj_request_create(env, DTJ_REQUEST_OPTION, opt, val); 9891449Stomee if (request) { 9901449Stomee if (!dtj_list_add(jc.dtjj_consumer->dtjc_request_list, 9911449Stomee request)) { 9921449Stomee dtj_throw_out_of_memory(env, 9931449Stomee "Failed to add setOption request"); 9941449Stomee } 9951449Stomee } /* else java exception pending */ 9961449Stomee (void) pthread_mutex_unlock( 9971449Stomee &jc.dtjj_consumer->dtjc_request_list_lock); 9981449Stomee } else { 9991449Stomee dtrace_hdl_t *dtp = jc.dtjj_consumer->dtjc_dtp; 10001449Stomee if (dtrace_setopt(dtp, opt, val) == -1) { 10011449Stomee dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp, 10021449Stomee dtrace_errno(dtp))); 10031449Stomee } 10041449Stomee } 10051449Stomee 10061449Stomee (*env)->ReleaseStringUTFChars(env, option, opt); 10071449Stomee if (value) { 10081449Stomee (*env)->ReleaseStringUTFChars(env, value, val); 10091449Stomee } 10101449Stomee } 10111449Stomee 10121449Stomee /* 10131449Stomee * Protected by global lock (LocalConsumer.class) 10141449Stomee */ 10151449Stomee JNIEXPORT jlong JNICALL 10161449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1getOption(JNIEnv *env, 10171449Stomee jobject obj, jstring option) 10181449Stomee { 10191449Stomee dtj_java_consumer_t jc; 10201449Stomee dtrace_hdl_t *dtp; 10211449Stomee const char *opt; 10221449Stomee dtrace_optval_t optval; 10231449Stomee boolean_t cflag; 10241449Stomee 10251449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 10261449Stomee return (0); /* java exception pending */ 10271449Stomee } 10281449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 10291449Stomee 10301449Stomee opt = (*env)->GetStringUTFChars(env, option, 0); 10311449Stomee if (!opt) { 10321449Stomee dtj_throw_out_of_memory(env, 10331449Stomee "could not allocate option string"); 10341449Stomee return (0); 10351449Stomee } 10361449Stomee 10371449Stomee /* 10381449Stomee * Check for boolean compile-time options not handled by 10391449Stomee * dtrace_setopt(). 10401449Stomee */ 10411449Stomee if (dtj_cflag(&jc, opt, &cflag, NULL)) { 10421449Stomee (*env)->ReleaseStringUTFChars(env, option, opt); 10431449Stomee return (cflag ? 1 : DTRACEOPT_UNSET); 10441449Stomee } 10451449Stomee 10461449Stomee if (dtrace_getopt(dtp, opt, &optval) == -1) { 10471449Stomee dtj_throw_dtrace_exception(&jc, 10481449Stomee "couldn't get option %s: %s", opt, 10491449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 10501449Stomee (*env)->ReleaseStringUTFChars(env, option, opt); 10511449Stomee return (0); 10521449Stomee } 10531449Stomee (*env)->ReleaseStringUTFChars(env, option, opt); 10541449Stomee return (optval); 10551449Stomee } 10561449Stomee 10571449Stomee /* 10581449Stomee * Throws IllegalStateException if not all compiled programs are enabled. 10591449Stomee */ 10601449Stomee JNIEXPORT void JNICALL 10611449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1checkProgramEnabling(JNIEnv *env, 10621449Stomee jobject obj) 10631449Stomee { 10641449Stomee dtj_java_consumer_t jc; 10651449Stomee dtj_program_t *p; 10661449Stomee uu_list_walk_t *itr; 10671449Stomee 10681449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 10691449Stomee return; /* java exception pending */ 10701449Stomee } 10711449Stomee 10721449Stomee if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { 10731449Stomee dtj_throw_illegal_state(env, "no program compiled"); 10741449Stomee return; 10751449Stomee } 10761449Stomee 10771449Stomee itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); 10781449Stomee while ((p = uu_list_walk_next(itr)) != NULL) { 10791449Stomee if (!p->dtjp_enabled) { 10801449Stomee const char *type; 10811449Stomee switch (p->dtjp_type) { 10821449Stomee case DTJ_PROGRAM_STRING: 10831449Stomee type = "description"; 10841449Stomee break; 10851449Stomee case DTJ_PROGRAM_FILE: 10861449Stomee type = "script"; 10871449Stomee break; 10881449Stomee default: 10891449Stomee assert(p->dtjp_type == DTJ_PROGRAM_STRING || 10901449Stomee p->dtjp_type == DTJ_PROGRAM_FILE); 10911449Stomee } 10921449Stomee dtj_throw_illegal_state(env, 10931449Stomee "Not all compiled probes are enabled. " 10941449Stomee "Compiled %s %s not enabled.", 10951449Stomee type, p->dtjp_name); 10961449Stomee uu_list_walk_end(itr); 10971449Stomee return; 10981449Stomee } 10991449Stomee } 11001449Stomee uu_list_walk_end(itr); 11011449Stomee } 11021449Stomee 11031449Stomee JNIEXPORT jboolean JNICALL 11041449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1isEnabled(JNIEnv *env, 11051449Stomee jobject obj) 11061449Stomee { 11071449Stomee dtj_java_consumer_t jc; 11081449Stomee dtj_program_t *p; 11091449Stomee uu_list_walk_t *itr; 11101449Stomee 11111449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 11121449Stomee return (JNI_FALSE); /* java exception pending */ 11131449Stomee } 11141449Stomee 11151449Stomee if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { 11161449Stomee return (JNI_FALSE); /* no program compiled */ 11171449Stomee } 11181449Stomee 11191449Stomee itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); 11201449Stomee while ((p = uu_list_walk_next(itr)) != NULL) { 11211449Stomee if (!p->dtjp_enabled) { 11221449Stomee uu_list_walk_end(itr); 11231449Stomee return (JNI_FALSE); 11241449Stomee } 11251449Stomee } 11261449Stomee uu_list_walk_end(itr); 11271449Stomee 11281449Stomee return (JNI_TRUE); 11291449Stomee } 11301449Stomee 11311449Stomee /* 11321449Stomee * Protected by global lock (LocalConsumer.class) 11331449Stomee */ 11341449Stomee JNIEXPORT void JNICALL 11351449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1go(JNIEnv *env, jobject obj) 11361449Stomee { 11371449Stomee dtj_java_consumer_t jc; 11381449Stomee dtrace_hdl_t *dtp; 11391449Stomee 11401449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 11411449Stomee return; /* java exception pending */ 11421449Stomee } 11431449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 11441449Stomee 11451449Stomee if (dtj_set_callback_handlers(&jc) != DTJ_OK) { 11461449Stomee return; /* java exception pending */ 11471449Stomee } 11481449Stomee 11491449Stomee if (dtrace_go(dtp) != 0) { 11501449Stomee dtj_throw_dtrace_exception(&jc, 11511449Stomee "could not enable tracing: %s", 11521449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 11531449Stomee return; 11541449Stomee } 11551449Stomee 11561449Stomee jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_GO; 11571449Stomee } 11581449Stomee 11591449Stomee /* 11601449Stomee * Protected by global lock (LocalConsumer.class). Called when aborting the 11611449Stomee * consumer loop before it starts. 11621449Stomee */ 11631449Stomee JNIEXPORT void JNICALL 11641449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1stop(JNIEnv *env, 11651449Stomee jobject obj) 11661449Stomee { 11671449Stomee dtj_java_consumer_t jc; 11681449Stomee dtrace_hdl_t *dtp; 11691449Stomee 11701449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 11711449Stomee return; /* java exception pending */ 11721449Stomee } 11731449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 11741449Stomee 11751449Stomee if (dtrace_stop(dtp) == -1) { 11761449Stomee dtj_throw_dtrace_exception(&jc, 11771449Stomee "couldn't stop tracing: %s", 11781449Stomee dtrace_errmsg(jc.dtjj_consumer->dtjc_dtp, 11791449Stomee dtrace_errno(jc.dtjj_consumer->dtjc_dtp))); 11801449Stomee } else { 11811449Stomee jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP; 11821449Stomee } 11831449Stomee } 11841449Stomee 11851449Stomee JNIEXPORT void JNICALL 11861449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1consume(JNIEnv *env, 11871449Stomee jobject obj) 11881449Stomee { 11891449Stomee dtj_java_consumer_t jc; 11901449Stomee dtrace_hdl_t *dtp; 11911449Stomee 11921449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 11931449Stomee return; /* java exception pending */ 11941449Stomee } 11951449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 11961449Stomee jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_START; 11971449Stomee 11981449Stomee if (dtj_java_consumer_init(env, &jc) != DTJ_OK) { 11991449Stomee return; /* java exception pending */ 12001449Stomee } 12011449Stomee 12021449Stomee /* 12031449Stomee * Must set the thread-specific java consumer before starting the 12041449Stomee * dtrace_work() loop because the bufhandler can also be invoked by 12051449Stomee * getAggregate() from another thread. The bufhandler needs access to 12061449Stomee * the correct JNI state specific to either the consumer loop or the 12071449Stomee * getAggregate() call. 12081449Stomee */ 12091449Stomee (void) pthread_setspecific(g_dtj_consumer_key, &jc); 12101449Stomee 12111449Stomee if (jc.dtjj_consumer->dtjc_process_list != NULL) { 12121449Stomee struct ps_prochandle *P; 12131449Stomee uu_list_walk_t *itr; 12141449Stomee 12153645Stomee /* Must not call MonitorEnter with a pending exception */ 12163645Stomee if ((*env)->ExceptionCheck(env)) { 12173645Stomee dtj_java_consumer_fini(env, &jc); 12183645Stomee return; /* java exception pending */ 12193645Stomee } 12201449Stomee (*env)->MonitorEnter(env, g_caller_jc); 12211449Stomee if ((*env)->ExceptionCheck(env)) { 12221449Stomee dtj_java_consumer_fini(env, &jc); 12231449Stomee return; /* java exception pending */ 12241449Stomee } 12251449Stomee 12261449Stomee itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list, 12271449Stomee 0); 12281449Stomee while ((P = dtj_pointer_list_walk_next(itr)) != 12291449Stomee DTJ_INVALID_PTR) { 12301449Stomee dtrace_proc_continue(dtp, P); 12311449Stomee } 12321449Stomee uu_list_walk_end(itr); 12331449Stomee 12341449Stomee (*env)->MonitorExit(env, g_caller_jc); 12351449Stomee if ((*env)->ExceptionCheck(env)) { 12361449Stomee dtj_java_consumer_fini(env, &jc); 12371449Stomee return; /* java exception pending */ 12381449Stomee } 12391449Stomee } 12401449Stomee 12411449Stomee /* 12421449Stomee * Blocking call starts consumer loop. 12431449Stomee */ 12441449Stomee (void) dtj_consume(&jc); 12451449Stomee 12461449Stomee dtj_java_consumer_fini(env, &jc); 12471449Stomee /* 12481449Stomee * Stop the consumer after the consumer loop terminates, whether 12491449Stomee * normally because of the exit() action or LocalConsumer.stop(), or 12501449Stomee * abnormally because of an exception. The call is ignored if the 12511449Stomee * consumer is already stopped. It is safe to call dtj_stop() with a 12521449Stomee * pending exception. 12531449Stomee */ 12541449Stomee dtj_stop(&jc); 12551449Stomee } 12561449Stomee 12571449Stomee /* 12581449Stomee * Interrupts a running consumer. May be called from any thread. 12591449Stomee */ 12601449Stomee JNIEXPORT void JNICALL 12611449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1interrupt(JNIEnv *env, 12621449Stomee jobject obj) 12631449Stomee { 12641449Stomee dtj_java_consumer_t jc; 12651449Stomee 12661449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 12671449Stomee return; /* java exception pending */ 12681449Stomee } 12691449Stomee 12701449Stomee jc.dtjj_consumer->dtjc_interrupt = B_TRUE; 12711449Stomee } 12721449Stomee 12731449Stomee /* 12741449Stomee * Protected by global lock (LocalConsumer.class) 12751449Stomee */ 12761449Stomee JNIEXPORT void JNICALL 12771449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1close(JNIEnv *env, jobject obj) 12781449Stomee { 12791449Stomee dtj_java_consumer_t jc; 12801449Stomee dtrace_hdl_t *dtp; 12811449Stomee 12821449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 12831449Stomee return; /* java exception pending */ 12841449Stomee } 12851449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 12861449Stomee 12871449Stomee /* 12881449Stomee * Need to release any created procs here, since the consumer_t 12891449Stomee * destructor (called by _destroy) will not have access to the dtrace 12901449Stomee * handle needed to release them (this function closes the dtrace 12911449Stomee * handle). 12921449Stomee */ 12931449Stomee if (jc.dtjj_consumer->dtjc_process_list != NULL) { 12941449Stomee struct ps_prochandle *P; 12951449Stomee uu_list_walk_t *itr; 12961449Stomee itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list, 12971449Stomee 0); 12981449Stomee while ((P = dtj_pointer_list_walk_next(itr)) != 12991449Stomee DTJ_INVALID_PTR) { 13001449Stomee dtrace_proc_release(dtp, P); 13011449Stomee } 13021449Stomee uu_list_walk_end(itr); 13031449Stomee } 13041449Stomee 13051449Stomee dtrace_close(dtp); 13061449Stomee } 13071449Stomee 13081449Stomee /* 13091449Stomee * Static Consumer.java method 13101449Stomee */ 13111449Stomee JNIEXPORT jint JNICALL 13121449Stomee /* ARGSUSED */ 13131449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1openCount(JNIEnv *env, jclass c) 13141449Stomee { 13151449Stomee int open; 13161449Stomee (void) pthread_mutex_lock(&g_table_lock); 13171449Stomee if (g_consumer_table == NULL) { 13181449Stomee open = 0; 13191449Stomee } else { 13201449Stomee open = g_consumer_count; 13211449Stomee } 13221449Stomee (void) pthread_mutex_unlock(&g_table_lock); 13231449Stomee return (open); 13241449Stomee } 13251449Stomee 13261449Stomee /* 13271449Stomee * Static Consumer.java method 13281449Stomee */ 13291449Stomee JNIEXPORT jlong JNICALL 13301449Stomee /* ARGSUSED */ 13311449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1quantizeBucket(JNIEnv *env, 13321449Stomee jclass c, jint bucket) 13331449Stomee { 13341449Stomee return (DTRACE_QUANTIZE_BUCKETVAL(bucket)); 13351449Stomee } 13361449Stomee 13371449Stomee /* 13381449Stomee * Protected by global lock (LocalConsumer.class) 13391449Stomee */ 13401449Stomee JNIEXPORT jstring JNICALL 13411449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupKernelFunction( 13421449Stomee JNIEnv *jenv, jobject caller, jobject address) 13431449Stomee { 13441449Stomee dtj_java_consumer_t jc; 13451449Stomee dtrace_hdl_t *dtp; 13461449Stomee 13471449Stomee jstring jfunc; /* return value */ 13481449Stomee 13491449Stomee GElf_Addr addr; 13501449Stomee char dummy; 13511449Stomee char *s; 13521449Stomee int rc; 13531449Stomee 13541449Stomee if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { 13551449Stomee return (NULL); /* java exception pending */ 13561449Stomee } 13571449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 13581449Stomee 13591449Stomee /* Does not throw exceptions */ 13601449Stomee if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) { 13611449Stomee /* intValue() of class Number does not throw exceptions */ 13621449Stomee addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv, 13631449Stomee address, g_intval_jm); 13641449Stomee } else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) { 13651449Stomee /* longValue() of class Number does not throw exceptions */ 13661449Stomee addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv, 13671449Stomee address, g_longval_jm); 13681449Stomee } else { 13691449Stomee dtj_throw_class_cast(jenv, "Expected Number address"); 13701449Stomee return (NULL); 13711449Stomee } 13721449Stomee 13731449Stomee rc = dtrace_addr2str(dtp, addr, &dummy, 1); 13741847Stomee s = malloc(rc + 1); 13751449Stomee if (!s) { 13761449Stomee dtj_throw_out_of_memory(jenv, 13771449Stomee "Failed to allocate kernel function name"); 13781449Stomee return (NULL); 13791449Stomee } 13801847Stomee (void) dtrace_addr2str(dtp, addr, s, rc + 1); 13811449Stomee 13821449Stomee jfunc = (*jenv)->NewStringUTF(jenv, s); 13831449Stomee free(s); 13841449Stomee return (jfunc); 13851449Stomee } 13861449Stomee 13871449Stomee /* 13881449Stomee * Protected by global lock in Consumer.java 13891449Stomee */ 13901449Stomee JNIEXPORT jstring JNICALL 13911449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupUserFunction(JNIEnv *jenv, 13921449Stomee jobject caller, jint pid, jobject address) 13931449Stomee { 13941449Stomee dtj_java_consumer_t jc; 13951449Stomee dtrace_hdl_t *dtp; 13961449Stomee 13971449Stomee jstring jfunc; /* return value */ 13981449Stomee 13991449Stomee GElf_Addr addr; 14001449Stomee char dummy; 14011449Stomee char *s; 14021449Stomee int rc; 14031449Stomee 14041449Stomee if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { 14051449Stomee return (NULL); /* java exception pending */ 14061449Stomee } 14071449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 14081449Stomee 14091449Stomee /* Does not throw exceptions */ 14101449Stomee if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) { 14111449Stomee /* intValue() of class Number does not throw exceptions */ 14121449Stomee addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv, 14131449Stomee address, g_intval_jm); 14141449Stomee } else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) { 14151449Stomee /* longValue() of class Number does not throw exceptions */ 14161449Stomee addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv, 14171449Stomee address, g_longval_jm); 14181449Stomee } else { 14191449Stomee dtj_throw_class_cast(jenv, "Expected Number address"); 14201449Stomee return (NULL); 14211449Stomee } 14221449Stomee 14231449Stomee rc = dtrace_uaddr2str(dtp, pid, addr, &dummy, 1); 14241847Stomee s = malloc(rc + 1); 14251449Stomee if (!s) { 14261449Stomee dtj_throw_out_of_memory(jenv, 14271449Stomee "Failed to allocate user function name"); 14281449Stomee return (NULL); 14291449Stomee } 14301847Stomee (void) dtrace_uaddr2str(dtp, pid, addr, s, rc + 1); 14311449Stomee 14321449Stomee jfunc = (*jenv)->NewStringUTF(jenv, s); 14331449Stomee free(s); 14341449Stomee return (jfunc); 14351449Stomee } 14361449Stomee 14371449Stomee JNIEXPORT jobject JNICALL 14381449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1getAggregate(JNIEnv *env, 14391449Stomee jobject obj, jobject spec) 14401449Stomee { 14411449Stomee dtj_java_consumer_t jc; 14421449Stomee 14431449Stomee jobject aggregate = NULL; 14441449Stomee 14451449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 14461449Stomee return (NULL); /* java exception pending */ 14471449Stomee } 14481449Stomee 14491449Stomee if (dtj_java_consumer_init(env, &jc) != DTJ_OK) { 14501449Stomee return (NULL); /* java exception pending */ 14511449Stomee } 14521449Stomee jc.dtjj_aggregate_spec = spec; 14531449Stomee 14541449Stomee /* 14551449Stomee * Must set the thread-specific java consumer before calling any 14561449Stomee * function that triggers callbacks to the bufhandler set by 14571449Stomee * dtrace_handle_buffered(). The bufhandler needs access to the correct 14581449Stomee * JNI state specific to either the consumer loop or the 14591449Stomee * getAggregate() call. 14601449Stomee */ 14611449Stomee (void) pthread_setspecific(g_dtj_consumer_key, &jc); 14621449Stomee aggregate = dtj_get_aggregate(&jc); 14631449Stomee if (!aggregate) { 14641449Stomee /* java exception pending */ 14651449Stomee jc.dtjj_consumer->dtjc_interrupt = B_TRUE; 14661449Stomee } 14671449Stomee /* 14681449Stomee * Cleans up only references created by this JNI invocation. Leaves 14691449Stomee * cached per-consumer state untouched. 14701449Stomee */ 14711449Stomee dtj_java_consumer_fini(env, &jc); 14721449Stomee return (aggregate); 14731449Stomee } 14741449Stomee 14751449Stomee /* 14761449Stomee * Returns the pid of the created process, or -1 in case of an error (java 14771449Stomee * exception pending). 14781449Stomee * Protected by global lock (LocalConsumer.class) 14791449Stomee */ 14801449Stomee JNIEXPORT int JNICALL 14811449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1createProcess(JNIEnv *jenv, 14821449Stomee jobject caller, jstring command) 14831449Stomee { 14841449Stomee dtj_java_consumer_t jc; 14851449Stomee dtrace_hdl_t *dtp; 14861449Stomee struct ps_prochandle *P; 14871449Stomee char **argv; 14881449Stomee int argc; 14891449Stomee 14901449Stomee if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { 14911449Stomee return (-1); /* java exception pending */ 14921449Stomee } 14931449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 14941449Stomee 14951449Stomee if (jc.dtjj_consumer->dtjc_process_list == NULL) { 14961449Stomee jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create(); 14971449Stomee if (!jc.dtjj_consumer->dtjc_process_list) { 14981449Stomee dtj_throw_out_of_memory(jenv, 14991449Stomee "Could not allocate process list"); 15001449Stomee return (-1); 15011449Stomee } 15021449Stomee } 15031449Stomee 15041449Stomee argv = dtj_make_argv(jenv, command, &argc); 15051449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 15061449Stomee return (-1); 15071449Stomee } 15081449Stomee 15091449Stomee P = dtrace_proc_create(dtp, argv[0], argv); 15101449Stomee dtj_free_argv(argv); 15111449Stomee 15121449Stomee if (!P) { 15131449Stomee dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp, 15141449Stomee dtrace_errno(dtp))); 15151449Stomee return (-1); 15161449Stomee } 15171449Stomee 15181449Stomee if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) { 15191449Stomee dtj_throw_out_of_memory(jenv, 15201449Stomee "Failed to add process to process list"); 15211449Stomee dtrace_proc_release(dtp, P); 15221449Stomee return (-1); 15231449Stomee } 15241449Stomee return (Pstatus(P)->pr_pid); 15251449Stomee } 15261449Stomee 15271449Stomee /* 15281449Stomee * Protected by global lock (LocalConsumer.class) 15291449Stomee */ 15301449Stomee JNIEXPORT void JNICALL 15311449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1grabProcess(JNIEnv *jenv, 15321449Stomee jobject caller, jint pid) 15331449Stomee { 15341449Stomee dtj_java_consumer_t jc; 15351449Stomee dtrace_hdl_t *dtp; 15361449Stomee struct ps_prochandle *P; 15371449Stomee 15381449Stomee if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { 15391449Stomee return; /* java exception pending */ 15401449Stomee } 15411449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 15421449Stomee 15431449Stomee if (jc.dtjj_consumer->dtjc_process_list == NULL) { 15441449Stomee jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create(); 15451449Stomee if (jc.dtjj_consumer->dtjc_process_list == NULL) { 15461449Stomee dtj_throw_out_of_memory(jenv, 15471449Stomee "Could not allocate process list"); 15481449Stomee return; 15491449Stomee } 15501449Stomee } 15511449Stomee 15521449Stomee P = dtrace_proc_grab(dtp, (pid_t)pid, 0); 15531449Stomee if (!P) { 15541449Stomee dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp, 15551449Stomee dtrace_errno(dtp))); 15561449Stomee return; 15571449Stomee } 15581449Stomee 15591449Stomee if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) { 15601449Stomee dtj_throw_out_of_memory(jenv, 15611449Stomee "Failed to add process to process list"); 15621449Stomee dtrace_proc_release(dtp, P); 15631449Stomee return; 15641449Stomee } 15651449Stomee } 15661449Stomee 15671449Stomee /* 15681449Stomee * Lists all probes, or lists matching probes (using the matching rules from 15691449Stomee * Table 4-1 of the DTrace manual). 15701449Stomee * 15711449Stomee * In the future it may be desirable to support an array of probe filters rather 15721449Stomee * than a single filter. It could be that if a probe matched any of the given 15731449Stomee * filters, it would be included (implied logical OR). 15741449Stomee * 15751449Stomee * Protected by global lock (LocalConsumer.class) 15761449Stomee * param list: an empty list to populate (this function empties the list if it 15771449Stomee * is not empty already) 15781449Stomee * param filter: a ProbeDescription instance; the list will include only probes 15791449Stomee * that match the filter (match all probes if filter is null) 15801449Stomee */ 15811449Stomee JNIEXPORT void JNICALL 15821449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbes(JNIEnv *env, 15831449Stomee jobject obj, jobject list, jobject filter) 15841449Stomee { 15851449Stomee dtj_list_probes(env, obj, list, filter, dtj_list_probe); 15861449Stomee } 15871449Stomee 15881449Stomee JNIEXPORT void JNICALL 15891449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbeDetail(JNIEnv *env, 15901449Stomee jobject obj, jobject list, jobject filter) 15911449Stomee { 15921449Stomee dtj_list_probes(env, obj, list, filter, dtj_list_probe_detail); 15931449Stomee } 15941449Stomee 15951449Stomee static void 15961449Stomee dtj_list_probes(JNIEnv *env, jobject obj, jobject list, jobject filter, 15971449Stomee dtrace_probe_f *func) 15981449Stomee { 15991449Stomee dtj_java_consumer_t jc; 16001449Stomee dtrace_hdl_t *dtp; 16011449Stomee dtrace_probedesc_t probe; 16021449Stomee dtrace_probedesc_t *pdp = NULL; 16031449Stomee const char *probestr; 16041449Stomee int rc; 16051449Stomee 16061449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 16071449Stomee return; /* java exception pending */ 16081449Stomee } 16091449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 16101449Stomee 16111449Stomee jc.dtjj_probelist = list; 16121449Stomee 16131449Stomee /* clear in-out list parameter */ 16141449Stomee (*env)->CallVoidMethod(env, list, g_listclear_jm); 16151449Stomee if ((*env)->ExceptionCheck(env)) { 16161449Stomee return; 16171449Stomee } 16181449Stomee 16191449Stomee if (filter) { 16201449Stomee jstring jprobestr = NULL; 16211449Stomee 16221449Stomee jprobestr = (*env)->CallObjectMethod(env, filter, 16231449Stomee g_tostring_jm); 16241449Stomee if ((*env)->ExceptionCheck(env)) { 16251449Stomee return; 16261449Stomee } 16271449Stomee probestr = (*env)->GetStringUTFChars(env, jprobestr, NULL); 16281449Stomee if (!probestr) { 16291449Stomee (*env)->DeleteLocalRef(env, jprobestr); 16301449Stomee return; /* java exception pending */ 16311449Stomee } 16321449Stomee 16331449Stomee bzero(&probe, sizeof (probe)); 16341449Stomee rc = dtrace_str2desc(dtp, DTRACE_PROBESPEC_NAME, probestr, 16351449Stomee &probe); 16361449Stomee (*env)->ReleaseStringUTFChars(env, jprobestr, probestr); 16371449Stomee (*env)->DeleteLocalRef(env, jprobestr); 16381449Stomee if (rc == -1) { 16391449Stomee dtj_throw_dtrace_exception(&jc, 16401449Stomee "%s is not a valid probe description: %s", 16411449Stomee probestr, dtrace_errmsg(dtp, 16421449Stomee dtrace_errno(dtp))); 16431449Stomee return; 16441449Stomee } 16451449Stomee 16461449Stomee pdp = &probe; 16471449Stomee } 16481449Stomee 16491449Stomee (void) dtrace_probe_iter(dtp, pdp, func, &jc); 16501449Stomee } 16511449Stomee 16521449Stomee /* 16531449Stomee * Returns 0 to indicate success, or -1 to cause dtrace_probe_iter() to return a 16541449Stomee * negative value prematurely (indicating no match or failure). 16551449Stomee */ 16561449Stomee static int 16571449Stomee /* ARGSUSED */ 16581449Stomee dtj_list_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg) 16591449Stomee { 16601449Stomee dtj_java_consumer_t *jc = arg; 16611449Stomee JNIEnv *jenv = jc->dtjj_jenv; 16621449Stomee 16631449Stomee jobject jprobedesc = NULL; 16641449Stomee 16651449Stomee jprobedesc = dtj_new_probedesc(jc, pdp); 16661449Stomee if (!jprobedesc) { 16671449Stomee return (-1); /* java exception pending */ 16681449Stomee } 16691449Stomee 16701449Stomee /* add probe to list */ 16711449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm, 16721449Stomee jprobedesc); 16731449Stomee (*jenv)->DeleteLocalRef(jenv, jprobedesc); 16741449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 16751449Stomee return (-1); 16761449Stomee } 16771449Stomee 16781449Stomee return (0); 16791449Stomee } 16801449Stomee 16811449Stomee /*ARGSUSED*/ 16821449Stomee static int 16831449Stomee dtj_list_probe_detail(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, 16841449Stomee void *arg) 16851449Stomee { 16861449Stomee dtj_java_consumer_t *jc = arg; 16871449Stomee JNIEnv *jenv = jc->dtjj_jenv; 16881449Stomee dtrace_probeinfo_t p; 16891449Stomee 16901449Stomee jobject jprobedesc = NULL; 16911449Stomee jobject jprobeinfo = NULL; 16921449Stomee jobject jprobe = NULL; 16931449Stomee 16941449Stomee jprobedesc = dtj_new_probedesc(jc, pdp); 16951449Stomee if (!jprobedesc) { 16961449Stomee return (-1); /* java exception pending */ 16971449Stomee } 16981449Stomee 16991449Stomee /* 17001449Stomee * If dtrace_probe_info() returns a non-zero value, dtrace_errno is set 17011449Stomee * for us. In that case, ignore the dtrace error and simply omit probe 17021449Stomee * info. That error is implicitly cleared the next time a call is made 17031449Stomee * using the same dtrace handle. 17041449Stomee */ 17051449Stomee if (dtrace_probe_info(dtp, pdp, &p) == 0) { 17061449Stomee /* create probe info instance */ 17071449Stomee jprobeinfo = dtj_new_probeinfo(jc, &p); 17081449Stomee if (!jprobeinfo) { 17091449Stomee (*jenv)->DeleteLocalRef(jenv, jprobedesc); 17101449Stomee return (-1); /* java exception pending */ 17111449Stomee } 17121449Stomee } 17131449Stomee 17141449Stomee /* create listed probe instance */ 17151449Stomee jprobe = (*jenv)->NewObject(jenv, g_probe_jc, g_probeinit_jm, 17161449Stomee jprobedesc, jprobeinfo); 17171449Stomee (*jenv)->DeleteLocalRef(jenv, jprobedesc); 17181449Stomee (*jenv)->DeleteLocalRef(jenv, jprobeinfo); 17191449Stomee if (!jprobe) { 17201449Stomee return (-1); /* java exception pending */ 17211449Stomee } 17221449Stomee 17231449Stomee /* add probe to list */ 17241449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm, 17251449Stomee jprobe); 17261449Stomee (*jenv)->DeleteLocalRef(jenv, jprobe); 17271449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 17281449Stomee return (-1); 17291449Stomee } 17301449Stomee 17311449Stomee return (0); 17321449Stomee } 17331449Stomee 17341449Stomee /*ARGSUSED*/ 17351449Stomee static int 17361449Stomee dtj_list_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, 17371449Stomee dtrace_stmtdesc_t *stp, void *arg) 17381449Stomee { 17391449Stomee dtj_java_consumer_t *jc = arg; 17401449Stomee dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; 17411449Stomee 17421449Stomee if (edp == jc->dtjj_consumer->dtjc_last_probe) { 17431449Stomee return (0); 17441449Stomee } 17451449Stomee 17461449Stomee if (dtrace_probe_iter(dtp, &edp->dted_probe, 17471449Stomee jc->dtjj_consumer->dtjc_plistfunc, arg) != 0) { 17481449Stomee dtj_throw_dtrace_exception(jc, 17491449Stomee "failed to match %s:%s:%s:%s: %s", 17501449Stomee edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod, 17511449Stomee edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name, 17521449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 17531449Stomee return (1); 17541449Stomee } 17551449Stomee 17561449Stomee jc->dtjj_consumer->dtjc_last_probe = edp; 17571449Stomee return (0); 17581449Stomee } 17591449Stomee 17601449Stomee /* 17611449Stomee * Protected by global lock in Consumer.java 17621449Stomee */ 17631449Stomee JNIEXPORT void JNICALL 17641449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbes(JNIEnv *env, 17651449Stomee jobject obj, jobject list, jobject program) 17661449Stomee { 17671449Stomee dtj_list_compiled_probes(env, obj, list, program, dtj_list_probe); 17681449Stomee } 17691449Stomee 17701449Stomee JNIEXPORT void JNICALL 17711449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbeDetail( 17721449Stomee JNIEnv *env, jobject obj, jobject list, jobject program) 17731449Stomee { 17741449Stomee dtj_list_compiled_probes(env, obj, list, program, 17751449Stomee dtj_list_probe_detail); 17761449Stomee } 17771449Stomee 17781449Stomee static void 17791449Stomee dtj_list_compiled_probes(JNIEnv *env, jobject obj, jobject list, 17801449Stomee jobject program, dtrace_probe_f *func) 17811449Stomee { 17821449Stomee dtj_java_consumer_t jc; 17831449Stomee dtrace_hdl_t *dtp; 17841449Stomee uu_list_walk_t *itr; 17851449Stomee dtj_program_t *p; 17861449Stomee boolean_t found; 17871449Stomee int progid = -1; 17881449Stomee int i; 17891449Stomee 17901449Stomee if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { 17911449Stomee return; /* java exception pending */ 17921449Stomee } 17931449Stomee dtp = jc.dtjj_consumer->dtjc_dtp; 17941449Stomee jc.dtjj_probelist = list; 17951449Stomee 17961449Stomee (*env)->CallVoidMethod(env, list, g_listclear_jm); 17971449Stomee if ((*env)->ExceptionCheck(env)) { 17981449Stomee return; 17991449Stomee } 18001449Stomee 18011449Stomee if (program) { 18021449Stomee if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { 18031449Stomee dtj_throw_no_such_element(env, "no compiled program"); 18041449Stomee return; 18051449Stomee } 18061449Stomee progid = (*env)->GetIntField(env, program, g_progid_jf); 18071449Stomee if (progid == -1) { 18081449Stomee dtj_throw_illegal_argument(env, "invalid program"); 18091449Stomee return; 18101449Stomee } 18111449Stomee } 18121449Stomee 18131449Stomee jc.dtjj_consumer->dtjc_plistfunc = func; 18141449Stomee found = B_FALSE; 18151449Stomee itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); 18161449Stomee for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) { 18171449Stomee if ((progid != -1) && (progid != i)) { 18181449Stomee continue; 18191449Stomee } 18201449Stomee 18211449Stomee found = B_TRUE; 18221449Stomee (void) dtrace_stmt_iter(dtp, p->dtjp_program, 18231449Stomee (dtrace_stmt_f *)dtj_list_stmt, &jc); 18241449Stomee } 18251449Stomee uu_list_walk_end(itr); 18261449Stomee 18271449Stomee if (program && !found) { 18281449Stomee dtj_throw_no_such_element(env, "program not found"); 18291449Stomee } 18301449Stomee } 18311449Stomee 18321449Stomee /* 18331449Stomee * Static LocalConsumer.java method 18341449Stomee * Protected by global lock (LocalConsumer.class) 18351449Stomee */ 18361449Stomee JNIEXPORT jstring JNICALL 18371449Stomee /* ARGSUSED */ 18381449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1getVersion(JNIEnv *env, 18391449Stomee jclass class) 18401449Stomee { 18411449Stomee /* 18421449Stomee * Handles the case of locale-specific encoding of the user-visible 18431449Stomee * version string containing non-ASCII characters. 18441449Stomee */ 18451449Stomee return (dtj_NewStringNative(env, _dtrace_version)); 18461449Stomee } 18471449Stomee 18481449Stomee /* 18491449Stomee * Static LocalConsumer.java method 18501449Stomee * Protected by global lock (LocalConsumer.class) 18511449Stomee */ 18521449Stomee JNIEXPORT jstring JNICALL 18531449Stomee /* ARGSUSED */ 18541449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1getExecutableName(JNIEnv *env, 18551449Stomee jclass class) 18561449Stomee { 18571449Stomee jstring jname = NULL; 18581449Stomee const char *name = NULL; 18591449Stomee char *s; 18601449Stomee int len; 18611449Stomee 18621449Stomee name = dtj_getexecname(); 18631449Stomee len = strlen(name); 18641449Stomee s = malloc(len + 1); 18651449Stomee if (!s) { 18661449Stomee dtj_throw_out_of_memory(env, "Failed to allocate execname"); 18671449Stomee return (NULL); 18681449Stomee } 18691449Stomee (void) strcpy(s, name); 18701449Stomee name = basename(s); 18711449Stomee free(s); 18721449Stomee jname = (*env)->NewStringUTF(env, name); 18731449Stomee return (jname); 18741449Stomee } 18751449Stomee 18761449Stomee /* 18771449Stomee * Static LocalConsumer.java method 18781449Stomee */ 18791449Stomee JNIEXPORT void JNICALL 18801449Stomee /* ARGSUSED */ 18811449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1setMaximumConsumers(JNIEnv *env, 18821449Stomee jclass class, jint max) 18831449Stomee { 18841449Stomee g_max_consumers = max; 18851449Stomee } 18861449Stomee 18871449Stomee /* 18881449Stomee * Static LocalConsumer.java method 18891449Stomee */ 18901449Stomee JNIEXPORT void JNICALL 18911449Stomee /* ARGSUSED */ 18921449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1setDebug(JNIEnv *env, 18931449Stomee jclass class, jboolean debug) 18941449Stomee { 18951449Stomee g_dtj_util_debug = debug; 18961449Stomee } 18971449Stomee 18981449Stomee JNIEXPORT void JNICALL 18991449Stomee Java_org_opensolaris_os_dtrace_LocalConsumer__1destroy(JNIEnv *env, jobject obj) 19001449Stomee { 19011449Stomee dtj_consumer_t *c; 19021449Stomee 19031449Stomee c = dtj_remove_consumer(env, obj); 19041449Stomee if (c == NULL) { 19051449Stomee return; /* java exception pending */ 19061449Stomee } 19071449Stomee dtj_consumer_destroy(c); 19081449Stomee } 1909