#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <assert.h>
#include <strings.h>
#include <libproc.h>
#include <pthread.h>
#include <dtrace_jni.h>
#include <LocalConsumer.h>
#define DTRACE_JNI_VERSION 3
#define FIRST_HANDLE 0
#define NO_HANDLE -1
#define INITIAL_CAPACITY 8
#define MAX_CAPACITY_INCREMENT 1024
static boolean_t g_dtj_load = B_FALSE;
static int g_handle_seq = NO_HANDLE;
static dtj_consumer_t **g_consumer_table = NULL;
static size_t g_consumer_capacity = 0;
static size_t g_consumer_count = 0;
static size_t g_max_capacity_increment = MAX_CAPACITY_INCREMENT;
static size_t g_max_consumers = 0;
static boolean_t g_init = B_FALSE;
static pthread_mutex_t g_table_lock;
static pthread_mutexattr_t g_table_lock_attr;
pthread_key_t g_dtj_consumer_key;
static int dtj_get_handle(JNIEnv *, jobject);
static dtj_status_t dtj_get_java_consumer(JNIEnv *, jobject,
dtj_java_consumer_t *);
static const char *dtj_getexecname(void);
static jobject dtj_get_program_info(dtj_java_consumer_t *, dtrace_proginfo_t *);
static jobject dtj_add_program(dtj_java_consumer_t *, dtj_program_t *);
static void dtj_flag(uint_t *, uint_t, boolean_t *, boolean_t *);
static boolean_t dtj_cflag(dtj_java_consumer_t *, const char *, boolean_t *,
boolean_t *);
static void dtj_list_probes(JNIEnv *, jobject, jobject, jobject,
dtrace_probe_f *);
static void dtj_list_compiled_probes(JNIEnv *, jobject, jobject, jobject,
dtrace_probe_f *);
static int dtj_list_probe(dtrace_hdl_t *, const dtrace_probedesc_t *, void *);
static int dtj_list_probe_detail(dtrace_hdl_t *, const dtrace_probedesc_t *,
void *);
static int dtj_list_stmt(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *,
void *);
static boolean_t dtj_add_consumer(JNIEnv *, dtj_consumer_t *, int *);
static dtj_consumer_t *dtj_remove_consumer(JNIEnv *, jobject);
static dtj_consumer_t *dtj_remove_consumer_at(int);
static int
dtj_get_handle(JNIEnv *jenv, jobject caller)
{
int handle;
if (!g_dtj_load) {
dtj_throw_illegal_state(jenv, "JNI table not loaded");
return (NO_HANDLE);
}
handle = (*jenv)->CallIntMethod(jenv, caller, g_gethandle_jm);
if ((*jenv)->ExceptionCheck(jenv)) {
return (NO_HANDLE);
}
if (handle == NO_HANDLE) {
dtj_throw_illegal_state(jenv, "no consumer handle");
}
return (handle);
}
static dtj_status_t
dtj_get_java_consumer(JNIEnv *jenv, jobject caller, dtj_java_consumer_t *jc)
{
dtj_consumer_t *consumer;
int handle = dtj_get_handle(jenv, caller);
if (handle == NO_HANDLE) {
return (DTJ_ERR);
}
(void) pthread_mutex_lock(&g_table_lock);
if (g_consumer_table) {
if ((handle >= 0) && (handle < g_consumer_capacity)) {
consumer = g_consumer_table[handle];
} else {
consumer = NULL;
}
} else {
consumer = NULL;
}
(void) pthread_mutex_unlock(&g_table_lock);
if (consumer == NULL) {
dtj_throw_no_such_element(jenv, "consumer handle %d", handle);
return (DTJ_ERR);
}
bzero(jc, sizeof (dtj_java_consumer_t));
jc->dtjj_consumer = consumer;
jc->dtjj_caller = caller;
jc->dtjj_jenv = jenv;
return (DTJ_OK);
}
static boolean_t
dtj_add_consumer(JNIEnv *jenv, dtj_consumer_t *c, int *seq)
{
int start;
if (!g_init) {
(void) pthread_key_create(&g_dtj_consumer_key, NULL);
(void) pthread_mutexattr_init(&g_table_lock_attr);
(void) pthread_mutexattr_settype(&g_table_lock_attr,
PTHREAD_MUTEX_RECURSIVE);
(void) pthread_mutex_init(&g_table_lock,
&g_table_lock_attr);
g_init = B_TRUE;
}
*seq = NO_HANDLE;
(void) pthread_mutex_lock(&g_table_lock);
if (g_consumer_table == NULL) {
g_consumer_table = malloc(INITIAL_CAPACITY *
sizeof (dtj_consumer_t *));
if (!g_consumer_table) {
g_handle_seq = NO_HANDLE;
dtj_throw_out_of_memory(jenv,
"could not allocate consumer table");
(void) pthread_mutex_unlock(&g_table_lock);
return (B_FALSE);
}
bzero(g_consumer_table, (INITIAL_CAPACITY *
sizeof (dtj_consumer_t *)));
g_consumer_capacity = INITIAL_CAPACITY;
} else if ((g_max_consumers > 0) && (g_consumer_count >=
g_max_consumers)) {
dtj_throw_resource_limit(jenv, "Too many consumers");
(void) pthread_mutex_unlock(&g_table_lock);
return (B_FALSE);
} else if (g_consumer_count >= g_consumer_capacity) {
dtj_consumer_t **t;
size_t new_capacity;
if (g_consumer_capacity <= g_max_capacity_increment) {
new_capacity = (g_consumer_capacity * 2);
} else {
new_capacity = (g_consumer_capacity +
g_max_capacity_increment);
}
if ((g_max_consumers > 0) && (new_capacity > g_max_consumers)) {
new_capacity = g_max_consumers;
}
t = realloc(g_consumer_table,
new_capacity * sizeof (dtj_consumer_t *));
if (!t) {
dtj_throw_out_of_memory(jenv,
"could not reallocate consumer table");
(void) pthread_mutex_unlock(&g_table_lock);
return (B_FALSE);
}
g_consumer_table = t;
bzero(g_consumer_table + g_consumer_capacity, ((new_capacity -
g_consumer_capacity) * sizeof (dtj_consumer_t *)));
g_consumer_capacity = new_capacity;
}
g_handle_seq = (g_handle_seq == NO_HANDLE
? FIRST_HANDLE : g_handle_seq + 1);
if (g_handle_seq >= g_consumer_capacity) {
g_handle_seq = FIRST_HANDLE;
}
start = g_handle_seq;
while (g_consumer_table[g_handle_seq] != NULL) {
++g_handle_seq;
if (g_handle_seq == start) {
dtj_throw_illegal_state(jenv, "consumer table full,"
" but count %d < capacity %d",
g_consumer_count, g_consumer_capacity);
(void) pthread_mutex_unlock(&g_table_lock);
return (B_FALSE);
} else if (g_handle_seq >= g_consumer_capacity) {
g_handle_seq = FIRST_HANDLE;
}
}
g_consumer_table[g_handle_seq] = c;
*seq = g_handle_seq;
++g_consumer_count;
(void) pthread_mutex_unlock(&g_table_lock);
return (B_TRUE);
}
static dtj_consumer_t *
dtj_remove_consumer(JNIEnv *jenv, jobject caller)
{
dtj_consumer_t *consumer;
int handle = dtj_get_handle(jenv, caller);
if (handle == NO_HANDLE) {
return (NULL);
}
consumer = dtj_remove_consumer_at(handle);
return (consumer);
}
static dtj_consumer_t *
dtj_remove_consumer_at(int handle)
{
dtj_consumer_t *consumer;
(void) pthread_mutex_lock(&g_table_lock);
if (g_consumer_table) {
if ((handle >= 0) && (handle < g_consumer_capacity)) {
consumer = g_consumer_table[handle];
if (consumer != NULL) {
g_consumer_table[handle] = NULL;
--g_consumer_count;
if (g_consumer_count == 0) {
free(g_consumer_table);
g_consumer_table = NULL;
g_consumer_capacity = 0;
g_handle_seq = NO_HANDLE;
}
}
} else {
consumer = NULL;
}
} else {
consumer = NULL;
}
(void) pthread_mutex_unlock(&g_table_lock);
return (consumer);
}
static const char *
dtj_getexecname(void)
{
const char *execname = NULL;
static auxv_t auxb;
int fd = open("/proc/self/auxv", O_RDONLY);
if (fd >= 0) {
while (read(fd, &auxb, sizeof (auxv_t)) == sizeof (auxv_t)) {
if (auxb.a_type == AT_SUN_EXECNAME) {
execname = auxb.a_un.a_ptr;
break;
}
}
(void) close(fd);
}
return (execname);
}
static jobject
dtj_add_program(dtj_java_consumer_t *jc, dtj_program_t *p)
{
JNIEnv *jenv = jc->dtjj_jenv;
jobject jprogram = NULL;
switch (p->dtjp_type) {
case DTJ_PROGRAM_STRING:
jprogram = (*jenv)->NewObject(jenv, g_program_jc,
g_proginit_jm);
break;
case DTJ_PROGRAM_FILE:
jprogram = (*jenv)->NewObject(jenv, g_programfile_jc,
g_fproginit_jm);
break;
default:
dtj_throw_illegal_argument(jenv, "unexpected program type %d\n",
p->dtjp_type);
}
if ((*jenv)->ExceptionCheck(jenv)) {
return (NULL);
}
(*jenv)->SetIntField(jenv, jprogram, g_progid_jf,
uu_list_numnodes(jc->dtjj_consumer->dtjc_program_list));
if (!dtj_list_add(jc->dtjj_consumer->dtjc_program_list, p)) {
(*jenv)->DeleteLocalRef(jenv, jprogram);
dtj_throw_out_of_memory(jenv,
"could not add program");
return (NULL);
}
return (jprogram);
}
static jobject
dtj_get_program_info(dtj_java_consumer_t *jc, dtrace_proginfo_t *pinfo)
{
JNIEnv *jenv = jc->dtjj_jenv;
jobject minProbeAttributes = NULL;
jobject minStatementAttributes = NULL;
jobject programInfo = NULL;
minProbeAttributes = dtj_new_attribute(jc, &pinfo->dpi_descattr);
if (!minProbeAttributes) {
return (NULL);
}
minStatementAttributes = dtj_new_attribute(jc, &pinfo->dpi_stmtattr);
if (!minStatementAttributes) {
(*jenv)->DeleteLocalRef(jenv, minProbeAttributes);
return (NULL);
}
programInfo = (*jenv)->NewObject(jenv, g_proginfo_jc,
g_proginfoinit_jm, minProbeAttributes, minStatementAttributes,
pinfo->dpi_matches);
(*jenv)->DeleteLocalRef(jenv, minProbeAttributes);
(*jenv)->DeleteLocalRef(jenv, minStatementAttributes);
return (programInfo);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1checkVersion(JNIEnv *env,
jclass class, jint version)
{
if (version != DTRACE_JNI_VERSION) {
dtj_throw_illegal_state(env,
"LocalConsumer version %d incompatible with native "
"implementation version %d", version, DTRACE_JNI_VERSION);
}
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1loadJniTable(JNIEnv *env,
jclass class)
{
if (g_dtj_load) {
dtj_throw_illegal_state(env, "JNI table already loaded");
return;
}
if (dtj_load(env) == DTJ_OK) {
g_dtj_load = B_TRUE;
}
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1open(JNIEnv *env, jobject obj,
jobjectArray flags)
{
dtrace_hdl_t *dtp;
dtj_consumer_t *c;
const char *f;
int oflags = 0;
int i, len;
int id;
int err;
jobject flag = NULL;
jstring flagname = NULL;
if (!g_dtj_load) {
dtj_throw_illegal_state(env, "JNI table not loaded");
return;
}
c = dtj_consumer_create(env);
if (!c) {
return;
}
len = (flags ? (*env)->GetArrayLength(env, flags) : 0);
for (i = 0; i < len; ++i) {
flag = (*env)->GetObjectArrayElement(env, flags, i);
if ((*env)->ExceptionCheck(env)) {
dtj_consumer_destroy(c);
return;
}
flagname = (*env)->CallObjectMethod(env, flag, g_enumname_jm);
(*env)->DeleteLocalRef(env, flag);
if ((*env)->ExceptionCheck(env)) {
dtj_consumer_destroy(c);
return;
}
f = (*env)->GetStringUTFChars(env, flagname, NULL);
if ((*env)->ExceptionCheck(env)) {
(*env)->DeleteLocalRef(env, flagname);
dtj_consumer_destroy(c);
return;
}
if (strcmp(f, "ILP32") == 0) {
oflags |= DTRACE_O_ILP32;
} else if (strcmp(f, "LP64") == 0) {
oflags |= DTRACE_O_LP64;
}
(*env)->ReleaseStringUTFChars(env, flagname, f);
(*env)->DeleteLocalRef(env, flagname);
}
if ((oflags & DTRACE_O_ILP32) && (oflags & DTRACE_O_LP64)) {
dtj_throw_illegal_argument(env,
"Cannot set both ILP32 and LP64");
dtj_consumer_destroy(c);
return;
}
if (!dtj_add_consumer(env, c, &id)) {
dtj_consumer_destroy(c);
return;
}
(*env)->CallVoidMethod(env, obj, g_sethandle_jm, id);
if ((*env)->ExceptionCheck(env)) {
(void) dtj_remove_consumer_at(id);
dtj_consumer_destroy(c);
return;
}
if ((dtp = dtrace_open(DTRACE_VERSION, oflags, &err)) == NULL) {
dtj_java_consumer_t jc;
jc.dtjj_jenv = env;
dtj_throw_dtrace_exception(&jc, dtrace_errmsg(NULL, err));
(void) dtj_remove_consumer_at(id);
dtj_consumer_destroy(c);
return;
}
c->dtjc_dtp = dtp;
}
static void
dtj_flag(uint_t *flags, uint_t flag, boolean_t *get, boolean_t *set)
{
assert((get && !set) || (set && !get));
if (get) {
*get = (*flags & flag);
} else {
if (*set) {
*flags |= flag;
} else {
*flags &= ~flag;
}
}
}
static boolean_t
dtj_cflag(dtj_java_consumer_t *jc, const char *opt, boolean_t *get,
boolean_t *set)
{
boolean_t is_cflag = B_TRUE;
uint_t *flags = &jc->dtjj_consumer->dtjc_cflags;
if (strcmp(opt, "argref") == 0) {
dtj_flag(flags, DTRACE_C_ARGREF, get, set);
} else if (strcmp(opt, "cpp") == 0) {
dtj_flag(flags, DTRACE_C_CPP, get, set);
} else if (strcmp(opt, "defaultargs") == 0) {
dtj_flag(flags, DTRACE_C_DEFARG, get, set);
} else if (strcmp(opt, "empty") == 0) {
dtj_flag(flags, DTRACE_C_EMPTY, get, set);
} else if (strcmp(opt, "errtags") == 0) {
dtj_flag(flags, DTRACE_C_ETAGS, get, set);
} else if (strcmp(opt, "knodefs") == 0) {
dtj_flag(flags, DTRACE_C_KNODEF, get, set);
} else if (strcmp(opt, "nolibs") == 0) {
dtj_flag(flags, DTRACE_C_NOLIBS, get, set);
} else if (strcmp(opt, "pspec") == 0) {
dtj_flag(flags, DTRACE_C_PSPEC, get, set);
} else if (strcmp(opt, "unodefs") == 0) {
dtj_flag(flags, DTRACE_C_UNODEF, get, set);
} else if (strcmp(opt, "verbose") == 0) {
dtj_flag(flags, DTRACE_C_DIFV, get, set);
} else if (strcmp(opt, "zdefs") == 0) {
dtj_flag(flags, DTRACE_C_ZDEFS, get, set);
} else {
is_cflag = B_FALSE;
}
if (is_cflag && set &&
(jc->dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT)) {
dtj_throw_illegal_state(jc->dtjj_jenv,
"cannot set compile time option \"%s\" after calling go()",
opt);
return (is_cflag);
}
return (is_cflag);
}
JNIEXPORT jobject JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1compileString(JNIEnv *env,
jobject obj, jstring program, jobjectArray args)
{
const char *prog;
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
dtj_program_t *p;
int argc = 0;
char **argv = NULL;
jstring jprogram = NULL;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return (NULL);
}
dtp = jc.dtjj_consumer->dtjc_dtp;
prog = (*env)->GetStringUTFChars(env, program, 0);
if ((*env)->ExceptionCheck(env)) {
return (NULL);
}
p = dtj_program_create(env, DTJ_PROGRAM_STRING, prog);
if (!p) {
(*env)->ReleaseStringUTFChars(env, program, prog);
return (NULL);
}
if (args) {
argv = dtj_get_argv(env, args, &argc);
if ((*env)->ExceptionCheck(env)) {
(*env)->ReleaseStringUTFChars(env, program, prog);
dtj_program_destroy(p, NULL);
return (NULL);
}
}
if ((p->dtjp_program = dtrace_program_strcompile(dtp,
prog, DTRACE_PROBESPEC_NAME, jc.dtjj_consumer->dtjc_cflags,
argc, argv)) == NULL) {
dtj_throw_dtrace_exception(&jc,
"invalid probe specifier %s: %s",
prog, dtrace_errmsg(dtp, dtrace_errno(dtp)));
(*env)->ReleaseStringUTFChars(env, program, prog);
dtj_program_destroy(p, NULL);
dtj_free_argv(argv);
return (NULL);
}
(*env)->ReleaseStringUTFChars(env, program, prog);
dtj_free_argv(argv);
jprogram = dtj_add_program(&jc, p);
return (jprogram);
}
JNIEXPORT jobject JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1compileFile(JNIEnv *env,
jobject obj, jstring filename, jobjectArray args)
{
FILE *fp;
const char *file;
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
dtj_program_t *p;
int argc = 0;
char **argv = NULL;
jstring jprogram = NULL;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return (NULL);
}
dtp = jc.dtjj_consumer->dtjc_dtp;
file = dtj_GetStringNativeChars(env, filename);
if ((fp = fopen(file, "r")) == NULL) {
dtj_throw_dtrace_exception(&jc, "failed to open %s", file);
dtj_ReleaseStringNativeChars(env, filename, file);
return (NULL);
}
p = dtj_program_create(env, DTJ_PROGRAM_FILE, file);
if (!p) {
(void) fclose(fp);
dtj_ReleaseStringNativeChars(env, filename, file);
return (NULL);
}
if (args) {
argv = dtj_get_argv(env, args, &argc);
if ((*env)->ExceptionCheck(env)) {
(void) fclose(fp);
dtj_ReleaseStringNativeChars(env, filename, file);
dtj_program_destroy(p, NULL);
return (NULL);
}
}
if ((p->dtjp_program = dtrace_program_fcompile(dtp,
fp, jc.dtjj_consumer->dtjc_cflags, argc, argv)) == NULL) {
dtj_throw_dtrace_exception(&jc,
"failed to compile script %s: %s", file,
dtrace_errmsg(dtp, dtrace_errno(dtp)));
(void) fclose(fp);
dtj_ReleaseStringNativeChars(env, filename, file);
dtj_program_destroy(p, NULL);
dtj_free_argv(argv);
return (NULL);
}
(void) fclose(fp);
dtj_ReleaseStringNativeChars(env, filename, file);
dtj_free_argv(argv);
jprogram = dtj_add_program(&jc, p);
return (jprogram);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1exec(JNIEnv *env, jobject obj,
jobject program)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
int progid = -1;
uu_list_walk_t *itr;
dtj_program_t *p;
dtrace_proginfo_t *pinfo = NULL;
int i;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
dtp = jc.dtjj_consumer->dtjc_dtp;
if (program) {
progid = (*env)->GetIntField(env, program, g_progid_jf);
}
if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
dtj_throw_illegal_state(env, "no program compiled");
return;
}
itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) {
if ((progid != -1) && (progid != i)) {
continue;
}
if (p->dtjp_enabled) {
dtj_throw_illegal_state(env, "program already enabled");
uu_list_walk_end(itr);
return;
}
pinfo = &p->dtjp_info;
if (dtrace_program_exec(dtp, p->dtjp_program, pinfo) == -1) {
dtj_throw_dtrace_exception(&jc,
"failed to enable %s: %s", p->dtjp_name,
dtrace_errmsg(dtp, dtrace_errno(dtp)));
uu_list_walk_end(itr);
return;
}
p->dtjp_enabled = B_TRUE;
}
uu_list_walk_end(itr);
if (program) {
jobject programInfo = NULL;
if (!pinfo) {
dtj_throw_illegal_state(env, "program not found");
return;
}
programInfo = dtj_get_program_info(&jc, pinfo);
if (!programInfo) {
return;
}
(*env)->SetObjectField(env, program, g_proginfo_jf,
programInfo);
(*env)->DeleteLocalRef(env, programInfo);
}
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1getProgramInfo(JNIEnv *env,
jobject obj, jobject program)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
int progid;
uu_list_walk_t *itr;
dtj_program_t *p;
dtrace_proginfo_t *pinfo = NULL;
int i;
jobject programInfo = NULL;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
dtp = jc.dtjj_consumer->dtjc_dtp;
progid = (*env)->GetIntField(env, program, g_progid_jf);
if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
dtj_throw_illegal_state(env, "no program compiled");
return;
}
itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
for (i = 0; ((p = uu_list_walk_next(itr)) != NULL) && !pinfo; ++i) {
if (progid != i) {
continue;
}
pinfo = &p->dtjp_info;
dtrace_program_info(dtp, p->dtjp_program, pinfo);
}
uu_list_walk_end(itr);
programInfo = dtj_get_program_info(&jc, pinfo);
if (!programInfo) {
return;
}
(*env)->SetObjectField(env, program, g_proginfo_jf,
programInfo);
(*env)->DeleteLocalRef(env, programInfo);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1setOption(JNIEnv *env,
jobject obj, jstring option, jstring value)
{
dtj_java_consumer_t jc;
const char *opt;
const char *val;
boolean_t on;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
opt = (*env)->GetStringUTFChars(env, option, 0);
if (!opt) {
dtj_throw_out_of_memory(env,
"could not allocate option string");
return;
}
if (value) {
val = (*env)->GetStringUTFChars(env, value, 0);
if (!val) {
dtj_throw_out_of_memory(env,
"could not allocate option value string");
(*env)->ReleaseStringUTFChars(env, option, opt);
return;
}
} else {
val = NULL;
}
on = (!val || (strcmp(val, "unset") != 0));
if (dtj_cflag(&jc, opt, NULL, &on)) {
(*env)->ReleaseStringUTFChars(env, option, opt);
if (value) {
(*env)->ReleaseStringUTFChars(env, value, val);
}
return;
}
if (jc.dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT) {
dtj_request_t *request;
(void) pthread_mutex_lock(
&jc.dtjj_consumer->dtjc_request_list_lock);
request = dtj_request_create(env, DTJ_REQUEST_OPTION, opt, val);
if (request) {
if (!dtj_list_add(jc.dtjj_consumer->dtjc_request_list,
request)) {
dtj_throw_out_of_memory(env,
"Failed to add setOption request");
}
}
(void) pthread_mutex_unlock(
&jc.dtjj_consumer->dtjc_request_list_lock);
} else {
dtrace_hdl_t *dtp = jc.dtjj_consumer->dtjc_dtp;
if (dtrace_setopt(dtp, opt, val) == -1) {
dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp,
dtrace_errno(dtp)));
}
}
(*env)->ReleaseStringUTFChars(env, option, opt);
if (value) {
(*env)->ReleaseStringUTFChars(env, value, val);
}
}
JNIEXPORT jlong JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1getOption(JNIEnv *env,
jobject obj, jstring option)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
const char *opt;
dtrace_optval_t optval;
boolean_t cflag;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return (0);
}
dtp = jc.dtjj_consumer->dtjc_dtp;
opt = (*env)->GetStringUTFChars(env, option, 0);
if (!opt) {
dtj_throw_out_of_memory(env,
"could not allocate option string");
return (0);
}
if (dtj_cflag(&jc, opt, &cflag, NULL)) {
(*env)->ReleaseStringUTFChars(env, option, opt);
return (cflag ? 1 : DTRACEOPT_UNSET);
}
if (dtrace_getopt(dtp, opt, &optval) == -1) {
dtj_throw_dtrace_exception(&jc,
"couldn't get option %s: %s", opt,
dtrace_errmsg(dtp, dtrace_errno(dtp)));
(*env)->ReleaseStringUTFChars(env, option, opt);
return (0);
}
(*env)->ReleaseStringUTFChars(env, option, opt);
return (optval);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1checkProgramEnabling(JNIEnv *env,
jobject obj)
{
dtj_java_consumer_t jc;
dtj_program_t *p;
uu_list_walk_t *itr;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
dtj_throw_illegal_state(env, "no program compiled");
return;
}
itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
while ((p = uu_list_walk_next(itr)) != NULL) {
if (!p->dtjp_enabled) {
const char *type;
switch (p->dtjp_type) {
case DTJ_PROGRAM_STRING:
type = "description";
break;
case DTJ_PROGRAM_FILE:
type = "script";
break;
default:
assert(p->dtjp_type == DTJ_PROGRAM_STRING ||
p->dtjp_type == DTJ_PROGRAM_FILE);
}
dtj_throw_illegal_state(env,
"Not all compiled probes are enabled. "
"Compiled %s %s not enabled.",
type, p->dtjp_name);
uu_list_walk_end(itr);
return;
}
}
uu_list_walk_end(itr);
}
JNIEXPORT jboolean JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1isEnabled(JNIEnv *env,
jobject obj)
{
dtj_java_consumer_t jc;
dtj_program_t *p;
uu_list_walk_t *itr;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return (JNI_FALSE);
}
if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
return (JNI_FALSE);
}
itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
while ((p = uu_list_walk_next(itr)) != NULL) {
if (!p->dtjp_enabled) {
uu_list_walk_end(itr);
return (JNI_FALSE);
}
}
uu_list_walk_end(itr);
return (JNI_TRUE);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1go(JNIEnv *env, jobject obj)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
dtp = jc.dtjj_consumer->dtjc_dtp;
if (dtj_set_callback_handlers(&jc) != DTJ_OK) {
return;
}
if (dtrace_go(dtp) != 0) {
dtj_throw_dtrace_exception(&jc,
"could not enable tracing: %s",
dtrace_errmsg(dtp, dtrace_errno(dtp)));
return;
}
jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_GO;
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1stop(JNIEnv *env,
jobject obj)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
dtp = jc.dtjj_consumer->dtjc_dtp;
if (dtrace_stop(dtp) == -1) {
dtj_throw_dtrace_exception(&jc,
"couldn't stop tracing: %s",
dtrace_errmsg(jc.dtjj_consumer->dtjc_dtp,
dtrace_errno(jc.dtjj_consumer->dtjc_dtp)));
} else {
jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP;
}
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1consume(JNIEnv *env,
jobject obj)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
dtp = jc.dtjj_consumer->dtjc_dtp;
jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_START;
if (dtj_java_consumer_init(env, &jc) != DTJ_OK) {
return;
}
(void) pthread_setspecific(g_dtj_consumer_key, &jc);
if (jc.dtjj_consumer->dtjc_process_list != NULL) {
struct ps_prochandle *P;
uu_list_walk_t *itr;
if ((*env)->ExceptionCheck(env)) {
dtj_java_consumer_fini(env, &jc);
return;
}
(*env)->MonitorEnter(env, g_caller_jc);
if ((*env)->ExceptionCheck(env)) {
dtj_java_consumer_fini(env, &jc);
return;
}
itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list,
0);
while ((P = dtj_pointer_list_walk_next(itr)) !=
DTJ_INVALID_PTR) {
dtrace_proc_continue(dtp, P);
}
uu_list_walk_end(itr);
(*env)->MonitorExit(env, g_caller_jc);
if ((*env)->ExceptionCheck(env)) {
dtj_java_consumer_fini(env, &jc);
return;
}
}
(void) dtj_consume(&jc);
dtj_java_consumer_fini(env, &jc);
dtj_stop(&jc);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1interrupt(JNIEnv *env,
jobject obj)
{
dtj_java_consumer_t jc;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
jc.dtjj_consumer->dtjc_interrupt = B_TRUE;
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1close(JNIEnv *env, jobject obj)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
dtp = jc.dtjj_consumer->dtjc_dtp;
if (jc.dtjj_consumer->dtjc_process_list != NULL) {
struct ps_prochandle *P;
uu_list_walk_t *itr;
itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list,
0);
while ((P = dtj_pointer_list_walk_next(itr)) !=
DTJ_INVALID_PTR) {
dtrace_proc_release(dtp, P);
}
uu_list_walk_end(itr);
}
dtrace_close(dtp);
}
JNIEXPORT jint JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1openCount(JNIEnv *env, jclass c)
{
int open;
(void) pthread_mutex_lock(&g_table_lock);
if (g_consumer_table == NULL) {
open = 0;
} else {
open = g_consumer_count;
}
(void) pthread_mutex_unlock(&g_table_lock);
return (open);
}
JNIEXPORT jlong JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1quantizeBucket(JNIEnv *env,
jclass c, jint bucket)
{
return (DTRACE_QUANTIZE_BUCKETVAL(bucket));
}
JNIEXPORT jstring JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupKernelFunction(
JNIEnv *jenv, jobject caller, jobject address)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
jstring jfunc;
GElf_Addr addr;
char dummy;
char *s;
int rc;
if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) {
return (NULL);
}
dtp = jc.dtjj_consumer->dtjc_dtp;
if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) {
addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv,
address, g_intval_jm);
} else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) {
addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv,
address, g_longval_jm);
} else {
dtj_throw_class_cast(jenv, "Expected Number address");
return (NULL);
}
rc = dtrace_addr2str(dtp, addr, &dummy, 1);
s = malloc(rc + 1);
if (!s) {
dtj_throw_out_of_memory(jenv,
"Failed to allocate kernel function name");
return (NULL);
}
(void) dtrace_addr2str(dtp, addr, s, rc + 1);
jfunc = (*jenv)->NewStringUTF(jenv, s);
free(s);
return (jfunc);
}
JNIEXPORT jstring JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupUserFunction(JNIEnv *jenv,
jobject caller, jint pid, jobject address)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
jstring jfunc;
GElf_Addr addr;
char dummy;
char *s;
int rc;
if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) {
return (NULL);
}
dtp = jc.dtjj_consumer->dtjc_dtp;
if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) {
addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv,
address, g_intval_jm);
} else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) {
addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv,
address, g_longval_jm);
} else {
dtj_throw_class_cast(jenv, "Expected Number address");
return (NULL);
}
rc = dtrace_uaddr2str(dtp, pid, addr, &dummy, 1);
s = malloc(rc + 1);
if (!s) {
dtj_throw_out_of_memory(jenv,
"Failed to allocate user function name");
return (NULL);
}
(void) dtrace_uaddr2str(dtp, pid, addr, s, rc + 1);
jfunc = (*jenv)->NewStringUTF(jenv, s);
free(s);
return (jfunc);
}
JNIEXPORT jobject JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1getAggregate(JNIEnv *env,
jobject obj, jobject spec)
{
dtj_java_consumer_t jc;
jobject aggregate = NULL;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return (NULL);
}
if (dtj_java_consumer_init(env, &jc) != DTJ_OK) {
return (NULL);
}
jc.dtjj_aggregate_spec = spec;
(void) pthread_setspecific(g_dtj_consumer_key, &jc);
aggregate = dtj_get_aggregate(&jc);
if (!aggregate) {
jc.dtjj_consumer->dtjc_interrupt = B_TRUE;
}
dtj_java_consumer_fini(env, &jc);
return (aggregate);
}
JNIEXPORT int JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1createProcess(JNIEnv *jenv,
jobject caller, jstring command)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
struct ps_prochandle *P;
char **argv;
int argc;
if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) {
return (-1);
}
dtp = jc.dtjj_consumer->dtjc_dtp;
if (jc.dtjj_consumer->dtjc_process_list == NULL) {
jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create();
if (!jc.dtjj_consumer->dtjc_process_list) {
dtj_throw_out_of_memory(jenv,
"Could not allocate process list");
return (-1);
}
}
argv = dtj_make_argv(jenv, command, &argc);
if ((*jenv)->ExceptionCheck(jenv)) {
return (-1);
}
P = dtrace_proc_create(dtp, argv[0], argv);
dtj_free_argv(argv);
if (!P) {
dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp,
dtrace_errno(dtp)));
return (-1);
}
if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) {
dtj_throw_out_of_memory(jenv,
"Failed to add process to process list");
dtrace_proc_release(dtp, P);
return (-1);
}
return (Pstatus(P)->pr_pid);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1grabProcess(JNIEnv *jenv,
jobject caller, jint pid)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
struct ps_prochandle *P;
if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) {
return;
}
dtp = jc.dtjj_consumer->dtjc_dtp;
if (jc.dtjj_consumer->dtjc_process_list == NULL) {
jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create();
if (jc.dtjj_consumer->dtjc_process_list == NULL) {
dtj_throw_out_of_memory(jenv,
"Could not allocate process list");
return;
}
}
P = dtrace_proc_grab(dtp, (pid_t)pid, 0);
if (!P) {
dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp,
dtrace_errno(dtp)));
return;
}
if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) {
dtj_throw_out_of_memory(jenv,
"Failed to add process to process list");
dtrace_proc_release(dtp, P);
return;
}
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbes(JNIEnv *env,
jobject obj, jobject list, jobject filter)
{
dtj_list_probes(env, obj, list, filter, dtj_list_probe);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbeDetail(JNIEnv *env,
jobject obj, jobject list, jobject filter)
{
dtj_list_probes(env, obj, list, filter, dtj_list_probe_detail);
}
static void
dtj_list_probes(JNIEnv *env, jobject obj, jobject list, jobject filter,
dtrace_probe_f *func)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
dtrace_probedesc_t probe;
dtrace_probedesc_t *pdp = NULL;
const char *probestr;
int rc;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
dtp = jc.dtjj_consumer->dtjc_dtp;
jc.dtjj_probelist = list;
(*env)->CallVoidMethod(env, list, g_listclear_jm);
if ((*env)->ExceptionCheck(env)) {
return;
}
if (filter) {
jstring jprobestr = NULL;
jprobestr = (*env)->CallObjectMethod(env, filter,
g_tostring_jm);
if ((*env)->ExceptionCheck(env)) {
return;
}
probestr = (*env)->GetStringUTFChars(env, jprobestr, NULL);
if (!probestr) {
(*env)->DeleteLocalRef(env, jprobestr);
return;
}
bzero(&probe, sizeof (probe));
rc = dtrace_str2desc(dtp, DTRACE_PROBESPEC_NAME, probestr,
&probe);
(*env)->ReleaseStringUTFChars(env, jprobestr, probestr);
(*env)->DeleteLocalRef(env, jprobestr);
if (rc == -1) {
dtj_throw_dtrace_exception(&jc,
"%s is not a valid probe description: %s",
probestr, dtrace_errmsg(dtp,
dtrace_errno(dtp)));
return;
}
pdp = &probe;
}
(void) dtrace_probe_iter(dtp, pdp, func, &jc);
}
static int
dtj_list_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg)
{
dtj_java_consumer_t *jc = arg;
JNIEnv *jenv = jc->dtjj_jenv;
jobject jprobedesc = NULL;
jprobedesc = dtj_new_probedesc(jc, pdp);
if (!jprobedesc) {
return (-1);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm,
jprobedesc);
(*jenv)->DeleteLocalRef(jenv, jprobedesc);
if ((*jenv)->ExceptionCheck(jenv)) {
return (-1);
}
return (0);
}
static int
dtj_list_probe_detail(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp,
void *arg)
{
dtj_java_consumer_t *jc = arg;
JNIEnv *jenv = jc->dtjj_jenv;
dtrace_probeinfo_t p;
jobject jprobedesc = NULL;
jobject jprobeinfo = NULL;
jobject jprobe = NULL;
jprobedesc = dtj_new_probedesc(jc, pdp);
if (!jprobedesc) {
return (-1);
}
if (dtrace_probe_info(dtp, pdp, &p) == 0) {
jprobeinfo = dtj_new_probeinfo(jc, &p);
if (!jprobeinfo) {
(*jenv)->DeleteLocalRef(jenv, jprobedesc);
return (-1);
}
}
jprobe = (*jenv)->NewObject(jenv, g_probe_jc, g_probeinit_jm,
jprobedesc, jprobeinfo);
(*jenv)->DeleteLocalRef(jenv, jprobedesc);
(*jenv)->DeleteLocalRef(jenv, jprobeinfo);
if (!jprobe) {
return (-1);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm,
jprobe);
(*jenv)->DeleteLocalRef(jenv, jprobe);
if ((*jenv)->ExceptionCheck(jenv)) {
return (-1);
}
return (0);
}
static int
dtj_list_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
dtrace_stmtdesc_t *stp, void *arg)
{
dtj_java_consumer_t *jc = arg;
dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc;
if (edp == jc->dtjj_consumer->dtjc_last_probe) {
return (0);
}
if (dtrace_probe_iter(dtp, &edp->dted_probe,
jc->dtjj_consumer->dtjc_plistfunc, arg) != 0) {
dtj_throw_dtrace_exception(jc,
"failed to match %s:%s:%s:%s: %s",
edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod,
edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name,
dtrace_errmsg(dtp, dtrace_errno(dtp)));
return (1);
}
jc->dtjj_consumer->dtjc_last_probe = edp;
return (0);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbes(JNIEnv *env,
jobject obj, jobject list, jobject program)
{
dtj_list_compiled_probes(env, obj, list, program, dtj_list_probe);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbeDetail(
JNIEnv *env, jobject obj, jobject list, jobject program)
{
dtj_list_compiled_probes(env, obj, list, program,
dtj_list_probe_detail);
}
static void
dtj_list_compiled_probes(JNIEnv *env, jobject obj, jobject list,
jobject program, dtrace_probe_f *func)
{
dtj_java_consumer_t jc;
dtrace_hdl_t *dtp;
uu_list_walk_t *itr;
dtj_program_t *p;
boolean_t found;
int progid = -1;
int i;
if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
return;
}
dtp = jc.dtjj_consumer->dtjc_dtp;
jc.dtjj_probelist = list;
(*env)->CallVoidMethod(env, list, g_listclear_jm);
if ((*env)->ExceptionCheck(env)) {
return;
}
if (program) {
if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
dtj_throw_no_such_element(env, "no compiled program");
return;
}
progid = (*env)->GetIntField(env, program, g_progid_jf);
if (progid == -1) {
dtj_throw_illegal_argument(env, "invalid program");
return;
}
}
jc.dtjj_consumer->dtjc_plistfunc = func;
found = B_FALSE;
itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) {
if ((progid != -1) && (progid != i)) {
continue;
}
found = B_TRUE;
(void) dtrace_stmt_iter(dtp, p->dtjp_program,
(dtrace_stmt_f *)dtj_list_stmt, &jc);
}
uu_list_walk_end(itr);
if (program && !found) {
dtj_throw_no_such_element(env, "program not found");
}
}
JNIEXPORT jstring JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1getVersion(JNIEnv *env,
jclass class)
{
return (dtj_NewStringNative(env, _dtrace_version));
}
JNIEXPORT jstring JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1getExecutableName(JNIEnv *env,
jclass class)
{
jstring jname = NULL;
const char *name = NULL;
char *s;
int len;
name = dtj_getexecname();
len = strlen(name);
s = malloc(len + 1);
if (!s) {
dtj_throw_out_of_memory(env, "Failed to allocate execname");
return (NULL);
}
(void) strcpy(s, name);
name = basename(s);
free(s);
jname = (*env)->NewStringUTF(env, name);
return (jname);
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1setMaximumConsumers(JNIEnv *env,
jclass class, jint max)
{
g_max_consumers = max;
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1setDebug(JNIEnv *env,
jclass class, jboolean debug)
{
g_dtj_util_debug = debug;
}
JNIEXPORT void JNICALL
Java_org_opensolaris_os_dtrace_LocalConsumer__1destroy(JNIEnv *env, jobject obj)
{
dtj_consumer_t *c;
c = dtj_remove_consumer(env, obj);
if (c == NULL) {
return;
}
dtj_consumer_destroy(c);
}