#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <sys/wait.h>
#include <limits.h>
#include <signal.h>
#include <libproc.h>
#include <pthread.h>
#include <dtrace_jni.h>
static int dtj_chewrec(const dtrace_probedata_t *, const dtrace_recdesc_t *,
void *);
static int dtj_chew(const dtrace_probedata_t *, void *);
static dtj_status_t dtj_process_requests(dtj_java_consumer_t *);
static int dtj_drophandler(const dtrace_dropdata_t *, void *);
static int dtj_errhandler(const dtrace_errdata_t *, void *);
static void dtj_prochandler(struct ps_prochandle *, const char *, void *);
static int dtj_setopthandler(const dtrace_setoptdata_t *, void *);
static int dtj_bufhandler(const dtrace_bufdata_t *, void *);
static jobject dtj_recdata(dtj_java_consumer_t *, uint32_t, caddr_t);
static jobject dtj_bytedata(JNIEnv *, uint32_t, caddr_t);
static jobject dtj_new_stack_record(const caddr_t, const dtrace_recdesc_t *,
dtj_java_consumer_t *);
static jobject dtj_new_probedata_stack_record(const dtrace_probedata_t *,
const dtrace_recdesc_t *, dtj_java_consumer_t *);
static jobject dtj_new_symbol_record(const caddr_t, const dtrace_recdesc_t *,
dtj_java_consumer_t *);
static jobject dtj_new_probedata_symbol_record(const dtrace_probedata_t *,
const dtrace_recdesc_t *, dtj_java_consumer_t *);
static jobject dtj_new_tuple_stack_record(const dtrace_aggdata_t *,
const dtrace_recdesc_t *, const char *, dtj_java_consumer_t *);
static jobject dtj_new_tuple_symbol_record(const dtrace_aggdata_t *,
const dtrace_recdesc_t *, const char *, dtj_java_consumer_t *);
static jobject dtj_new_distribution(const dtrace_aggdata_t *,
const dtrace_recdesc_t *, dtj_java_consumer_t *);
static jobject dtj_new_aggval(dtj_java_consumer_t *, const dtrace_aggdata_t *,
const dtrace_recdesc_t *);
static int64_t dtj_average(caddr_t, uint64_t);
static int64_t dtj_avg_total(caddr_t, uint64_t);
static int64_t dtj_avg_count(caddr_t);
static jobject dtj_stddev(JNIEnv *, caddr_t, uint64_t);
static void dtj_aggwalk_init(dtj_java_consumer_t *);
static int dtj_agghandler(const dtrace_bufdata_t *, dtj_java_consumer_t *);
static boolean_t dtj_is_included(const dtrace_aggdata_t *,
dtj_java_consumer_t *);
static void dtj_attach_frames(dtj_java_consumer_t *, jobject, jobjectArray);
static void dtj_attach_name(dtj_java_consumer_t *, jobject, jstring);
static boolean_t dtj_is_stack_action(dtrace_actkind_t);
static boolean_t dtj_is_symbol_action(dtrace_actkind_t);
static int dtj_clear(const dtrace_aggdata_t *, void *);
dtj_status_t
dtj_get_dtrace_error(dtj_java_consumer_t *jc, dtj_error_t *e)
{
JNIEnv *jenv = jc->dtjj_jenv;
dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp;
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTJ_ERR);
}
(*jenv)->MonitorEnter(jenv, g_caller_jc);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTJ_ERR);
}
e->dtje_number = dtrace_errno(dtp);
e->dtje_message = dtrace_errmsg(dtp, e->dtje_number);
(*jenv)->MonitorExit(jenv, g_caller_jc);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTJ_ERR);
}
return (DTJ_OK);
}
dtj_status_t
dtj_set_callback_handlers(dtj_java_consumer_t *jc)
{
dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp;
dtrace_optval_t optval;
if (dtrace_handle_buffered(dtp, &dtj_bufhandler, NULL) == -1) {
dtj_throw_dtrace_exception(jc,
"failed to establish buffered handler: %s",
dtrace_errmsg(dtp, dtrace_errno(dtp)));
return (DTJ_ERR);
}
if (dtrace_handle_drop(dtp, &dtj_drophandler, NULL) == -1) {
dtj_throw_dtrace_exception(jc,
"failed to establish drop handler: %s",
dtrace_errmsg(dtp, dtrace_errno(dtp)));
return (DTJ_ERR);
}
if (dtrace_handle_err(dtp, &dtj_errhandler, NULL) == -1) {
dtj_throw_dtrace_exception(jc,
"failed to establish error handler: %s",
dtrace_errmsg(dtp, dtrace_errno(dtp)));
return (DTJ_ERR);
}
if (dtrace_handle_proc(dtp, &dtj_prochandler, NULL) == -1) {
dtj_throw_dtrace_exception(jc,
"failed to establish proc handler: %s",
dtrace_errmsg(dtp, dtrace_errno(dtp)));
return (DTJ_ERR);
}
if (dtrace_getopt(dtp, "flowindent", &optval) == -1) {
dtj_throw_dtrace_exception(jc,
"couldn't get option %s: %s", "flowindent",
dtrace_errmsg(dtp, dtrace_errno(dtp)));
return (DTJ_ERR);
}
jc->dtjj_consumer->dtjc_flow = (optval != DTRACEOPT_UNSET);
if (dtrace_handle_setopt(dtp, &dtj_setopthandler, NULL) == -1) {
dtj_throw_dtrace_exception(jc,
"failed to establish setopt handler: %s",
dtrace_errmsg(dtp, dtrace_errno(dtp)));
return (DTJ_ERR);
}
return (DTJ_OK);
}
static int
dtj_drophandler(const dtrace_dropdata_t *data, void *arg)
{
dtj_java_consumer_t *jc;
JNIEnv *jenv;
const char *dropkind;
jstring msg = NULL;
jstring kind = NULL;
jobject drop = NULL;
jc = pthread_getspecific(g_dtj_consumer_key);
jenv = jc->dtjj_jenv;
msg = dtj_NewStringNative(jenv, data->dtdda_msg);
if ((*jenv)->ExceptionCheck(jenv)) {
return (DTRACE_HANDLE_ABORT);
}
switch (data->dtdda_kind) {
case DTRACEDROP_PRINCIPAL:
dropkind = "PRINCIPAL";
break;
case DTRACEDROP_AGGREGATION:
dropkind = "AGGREGATION";
break;
case DTRACEDROP_DYNAMIC:
dropkind = "DYNAMIC";
break;
case DTRACEDROP_DYNRINSE:
dropkind = "DYNRINSE";
break;
case DTRACEDROP_DYNDIRTY:
dropkind = "DYNDIRTY";
break;
case DTRACEDROP_SPEC:
dropkind = "SPEC";
break;
case DTRACEDROP_SPECBUSY:
dropkind = "SPECBUSY";
break;
case DTRACEDROP_SPECUNAVAIL:
dropkind = "SPECUNAVAIL";
break;
case DTRACEDROP_STKSTROVERFLOW:
dropkind = "STKSTROVERFLOW";
break;
case DTRACEDROP_DBLERROR:
dropkind = "DBLERROR";
break;
default:
dropkind = "UNKNOWN";
}
kind = (*jenv)->NewStringUTF(jenv, dropkind);
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->DeleteLocalRef(jenv, msg);
return (DTRACE_HANDLE_ABORT);
}
drop = (*jenv)->NewObject(jenv, g_drop_jc, g_dropinit_jm,
data->dtdda_cpu, kind, data->dtdda_drops, data->dtdda_total, msg);
(*jenv)->DeleteLocalRef(jenv, kind);
(*jenv)->DeleteLocalRef(jenv, msg);
if ((*jenv)->ExceptionCheck(jenv)) {
return (DTRACE_HANDLE_ABORT);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_drop_jm, drop);
(*jenv)->DeleteLocalRef(jenv, drop);
if ((*jenv)->ExceptionCheck(jenv)) {
return (DTRACE_HANDLE_ABORT);
}
return (DTRACE_HANDLE_OK);
}
static int
dtj_errhandler(const dtrace_errdata_t *data, void *arg)
{
dtj_java_consumer_t *jc;
JNIEnv *jenv;
const char *f;
int64_t addr;
jobject probe = NULL;
jstring fault = NULL;
jstring msg = NULL;
jobject error = NULL;
jc = pthread_getspecific(g_dtj_consumer_key);
jenv = jc->dtjj_jenv;
probe = dtj_new_probedesc(jc, data->dteda_pdesc);
if (!probe) {
return (DTRACE_HANDLE_ABORT);
}
f = dtj_get_fault_name(data->dteda_fault);
if (f) {
fault = (*jenv)->NewStringUTF(jenv, f);
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->DeleteLocalRef(jenv, probe);
return (DTRACE_HANDLE_ABORT);
}
}
switch (data->dteda_fault) {
case DTRACEFLT_BADADDR:
case DTRACEFLT_BADALIGN:
case DTRACEFLT_BADSTACK:
addr = data->dteda_addr;
break;
default:
addr = -1;
}
msg = dtj_NewStringNative(jenv, data->dteda_msg);
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->DeleteLocalRef(jenv, probe);
(*jenv)->DeleteLocalRef(jenv, fault);
return (DTRACE_HANDLE_ABORT);
}
error = (*jenv)->NewObject(jenv, g_error_jc, g_errinit_jm,
probe,
data->dteda_edesc->dtepd_epid,
data->dteda_cpu,
data->dteda_action,
data->dteda_offset,
fault, addr, msg);
(*jenv)->DeleteLocalRef(jenv, msg);
(*jenv)->DeleteLocalRef(jenv, fault);
(*jenv)->DeleteLocalRef(jenv, probe);
if ((*jenv)->ExceptionCheck(jenv)) {
return (DTRACE_HANDLE_ABORT);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_error_jm, error);
(*jenv)->DeleteLocalRef(jenv, error);
if ((*jenv)->ExceptionCheck(jenv)) {
return (DTRACE_HANDLE_ABORT);
}
return (DTRACE_HANDLE_OK);
}
static void
dtj_prochandler(struct ps_prochandle *P, const char *msg, void *arg)
{
dtj_java_consumer_t *jc;
JNIEnv *jenv;
const psinfo_t *prp = Ppsinfo(P);
int pid = Pstatus(P)->pr_pid;
int signal = -1;
char signame[SIG2STR_MAX];
const char *statusname;
int exit = INT_MAX;
jstring status = NULL;
jstring signalName = NULL;
jstring message = NULL;
jobject process = NULL;
jc = pthread_getspecific(g_dtj_consumer_key);
jenv = jc->dtjj_jenv;
switch (Pstate(P)) {
case PS_RUN:
statusname = "RUN";
break;
case PS_STOP:
statusname = "STOP";
break;
case PS_UNDEAD:
statusname = "UNDEAD";
if (prp != NULL) {
exit = WEXITSTATUS(prp->pr_wstat);
}
if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) {
signal = WTERMSIG(prp->pr_wstat);
(void) proc_signame(signal, signame, sizeof (signame));
signalName = (*jenv)->NewStringUTF(jenv, signame);
if ((*jenv)->ExceptionCheck(jenv)) {
goto proc_end;
}
}
++jc->dtjj_consumer->dtjc_procs_ended;
break;
case PS_LOST:
statusname = "LOST";
++jc->dtjj_consumer->dtjc_procs_ended;
break;
case PS_DEAD:
statusname = "DEAD";
++jc->dtjj_consumer->dtjc_procs_ended;
break;
default:
return;
}
status = (*jenv)->NewStringUTF(jenv, statusname);
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->DeleteLocalRef(jenv, signalName);
goto proc_end;
}
if (msg) {
message = dtj_NewStringNative(jenv, msg);
if (!message) {
(*jenv)->DeleteLocalRef(jenv, status);
(*jenv)->DeleteLocalRef(jenv, signalName);
goto proc_end;
}
}
process = (*jenv)->NewObject(jenv, g_process_jc, g_procinit_jm,
pid, status, signal, signalName, NULL, message);
(*jenv)->DeleteLocalRef(jenv, status);
(*jenv)->DeleteLocalRef(jenv, signalName);
(*jenv)->DeleteLocalRef(jenv, message);
if ((*jenv)->ExceptionCheck(jenv)) {
goto proc_end;
}
if (exit != INT_MAX) {
(*jenv)->CallVoidMethod(jenv, process, g_procexit_jm, exit);
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->DeleteLocalRef(jenv, process);
goto proc_end;
}
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_proc_jm, process);
(*jenv)->DeleteLocalRef(jenv, process);
proc_end:
if ((*jenv)->ExceptionCheck(jenv)) {
if (!jc->dtjj_exception) {
jthrowable e = (*jenv)->ExceptionOccurred(jenv);
jc->dtjj_exception = e;
}
(*jenv)->ExceptionClear(jenv);
}
}
static int
dtj_setopthandler(const dtrace_setoptdata_t *data, void *arg)
{
dtj_java_consumer_t *jc;
jc = pthread_getspecific(g_dtj_consumer_key);
if (strcmp(data->dtsda_option, "flowindent") == 0) {
jc->dtjj_consumer->dtjc_flow =
(data->dtsda_newval != DTRACEOPT_UNSET);
}
return (DTRACE_HANDLE_OK);
}
static jobject
dtj_bytedata(JNIEnv *jenv, uint32_t nbytes, caddr_t addr)
{
int i, j;
char *c = addr;
jobject jobj = NULL;
if (nbytes == 0) {
return ((*jenv)->NewStringUTF(jenv, ""));
}
for (i = 0; i < nbytes; i++) {
if (isprint(c[i]) || isspace(c[i]) ||
c[i] == '\b' || c[i] == '\a')
continue;
if (c[i] == '\0' && i > 0) {
for (j = i + 1; j < nbytes; j++) {
if (c[j] != '\0')
break;
}
if (j != nbytes)
break;
return (dtj_NewStringNative(jenv, (char *)addr));
}
break;
}
if (i == nbytes) {
char *s = malloc(nbytes + 1);
if (!s) {
dtj_throw_out_of_memory(jenv,
"failed to allocate string value");
return (NULL);
}
(void) strncpy(s, c, nbytes);
s[nbytes] = '\0';
jobj = dtj_NewStringNative(jenv, s);
free(s);
return (jobj);
}
jobj = (*jenv)->NewByteArray(jenv, nbytes);
if ((*jenv)->ExceptionCheck(jenv)) {
return (NULL);
}
(*jenv)->SetByteArrayRegion(jenv, (jbyteArray)jobj, 0, nbytes,
(const jbyte *)c);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->DeleteLocalRef(jenv, jobj);
return (NULL);
}
return (jobj);
}
static jobject
dtj_recdata(dtj_java_consumer_t *jc, uint32_t size, caddr_t addr)
{
JNIEnv *jenv = jc->dtjj_jenv;
jobject jobj;
jobject jrec;
switch (size) {
case 1:
jobj = (*jenv)->NewObject(jenv, g_int_jc,
g_intinit_jm, (int)(*((uint8_t *)addr)));
break;
case 2:
jobj = (*jenv)->NewObject(jenv, g_int_jc,
g_intinit_jm, (int)(*((uint16_t *)addr)));
break;
case 4:
jobj = (*jenv)->NewObject(jenv, g_int_jc,
g_intinit_jm, *((int32_t *)addr));
break;
case 8:
jobj = (*jenv)->NewObject(jenv, g_long_jc,
g_longinit_jm, *((int64_t *)addr));
break;
default:
jobj = dtj_bytedata(jenv, size, addr);
break;
}
if (!jobj) {
return (NULL);
}
jrec = (*jenv)->NewObject(jenv, g_scalar_jc,
g_scalarinit_jm, jobj, size);
(*jenv)->DeleteLocalRef(jenv, jobj);
return (jrec);
}
static int
dtj_chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec,
void *arg)
{
dtj_java_consumer_t *jc = arg;
JNIEnv *jenv = jc->dtjj_jenv;
const dtrace_eprobedesc_t *edesc = data->dtpda_edesc;
dtrace_actkind_t act;
int r;
if (rec == NULL) {
r = edesc->dtepd_nrecs;
} else {
for (r = jc->dtjj_consumer->dtjc_probedata_rec_i;
((r < edesc->dtepd_nrecs) &&
(edesc->dtepd_rec[r].dtrd_offset < rec->dtrd_offset));
++r) {
}
}
if (jc->dtjj_consumer->dtjc_probedata_act == DTRACEACT_PRINTF) {
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataattach_jm,
jc->dtjj_consumer->dtjc_probedata_rec_i, r - 1);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_CONSUME_ABORT);
}
}
if (rec == NULL) {
if (jc->dtjj_probedata) {
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataclear_jm);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_CONSUME_ABORT);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller,
g_pdatanext_jm, jc->dtjj_probedata);
(*jenv)->DeleteLocalRef(jenv, jc->dtjj_probedata);
jc->dtjj_probedata = NULL;
if ((*jenv)->ExceptionCheck(jenv)) {
return (DTRACE_CONSUME_ABORT);
}
}
(*jenv)->DeleteLocalRef(jenv, jc->dtjj_printa_buffer);
jc->dtjj_printa_buffer = NULL;
return (DTRACE_CONSUME_NEXT);
}
act = rec->dtrd_action;
jc->dtjj_consumer->dtjc_probedata_act = act;
jc->dtjj_consumer->dtjc_probedata_rec_i = r;
switch (act) {
case DTRACEACT_DIFEXPR:
case DTRACEACT_TRACEMEM:
if (rec->dtrd_size == 0) {
break;
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataadd_trace_jm,
jc->dtjj_consumer->dtjc_probedata_rec_i);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_CONSUME_ABORT);
}
break;
case DTRACEACT_PRINTF:
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataadd_printf_jm);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_CONSUME_ABORT);
}
break;
case DTRACEACT_PRINTA: {
jobject jbuf = NULL;
dtj_aggwalk_init(jc);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_CONSUME_ABORT);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataadd_printa_jm,
jc->dtjj_consumer->dtjc_printa_snaptime,
(rec->dtrd_format != 0));
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_CONSUME_ABORT);
}
if (jc->dtjj_printa_buffer == NULL) {
jbuf = (*jenv)->NewObject(jenv, g_buf_jc,
g_bufinit_jm);
if (!jbuf) {
return (DTRACE_CONSUME_ABORT);
}
jc->dtjj_printa_buffer = jbuf;
}
break;
}
case DTRACEACT_EXIT:
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataadd_exit_jm,
jc->dtjj_consumer->dtjc_probedata_rec_i);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_CONSUME_ABORT);
}
return (DTRACE_CONSUME_NEXT);
}
return (DTRACE_CONSUME_THIS);
}
static int
dtj_chew(const dtrace_probedata_t *data, void *arg)
{
dtj_java_consumer_t *jc = arg;
JNIEnv *jenv = jc->dtjj_jenv;
dtrace_eprobedesc_t *edesc;
dtrace_probedesc_t *pdesc;
dtrace_recdesc_t *rec;
int epid;
int cpu;
int nrecs;
int i;
jobject jpdata = NULL;
jobject jprobe = NULL;
jobject jflow = NULL;
jstring jflowkind = NULL;
jobject jobj = NULL;
edesc = data->dtpda_edesc;
epid = (int)edesc->dtepd_epid;
pdesc = data->dtpda_pdesc;
cpu = (int)data->dtpda_cpu;
if ((jprobe = dtj_new_probedesc(jc, pdesc)) == NULL) {
return (DTRACE_CONSUME_ABORT);
}
nrecs = edesc->dtepd_nrecs;
if (jc->dtjj_consumer->dtjc_flow) {
const char *kind;
switch (data->dtpda_flow) {
case DTRACEFLOW_ENTRY:
kind = "ENTRY";
break;
case DTRACEFLOW_RETURN:
kind = "RETURN";
break;
case DTRACEFLOW_NONE:
kind = "NONE";
break;
default:
kind = NULL;
}
if (kind != NULL) {
int depth;
jflowkind = (*jenv)->NewStringUTF(jenv, kind);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->DeleteLocalRef(jenv, jprobe);
return (DTRACE_CONSUME_ABORT);
}
depth = (data->dtpda_indent / 2);
jflow = (*jenv)->NewObject(jenv, g_flow_jc,
g_flowinit_jm, jflowkind, depth);
(*jenv)->DeleteLocalRef(jenv, jflowkind);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->DeleteLocalRef(jenv, jprobe);
return (DTRACE_CONSUME_ABORT);
}
}
}
jpdata = (*jenv)->NewObject(jenv, g_pdata_jc, g_pdatainit_jm,
epid, cpu, jprobe, jflow, nrecs);
(*jenv)->DeleteLocalRef(jenv, jprobe);
(*jenv)->DeleteLocalRef(jenv, jflow);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_CONSUME_ABORT);
}
for (i = 0; i < nrecs; ++i) {
rec = &edesc->dtepd_rec[i];
jobj = NULL;
if (rec->dtrd_size > 0) {
if (dtj_is_stack_action(rec->dtrd_action)) {
jobj = dtj_new_probedata_stack_record(data,
rec, jc);
} else if (dtj_is_symbol_action(rec->dtrd_action)) {
jobj = dtj_new_probedata_symbol_record(data,
rec, jc);
} else {
jobj = dtj_recdata(jc, rec->dtrd_size,
(data->dtpda_data + rec->dtrd_offset));
}
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->DeleteLocalRef(jenv, jpdata);
return (DTRACE_CONSUME_ABORT);
}
}
(*jenv)->CallVoidMethod(jenv, jpdata, g_pdataadd_jm, jobj);
(*jenv)->DeleteLocalRef(jenv, jobj);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->DeleteLocalRef(jenv, jpdata);
return (DTRACE_CONSUME_ABORT);
}
}
if (jc->dtjj_probedata != NULL) {
dtj_throw_illegal_state(jenv, "unfinished probedata");
WRAP_EXCEPTION(jenv);
(*jenv)->DeleteLocalRef(jenv, jpdata);
return (DTRACE_CONSUME_ABORT);
}
jc->dtjj_probedata = jpdata;
jc->dtjj_consumer->dtjc_probedata_rec_i = 0;
jc->dtjj_consumer->dtjc_probedata_act = DTRACEACT_NONE;
dtj_aggwalk_init(jc);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_CONSUME_ABORT);
}
return (DTRACE_CONSUME_THIS);
}
static int
dtj_bufhandler(const dtrace_bufdata_t *bufdata, void *arg)
{
dtj_java_consumer_t *jc;
JNIEnv *jenv;
const dtrace_recdesc_t *rec;
dtrace_actkind_t act = DTRACEACT_NONE;
const char *s;
jobject jstr = NULL;
jc = pthread_getspecific(g_dtj_consumer_key);
jenv = jc->dtjj_jenv;
if ((*jenv)->ExceptionCheck(jenv)) {
return (DTRACE_HANDLE_ABORT);
}
if (bufdata->dtbda_aggdata) {
return (dtj_agghandler(bufdata, jc));
}
s = bufdata->dtbda_buffered;
if (s == NULL) {
return (DTRACE_HANDLE_OK);
}
rec = bufdata->dtbda_recdesc;
if (rec) {
act = rec->dtrd_action;
}
switch (act) {
case DTRACEACT_DIFEXPR:
case DTRACEACT_TRACEMEM:
break;
case DTRACEACT_PRINTF:
jstr = dtj_NewStringNative(jenv, s);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataset_formatted_jm, jstr);
(*jenv)->DeleteLocalRef(jenv, jstr);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
break;
case DTRACEACT_STACK:
case DTRACEACT_USTACK:
case DTRACEACT_JSTACK:
jstr = (*jenv)->NewStringUTF(jenv, s);
if (!jstr) {
return (DTRACE_HANDLE_ABORT);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataadd_stack_jm,
jc->dtjj_consumer->dtjc_probedata_rec_i, jstr);
(*jenv)->DeleteLocalRef(jenv, jstr);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
break;
case DTRACEACT_USYM:
case DTRACEACT_UADDR:
case DTRACEACT_UMOD:
case DTRACEACT_SYM:
case DTRACEACT_MOD:
jstr = (*jenv)->NewStringUTF(jenv, s);
if (!jstr) {
return (DTRACE_HANDLE_ABORT);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataadd_symbol_jm,
jc->dtjj_consumer->dtjc_probedata_rec_i, jstr);
(*jenv)->DeleteLocalRef(jenv, jstr);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
break;
default:
break;
}
return (DTRACE_HANDLE_OK);
}
static boolean_t
dtj_is_stack_action(dtrace_actkind_t act)
{
boolean_t stack_action;
switch (act) {
case DTRACEACT_STACK:
case DTRACEACT_USTACK:
case DTRACEACT_JSTACK:
stack_action = B_TRUE;
break;
default:
stack_action = B_FALSE;
}
return (stack_action);
}
static boolean_t
dtj_is_symbol_action(dtrace_actkind_t act)
{
boolean_t symbol_action;
switch (act) {
case DTRACEACT_USYM:
case DTRACEACT_UADDR:
case DTRACEACT_UMOD:
case DTRACEACT_SYM:
case DTRACEACT_MOD:
symbol_action = B_TRUE;
break;
default:
symbol_action = B_FALSE;
}
return (symbol_action);
}
static int
dtj_clear(const dtrace_aggdata_t *data, void *arg)
{
dtj_java_consumer_t *jc = arg;
jboolean cleared = JNI_FALSE;
jstring jname = NULL;
if (jc->dtjj_aggregate_spec) {
JNIEnv *jenv = jc->dtjj_jenv;
dtrace_aggdesc_t *aggdesc = data->dtada_desc;
jname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name);
if (!jname) {
return (DTRACE_AGGWALK_ABORT);
}
cleared = (*jenv)->CallBooleanMethod(jenv,
jc->dtjj_aggregate_spec, g_aggspec_cleared_jm, jname);
(*jenv)->DeleteLocalRef(jenv, jname);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_AGGWALK_ABORT);
}
}
return (cleared ? DTRACE_AGGWALK_CLEAR : DTRACE_AGGWALK_NEXT);
}
static int64_t
dtj_average(caddr_t addr, uint64_t normal)
{
int64_t *data = (int64_t *)addr;
return (data[0] ?
(data[1] / (int64_t)normal / data[0]) : 0);
}
static int64_t
dtj_avg_total(caddr_t addr, uint64_t normal)
{
int64_t *data = (int64_t *)addr;
return (data[1] / (int64_t)normal);
}
static int64_t
dtj_avg_count(caddr_t addr)
{
int64_t *data = (int64_t *)addr;
return (data[0]);
}
static jobject
dtj_stddev_total_squares(JNIEnv *jenv, caddr_t addr, uint64_t normal)
{
jobject val128;
uint64_t *data = (uint64_t *)addr;
if (data[0] == 0) {
val128 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc,
g_bigint_val_jsm, (uint64_t)0);
} else {
val128 = dtj_int128(jenv, data[3], data[2]);
if (normal != 1) {
jobject divisor;
jobject tmp;
divisor = (*jenv)->CallStaticObjectMethod(jenv,
g_bigint_jc, g_bigint_val_jsm, normal);
tmp = val128;
val128 = (*jenv)->CallObjectMethod(jenv, tmp,
g_bigint_div_jm, divisor);
(*jenv)->DeleteLocalRef(jenv, tmp);
(*jenv)->DeleteLocalRef(jenv, divisor);
}
}
return (val128);
}
static jobject
dtj_stddev(JNIEnv *jenv, caddr_t addr, uint64_t normal)
{
jobject total_squares;
jobject stddev;
total_squares = dtj_stddev_total_squares(jenv, addr, normal);
stddev = (*jenv)->NewObject(jenv, g_aggstddev_jc, g_aggstddevinit_jm,
dtj_avg_count(addr), dtj_avg_total(addr, normal), total_squares);
(*jenv)->DeleteLocalRef(jenv, total_squares);
return (stddev);
}
static jobject
dtj_new_probedata_stack_record(const dtrace_probedata_t *data,
const dtrace_recdesc_t *rec, dtj_java_consumer_t *jc)
{
caddr_t addr;
addr = data->dtpda_data + rec->dtrd_offset;
return (dtj_new_stack_record(addr, rec, jc));
}
static jobject
dtj_new_tuple_stack_record(const dtrace_aggdata_t *data,
const dtrace_recdesc_t *rec, const char *s, dtj_java_consumer_t *jc)
{
caddr_t addr;
JNIEnv *jenv = jc->dtjj_jenv;
jobjectArray frames = NULL;
jobject jobj = NULL;
jstring jstr = NULL;
addr = data->dtada_data + rec->dtrd_offset;
jobj = dtj_new_stack_record(addr, rec, jc);
if (!jobj) {
return (NULL);
}
jstr = dtj_NewStringNative(jenv, s);
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->DeleteLocalRef(jenv, jobj);
return (NULL);
}
frames = (*jenv)->CallStaticObjectMethod(jenv, g_stack_jc,
g_parsestack_jsm, jstr);
(*jenv)->DeleteLocalRef(jenv, jstr);
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->DeleteLocalRef(jenv, jobj);
return (NULL);
}
dtj_attach_frames(jc, jobj, frames);
(*jenv)->DeleteLocalRef(jenv, frames);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (NULL);
}
return (jobj);
}
static jobject
dtj_new_probedata_symbol_record(const dtrace_probedata_t *data,
const dtrace_recdesc_t *rec, dtj_java_consumer_t *jc)
{
caddr_t addr;
addr = data->dtpda_data + rec->dtrd_offset;
return (dtj_new_symbol_record(addr, rec, jc));
}
static jobject
dtj_new_tuple_symbol_record(const dtrace_aggdata_t *data,
const dtrace_recdesc_t *rec, const char *s, dtj_java_consumer_t *jc)
{
caddr_t addr;
JNIEnv *jenv = jc->dtjj_jenv;
jobject jobj = NULL;
jstring jstr = NULL;
jstring tstr = NULL;
addr = data->dtada_data + rec->dtrd_offset;
jobj = dtj_new_symbol_record(addr, rec, jc);
if (!jobj) {
return (NULL);
}
jstr = (*jenv)->NewStringUTF(jenv, s);
if (!jstr) {
(*jenv)->DeleteLocalRef(jenv, jobj);
return (NULL);
}
tstr = (*jenv)->CallObjectMethod(jenv, jstr, g_trim_jm);
(*jenv)->DeleteLocalRef(jenv, jstr);
jstr = tstr;
tstr = NULL;
dtj_attach_name(jc, jobj, jstr);
(*jenv)->DeleteLocalRef(jenv, jstr);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (NULL);
}
return (jobj);
}
static void
dtj_aggwalk_init(dtj_java_consumer_t *jc)
{
jc->dtjj_consumer->dtjc_aggid = -1;
jc->dtjj_consumer->dtjc_expected = -1;
if (jc->dtjj_tuple != NULL) {
dtj_throw_illegal_state(jc->dtjj_jenv,
"stale aggregation tuple");
}
}
static jobject
dtj_new_stack_record(const caddr_t addr, const dtrace_recdesc_t *rec,
dtj_java_consumer_t *jc)
{
JNIEnv *jenv = jc->dtjj_jenv;
dtrace_actkind_t act;
uint64_t *pc;
pid_t pid = -1;
int size;
int i;
jbyteArray raw = NULL;
jobject stack = NULL;
for (i = rec->dtrd_size - 1; (i >= 0) && !addr[i]; --i) {
}
size = (i + 1);
raw = (*jenv)->NewByteArray(jenv, size);
if (!raw) {
return (NULL);
}
(*jenv)->SetByteArrayRegion(jenv, raw, 0, size,
(const jbyte *)addr);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->DeleteLocalRef(jenv, raw);
return (NULL);
}
act = rec->dtrd_action;
switch (act) {
case DTRACEACT_STACK:
stack = (*jenv)->NewObject(jenv, g_stack_jc,
g_stackinit_jm, raw);
break;
case DTRACEACT_USTACK:
case DTRACEACT_JSTACK:
pc = (uint64_t *)(uintptr_t)addr;
pid = (pid_t)*pc;
stack = (*jenv)->NewObject(jenv, g_ustack_jc,
g_ustackinit_jm, pid, raw);
break;
default:
dtj_throw_illegal_argument(jenv,
"Expected stack action, got %d\n", act);
}
(*jenv)->DeleteLocalRef(jenv, raw);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (NULL);
}
return (stack);
}
static jobject
dtj_new_symbol_record(const caddr_t addr, const dtrace_recdesc_t *rec,
dtj_java_consumer_t *jc)
{
JNIEnv *jenv = jc->dtjj_jenv;
dtrace_actkind_t act;
uint64_t *pc;
pid_t pid = -1;
jobject symbol = NULL;
act = rec->dtrd_action;
switch (act) {
case DTRACEACT_SYM:
case DTRACEACT_MOD:
pc = (uint64_t *)addr;
symbol = (*jenv)->NewObject(jenv, g_symbol_jc,
g_symbolinit_jm, *pc);
break;
case DTRACEACT_USYM:
case DTRACEACT_UADDR:
case DTRACEACT_UMOD:
pc = (uint64_t *)(uintptr_t)addr;
pid = (pid_t)*pc;
++pc;
symbol = (*jenv)->NewObject(jenv, g_usymbol_jc,
g_usymbolinit_jm, pid, *pc);
break;
default:
dtj_throw_illegal_argument(jenv,
"Expected stack action, got %d\n", act);
}
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (NULL);
}
return (symbol);
}
static jobject
dtj_new_distribution(const dtrace_aggdata_t *data, const dtrace_recdesc_t *rec,
dtj_java_consumer_t *jc)
{
JNIEnv *jenv = jc->dtjj_jenv;
jlongArray jbuckets = NULL;
jobject jdist = NULL;
dtrace_actkind_t act = rec->dtrd_action;
int64_t *aggbuckets = (int64_t *)
(data->dtada_data + rec->dtrd_offset);
size_t size = rec->dtrd_size;
int64_t value;
uint64_t normal = data->dtada_normal;
int64_t base, step;
int levels;
int n;
switch (act) {
case DTRACEAGG_LQUANTIZE:
value = *aggbuckets++;
base = DTRACE_LQUANTIZE_BASE(value);
step = DTRACE_LQUANTIZE_STEP(value);
levels = DTRACE_LQUANTIZE_LEVELS(value);
size -= sizeof (int64_t);
n = levels + 2;
break;
case DTRACEAGG_LLQUANTIZE:
value = *aggbuckets++;
size -= sizeof (int64_t);
levels = size / sizeof (int64_t);
n = levels;
break;
case DTRACEAGG_QUANTIZE:
n = DTRACE_QUANTIZE_NBUCKETS;
levels = n - 1;
break;
}
if (size != (n * sizeof (uint64_t)) || n < 1) {
dtj_throw_illegal_state(jenv,
"size mismatch: record %d, buckets %d", size,
(n * sizeof (uint64_t)));
WRAP_EXCEPTION(jenv);
return (NULL);
}
jbuckets = (*jenv)->NewLongArray(jenv, n);
if (!jbuckets) {
return (NULL);
}
if (n > 0) {
(*jenv)->SetLongArrayRegion(jenv, jbuckets, 0, n, aggbuckets);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->DeleteLocalRef(jenv, jbuckets);
return (NULL);
}
}
switch (act) {
case DTRACEAGG_LQUANTIZE:
jdist = (*jenv)->NewObject(jenv, g_ldist_jc, g_ldistinit_jm,
base, step, jbuckets);
break;
case DTRACEAGG_QUANTIZE:
jdist = (*jenv)->NewObject(jenv, g_dist_jc, g_distinit_jm,
jbuckets);
break;
case DTRACEAGG_LLQUANTIZE:
jdist = (*jenv)->NewObject(jenv, g_lldist_jc, g_lldistinit_jm,
value, jbuckets);
break;
}
(*jenv)->DeleteLocalRef(jenv, jbuckets);
if (!jdist) {
return (NULL);
}
if (normal != 1) {
(*jenv)->CallVoidMethod(jenv, jdist, g_dist_normal_jm, normal);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->DeleteLocalRef(jenv, jdist);
return (NULL);
}
}
return (jdist);
}
static void
dtj_attach_frames(dtj_java_consumer_t *jc, jobject stack,
jobjectArray frames)
{
JNIEnv *jenv = jc->dtjj_jenv;
if ((*jenv)->IsInstanceOf(jenv, stack, g_stack_jc)) {
(*jenv)->CallVoidMethod(jenv, stack, g_stackset_frames_jm,
frames);
} else if ((*jenv)->IsInstanceOf(jenv, stack, g_ustack_jc)) {
(*jenv)->CallVoidMethod(jenv, stack, g_ustackset_frames_jm,
frames);
}
}
static void
dtj_attach_name(dtj_java_consumer_t *jc, jobject symbol, jstring s)
{
JNIEnv *jenv = jc->dtjj_jenv;
if ((*jenv)->IsInstanceOf(jenv, symbol, g_symbol_jc)) {
(*jenv)->CallVoidMethod(jenv, symbol, g_symbolset_name_jm, s);
} else if ((*jenv)->IsInstanceOf(jenv, symbol, g_usymbol_jc)) {
(*jenv)->CallVoidMethod(jenv, symbol, g_usymbolset_name_jm, s);
}
}
static int
dtj_agghandler(const dtrace_bufdata_t *bufdata, dtj_java_consumer_t *jc)
{
JNIEnv *jenv = jc->dtjj_jenv;
const dtrace_aggdata_t *aggdata = bufdata->dtbda_aggdata;
const dtrace_aggdesc_t *aggdesc;
const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc;
const char *s = bufdata->dtbda_buffered;
dtrace_actkind_t act = DTRACEACT_NONE;
int64_t aggid;
jobject jobj = NULL;
if (aggdata == NULL) {
dtj_throw_illegal_state(jenv, "null aggdata");
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
aggdesc = aggdata->dtada_desc;
aggid = *((int64_t *)(aggdata->dtada_data +
aggdesc->dtagd_rec[0].dtrd_offset));
if (aggid < 0) {
dtj_throw_illegal_argument(jenv, "negative aggregation ID");
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
if (jc->dtjj_consumer->dtjc_printa_snaptime) {
jstring jstr = dtj_NewStringNative(jenv, s);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
jobj = (*jenv)->CallObjectMethod(jenv,
jc->dtjj_printa_buffer, g_buf_append_str_jm, jstr);
(*jenv)->DeleteLocalRef(jenv, jstr);
(*jenv)->DeleteLocalRef(jenv, jobj);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
} else {
if (aggid != jc->dtjj_consumer->dtjc_aggid) {
jc->dtjj_consumer->dtjc_included =
dtj_is_included(aggdata, jc);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
}
if (!jc->dtjj_consumer->dtjc_included) {
return (DTRACE_HANDLE_OK);
}
}
jc->dtjj_consumer->dtjc_aggid = aggid;
if (jc->dtjj_consumer->dtjc_expected < 0) {
int r;
for (r = 1; r < aggdesc->dtagd_nrecs; ++r) {
act = aggdesc->dtagd_rec[r].dtrd_action;
if (DTRACEACT_ISAGG(act) ||
aggdesc->dtagd_rec[r].dtrd_size == 0) {
break;
}
}
jc->dtjj_consumer->dtjc_expected = r - 1;
}
if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGKEY) {
if (jc->dtjj_tuple == NULL) {
jc->dtjj_tuple = (*jenv)->NewObject(jenv,
g_tuple_jc, g_tupleinit_jm);
if (!jc->dtjj_tuple) {
return (DTRACE_HANDLE_ABORT);
}
}
act = rec->dtrd_action;
switch (act) {
case DTRACEACT_STACK:
case DTRACEACT_USTACK:
case DTRACEACT_JSTACK:
jobj = dtj_new_tuple_stack_record(aggdata, rec, s, jc);
break;
case DTRACEACT_USYM:
case DTRACEACT_UADDR:
case DTRACEACT_UMOD:
case DTRACEACT_SYM:
case DTRACEACT_MOD:
jobj = dtj_new_tuple_symbol_record(aggdata, rec, s, jc);
break;
default:
jobj = dtj_recdata(jc, rec->dtrd_size,
(aggdata->dtada_data + rec->dtrd_offset));
}
if (!jobj) {
return (DTRACE_HANDLE_ABORT);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_tuple,
g_tupleadd_jm, jobj);
(*jenv)->DeleteLocalRef(jenv, jobj);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
} else if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGVAL) {
dtj_aggval_t *aggval;
jstring jvalue = NULL;
jvalue = dtj_new_aggval(jc, aggdata, rec);
if (!jvalue) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
aggval = dtj_aggval_create(jenv, jvalue, aggdesc->dtagd_name,
aggid);
if (!aggval) {
(*jenv)->DeleteLocalRef(jenv, jvalue);
return (DTRACE_HANDLE_ABORT);
}
if (!dtj_list_add(jc->dtjj_aggval_list, aggval)) {
dtj_aggval_destroy(aggval, jenv);
dtj_throw_out_of_memory(jenv, "Failed to add aggval");
return (DTRACE_HANDLE_ABORT);
}
}
if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGLAST) {
dtj_aggval_t *aggval;
uu_list_walk_t *itr;
int tuple_member_count;
jobject jrec = NULL;
jstring jname = NULL;
if (jc->dtjj_consumer->dtjc_expected == 0) {
jc->dtjj_tuple = (*jenv)->GetStaticObjectField(jenv,
g_tuple_jc, g_tuple_EMPTY_jsf);
if (jc->dtjj_tuple == NULL) {
dtj_throw_out_of_memory(jenv,
"Failed to reference Tuple.EMPTY");
return (DTRACE_HANDLE_ABORT);
}
}
if (jc->dtjj_tuple == NULL) {
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdatainvalidate_printa_jm);
goto printa_output;
}
tuple_member_count = (*jenv)->CallIntMethod(jenv,
jc->dtjj_tuple, g_tuplesize_jm);
if (tuple_member_count <
jc->dtjj_consumer->dtjc_expected) {
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdatainvalidate_printa_jm);
(*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple);
jc->dtjj_tuple = NULL;
goto printa_output;
}
itr = uu_list_walk_start(jc->dtjj_aggval_list, 0);
while ((aggval = uu_list_walk_next(itr)) != NULL) {
jrec = (*jenv)->NewObject(jenv, g_aggrec_jc,
g_aggrecinit_jm, jc->dtjj_tuple,
aggval->dtja_value);
(*jenv)->DeleteLocalRef(jenv, aggval->dtja_value);
aggval->dtja_value = NULL;
if (!jrec) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
jname = (*jenv)->NewStringUTF(jenv,
aggval->dtja_aggname);
if (!jname) {
(*jenv)->DeleteLocalRef(jenv, jrec);
return (DTRACE_HANDLE_ABORT);
}
if (jc->dtjj_consumer->dtjc_printa_snaptime) {
(*jenv)->CallVoidMethod(jenv,
jc->dtjj_probedata,
g_pdataadd_aggrec_jm,
jname, aggval->dtja_aggid, jrec);
} else {
(*jenv)->CallVoidMethod(jenv,
jc->dtjj_aggregate, g_aggaddrec_jm,
jname, aggval->dtja_aggid, jrec);
}
(*jenv)->DeleteLocalRef(jenv, jrec);
(*jenv)->DeleteLocalRef(jenv, jname);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
}
uu_list_walk_end(itr);
dtj_list_clear(jc->dtjj_aggval_list, dtj_aggval_destroy,
jenv);
printa_output:
if (jc->dtjj_consumer->dtjc_printa_snaptime) {
jstring jstr = (*jenv)->CallObjectMethod(jenv,
jc->dtjj_printa_buffer, g_tostring_jm);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_printa_buffer,
g_bufsetlen_jm, 0);
(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
g_pdataadd_printa_str_jm, jc->dtjj_tuple, jstr);
(*jenv)->DeleteLocalRef(jenv, jstr);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTRACE_HANDLE_ABORT);
}
}
(*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple);
jc->dtjj_tuple = NULL;
jc->dtjj_consumer->dtjc_expected = -1;
}
return (DTRACE_HANDLE_OK);
}
static boolean_t
dtj_is_included(const dtrace_aggdata_t *data, dtj_java_consumer_t *jc)
{
JNIEnv *jenv = jc->dtjj_jenv;
if (jc->dtjj_aggregate_spec) {
jboolean included;
jstring aggname = NULL;
const dtrace_aggdesc_t *aggdesc = data->dtada_desc;
aggname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name);
if (!aggname) {
return (B_FALSE);
}
included = (*jenv)->CallBooleanMethod(jenv,
jc->dtjj_aggregate_spec, g_aggspec_included_jm,
aggname);
(*jenv)->DeleteLocalRef(jenv, aggname);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (B_FALSE);
}
return (included);
}
return (B_TRUE);
}
static jobject
dtj_new_aggval(dtj_java_consumer_t *jc, const dtrace_aggdata_t *data,
const dtrace_recdesc_t *rec)
{
JNIEnv *jenv = jc->dtjj_jenv;
jobject jvalue = NULL;
dtrace_actkind_t act;
uint64_t normal;
caddr_t addr;
int64_t value;
act = rec->dtrd_action;
normal = data->dtada_normal;
addr = data->dtada_data + rec->dtrd_offset;
if (act == DTRACEAGG_AVG) {
value = dtj_average(addr, normal);
} else {
value = (*((int64_t *)addr)) / normal;
}
if ((act == DTRACEAGG_QUANTIZE) || (act == DTRACEAGG_LQUANTIZE) ||
(act == DTRACEAGG_LLQUANTIZE)) {
jvalue = dtj_new_distribution(data, rec, jc);
} else {
switch (act) {
case DTRACEAGG_COUNT:
jvalue = (*jenv)->NewObject(jenv, g_aggcount_jc,
g_aggcountinit_jm, value);
break;
case DTRACEAGG_SUM:
jvalue = (*jenv)->NewObject(jenv, g_aggsum_jc,
g_aggsuminit_jm, value);
break;
case DTRACEAGG_AVG:
jvalue = (*jenv)->NewObject(jenv, g_aggavg_jc,
g_aggavginit_jm, value, dtj_avg_total(addr,
normal), dtj_avg_count(addr));
break;
case DTRACEAGG_MIN:
jvalue = (*jenv)->NewObject(jenv, g_aggmin_jc,
g_aggmininit_jm, value);
break;
case DTRACEAGG_MAX:
jvalue = (*jenv)->NewObject(jenv, g_aggmax_jc,
g_aggmaxinit_jm, value);
break;
case DTRACEAGG_STDDEV:
jvalue = dtj_stddev(jenv, addr, normal);
break;
default:
jvalue = NULL;
dtj_throw_illegal_argument(jenv,
"unexpected aggregation action: %d", act);
}
}
return (jvalue);
}
void
dtj_stop(dtj_java_consumer_t *jc)
{
JNIEnv *jenv;
int rc;
jthrowable e;
switch (jc->dtjj_consumer->dtjc_state) {
case DTJ_CONSUMER_GO:
case DTJ_CONSUMER_START:
break;
default:
return;
}
jenv = jc->dtjj_jenv;
e = (*jenv)->ExceptionOccurred(jenv);
if (e) {
(*jenv)->ExceptionClear(jenv);
}
(*jenv)->MonitorEnter(jenv, g_caller_jc);
if ((*jenv)->ExceptionCheck(jenv)) {
goto rethrow;
}
rc = dtrace_status(jc->dtjj_consumer->dtjc_dtp);
if (rc != DTRACE_STATUS_STOPPED) {
rc = dtrace_stop(jc->dtjj_consumer->dtjc_dtp);
}
(*jenv)->MonitorExit(jenv, g_caller_jc);
if ((*jenv)->ExceptionCheck(jenv)) {
goto rethrow;
}
if (rc == -1) {
(*jenv)->MonitorEnter(jenv, g_caller_jc);
if ((*jenv)->ExceptionCheck(jenv)) {
goto rethrow;
}
dtj_throw_dtrace_exception(jc,
"couldn't stop tracing: %s",
dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp,
dtrace_errno(jc->dtjj_consumer->dtjc_dtp)));
(*jenv)->MonitorExit(jenv, g_caller_jc);
} else {
jc->dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP;
}
rethrow:
if (e) {
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->ExceptionClear(jenv);
}
(*jenv)->Throw(jenv, e);
(*jenv)->DeleteLocalRef(jenv, e);
}
}
jobject
dtj_get_aggregate(dtj_java_consumer_t *jc)
{
JNIEnv *jenv = jc->dtjj_jenv;
hrtime_t snaptime;
int rc;
jobject aggregate = NULL;
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (NULL);
}
(*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (NULL);
}
dtj_aggwalk_init(jc);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
snaptime = gethrtime();
if (dtrace_aggregate_snap(jc->dtjj_consumer->dtjc_dtp) != 0) {
dtj_error_t e;
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) {
dtj_throw_dtrace_exception(jc, e.dtje_message);
}
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
aggregate = (*jenv)->NewObject(jenv, g_agg_jc, g_agginit_jm,
snaptime);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
jc->dtjj_aggregate = aggregate;
rc = dtrace_aggregate_print(jc->dtjj_consumer->dtjc_dtp, NULL, NULL);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
if (rc != 0) {
dtj_error_t e;
if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) {
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
if (e.dtje_number != EINTR) {
dtj_throw_dtrace_exception(jc, e.dtje_message);
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
}
dtj_aggwalk_init(jc);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
rc = dtrace_aggregate_walk(jc->dtjj_consumer->dtjc_dtp, dtj_clear, jc);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
if (rc != 0) {
dtj_error_t e;
if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) {
dtj_throw_dtrace_exception(jc, e.dtje_message);
}
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (NULL);
}
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (NULL);
}
aggregate = jc->dtjj_aggregate;
jc->dtjj_aggregate = NULL;
return (aggregate);
}
static dtj_status_t
dtj_process_requests(dtj_java_consumer_t *jc)
{
dtj_request_t *r;
uu_list_t *list = jc->dtjj_consumer->dtjc_request_list;
pthread_mutex_t *list_lock = &jc->dtjj_consumer->
dtjc_request_list_lock;
const char *opt;
const char *val;
(void) pthread_mutex_lock(list_lock);
while (!dtj_list_empty(list)) {
r = uu_list_first(list);
uu_list_remove(list, r);
switch (r->dtjr_type) {
case DTJ_REQUEST_OPTION:
opt = dtj_string_list_first(r->dtjr_args);
val = dtj_string_list_last(r->dtjr_args);
if (dtrace_setopt(jc->dtjj_consumer->dtjc_dtp, opt,
val) == -1) {
dtj_throw_dtrace_exception(jc,
"failed to set %s: %s", opt,
dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp,
dtrace_errno(jc->dtjj_consumer->dtjc_dtp)));
dtj_request_destroy(r, NULL);
(void) pthread_mutex_unlock(list_lock);
return (DTJ_ERR);
}
break;
}
dtj_request_destroy(r, NULL);
}
(void) pthread_mutex_unlock(list_lock);
return (DTJ_OK);
}
dtj_status_t
dtj_consume(dtj_java_consumer_t *jc)
{
JNIEnv *jenv = jc->dtjj_jenv;
dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp;
boolean_t done = B_FALSE;
dtj_error_t e;
do {
if (!jc->dtjj_consumer->dtjc_interrupt) {
dtrace_sleep(dtp);
}
if (jc->dtjj_consumer->dtjc_interrupt) {
done = B_TRUE;
dtj_stop(jc);
if ((*jenv)->ExceptionCheck(jenv)) {
return (DTJ_ERR);
}
} else if (jc->dtjj_consumer->dtjc_process_list != NULL) {
int nprocs = uu_list_numnodes(jc->dtjj_consumer->
dtjc_process_list);
if (jc->dtjj_consumer->dtjc_procs_ended == nprocs) {
done = B_TRUE;
dtj_stop(jc);
}
}
if (!done) {
if (dtj_process_requests(jc) != DTJ_OK) {
return (DTJ_ERR);
}
}
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTJ_ERR);
}
(*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock);
if ((*jenv)->ExceptionCheck(jenv)) {
WRAP_EXCEPTION(jenv);
return (DTJ_ERR);
}
(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller,
g_interval_began_jm);
if ((*jenv)->ExceptionCheck(jenv)) {
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (DTJ_ERR);
}
jc->dtjj_consumer->dtjc_printa_snaptime = gethrtime();
switch (dtrace_work(dtp, NULL, dtj_chew, dtj_chewrec, jc)) {
case DTRACE_WORKSTATUS_DONE:
done = B_TRUE;
break;
case DTRACE_WORKSTATUS_OKAY:
break;
default:
if ((*jenv)->ExceptionCheck(jenv)) {
jc->dtjj_consumer->dtjc_printa_snaptime = 0;
(*jenv)->MonitorExit(jenv,
jc->dtjj_consumer_lock);
return (DTJ_ERR);
}
if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) {
jc->dtjj_consumer->dtjc_printa_snaptime = 0;
(*jenv)->MonitorExit(jenv,
jc->dtjj_consumer_lock);
return (DTJ_ERR);
}
if (e.dtje_number != EINTR) {
dtj_throw_dtrace_exception(jc, e.dtje_message);
jc->dtjj_consumer->dtjc_printa_snaptime = 0;
(*jenv)->MonitorExit(jenv,
jc->dtjj_consumer_lock);
return (DTJ_ERR);
}
}
if ((*jenv)->ExceptionCheck(jenv)) {
jc->dtjj_consumer->dtjc_printa_snaptime = 0;
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
return (DTJ_ERR);
}
jc->dtjj_consumer->dtjc_printa_snaptime = 0;
(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller,
g_interval_ended_jm);
(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
if ((*jenv)->ExceptionCheck(jenv)) {
return (DTJ_ERR);
}
if (jc->dtjj_exception) {
(*jenv)->Throw(jenv, jc->dtjj_exception);
(*jenv)->DeleteLocalRef(jenv, jc->dtjj_exception);
jc->dtjj_exception = NULL;
return (DTJ_ERR);
}
} while (!done);
return (DTJ_OK);
}