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