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