11449Stomee /*
21449Stomee * CDDL HEADER START
31449Stomee *
41449Stomee * The contents of this file are subject to the terms of the
51449Stomee * Common Development and Distribution License (the "License").
61449Stomee * You may not use this file except in compliance with the License.
71449Stomee *
81449Stomee * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91449Stomee * or http://www.opensolaris.org/os/licensing.
101449Stomee * See the License for the specific language governing permissions
111449Stomee * and limitations under the License.
121449Stomee *
131449Stomee * When distributing Covered Code, include this CDDL HEADER in each
141449Stomee * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151449Stomee * If applicable, add the following below this CDDL HEADER, with the
161449Stomee * fields enclosed by brackets "[]" replaced with your own identifying
171449Stomee * information: Portions Copyright [yyyy] [name of copyright owner]
181449Stomee *
191449Stomee * CDDL HEADER END
201449Stomee */
211449Stomee
221449Stomee /*
23*6136Stomee * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
241449Stomee * Use is subject to license terms.
251449Stomee */
261449Stomee
271449Stomee #pragma ident "%Z%%M% %I% %E% SMI"
281449Stomee
291449Stomee #include <stdio.h>
301449Stomee #include <ctype.h>
311449Stomee #include <limits.h>
321449Stomee #include <errno.h>
331449Stomee #include <stdlib.h>
341449Stomee #include <unistd.h>
351449Stomee #include <strings.h>
361449Stomee #include <sys/wait.h>
371449Stomee #include <limits.h>
381449Stomee #include <signal.h>
391449Stomee #include <libproc.h>
401449Stomee #include <pthread.h>
411449Stomee #include <dtrace_jni.h>
421449Stomee
431449Stomee /*
441449Stomee * Implements the work done in the running consumer loop. The native Java
451449Stomee * methods (JNI layer) are implemented in dtrace_jni.c.
461449Stomee */
471449Stomee
481449Stomee /* Record handler passed to dtrace_work() */
491449Stomee static int dtj_chewrec(const dtrace_probedata_t *, const dtrace_recdesc_t *,
501449Stomee void *);
511449Stomee /* Probe data handler passed to dtrace_work() */
521449Stomee static int dtj_chew(const dtrace_probedata_t *, void *);
531449Stomee
541449Stomee /* Processes requests from LocalConsumer enqueued during dtrace_sleep() */
551449Stomee static dtj_status_t dtj_process_requests(dtj_java_consumer_t *);
561449Stomee
571449Stomee /*
581449Stomee * Callback handlers set in dtj_set_callback_handlers(), called from libdtrace
591449Stomee * in the consumer loop (from dtrace_work())
601449Stomee */
611449Stomee static int dtj_drophandler(const dtrace_dropdata_t *, void *);
621449Stomee static int dtj_errhandler(const dtrace_errdata_t *, void *);
631449Stomee static void dtj_prochandler(struct ps_prochandle *, const char *, void *);
641449Stomee static int dtj_setopthandler(const dtrace_setoptdata_t *, void *);
651449Stomee /*
661449Stomee * Buffered output handler called from libdtrace in both the consumer loop (from
671449Stomee * dtrace_work()) and the get_aggregate() function (from
681449Stomee * dtrace_aggregate_print()).
691449Stomee */
701449Stomee static int dtj_bufhandler(const dtrace_bufdata_t *, void *);
711449Stomee
721449Stomee /* Conversion of libdtrace data into Java Objects */
731449Stomee static jobject dtj_recdata(dtj_java_consumer_t *, uint32_t, caddr_t);
741449Stomee static jobject dtj_bytedata(JNIEnv *, uint32_t, caddr_t);
751449Stomee static jobject dtj_new_stack_record(const caddr_t, const dtrace_recdesc_t *,
761449Stomee dtj_java_consumer_t *);
771449Stomee static jobject dtj_new_probedata_stack_record(const dtrace_probedata_t *,
781449Stomee const dtrace_recdesc_t *, dtj_java_consumer_t *);
792777Stomee static jobject dtj_new_symbol_record(const caddr_t, const dtrace_recdesc_t *,
802777Stomee dtj_java_consumer_t *);
812777Stomee static jobject dtj_new_probedata_symbol_record(const dtrace_probedata_t *,
822777Stomee const dtrace_recdesc_t *, dtj_java_consumer_t *);
831449Stomee /* Aggregation data */
841449Stomee static jobject dtj_new_tuple_stack_record(const dtrace_aggdata_t *,
851449Stomee const dtrace_recdesc_t *, const char *, dtj_java_consumer_t *);
862777Stomee static jobject dtj_new_tuple_symbol_record(const dtrace_aggdata_t *,
872777Stomee const dtrace_recdesc_t *, const char *, dtj_java_consumer_t *);
881449Stomee static jobject dtj_new_distribution(const dtrace_aggdata_t *,
891449Stomee const dtrace_recdesc_t *, dtj_java_consumer_t *);
901449Stomee static jobject dtj_new_aggval(dtj_java_consumer_t *, const dtrace_aggdata_t *,
911449Stomee const dtrace_recdesc_t *);
921449Stomee static int64_t dtj_average(caddr_t, uint64_t);
931449Stomee static int64_t dtj_avg_total(caddr_t, uint64_t);
941449Stomee static int64_t dtj_avg_count(caddr_t);
95*6136Stomee static jobject dtj_stddev(JNIEnv *, caddr_t, uint64_t);
961449Stomee
971449Stomee /* Aggregation functions */
981449Stomee static void dtj_aggwalk_init(dtj_java_consumer_t *);
991449Stomee static int dtj_agghandler(const dtrace_bufdata_t *, dtj_java_consumer_t *);
1001449Stomee static boolean_t dtj_is_included(const dtrace_aggdata_t *,
1011449Stomee dtj_java_consumer_t *);
1022777Stomee static void dtj_attach_frames(dtj_java_consumer_t *, jobject, jobjectArray);
1032777Stomee static void dtj_attach_name(dtj_java_consumer_t *, jobject, jstring);
1041449Stomee static boolean_t dtj_is_stack_action(dtrace_actkind_t);
1052777Stomee static boolean_t dtj_is_symbol_action(dtrace_actkind_t);
1061449Stomee static int dtj_clear(const dtrace_aggdata_t *, void *);
1071449Stomee
1081449Stomee /*
1091449Stomee * The consumer loop needs to protect calls to libdtrace functions with a global
1101449Stomee * lock. JNI native method calls in dtrace_jni.c are already protected and do
1111449Stomee * not need this function.
1121449Stomee */
1131449Stomee dtj_status_t
dtj_get_dtrace_error(dtj_java_consumer_t * jc,dtj_error_t * e)1141449Stomee dtj_get_dtrace_error(dtj_java_consumer_t *jc, dtj_error_t *e)
1151449Stomee {
1161449Stomee JNIEnv *jenv = jc->dtjj_jenv;
1171449Stomee dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp;
1181449Stomee
1193645Stomee /* Must not call MonitorEnter with a pending exception */
1203645Stomee if ((*jenv)->ExceptionCheck(jenv)) {
1213645Stomee WRAP_EXCEPTION(jenv);
1223645Stomee return (DTJ_ERR);
1233645Stomee }
1241449Stomee /* Grab global lock */
1251449Stomee (*jenv)->MonitorEnter(jenv, g_caller_jc);
1261449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
1271449Stomee WRAP_EXCEPTION(jenv);
1281449Stomee return (DTJ_ERR);
1291449Stomee }
1301449Stomee e->dtje_number = dtrace_errno(dtp);
1311449Stomee e->dtje_message = dtrace_errmsg(dtp, e->dtje_number);
1321449Stomee (*jenv)->MonitorExit(jenv, g_caller_jc);
1331449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
1341449Stomee WRAP_EXCEPTION(jenv);
1351449Stomee return (DTJ_ERR);
1361449Stomee }
1371449Stomee return (DTJ_OK);
1381449Stomee }
1391449Stomee
1401449Stomee /*
1411449Stomee * Protected by global lock (LocalConsumer.class) that protects call to
1421449Stomee * Java_org_opensolaris_os_dtrace_LocalConsumer__1go()
1431449Stomee */
1441449Stomee dtj_status_t
dtj_set_callback_handlers(dtj_java_consumer_t * jc)1451449Stomee dtj_set_callback_handlers(dtj_java_consumer_t *jc)
1461449Stomee {
1471449Stomee dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp;
1481449Stomee dtrace_optval_t optval;
1491449Stomee
1501449Stomee if (dtrace_handle_buffered(dtp, &dtj_bufhandler, NULL) == -1) {
1511449Stomee dtj_throw_dtrace_exception(jc,
1521449Stomee "failed to establish buffered handler: %s",
1531449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp)));
1541449Stomee return (DTJ_ERR);
1551449Stomee }
1561449Stomee
1571449Stomee if (dtrace_handle_drop(dtp, &dtj_drophandler, NULL) == -1) {
1581449Stomee dtj_throw_dtrace_exception(jc,
1591449Stomee "failed to establish drop handler: %s",
1601449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp)));
1611449Stomee return (DTJ_ERR);
1621449Stomee }
1631449Stomee
1641449Stomee if (dtrace_handle_err(dtp, &dtj_errhandler, NULL) == -1) {
1651449Stomee dtj_throw_dtrace_exception(jc,
1661449Stomee "failed to establish error handler: %s",
1671449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp)));
1681449Stomee return (DTJ_ERR);
1691449Stomee }
1701449Stomee
1711449Stomee if (dtrace_handle_proc(dtp, &dtj_prochandler, NULL) == -1) {
1721449Stomee dtj_throw_dtrace_exception(jc,
1731449Stomee "failed to establish proc handler: %s",
1741449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp)));
1751449Stomee return (DTJ_ERR);
1761449Stomee }
1771449Stomee
1781449Stomee if (dtrace_getopt(dtp, "flowindent", &optval) == -1) {
1791449Stomee dtj_throw_dtrace_exception(jc,
1801449Stomee "couldn't get option %s: %s", "flowindent",
1811449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp)));
1821449Stomee return (DTJ_ERR);
1831449Stomee }
1841449Stomee
1851449Stomee jc->dtjj_consumer->dtjc_flow = (optval != DTRACEOPT_UNSET);
1861449Stomee
1871449Stomee if (dtrace_handle_setopt(dtp, &dtj_setopthandler, NULL) == -1) {
1881449Stomee dtj_throw_dtrace_exception(jc,
1891449Stomee "failed to establish setopt handler: %s",
1901449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp)));
1911449Stomee return (DTJ_ERR);
1921449Stomee }
1931449Stomee
1941449Stomee return (DTJ_OK);
1951449Stomee }
1961449Stomee
1971449Stomee static int
1981449Stomee /* ARGSUSED */
dtj_drophandler(const dtrace_dropdata_t * data,void * arg)1991449Stomee dtj_drophandler(const dtrace_dropdata_t *data, void *arg)
2001449Stomee {
2011449Stomee dtj_java_consumer_t *jc;
2021449Stomee JNIEnv *jenv;
2031449Stomee
2041449Stomee const char *dropkind;
2051449Stomee
2061449Stomee jstring msg = NULL;
2071449Stomee jstring kind = NULL;
2081449Stomee jobject drop = NULL;
2091449Stomee
2101449Stomee jc = pthread_getspecific(g_dtj_consumer_key);
2111449Stomee jenv = jc->dtjj_jenv;
2121449Stomee
2131449Stomee msg = dtj_NewStringNative(jenv, data->dtdda_msg);
2141449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
2151449Stomee return (DTRACE_HANDLE_ABORT);
2161449Stomee }
2171449Stomee switch (data->dtdda_kind) {
2181449Stomee case DTRACEDROP_PRINCIPAL:
2191449Stomee dropkind = "PRINCIPAL";
2201449Stomee break;
2211449Stomee case DTRACEDROP_AGGREGATION:
2221449Stomee dropkind = "AGGREGATION";
2231449Stomee break;
2241449Stomee case DTRACEDROP_DYNAMIC:
2251449Stomee dropkind = "DYNAMIC";
2261449Stomee break;
2271449Stomee case DTRACEDROP_DYNRINSE:
2281449Stomee dropkind = "DYNRINSE";
2291449Stomee break;
2301449Stomee case DTRACEDROP_DYNDIRTY:
2311449Stomee dropkind = "DYNDIRTY";
2321449Stomee break;
2331449Stomee case DTRACEDROP_SPEC:
2341449Stomee dropkind = "SPEC";
2351449Stomee break;
2361449Stomee case DTRACEDROP_SPECBUSY:
2371449Stomee dropkind = "SPECBUSY";
2381449Stomee break;
2391449Stomee case DTRACEDROP_SPECUNAVAIL:
2401449Stomee dropkind = "SPECUNAVAIL";
2411449Stomee break;
2421449Stomee case DTRACEDROP_STKSTROVERFLOW:
2431449Stomee dropkind = "STKSTROVERFLOW";
2441449Stomee break;
2451449Stomee case DTRACEDROP_DBLERROR:
2461449Stomee dropkind = "DBLERROR";
2471449Stomee break;
2481449Stomee default:
2491449Stomee dropkind = "UNKNOWN";
2501449Stomee }
2511449Stomee kind = (*jenv)->NewStringUTF(jenv, dropkind);
2521449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
2531449Stomee (*jenv)->DeleteLocalRef(jenv, msg);
2541449Stomee return (DTRACE_HANDLE_ABORT);
2551449Stomee }
2561449Stomee drop = (*jenv)->NewObject(jenv, g_drop_jc, g_dropinit_jm,
2571449Stomee data->dtdda_cpu, kind, data->dtdda_drops, data->dtdda_total, msg);
2581449Stomee (*jenv)->DeleteLocalRef(jenv, kind);
2591449Stomee (*jenv)->DeleteLocalRef(jenv, msg);
2601449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
2611449Stomee return (DTRACE_HANDLE_ABORT);
2621449Stomee }
2631449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_drop_jm, drop);
2641449Stomee (*jenv)->DeleteLocalRef(jenv, drop);
2651449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
2661449Stomee return (DTRACE_HANDLE_ABORT);
2671449Stomee }
2681449Stomee
2691449Stomee return (DTRACE_HANDLE_OK);
2701449Stomee }
2711449Stomee
2721449Stomee static int
2731449Stomee /* ARGSUSED */
dtj_errhandler(const dtrace_errdata_t * data,void * arg)2741449Stomee dtj_errhandler(const dtrace_errdata_t *data, void *arg)
2751449Stomee {
2761449Stomee dtj_java_consumer_t *jc;
2771449Stomee JNIEnv *jenv;
2781449Stomee
2791449Stomee const char *f;
2801449Stomee int64_t addr;
2811449Stomee
2821449Stomee jobject probe = NULL;
2831449Stomee jstring fault = NULL;
2841449Stomee jstring msg = NULL;
2851449Stomee jobject error = NULL;
2861449Stomee
2871449Stomee jc = pthread_getspecific(g_dtj_consumer_key);
2881449Stomee jenv = jc->dtjj_jenv;
2891449Stomee
2901449Stomee probe = dtj_new_probedesc(jc, data->dteda_pdesc);
2911449Stomee if (!probe) {
2921449Stomee return (DTRACE_HANDLE_ABORT);
2931449Stomee }
2941449Stomee f = dtj_get_fault_name(data->dteda_fault);
2951449Stomee if (f) {
2961449Stomee fault = (*jenv)->NewStringUTF(jenv, f);
2971449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
2981449Stomee (*jenv)->DeleteLocalRef(jenv, probe);
2991449Stomee return (DTRACE_HANDLE_ABORT);
3001449Stomee }
3011449Stomee }
3021449Stomee switch (data->dteda_fault) {
3031449Stomee case DTRACEFLT_BADADDR:
3041449Stomee case DTRACEFLT_BADALIGN:
3053682Sjhaslam case DTRACEFLT_BADSTACK:
3061449Stomee addr = data->dteda_addr;
3071449Stomee break;
3081449Stomee default:
3091449Stomee addr = -1;
3101449Stomee }
3111449Stomee msg = dtj_NewStringNative(jenv, data->dteda_msg);
3121449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
3131449Stomee (*jenv)->DeleteLocalRef(jenv, probe);
3141449Stomee (*jenv)->DeleteLocalRef(jenv, fault);
3151449Stomee return (DTRACE_HANDLE_ABORT);
3161449Stomee }
3171449Stomee error = (*jenv)->NewObject(jenv, g_error_jc, g_errinit_jm,
3181449Stomee probe,
3191449Stomee data->dteda_edesc->dtepd_epid,
3201449Stomee data->dteda_cpu,
3211449Stomee data->dteda_action,
3221449Stomee data->dteda_offset,
3231449Stomee fault, addr, msg);
3241449Stomee (*jenv)->DeleteLocalRef(jenv, msg);
3251449Stomee (*jenv)->DeleteLocalRef(jenv, fault);
3261449Stomee (*jenv)->DeleteLocalRef(jenv, probe);
3271449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
3281449Stomee return (DTRACE_HANDLE_ABORT);
3291449Stomee }
3301449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_error_jm, error);
3311449Stomee (*jenv)->DeleteLocalRef(jenv, error);
3321449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
3331449Stomee return (DTRACE_HANDLE_ABORT);
3341449Stomee }
3351449Stomee
3361449Stomee return (DTRACE_HANDLE_OK);
3371449Stomee }
3381449Stomee
3391449Stomee /*
3401449Stomee * Since the function signature does not allow us to return an abort signal, we
3411449Stomee * need to temporarily clear any pending exception before returning, since
3421449Stomee * without the abort we can't guarantee that the exception will be checked in
3431449Stomee * time to prevent invalid JNI function calls.
3441449Stomee */
3451449Stomee static void
3461449Stomee /* ARGSUSED */
dtj_prochandler(struct ps_prochandle * P,const char * msg,void * arg)3471449Stomee dtj_prochandler(struct ps_prochandle *P, const char *msg, void *arg)
3481449Stomee {
3491449Stomee dtj_java_consumer_t *jc;
3501449Stomee JNIEnv *jenv;
3511449Stomee
3521449Stomee const psinfo_t *prp = Ppsinfo(P);
3531449Stomee int pid = Pstatus(P)->pr_pid;
3541449Stomee int signal = -1;
3551449Stomee char signame[SIG2STR_MAX];
3561449Stomee const char *statusname;
3571449Stomee int exit = INT_MAX; /* invalid initial status */
3581449Stomee
3591449Stomee jstring status = NULL;
3601449Stomee jstring signalName = NULL;
3611449Stomee jstring message = NULL;
3621449Stomee jobject process = NULL;
3631449Stomee
3641449Stomee jc = pthread_getspecific(g_dtj_consumer_key);
3651449Stomee jenv = jc->dtjj_jenv;
3661449Stomee
3671449Stomee switch (Pstate(P)) {
3681449Stomee case PS_RUN:
3691449Stomee statusname = "RUN";
3701449Stomee break;
3711449Stomee case PS_STOP:
3721449Stomee statusname = "STOP";
3731449Stomee break;
3741449Stomee case PS_UNDEAD:
3751449Stomee statusname = "UNDEAD";
3761449Stomee if (prp != NULL) {
3771449Stomee exit = WEXITSTATUS(prp->pr_wstat);
3781449Stomee }
3791449Stomee if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) {
3801449Stomee signal = WTERMSIG(prp->pr_wstat);
3811449Stomee (void) proc_signame(signal, signame, sizeof (signame));
3821449Stomee signalName = (*jenv)->NewStringUTF(jenv, signame);
3831449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
3841449Stomee goto proc_end;
3851449Stomee }
3861449Stomee }
3871449Stomee ++jc->dtjj_consumer->dtjc_procs_ended;
3881449Stomee break;
3891449Stomee case PS_LOST:
3901449Stomee statusname = "LOST";
3911449Stomee ++jc->dtjj_consumer->dtjc_procs_ended;
3921449Stomee break;
3931449Stomee case PS_DEAD:
3941449Stomee /*
3951449Stomee * PS_DEAD not handled by dtrace.c prochandler, still this is a
3961449Stomee * case of process termination and it can't hurt to handle it.
3971449Stomee */
3981449Stomee statusname = "DEAD";
3991449Stomee ++jc->dtjj_consumer->dtjc_procs_ended;
4001449Stomee break;
4011449Stomee default:
4021449Stomee /*
4031449Stomee * Unexpected, but erring on the side of tolerance by not
4041449Stomee * crashing the consumer. Failure to notify listeners of
4051449Stomee * process state not handled by the dtrace.c prochandler does
4061449Stomee * not seem serious.
4071449Stomee */
4081449Stomee return;
4091449Stomee }
4101449Stomee
4111449Stomee status = (*jenv)->NewStringUTF(jenv, statusname);
4121449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
4131449Stomee (*jenv)->DeleteLocalRef(jenv, signalName);
4141449Stomee goto proc_end;
4151449Stomee }
4161449Stomee if (msg) {
4171449Stomee message = dtj_NewStringNative(jenv, msg);
4181449Stomee if (!message) {
4191449Stomee (*jenv)->DeleteLocalRef(jenv, status);
4201449Stomee (*jenv)->DeleteLocalRef(jenv, signalName);
4211449Stomee goto proc_end;
4221449Stomee }
4231449Stomee }
4241449Stomee process = (*jenv)->NewObject(jenv, g_process_jc, g_procinit_jm,
4251449Stomee pid, status, signal, signalName, NULL, message);
4261449Stomee (*jenv)->DeleteLocalRef(jenv, status);
4271449Stomee (*jenv)->DeleteLocalRef(jenv, signalName);
4281449Stomee (*jenv)->DeleteLocalRef(jenv, message);
4291449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
4301449Stomee goto proc_end;
4311449Stomee }
4321449Stomee if (exit != INT_MAX) {
4331449Stomee /* valid exit status */
4341449Stomee (*jenv)->CallVoidMethod(jenv, process, g_procexit_jm, exit);
4351449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
4361449Stomee (*jenv)->DeleteLocalRef(jenv, process);
4371449Stomee goto proc_end;
4381449Stomee }
4391449Stomee }
4401449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_proc_jm, process);
4411449Stomee (*jenv)->DeleteLocalRef(jenv, process);
4421449Stomee
4431449Stomee proc_end:
4441449Stomee
4451449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
4461449Stomee /*
4471449Stomee * Save the exception so we can rethrow it later when it's safe.
4481449Stomee */
4491449Stomee if (!jc->dtjj_exception) {
4501449Stomee jthrowable e = (*jenv)->ExceptionOccurred(jenv);
4511449Stomee jc->dtjj_exception = e;
4521449Stomee }
4531449Stomee (*jenv)->ExceptionClear(jenv);
4541449Stomee }
4551449Stomee }
4561449Stomee
4571449Stomee static int
4581449Stomee /* ARGSUSED */
dtj_setopthandler(const dtrace_setoptdata_t * data,void * arg)4591449Stomee dtj_setopthandler(const dtrace_setoptdata_t *data, void *arg)
4601449Stomee {
4611449Stomee dtj_java_consumer_t *jc;
4621449Stomee
4631449Stomee jc = pthread_getspecific(g_dtj_consumer_key);
4641449Stomee if (strcmp(data->dtsda_option, "flowindent") == 0) {
4651449Stomee jc->dtjj_consumer->dtjc_flow =
4661449Stomee (data->dtsda_newval != DTRACEOPT_UNSET);
4671449Stomee }
4681449Stomee return (DTRACE_HANDLE_OK);
4691449Stomee }
4701449Stomee
4711449Stomee /*
4721449Stomee * Most of this function lifted from libdtrace/common/dt_consume.c
4731449Stomee * dt_print_bytes().
4741449Stomee */
4751449Stomee static jobject
dtj_bytedata(JNIEnv * jenv,uint32_t nbytes,caddr_t addr)4761449Stomee dtj_bytedata(JNIEnv *jenv, uint32_t nbytes, caddr_t addr)
4771449Stomee {
4781449Stomee /*
4791449Stomee * If the byte stream is a series of printable characters, followed by
4801449Stomee * a terminating byte, we print it out as a string. Otherwise, we
4811449Stomee * assume that it's something else and just print the bytes.
4821449Stomee */
4831449Stomee int i, j;
4841449Stomee char *c = addr;
4851449Stomee
4861449Stomee jobject jobj = NULL; /* return value */
4871449Stomee
4881449Stomee if (nbytes == 0) {
4891449Stomee return ((*jenv)->NewStringUTF(jenv, ""));
4901449Stomee }
4911449Stomee
4921449Stomee for (i = 0; i < nbytes; i++) {
4931449Stomee /*
4941449Stomee * We define a "printable character" to be one for which
4951449Stomee * isprint(3C) returns non-zero, isspace(3C) returns non-zero,
4961449Stomee * or a character which is either backspace or the bell.
4971449Stomee * Backspace and the bell are regrettably special because
4981449Stomee * they fail the first two tests -- and yet they are entirely
4991449Stomee * printable. These are the only two control characters that
5001449Stomee * have meaning for the terminal and for which isprint(3C) and
5011449Stomee * isspace(3C) return 0.
5021449Stomee */
5031449Stomee if (isprint(c[i]) || isspace(c[i]) ||
5041449Stomee c[i] == '\b' || c[i] == '\a')
5051449Stomee continue;
5061449Stomee
5071449Stomee if (c[i] == '\0' && i > 0) {
5081449Stomee /*
5091449Stomee * This looks like it might be a string. Before we
5101449Stomee * assume that it is indeed a string, check the
5111449Stomee * remainder of the byte range; if it contains
5121449Stomee * additional non-nul characters, we'll assume that
5131449Stomee * it's a binary stream that just happens to look like
5141449Stomee * a string.
5151449Stomee */
5161449Stomee for (j = i + 1; j < nbytes; j++) {
5171449Stomee if (c[j] != '\0')
5181449Stomee break;
5191449Stomee }
5201449Stomee
5211449Stomee if (j != nbytes)
5221449Stomee break;
5231449Stomee
5241449Stomee /* It's a string */
5251449Stomee return (dtj_NewStringNative(jenv, (char *)addr));
5261449Stomee }
5271449Stomee
5281449Stomee break;
5291449Stomee }
5301449Stomee
5311449Stomee if (i == nbytes) {
5321449Stomee /*
5331449Stomee * The byte range is all printable characters, but there is
5341449Stomee * no trailing nul byte. We'll assume that it's a string.
5351449Stomee */
5361449Stomee char *s = malloc(nbytes + 1);
5371449Stomee if (!s) {
5381449Stomee dtj_throw_out_of_memory(jenv,
5391449Stomee "failed to allocate string value");
5401449Stomee return (NULL);
5411449Stomee }
5421449Stomee (void) strncpy(s, c, nbytes);
5431449Stomee s[nbytes] = '\0';
5441449Stomee jobj = dtj_NewStringNative(jenv, s);
5451449Stomee free(s);
5461449Stomee return (jobj);
5471449Stomee }
5481449Stomee
5491449Stomee /* return byte array */
5501449Stomee jobj = (*jenv)->NewByteArray(jenv, nbytes);
5511449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
5521449Stomee return (NULL);
5531449Stomee }
5541449Stomee (*jenv)->SetByteArrayRegion(jenv, (jbyteArray)jobj, 0, nbytes,
5551449Stomee (const jbyte *)c);
5561449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
5571449Stomee WRAP_EXCEPTION(jenv);
5581449Stomee (*jenv)->DeleteLocalRef(jenv, jobj);
5591449Stomee return (NULL);
5601449Stomee }
5611449Stomee return (jobj);
5621449Stomee }
5631449Stomee
5641449Stomee /*
5651449Stomee * Return NULL if memory could not be allocated (OutOfMemoryError is thrown in
5661449Stomee * that case).
5671449Stomee */
5681449Stomee static jobject
dtj_recdata(dtj_java_consumer_t * jc,uint32_t size,caddr_t addr)5691449Stomee dtj_recdata(dtj_java_consumer_t *jc, uint32_t size, caddr_t addr)
5701449Stomee {
5711449Stomee JNIEnv *jenv = jc->dtjj_jenv;
5721449Stomee jobject jobj;
5732777Stomee jobject jrec;
5741449Stomee
5751449Stomee switch (size) {
5761449Stomee case 1:
5772777Stomee jobj = (*jenv)->NewObject(jenv, g_int_jc,
5782777Stomee g_intinit_jm, (int)(*((uint8_t *)addr)));
5791449Stomee break;
5801449Stomee case 2:
5812777Stomee jobj = (*jenv)->NewObject(jenv, g_int_jc,
5821449Stomee /* LINTED - alignment */
5832777Stomee g_intinit_jm, (int)(*((uint16_t *)addr)));
5841449Stomee break;
5851449Stomee case 4:
5861449Stomee jobj = (*jenv)->NewObject(jenv, g_int_jc,
5871449Stomee /* LINTED - alignment */
5881449Stomee g_intinit_jm, *((int32_t *)addr));
5891449Stomee break;
5901449Stomee case 8:
5911449Stomee jobj = (*jenv)->NewObject(jenv, g_long_jc,
5921449Stomee /* LINTED - alignment */
5931449Stomee g_longinit_jm, *((int64_t *)addr));
5941449Stomee break;
5951449Stomee default:
5961449Stomee jobj = dtj_bytedata(jenv, size, addr);
5971449Stomee break;
5981449Stomee }
5991449Stomee
6002777Stomee if (!jobj) {
6012777Stomee return (NULL); /* OutOfMemoryError pending */
6022777Stomee }
6032777Stomee
6042777Stomee jrec = (*jenv)->NewObject(jenv, g_scalar_jc,
6052777Stomee g_scalarinit_jm, jobj, size);
6062777Stomee (*jenv)->DeleteLocalRef(jenv, jobj);
6072777Stomee
6082777Stomee return (jrec);
6091449Stomee }
6101449Stomee
6111449Stomee /*
6121449Stomee * This is the record handling function passed to dtrace_work(). It differs
6131449Stomee * from the bufhandler registered with dtrace_handle_buffered() as follows:
6141449Stomee *
6151449Stomee * 1. It does not have access to libdtrace formatted output.
6161449Stomee * 2. It is called once for every D program statement, not for every
6171449Stomee * output-producing D action or aggregation record. A statement may be a
6181449Stomee * variable assignment, having no size and producing no output.
6191449Stomee * 3. It is called for the D exit() action; the bufhandler is not.
6201449Stomee * 4. In response to the printa() action, it is called with a record having an
6211449Stomee * action of type DTRACEACT_PRINTA. The bufhandler never sees that action
6221449Stomee * value. It only sees the output-producing aggregation records.
6231449Stomee * 5. It is called with a NULL record at the end of each probedata.
6241449Stomee */
6251449Stomee static int
dtj_chewrec(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,void * arg)6261449Stomee dtj_chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec,
6271449Stomee void *arg)
6281449Stomee {
6291449Stomee dtj_java_consumer_t *jc = arg;
6301449Stomee JNIEnv *jenv = jc->dtjj_jenv;
6311449Stomee
6321449Stomee const dtrace_eprobedesc_t *edesc = data->dtpda_edesc;
6331449Stomee dtrace_actkind_t act;
6341449Stomee int r;
6351449Stomee
6361449Stomee /*
6371449Stomee * Update the record index to that of the current record, or to that of
6381449Stomee * the last record if rec is NULL (signalling end of probe data).
6391449Stomee */
6401449Stomee if (rec == NULL) {
6411449Stomee r = edesc->dtepd_nrecs; /* end of probe data */
6421449Stomee } else {
6431449Stomee /*
6441449Stomee * This record handler is called once for the printf() action,
6451449Stomee * but there may be multiple records in the probedata
6461449Stomee * corresponding to the unformatted elements of that printf().
6471449Stomee * We don't know ahead of time how many probedata records
6481449Stomee * libdtrace will consume to produce output for one printf()
6491449Stomee * action, so we look back at the previous call to dtj_chewrec()
6501449Stomee * to see how many probedata records were consumed. All
6511449Stomee * non-null elements in the range from the previous record index
6521449Stomee * up to and not including the current record index are assumed
6531449Stomee * to be unformatted printf() elements, and will be attached to
6541449Stomee * the PrintfRecord from the previous call. A null element in
6551449Stomee * that range is the result of a D program statement preceding
6561449Stomee * the printf() that is not a D action. These generate
6571449Stomee * probedata records accounted for by the null placeholder, but
6581449Stomee * do not advance the probedata offset and are not part of the
6591449Stomee * subsequent printf().
6601449Stomee *
6611449Stomee * If rec->dtrd_size == 0, the record represents a D program
6621449Stomee * statement that is not a D action. It has no size and does
6631449Stomee * not advance the offset in the probedata. Handle it normally
6641449Stomee * without special-casing or premature return, since in all
6651449Stomee * cases we look at the previous record later in this function.
6661449Stomee */
6671449Stomee for (r = jc->dtjj_consumer->dtjc_probedata_rec_i;
6681449Stomee ((r < edesc->dtepd_nrecs) &&
6691449Stomee (edesc->dtepd_rec[r].dtrd_offset < rec->dtrd_offset));
6701449Stomee ++r) {
6711449Stomee }
6721449Stomee }
6731449Stomee
6741449Stomee /*
6751449Stomee * Attach the Java representations of the libdtrace data elements
6761449Stomee * pertaining to the previous call to this record handler to the
6771449Stomee * previous Java Record. (All data elements belonging to the current
6781449Stomee * probedata are added to a single list by the probedata consumer
6791449Stomee * function dtj_chew() before this record consumer function is ever
6801449Stomee * called.) For example, if the previous Record was generated by the
6811449Stomee * printf() action, and dtj_chew() listed 3 records for its 3
6821449Stomee * unformatted elements, those 3 libdtrace records comprise 1
6831449Stomee * PrintfRecord. Note that we cannot know how many data elements apply
6841449Stomee * to the current rec until we find out the data index where the next
6851449Stomee * rec starts. (The knowledge of how many probedata records to consume
6861449Stomee * is private to libdtrace.)
6871449Stomee */
6881449Stomee if (jc->dtjj_consumer->dtjc_probedata_act == DTRACEACT_PRINTF) {
6891449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
6901449Stomee g_pdataattach_jm,
6911449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i, r - 1);
6921449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
6931449Stomee WRAP_EXCEPTION(jenv);
6941449Stomee return (DTRACE_CONSUME_ABORT);
6951449Stomee }
6961449Stomee }
6971449Stomee
6981449Stomee if (rec == NULL) {
6991449Stomee /*
7001449Stomee * End of probe data. Notify listeners of the new ProbeData
7011449Stomee * instance.
7021449Stomee */
7031449Stomee if (jc->dtjj_probedata) {
7041449Stomee /* previous probedata */
7051449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
7061449Stomee g_pdataclear_jm);
7071449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
7081449Stomee WRAP_EXCEPTION(jenv);
7091449Stomee return (DTRACE_CONSUME_ABORT);
7101449Stomee }
7111449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller,
7121449Stomee g_pdatanext_jm, jc->dtjj_probedata);
7131449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_probedata);
7141449Stomee jc->dtjj_probedata = NULL;
7151449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
7161449Stomee /*
7171449Stomee * Do not wrap exception thrown from
7181449Stomee * ConsumerListener.
7191449Stomee */
7201449Stomee return (DTRACE_CONSUME_ABORT);
7211449Stomee }
7221449Stomee }
7231449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_printa_buffer);
7241449Stomee jc->dtjj_printa_buffer = NULL;
7251449Stomee return (DTRACE_CONSUME_NEXT);
7261449Stomee }
7271449Stomee
7281449Stomee act = rec->dtrd_action;
7291449Stomee
7301449Stomee /* Set previous record action and data index to current */
7311449Stomee jc->dtjj_consumer->dtjc_probedata_act = act;
7321449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i = r;
7331449Stomee
7341449Stomee switch (act) {
7351449Stomee case DTRACEACT_DIFEXPR:
7361449Stomee if (rec->dtrd_size == 0) {
7371449Stomee /*
7381449Stomee * The current record is not a D action, but a program
7391449Stomee * statement such as a variable assignment, not to be
7401449Stomee * confused with the trace() action.
7411449Stomee */
7421449Stomee break;
7431449Stomee }
7441449Stomee /*
7451449Stomee * Add a Record for the trace() action that references the
7461449Stomee * native probedata element listed at the current index.
7471449Stomee */
7481449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
7491449Stomee g_pdataadd_trace_jm,
7501449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i);
7511449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
7521449Stomee WRAP_EXCEPTION(jenv);
7531449Stomee return (DTRACE_CONSUME_ABORT);
7541449Stomee }
7551449Stomee break;
7561449Stomee case DTRACEACT_PRINTF:
7571449Stomee /*
7581449Stomee * Just add an empty PrintfRecord for now. We'll attach the
7591449Stomee * unformatted elements in a subsequent call to this function.
7601449Stomee * (We don't know how many there will be.)
7611449Stomee */
7621449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
7631449Stomee g_pdataadd_printf_jm);
7641449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
7651449Stomee WRAP_EXCEPTION(jenv);
7661449Stomee return (DTRACE_CONSUME_ABORT);
7671449Stomee }
7681449Stomee /* defer formatted string to dtj_bufhandler() */
7691449Stomee break;
7701449Stomee case DTRACEACT_PRINTA: {
7711449Stomee jobject jbuf = NULL;
7721449Stomee
7731449Stomee dtj_aggwalk_init(jc);
7741449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
7751449Stomee WRAP_EXCEPTION(jenv);
7761449Stomee return (DTRACE_CONSUME_ABORT);
7771449Stomee }
7781449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
7791449Stomee g_pdataadd_printa_jm,
7801449Stomee jc->dtjj_consumer->dtjc_printa_snaptime,
7811449Stomee (rec->dtrd_format != 0));
7821449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
7831449Stomee WRAP_EXCEPTION(jenv);
7841449Stomee return (DTRACE_CONSUME_ABORT);
7851449Stomee }
7861449Stomee if (jc->dtjj_printa_buffer == NULL) {
7871449Stomee /*
7883645Stomee * Create a StringBuilder to collect the pieces of
7891449Stomee * formatted output into a single String.
7901449Stomee */
7911449Stomee jbuf = (*jenv)->NewObject(jenv, g_buf_jc,
7921449Stomee g_bufinit_jm);
7931449Stomee if (!jbuf) {
7941449Stomee /* OutOfMemoryError pending */
7951449Stomee return (DTRACE_CONSUME_ABORT);
7961449Stomee }
7971449Stomee jc->dtjj_printa_buffer = jbuf;
7981449Stomee }
7991449Stomee /* defer aggregation records to dtj_bufhandler() */
8001449Stomee break;
8011449Stomee }
8021449Stomee case DTRACEACT_EXIT:
8031449Stomee /*
8041449Stomee * Add a Record for the exit() action that references the native
8051449Stomee * probedata element listed at the current index.
8061449Stomee */
8071449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
8081449Stomee g_pdataadd_exit_jm,
8091449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i);
8101449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
8111449Stomee WRAP_EXCEPTION(jenv);
8121449Stomee return (DTRACE_CONSUME_ABORT);
8131449Stomee }
8141449Stomee return (DTRACE_CONSUME_NEXT);
8151449Stomee }
8161449Stomee
8171449Stomee return (DTRACE_CONSUME_THIS);
8181449Stomee }
8191449Stomee
8201449Stomee /*
8211449Stomee * This is the probe handling function passed to dtrace_work(). It is is called
8221449Stomee * once every time a probe fires. It is the first of all the callbacks for the
8231449Stomee * current probe. It is followed by multiple callbacks to dtj_chewrec(), one
8241449Stomee * for each probedata record. Each call to dtj_chewrec() is followed by zero or
8251449Stomee * more callbacks to the bufhandler, one for each output-producing action or
8261449Stomee * aggregation record.
8271449Stomee */
8281449Stomee static int
dtj_chew(const dtrace_probedata_t * data,void * arg)8291449Stomee dtj_chew(const dtrace_probedata_t *data, void *arg)
8301449Stomee {
8311449Stomee dtj_java_consumer_t *jc = arg;
8321449Stomee JNIEnv *jenv = jc->dtjj_jenv;
8331449Stomee
8341449Stomee dtrace_eprobedesc_t *edesc;
8351449Stomee dtrace_probedesc_t *pdesc;
8361449Stomee dtrace_recdesc_t *rec;
8371449Stomee int epid;
8381449Stomee int cpu;
8391449Stomee int nrecs;
8401449Stomee int i;
8411449Stomee
8421449Stomee jobject jpdata = NULL;
8431449Stomee jobject jprobe = NULL;
8441449Stomee jobject jflow = NULL;
8451449Stomee jstring jflowkind = NULL;
8461449Stomee jobject jobj = NULL;
8471449Stomee
8481449Stomee edesc = data->dtpda_edesc;
8491449Stomee epid = (int)edesc->dtepd_epid;
8501449Stomee pdesc = data->dtpda_pdesc;
8511449Stomee cpu = (int)data->dtpda_cpu;
8521449Stomee if ((jprobe = dtj_new_probedesc(jc, pdesc)) == NULL) {
8531449Stomee /* java exception pending */
8541449Stomee return (DTRACE_CONSUME_ABORT);
8551449Stomee }
8561449Stomee nrecs = edesc->dtepd_nrecs;
8571449Stomee
8581449Stomee if (jc->dtjj_consumer->dtjc_flow) {
8591449Stomee const char *kind;
8601449Stomee switch (data->dtpda_flow) {
8611449Stomee case DTRACEFLOW_ENTRY:
8621449Stomee kind = "ENTRY";
8631449Stomee break;
8641449Stomee case DTRACEFLOW_RETURN:
8651449Stomee kind = "RETURN";
8661449Stomee break;
8671449Stomee case DTRACEFLOW_NONE:
8681449Stomee kind = "NONE";
8691449Stomee break;
8701449Stomee default:
8711449Stomee kind = NULL;
8721449Stomee }
8731449Stomee if (kind != NULL) {
8741449Stomee int depth;
8751449Stomee jflowkind = (*jenv)->NewStringUTF(jenv, kind);
8761449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
8771449Stomee WRAP_EXCEPTION(jenv);
8781449Stomee (*jenv)->DeleteLocalRef(jenv, jprobe);
8791449Stomee return (DTRACE_CONSUME_ABORT);
8801449Stomee }
8811449Stomee /*
8821449Stomee * Use the knowledge that libdtrace indents 2 spaces per
8831449Stomee * level in the call stack to calculate the depth.
8841449Stomee */
8851449Stomee depth = (data->dtpda_indent / 2);
8861449Stomee jflow = (*jenv)->NewObject(jenv, g_flow_jc,
8871449Stomee g_flowinit_jm, jflowkind, depth);
8881449Stomee (*jenv)->DeleteLocalRef(jenv, jflowkind);
8891449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
8901449Stomee WRAP_EXCEPTION(jenv);
8911449Stomee (*jenv)->DeleteLocalRef(jenv, jprobe);
8921449Stomee return (DTRACE_CONSUME_ABORT);
8931449Stomee }
8941449Stomee }
8951449Stomee }
8961449Stomee
8971449Stomee /* Create ProbeData instance */
8981449Stomee jpdata = (*jenv)->NewObject(jenv, g_pdata_jc, g_pdatainit_jm,
8991449Stomee epid, cpu, jprobe, jflow, nrecs);
9001449Stomee (*jenv)->DeleteLocalRef(jenv, jprobe);
9011449Stomee (*jenv)->DeleteLocalRef(jenv, jflow);
9021449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
9031449Stomee WRAP_EXCEPTION(jenv);
9041449Stomee return (DTRACE_CONSUME_ABORT);
9051449Stomee }
9061449Stomee
9071449Stomee /*
9081449Stomee * Populate the ProbeData list of Java data elements in advance so we
9091449Stomee * don't need to peek back in the record handler at libdtrace records
9101449Stomee * that have already been consumed. In the Java API, each ProbeData
9111449Stomee * Record is generated by one D action, while in the native libdtrace
9121449Stomee * there may be more than one probedata record (each a single data
9131449Stomee * element) per D action. For example PrintfRecord has multiple
9141449Stomee * unformatted elements, each represented by a native probedata record,
9151449Stomee * but combined by the API into a single PrintfRecord.
9161449Stomee */
9171449Stomee for (i = 0; i < nrecs; ++i) {
9181449Stomee rec = &edesc->dtepd_rec[i];
9191449Stomee /*
9201449Stomee * A statement that is not a D action, such as assignment to a
9211449Stomee * variable, has no size. Add a NULL placeholder to the scratch
9221449Stomee * list of Java probedata elements in that case.
9231449Stomee */
9241449Stomee jobj = NULL; /* initialize object reference to null */
9251449Stomee if (rec->dtrd_size > 0) {
9261449Stomee if (dtj_is_stack_action(rec->dtrd_action)) {
9271449Stomee jobj = dtj_new_probedata_stack_record(data,
9281449Stomee rec, jc);
9292777Stomee } else if (dtj_is_symbol_action(rec->dtrd_action)) {
9302777Stomee jobj = dtj_new_probedata_symbol_record(data,
9312777Stomee rec, jc);
9321449Stomee } else {
9331449Stomee jobj = dtj_recdata(jc, rec->dtrd_size,
9341449Stomee (data->dtpda_data + rec->dtrd_offset));
9351449Stomee }
9361449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
9371449Stomee WRAP_EXCEPTION(jenv);
9381449Stomee (*jenv)->DeleteLocalRef(jenv, jpdata);
9391449Stomee return (DTRACE_CONSUME_ABORT);
9401449Stomee }
9411449Stomee }
9421449Stomee
9431449Stomee (*jenv)->CallVoidMethod(jenv, jpdata, g_pdataadd_jm, jobj);
9441449Stomee (*jenv)->DeleteLocalRef(jenv, jobj);
9451449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
9461449Stomee WRAP_EXCEPTION(jenv);
9471449Stomee (*jenv)->DeleteLocalRef(jenv, jpdata);
9481449Stomee return (DTRACE_CONSUME_ABORT);
9491449Stomee }
9501449Stomee }
9511449Stomee
9521449Stomee if (jc->dtjj_probedata != NULL) {
9531449Stomee dtj_throw_illegal_state(jenv, "unfinished probedata");
9541449Stomee WRAP_EXCEPTION(jenv);
9551449Stomee (*jenv)->DeleteLocalRef(jenv, jpdata);
9561449Stomee return (DTRACE_CONSUME_ABORT);
9571449Stomee }
9581449Stomee jc->dtjj_probedata = jpdata;
9591449Stomee
9601449Stomee /* Initialize per-consumer probedata fields */
9611449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i = 0;
9621449Stomee jc->dtjj_consumer->dtjc_probedata_act = DTRACEACT_NONE;
9631449Stomee dtj_aggwalk_init(jc);
9641449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
9651449Stomee WRAP_EXCEPTION(jenv);
9661449Stomee return (DTRACE_CONSUME_ABORT);
9671449Stomee }
9681449Stomee
9691449Stomee return (DTRACE_CONSUME_THIS);
9701449Stomee }
9711449Stomee
9721449Stomee /*
9731449Stomee * This is the buffered output handler registered with dtrace_handle_buffered().
9741449Stomee * It's purpose is to make the output of the libdtrace print routines available
9751449Stomee * to this API, without writing any of it to a file (such as stdout). This is
9761449Stomee * needed for the stack(), ustack(), and jstack() actions to get human-readable
9771449Stomee * stack values, since there is no public function in libdtrace to convert stack
9781449Stomee * values to strings. It is also used to get the formatted output of the D
9791449Stomee * printf() and printa() actions.
9801449Stomee *
9811449Stomee * The bufhandler is called once for each output-producing, non-aggregating D
9821449Stomee * action, such as trace() or printf(), and once for each libdtrace aggregation
9831449Stomee * record (whether in response to the D printa() action, or the Consumer
9841449Stomee * getAggregate() method). In the simple printa() case that takes one
9851449Stomee * aggregation and does not specify a format string, there is one libdtrace
9861449Stomee * record per tuple element plus one for the corresponding value. The complete
9871449Stomee * tuple/value pair becomes a single AggregationRecord exported by the API.
9881449Stomee * When multiple aggregations are passed to printa(), each tuple is associated
9891449Stomee * with a list of values, one from each aggregation. If a printa() format
9901449Stomee * string does not specify placeholders for every aggregation value and tuple
9911449Stomee * member, callbacks for those values and tuple members are omitted (and the
9921449Stomee * data is omitted from the resulting PrintaRecord).
9931449Stomee *
9941449Stomee * Notes to characterize some non-obvious bufhandler behavior:
9951449Stomee *
9961449Stomee * 1. dtj_bufhandler() is never called with bufdata->dtbda_recdesc->dtrd_action
9971449Stomee * DTRACEACT_PRINTA. That action only appears in the probedata consumer
9981449Stomee * functions dtj_chew() and dtj_chewrec() before the bufhandler is called with
9991449Stomee * subsequent aggregation records.
10001449Stomee *
10011449Stomee * 2. If printa() specifies a format string argument, then the bufhandler is
10021449Stomee * called only for those elements of the tuple/value pair that are included in
10031449Stomee * the format string. If a stack() tuple member is omitted from the format
10041449Stomee * string, its human-readable representation will not be available to this API,
10051449Stomee * so the stack frame array is also omitted from the resulting
10061449Stomee * AggregationRecord. The bufhandler is also called once for each string of
10071449Stomee * characters surrounding printa() format string placeholders. For example,
10081449Stomee * " %@d %d stack%k\n" results in the following callbacks:
10091449Stomee * - two spaces
10101449Stomee * - the aggregation value
10111449Stomee * - a single space
10121449Stomee * - the first tuple member (an integer)
10131449Stomee * - " stack"
10141449Stomee * - the second tuple member (a stack)
10151449Stomee * - a newline
10161449Stomee * A NULL record (NULL dtbda_recdesc) distinguishes a callback with interstitial
10171449Stomee * format string characters from a callback with a tuple member or aggregation
10181449Stomee * value (which has a non-NULL recdesc). The contents are also distinguished by
10191449Stomee * the following flags:
10201449Stomee * DTRACE_BUFDATA_AGGKEY
10211449Stomee * DTRACE_BUFDATA_AGGVAL
10221449Stomee * DTRACE_BUFDATA_AGGFORMAT
10231449Stomee * DTRACE_BUFDATA_AGGLAST
10241449Stomee *
10251449Stomee * There is no final callback with the complete formatted string, so that must
10261449Stomee * be concatenated across multiple callbacks to the bufhandler.
10271449Stomee *
10281449Stomee * 3. bufdata->dtbda_probe->dtpda_data may be overwritten by libdtrace print
10291449Stomee * routines. The address is cached in the dtj_chew() function in case it is
10301449Stomee * needed in the bufhandler.
10311449Stomee */
10321449Stomee static int
10331449Stomee /* ARGSUSED */
dtj_bufhandler(const dtrace_bufdata_t * bufdata,void * arg)10341449Stomee dtj_bufhandler(const dtrace_bufdata_t *bufdata, void *arg)
10351449Stomee {
10361449Stomee dtj_java_consumer_t *jc;
10371449Stomee JNIEnv *jenv;
10381449Stomee const dtrace_recdesc_t *rec;
10391449Stomee dtrace_actkind_t act = DTRACEACT_NONE;
10401449Stomee const char *s;
10411449Stomee
10421449Stomee jobject jstr = NULL;
10431449Stomee
10441449Stomee /*
10451449Stomee * Get the thread-specific java consumer. The bufhandler needs access
10461449Stomee * to the correct JNI state specific to either the consumer loop or the
10471449Stomee * getAggregate() call (aggregation snapshots can be requested
10481449Stomee * asynchronously while the consumer loop generates PrintaRecords in
10491449Stomee * dtrace_work() for ConsumerListeners).
10501449Stomee */
10511449Stomee jc = pthread_getspecific(g_dtj_consumer_key);
10521449Stomee jenv = jc->dtjj_jenv;
10531449Stomee
10541449Stomee /*
10551449Stomee * In at least one corner case (printa with multiple aggregations and a
10561449Stomee * format string that does not completely specify the tuple), returning
10571449Stomee * DTRACE_HANDLE_ABORT does not prevent a subsequent callback to this
10581449Stomee * bufhandler. This check ensures that the invalid call is ignored.
10591449Stomee */
10601449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
10611449Stomee return (DTRACE_HANDLE_ABORT);
10621449Stomee }
10631449Stomee
10641449Stomee if (bufdata->dtbda_aggdata) {
10651449Stomee return (dtj_agghandler(bufdata, jc));
10661449Stomee }
10671449Stomee
10681449Stomee s = bufdata->dtbda_buffered;
10691449Stomee if (s == NULL) {
10701449Stomee return (DTRACE_HANDLE_OK);
10711449Stomee }
10721449Stomee
10731449Stomee rec = bufdata->dtbda_recdesc;
10741449Stomee if (rec) {
10751449Stomee act = rec->dtrd_action;
10761449Stomee }
10771449Stomee
10781449Stomee switch (act) {
10791449Stomee case DTRACEACT_DIFEXPR:
10801449Stomee /* trace() action */
10811449Stomee break;
10821449Stomee case DTRACEACT_PRINTF:
10831449Stomee /*
10841449Stomee * Only the formatted string was not available to dtj_chewrec(),
10851449Stomee * so we attach that now.
10861449Stomee */
10871449Stomee jstr = dtj_NewStringNative(jenv, s);
10881449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
10891449Stomee WRAP_EXCEPTION(jenv);
10901449Stomee return (DTRACE_HANDLE_ABORT);
10911449Stomee }
10921449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
10931449Stomee g_pdataset_formatted_jm, jstr);
10941449Stomee (*jenv)->DeleteLocalRef(jenv, jstr);
10951449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
10961449Stomee WRAP_EXCEPTION(jenv);
10971449Stomee return (DTRACE_HANDLE_ABORT);
10981449Stomee }
10991449Stomee break;
11001449Stomee case DTRACEACT_STACK:
11011449Stomee case DTRACEACT_USTACK:
11021449Stomee case DTRACEACT_JSTACK:
11031449Stomee /* stand-alone stack(), ustack(), or jstack() action */
11041449Stomee jstr = (*jenv)->NewStringUTF(jenv, s);
11051449Stomee if (!jstr) {
11061449Stomee /* OutOfMemoryError pending */
11071449Stomee return (DTRACE_HANDLE_ABORT);
11081449Stomee }
11091449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
11101449Stomee g_pdataadd_stack_jm,
11111449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i, jstr);
11121449Stomee (*jenv)->DeleteLocalRef(jenv, jstr);
11131449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
11141449Stomee WRAP_EXCEPTION(jenv);
11151449Stomee return (DTRACE_HANDLE_ABORT);
11161449Stomee }
11171449Stomee break;
11182777Stomee case DTRACEACT_USYM:
11192777Stomee case DTRACEACT_UADDR:
11202777Stomee case DTRACEACT_UMOD:
11212777Stomee case DTRACEACT_SYM:
11222777Stomee case DTRACEACT_MOD:
11232777Stomee /* stand-alone symbol lookup action */
11242777Stomee jstr = (*jenv)->NewStringUTF(jenv, s);
11252777Stomee if (!jstr) {
11262777Stomee /* OutOfMemoryError pending */
11272777Stomee return (DTRACE_HANDLE_ABORT);
11282777Stomee }
11292777Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
11302777Stomee g_pdataadd_symbol_jm,
11312777Stomee jc->dtjj_consumer->dtjc_probedata_rec_i, jstr);
11322777Stomee (*jenv)->DeleteLocalRef(jenv, jstr);
11332777Stomee if ((*jenv)->ExceptionCheck(jenv)) {
11342777Stomee WRAP_EXCEPTION(jenv);
11352777Stomee return (DTRACE_HANDLE_ABORT);
11362777Stomee }
11372777Stomee break;
11381449Stomee default:
11391449Stomee /*
11401449Stomee * The record handler dtj_chewrec() defers nothing else to this
11411449Stomee * bufhandler.
11421449Stomee */
11431449Stomee break;
11441449Stomee }
11451449Stomee
11461449Stomee return (DTRACE_HANDLE_OK);
11471449Stomee }
11481449Stomee
11491449Stomee static boolean_t
dtj_is_stack_action(dtrace_actkind_t act)11501449Stomee dtj_is_stack_action(dtrace_actkind_t act)
11511449Stomee {
11521449Stomee boolean_t stack_action;
11531449Stomee switch (act) {
11541449Stomee case DTRACEACT_STACK:
11551449Stomee case DTRACEACT_USTACK:
11561449Stomee case DTRACEACT_JSTACK:
11571449Stomee stack_action = B_TRUE;
11581449Stomee break;
11591449Stomee default:
11601449Stomee stack_action = B_FALSE;
11611449Stomee }
11621449Stomee return (stack_action);
11631449Stomee }
11641449Stomee
11652777Stomee static boolean_t
dtj_is_symbol_action(dtrace_actkind_t act)11662777Stomee dtj_is_symbol_action(dtrace_actkind_t act)
11672777Stomee {
11682777Stomee boolean_t symbol_action;
11692777Stomee switch (act) {
11702777Stomee case DTRACEACT_USYM:
11712777Stomee case DTRACEACT_UADDR:
11722777Stomee case DTRACEACT_UMOD:
11732777Stomee case DTRACEACT_SYM:
11742777Stomee case DTRACEACT_MOD:
11752777Stomee symbol_action = B_TRUE;
11762777Stomee break;
11772777Stomee default:
11782777Stomee symbol_action = B_FALSE;
11792777Stomee }
11802777Stomee return (symbol_action);
11812777Stomee }
11822777Stomee
11831449Stomee /*
11841449Stomee * Called by get_aggregate() to clear only those aggregations specified by the
11851449Stomee * caller.
11861449Stomee */
11871449Stomee static int
dtj_clear(const dtrace_aggdata_t * data,void * arg)11881449Stomee dtj_clear(const dtrace_aggdata_t *data, void *arg)
11891449Stomee {
11901449Stomee dtj_java_consumer_t *jc = arg;
11911449Stomee jboolean cleared = JNI_FALSE;
11921449Stomee
11931449Stomee jstring jname = NULL;
11941449Stomee
11951449Stomee if (jc->dtjj_aggregate_spec) {
11961449Stomee JNIEnv *jenv = jc->dtjj_jenv;
11971449Stomee
11981449Stomee dtrace_aggdesc_t *aggdesc = data->dtada_desc;
11991449Stomee
12001449Stomee jname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name);
12011449Stomee if (!jname) {
12021449Stomee /* java exception pending */
12031449Stomee return (DTRACE_AGGWALK_ABORT);
12041449Stomee }
12051449Stomee
12061449Stomee cleared = (*jenv)->CallBooleanMethod(jenv,
12071449Stomee jc->dtjj_aggregate_spec, g_aggspec_cleared_jm, jname);
12081449Stomee (*jenv)->DeleteLocalRef(jenv, jname);
12091449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
12101449Stomee WRAP_EXCEPTION(jenv);
12111449Stomee return (DTRACE_AGGWALK_ABORT);
12121449Stomee }
12131449Stomee }
12141449Stomee
12151449Stomee return (cleared ? DTRACE_AGGWALK_CLEAR : DTRACE_AGGWALK_NEXT);
12161449Stomee }
12171449Stomee
12181449Stomee static int64_t
dtj_average(caddr_t addr,uint64_t normal)12191449Stomee dtj_average(caddr_t addr, uint64_t normal)
12201449Stomee {
12211449Stomee /* LINTED - alignment */
1222*6136Stomee int64_t *data = (int64_t *)addr;
12231449Stomee
12241449Stomee return (data[0] ?
1225*6136Stomee (data[1] / (int64_t)normal / data[0]) : 0);
12261449Stomee }
12271449Stomee
12281449Stomee static int64_t
dtj_avg_total(caddr_t addr,uint64_t normal)12291449Stomee dtj_avg_total(caddr_t addr, uint64_t normal)
12301449Stomee {
12311449Stomee /* LINTED - alignment */
1232*6136Stomee int64_t *data = (int64_t *)addr;
12331449Stomee
1234*6136Stomee return (data[1] / (int64_t)normal);
12351449Stomee }
12361449Stomee
12371449Stomee static int64_t
dtj_avg_count(caddr_t addr)12381449Stomee dtj_avg_count(caddr_t addr)
12391449Stomee {
12401449Stomee /* LINTED - alignment */
1241*6136Stomee int64_t *data = (int64_t *)addr;
1242*6136Stomee
1243*6136Stomee return (data[0]);
1244*6136Stomee }
1245*6136Stomee
1246*6136Stomee static jobject
dtj_stddev_total_squares(JNIEnv * jenv,caddr_t addr,uint64_t normal)1247*6136Stomee dtj_stddev_total_squares(JNIEnv *jenv, caddr_t addr, uint64_t normal)
1248*6136Stomee {
1249*6136Stomee jobject val128;
1250*6136Stomee
1251*6136Stomee /* LINTED - alignment */
12521449Stomee uint64_t *data = (uint64_t *)addr;
12531449Stomee
1254*6136Stomee if (data[0] == 0) {
1255*6136Stomee val128 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc,
1256*6136Stomee g_bigint_val_jsm, (uint64_t)0);
1257*6136Stomee } else {
1258*6136Stomee val128 = dtj_int128(jenv, data[3], data[2]);
1259*6136Stomee
1260*6136Stomee if (normal != 1) {
1261*6136Stomee jobject divisor;
1262*6136Stomee jobject tmp;
1263*6136Stomee
1264*6136Stomee divisor = (*jenv)->CallStaticObjectMethod(jenv,
1265*6136Stomee g_bigint_jc, g_bigint_val_jsm, normal);
1266*6136Stomee tmp = val128;
1267*6136Stomee val128 = (*jenv)->CallObjectMethod(jenv, tmp,
1268*6136Stomee g_bigint_div_jm, divisor);
1269*6136Stomee (*jenv)->DeleteLocalRef(jenv, tmp);
1270*6136Stomee (*jenv)->DeleteLocalRef(jenv, divisor);
1271*6136Stomee }
1272*6136Stomee }
1273*6136Stomee
1274*6136Stomee return (val128);
1275*6136Stomee }
1276*6136Stomee
1277*6136Stomee /*
1278*6136Stomee * Return NULL if a java exception is pending, otherwise return a new
1279*6136Stomee * StddevValue instance.
1280*6136Stomee */
1281*6136Stomee static jobject
dtj_stddev(JNIEnv * jenv,caddr_t addr,uint64_t normal)1282*6136Stomee dtj_stddev(JNIEnv *jenv, caddr_t addr, uint64_t normal)
1283*6136Stomee {
1284*6136Stomee jobject total_squares;
1285*6136Stomee jobject stddev;
1286*6136Stomee
1287*6136Stomee total_squares = dtj_stddev_total_squares(jenv, addr, normal);
1288*6136Stomee stddev = (*jenv)->NewObject(jenv, g_aggstddev_jc, g_aggstddevinit_jm,
1289*6136Stomee dtj_avg_count(addr), dtj_avg_total(addr, normal), total_squares);
1290*6136Stomee (*jenv)->DeleteLocalRef(jenv, total_squares);
1291*6136Stomee
1292*6136Stomee return (stddev);
12931449Stomee }
12941449Stomee
12951449Stomee static jobject
dtj_new_probedata_stack_record(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)12961449Stomee dtj_new_probedata_stack_record(const dtrace_probedata_t *data,
12971449Stomee const dtrace_recdesc_t *rec, dtj_java_consumer_t *jc)
12981449Stomee {
12991449Stomee caddr_t addr;
13001449Stomee
13011449Stomee /* Get raw stack data */
13021449Stomee addr = data->dtpda_data + rec->dtrd_offset;
13031449Stomee return (dtj_new_stack_record(addr, rec, jc));
13041449Stomee }
13051449Stomee
13061449Stomee 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)13071449Stomee dtj_new_tuple_stack_record(const dtrace_aggdata_t *data,
13081449Stomee const dtrace_recdesc_t *rec, const char *s, dtj_java_consumer_t *jc)
13091449Stomee {
13101449Stomee caddr_t addr;
13111449Stomee JNIEnv *jenv = jc->dtjj_jenv;
13121449Stomee
13131449Stomee jobjectArray frames = NULL;
13141449Stomee jobject jobj = NULL; /* tuple element */
13151449Stomee jstring jstr = NULL;
13161449Stomee
13171449Stomee /* Get raw stack data */
13181449Stomee addr = data->dtada_data + rec->dtrd_offset;
13191449Stomee jobj = dtj_new_stack_record(addr, rec, jc);
13201449Stomee if (!jobj) {
13211449Stomee return (NULL); /* java exception pending */
13221449Stomee }
13231449Stomee
13241449Stomee jstr = dtj_NewStringNative(jenv, s);
13251449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
13261449Stomee (*jenv)->DeleteLocalRef(jenv, jobj);
13271449Stomee return (NULL);
13281449Stomee }
13291449Stomee frames = (*jenv)->CallStaticObjectMethod(jenv, g_stack_jc,
13301449Stomee g_parsestack_jsm, jstr);
13311449Stomee (*jenv)->DeleteLocalRef(jenv, jstr);
13321449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
13331449Stomee (*jenv)->DeleteLocalRef(jenv, jobj);
13341449Stomee return (NULL);
13351449Stomee }
13361449Stomee dtj_attach_frames(jc, jobj, frames);
13371449Stomee (*jenv)->DeleteLocalRef(jenv, frames);
13381449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
13392777Stomee WRAP_EXCEPTION(jenv);
13402777Stomee return (NULL);
13412777Stomee }
13422777Stomee
13432777Stomee return (jobj);
13442777Stomee }
13452777Stomee
13462777Stomee static jobject
dtj_new_probedata_symbol_record(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)13472777Stomee dtj_new_probedata_symbol_record(const dtrace_probedata_t *data,
13482777Stomee const dtrace_recdesc_t *rec, dtj_java_consumer_t *jc)
13492777Stomee {
13502777Stomee caddr_t addr;
13512777Stomee
13522777Stomee addr = data->dtpda_data + rec->dtrd_offset;
13532777Stomee return (dtj_new_symbol_record(addr, rec, jc));
13542777Stomee }
13552777Stomee
13562777Stomee 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)13572777Stomee dtj_new_tuple_symbol_record(const dtrace_aggdata_t *data,
13582777Stomee const dtrace_recdesc_t *rec, const char *s, dtj_java_consumer_t *jc)
13592777Stomee {
13602777Stomee caddr_t addr;
13612777Stomee JNIEnv *jenv = jc->dtjj_jenv;
13622777Stomee
13632777Stomee jobject jobj = NULL; /* tuple element */
13642777Stomee jstring jstr = NULL; /* lookup value */
13652777Stomee jstring tstr = NULL; /* trimmed lookup value */
13662777Stomee
13672777Stomee addr = data->dtada_data + rec->dtrd_offset;
13682777Stomee jobj = dtj_new_symbol_record(addr, rec, jc);
13692777Stomee if (!jobj) {
13702777Stomee return (NULL); /* java exception pending */
13712777Stomee }
13722777Stomee
13732777Stomee /* Get symbol lookup */
13742777Stomee jstr = (*jenv)->NewStringUTF(jenv, s);
13752777Stomee if (!jstr) {
13762777Stomee /* OutOfMemoryError pending */
13772777Stomee (*jenv)->DeleteLocalRef(jenv, jobj);
13782777Stomee return (NULL);
13792777Stomee }
13802777Stomee /* Trim leading and trailing whitespace */
13812777Stomee tstr = (*jenv)->CallObjectMethod(jenv, jstr, g_trim_jm);
13822777Stomee /* trim() returns a new string; don't leak the old one */
13832777Stomee (*jenv)->DeleteLocalRef(jenv, jstr);
13842777Stomee jstr = tstr;
13852777Stomee tstr = NULL;
13862777Stomee
13872777Stomee dtj_attach_name(jc, jobj, jstr);
13882777Stomee (*jenv)->DeleteLocalRef(jenv, jstr);
13892777Stomee if ((*jenv)->ExceptionCheck(jenv)) {
13902777Stomee WRAP_EXCEPTION(jenv);
13911449Stomee return (NULL);
13921449Stomee }
13931449Stomee
13941449Stomee return (jobj);
13951449Stomee }
13961449Stomee
13971449Stomee /* Caller must be holding per-consumer lock */
13981449Stomee static void
dtj_aggwalk_init(dtj_java_consumer_t * jc)13991449Stomee dtj_aggwalk_init(dtj_java_consumer_t *jc)
14001449Stomee {
14011449Stomee jc->dtjj_consumer->dtjc_aggid = -1;
14021449Stomee jc->dtjj_consumer->dtjc_expected = -1;
14031449Stomee if (jc->dtjj_tuple != NULL) {
14041449Stomee /* assert without crashing */
14051449Stomee dtj_throw_illegal_state(jc->dtjj_jenv,
14061449Stomee "stale aggregation tuple");
14071449Stomee }
14081449Stomee }
14091449Stomee
14101449Stomee static jobject
dtj_new_stack_record(const caddr_t addr,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)14112777Stomee dtj_new_stack_record(const caddr_t addr, const dtrace_recdesc_t *rec,
14121449Stomee dtj_java_consumer_t *jc)
14131449Stomee {
14141449Stomee JNIEnv *jenv = jc->dtjj_jenv;
14151449Stomee
14161449Stomee dtrace_actkind_t act;
14171449Stomee uint64_t *pc;
14181449Stomee pid_t pid = -1;
14191449Stomee int size; /* size of raw bytes not including trailing zeros */
14201449Stomee int i; /* index of last non-zero byte */
14211449Stomee
14221449Stomee jbyteArray raw = NULL;
14231449Stomee jobject stack = NULL; /* return value */
14241449Stomee
14251449Stomee /* trim trailing zeros */
14261449Stomee for (i = rec->dtrd_size - 1; (i >= 0) && !addr[i]; --i) {
14271449Stomee }
14281449Stomee size = (i + 1);
14291449Stomee raw = (*jenv)->NewByteArray(jenv, size);
14301449Stomee if (!raw) {
14311449Stomee return (NULL); /* OutOfMemoryError pending */
14321449Stomee }
14331449Stomee (*jenv)->SetByteArrayRegion(jenv, raw, 0, size,
14341449Stomee (const jbyte *)addr);
14351449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
14361449Stomee WRAP_EXCEPTION(jenv);
14371449Stomee (*jenv)->DeleteLocalRef(jenv, raw);
14381449Stomee return (NULL);
14391449Stomee }
14401449Stomee
14411449Stomee /* Create StackValueRecord instance from raw stack data */
14421449Stomee act = rec->dtrd_action;
14431449Stomee switch (act) {
14441449Stomee case DTRACEACT_STACK:
14451449Stomee stack = (*jenv)->NewObject(jenv, g_stack_jc,
14461449Stomee g_stackinit_jm, raw);
14471449Stomee break;
14481449Stomee case DTRACEACT_USTACK:
14491449Stomee case DTRACEACT_JSTACK:
14501449Stomee /* Get pid of user process */
14511449Stomee pc = (uint64_t *)(uintptr_t)addr;
14521449Stomee pid = (pid_t)*pc;
14531449Stomee stack = (*jenv)->NewObject(jenv, g_ustack_jc,
14541449Stomee g_ustackinit_jm, pid, raw);
14551449Stomee break;
14561449Stomee default:
14571449Stomee dtj_throw_illegal_argument(jenv,
14581449Stomee "Expected stack action, got %d\n", act);
14591449Stomee }
14601449Stomee (*jenv)->DeleteLocalRef(jenv, raw);
14611449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
14621449Stomee WRAP_EXCEPTION(jenv);
14631449Stomee return (NULL);
14641449Stomee }
14651449Stomee return (stack);
14661449Stomee }
14671449Stomee
14682777Stomee static jobject
dtj_new_symbol_record(const caddr_t addr,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)14692777Stomee dtj_new_symbol_record(const caddr_t addr, const dtrace_recdesc_t *rec,
14702777Stomee dtj_java_consumer_t *jc)
14712777Stomee {
14722777Stomee JNIEnv *jenv = jc->dtjj_jenv;
14732777Stomee
14742777Stomee dtrace_actkind_t act;
14752777Stomee uint64_t *pc;
14762777Stomee pid_t pid = -1;
14772777Stomee
14782777Stomee jobject symbol = NULL; /* return value */
14792777Stomee
14802777Stomee act = rec->dtrd_action;
14812777Stomee switch (act) {
14822777Stomee case DTRACEACT_SYM:
14832777Stomee case DTRACEACT_MOD:
14842777Stomee /* LINTED - alignment */
14852777Stomee pc = (uint64_t *)addr;
14862777Stomee symbol = (*jenv)->NewObject(jenv, g_symbol_jc,
14872777Stomee g_symbolinit_jm, *pc);
14882777Stomee break;
14892777Stomee case DTRACEACT_USYM:
14902777Stomee case DTRACEACT_UADDR:
14912777Stomee case DTRACEACT_UMOD:
14922777Stomee /* Get pid of user process */
14932777Stomee pc = (uint64_t *)(uintptr_t)addr;
14942777Stomee pid = (pid_t)*pc;
14952777Stomee ++pc;
14962777Stomee symbol = (*jenv)->NewObject(jenv, g_usymbol_jc,
14972777Stomee g_usymbolinit_jm, pid, *pc);
14982777Stomee break;
14992777Stomee default:
15002777Stomee dtj_throw_illegal_argument(jenv,
15012777Stomee "Expected stack action, got %d\n", act);
15022777Stomee }
15032777Stomee if ((*jenv)->ExceptionCheck(jenv)) {
15042777Stomee WRAP_EXCEPTION(jenv);
15052777Stomee return (NULL);
15062777Stomee }
15072777Stomee return (symbol);
15082777Stomee }
15092777Stomee
15101449Stomee /*
15111449Stomee * Return NULL if java exception pending, otherwise return Distribution value.
15121449Stomee */
15131449Stomee static jobject
dtj_new_distribution(const dtrace_aggdata_t * data,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)15141449Stomee dtj_new_distribution(const dtrace_aggdata_t *data, const dtrace_recdesc_t *rec,
15151449Stomee dtj_java_consumer_t *jc)
15161449Stomee {
15171449Stomee JNIEnv *jenv = jc->dtjj_jenv;
15181449Stomee
15191449Stomee jlongArray jbuckets = NULL;
15201449Stomee jobject jdist = NULL; /* return value */
15211449Stomee
15221449Stomee dtrace_actkind_t act = rec->dtrd_action;
15231449Stomee /* LINTED - alignment */
15241449Stomee int64_t *aggbuckets = (int64_t *)
15251449Stomee (data->dtada_data + rec->dtrd_offset);
15261449Stomee size_t size = rec->dtrd_size;
15271449Stomee int64_t value;
15281449Stomee uint64_t normal = data->dtada_normal;
15291449Stomee int64_t base, step;
15301449Stomee int levels;
15311449Stomee int n; /* number of buckets */
15321449Stomee
15331449Stomee /* distribution */
15341449Stomee if (act == DTRACEAGG_LQUANTIZE) {
15351449Stomee /* first "bucket" used for range and step */
15361449Stomee value = *aggbuckets++;
15371449Stomee base = DTRACE_LQUANTIZE_BASE(value);
15381449Stomee step = DTRACE_LQUANTIZE_STEP(value);
15391449Stomee levels = DTRACE_LQUANTIZE_LEVELS(value);
15401449Stomee size -= sizeof (int64_t); /* exclude non-bucket */
15411449Stomee /*
15421449Stomee * Add one for the base bucket and one for the bucket of values
15431449Stomee * less than the base.
15441449Stomee */
15451449Stomee n = levels + 2;
15461449Stomee } else {
15471449Stomee n = DTRACE_QUANTIZE_NBUCKETS;
15481449Stomee levels = n - 1; /* levels excludes base */
15491449Stomee }
15501449Stomee if (size != (n * sizeof (uint64_t)) || n < 1) {
15511449Stomee dtj_throw_illegal_state(jenv,
15521449Stomee "size mismatch: record %d, buckets %d", size,
15531449Stomee (n * sizeof (uint64_t)));
15541449Stomee WRAP_EXCEPTION(jenv);
15551449Stomee return (NULL);
15561449Stomee }
15571449Stomee
15581449Stomee jbuckets = (*jenv)->NewLongArray(jenv, n);
15591449Stomee if (!jbuckets) {
15601449Stomee return (NULL); /* exception pending */
15611449Stomee }
15621449Stomee if (n > 0) {
15631449Stomee (*jenv)->SetLongArrayRegion(jenv, jbuckets, 0, n, aggbuckets);
15641449Stomee /* check for ArrayIndexOutOfBounds */
15651449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
15661449Stomee WRAP_EXCEPTION(jenv);
15671449Stomee (*jenv)->DeleteLocalRef(jenv, jbuckets);
15681449Stomee return (NULL);
15691449Stomee }
15701449Stomee }
15711449Stomee
15721449Stomee if (act == DTRACEAGG_LQUANTIZE) {
15731449Stomee /* Must pass 64-bit base and step or constructor gets junk. */
15741449Stomee jdist = (*jenv)->NewObject(jenv, g_ldist_jc, g_ldistinit_jm,
15751449Stomee base, step, jbuckets);
15761449Stomee } else {
15771449Stomee jdist = (*jenv)->NewObject(jenv, g_dist_jc, g_distinit_jm,
15781449Stomee jbuckets);
15791449Stomee }
15801449Stomee
15811449Stomee (*jenv)->DeleteLocalRef(jenv, jbuckets);
15821449Stomee if (!jdist) {
15831449Stomee return (NULL); /* exception pending */
15841449Stomee }
15851449Stomee
15861449Stomee if (normal != 1) {
15871449Stomee (*jenv)->CallVoidMethod(jenv, jdist, g_dist_normal_jm, normal);
15881449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
15891449Stomee WRAP_EXCEPTION(jenv);
15901449Stomee (*jenv)->DeleteLocalRef(jenv, jdist);
15911449Stomee return (NULL);
15921449Stomee }
15931449Stomee }
15941449Stomee return (jdist);
15951449Stomee }
15961449Stomee
15971449Stomee static void
dtj_attach_frames(dtj_java_consumer_t * jc,jobject stack,jobjectArray frames)15981449Stomee dtj_attach_frames(dtj_java_consumer_t *jc, jobject stack,
15991449Stomee jobjectArray frames)
16001449Stomee {
16011449Stomee JNIEnv *jenv = jc->dtjj_jenv;
16021449Stomee
16031449Stomee if ((*jenv)->IsInstanceOf(jenv, stack, g_stack_jc)) {
16041449Stomee (*jenv)->CallVoidMethod(jenv, stack, g_stackset_frames_jm,
16051449Stomee frames);
16061449Stomee } else if ((*jenv)->IsInstanceOf(jenv, stack, g_ustack_jc)) {
16071449Stomee (*jenv)->CallVoidMethod(jenv, stack, g_ustackset_frames_jm,
16081449Stomee frames);
16091449Stomee }
16101449Stomee }
16111449Stomee
16122777Stomee static void
dtj_attach_name(dtj_java_consumer_t * jc,jobject symbol,jstring s)16132777Stomee dtj_attach_name(dtj_java_consumer_t *jc, jobject symbol, jstring s)
16142777Stomee {
16152777Stomee JNIEnv *jenv = jc->dtjj_jenv;
16162777Stomee
16172777Stomee if ((*jenv)->IsInstanceOf(jenv, symbol, g_symbol_jc)) {
16182777Stomee (*jenv)->CallVoidMethod(jenv, symbol, g_symbolset_name_jm, s);
16192777Stomee } else if ((*jenv)->IsInstanceOf(jenv, symbol, g_usymbol_jc)) {
16202777Stomee (*jenv)->CallVoidMethod(jenv, symbol, g_usymbolset_name_jm, s);
16212777Stomee }
16222777Stomee }
16232777Stomee
16241449Stomee /*
16251449Stomee * Note: It is not valid to look outside the current libdtrace record in the
16261449Stomee * given aggdata (except to get the aggregation ID from the first record).
16271449Stomee *
16281449Stomee * Return DTRACE_HANDLE_ABORT if java exception pending, otherwise
16291449Stomee * DTRACE_HANDLE_OK.
16301449Stomee */
16311449Stomee static int
dtj_agghandler(const dtrace_bufdata_t * bufdata,dtj_java_consumer_t * jc)16321449Stomee dtj_agghandler(const dtrace_bufdata_t *bufdata, dtj_java_consumer_t *jc)
16331449Stomee {
16341449Stomee JNIEnv *jenv = jc->dtjj_jenv;
16351449Stomee
16361449Stomee const dtrace_aggdata_t *aggdata = bufdata->dtbda_aggdata;
16371449Stomee const dtrace_aggdesc_t *aggdesc;
16381449Stomee const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc;
16391449Stomee const char *s = bufdata->dtbda_buffered;
16401449Stomee dtrace_actkind_t act = DTRACEACT_NONE;
16411449Stomee int64_t aggid;
16421449Stomee
16431449Stomee jobject jobj = NULL;
16441449Stomee
16451449Stomee if (aggdata == NULL) {
16461449Stomee /* Assert without crashing */
16471449Stomee dtj_throw_illegal_state(jenv, "null aggdata");
16481449Stomee WRAP_EXCEPTION(jenv);
16491449Stomee return (DTRACE_HANDLE_ABORT);
16501449Stomee }
16511449Stomee aggdesc = aggdata->dtada_desc;
16521449Stomee
16531449Stomee /*
16541449Stomee * Get the aggregation ID from the first record.
16551449Stomee */
16561449Stomee /* LINTED - alignment */
16571449Stomee aggid = *((int64_t *)(aggdata->dtada_data +
16581449Stomee aggdesc->dtagd_rec[0].dtrd_offset));
16591449Stomee if (aggid < 0) {
16601449Stomee /* Assert without crashing */
16611449Stomee dtj_throw_illegal_argument(jenv, "negative aggregation ID");
16621449Stomee WRAP_EXCEPTION(jenv);
16631449Stomee return (DTRACE_HANDLE_ABORT);
16641449Stomee }
16651449Stomee
16661449Stomee if (jc->dtjj_consumer->dtjc_printa_snaptime) {
16671449Stomee /* Append buffered output if this is a printa() callback. */
16681449Stomee jstring jstr = dtj_NewStringNative(jenv, s);
16691449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
16701449Stomee WRAP_EXCEPTION(jenv);
16711449Stomee return (DTRACE_HANDLE_ABORT);
16721449Stomee }
16731449Stomee /*
16743645Stomee * StringBuilder append() returns a reference to the
16753645Stomee * StringBuilder; must not leak the returned reference.
16761449Stomee */
16771449Stomee jobj = (*jenv)->CallObjectMethod(jenv,
16781449Stomee jc->dtjj_printa_buffer, g_buf_append_str_jm, jstr);
16791449Stomee (*jenv)->DeleteLocalRef(jenv, jstr);
16801449Stomee (*jenv)->DeleteLocalRef(jenv, jobj);
16811449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
16821449Stomee WRAP_EXCEPTION(jenv);
16831449Stomee return (DTRACE_HANDLE_ABORT);
16841449Stomee }
16851449Stomee } else {
16861449Stomee /*
16871449Stomee * Test whether to include the aggregation if this is a
1688*6136Stomee * getAggregate() call. Optimization: perform the inclusion
16891449Stomee * test only when the aggregation has changed.
16901449Stomee */
16911449Stomee if (aggid != jc->dtjj_consumer->dtjc_aggid) {
16921449Stomee jc->dtjj_consumer->dtjc_included =
16931449Stomee dtj_is_included(aggdata, jc);
16941449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
16951449Stomee WRAP_EXCEPTION(jenv);
16961449Stomee return (DTRACE_HANDLE_ABORT);
16971449Stomee }
16981449Stomee }
16991449Stomee if (!jc->dtjj_consumer->dtjc_included) {
17001449Stomee return (DTRACE_HANDLE_OK);
17011449Stomee }
17021449Stomee }
17031449Stomee jc->dtjj_consumer->dtjc_aggid = aggid;
17041449Stomee
17051449Stomee /*
17061449Stomee * Determine the expected number of tuple members. While it is not
17071449Stomee * technically valid to look outside the current record in the current
17081449Stomee * aggdata, this implementation does so without a known failure case.
17091449Stomee * Any method relying only on the current callback record makes riskier
17101449Stomee * assumptions and still does not cover every corner case (for example,
17111449Stomee * counting the records from index 1 up to and not including the index
17121449Stomee * of the current DTRACE_BUFDATA_AGGVAL record, which fails when a
17131449Stomee * format string specifies the value ahead of one or more tuple
17141449Stomee * elements). Knowing that the calculation of the expected tuple size
17151449Stomee * is technically invalid (because it looks outside the current record),
17161449Stomee * we make the calculation at the earliest opportunity, before anything
17171449Stomee * might happen to invalidate any part of the aggdata. It ought to be
17181449Stomee * safe in any case: dtrd_action and dtrd_size do not appear ever to be
17191449Stomee * overwritten, and dtrd_offset is not used outside the current record.
17201449Stomee *
17211449Stomee * It is possible (if the assumptions here ever prove untrue) that the
17221449Stomee * libdtrace buffered output handler may need to be enhanced to provide
17231449Stomee * the expected number of tuple members.
17241449Stomee */
17251449Stomee if (jc->dtjj_consumer->dtjc_expected < 0) {
17261449Stomee int r;
17271449Stomee for (r = 1; r < aggdesc->dtagd_nrecs; ++r) {
17281449Stomee act = aggdesc->dtagd_rec[r].dtrd_action;
17291449Stomee if (DTRACEACT_ISAGG(act) ||
17301449Stomee aggdesc->dtagd_rec[r].dtrd_size == 0) {
17311449Stomee break;
17321449Stomee }
17331449Stomee }
17341449Stomee jc->dtjj_consumer->dtjc_expected = r - 1;
17351449Stomee }
17361449Stomee
17371449Stomee if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGKEY) {
17381449Stomee /* record value is a tuple member */
17391449Stomee
17401449Stomee if (jc->dtjj_tuple == NULL) {
17411449Stomee jc->dtjj_tuple = (*jenv)->NewObject(jenv,
17421449Stomee g_tuple_jc, g_tupleinit_jm);
17431449Stomee if (!jc->dtjj_tuple) {
17441449Stomee /* java exception pending */
17451449Stomee return (DTRACE_HANDLE_ABORT);
17461449Stomee }
17471449Stomee }
17481449Stomee
17491449Stomee act = rec->dtrd_action;
17501449Stomee
17511449Stomee switch (act) {
17521449Stomee case DTRACEACT_STACK:
17531449Stomee case DTRACEACT_USTACK:
17541449Stomee case DTRACEACT_JSTACK:
17551449Stomee jobj = dtj_new_tuple_stack_record(aggdata, rec, s, jc);
17561449Stomee break;
17572777Stomee case DTRACEACT_USYM:
17582777Stomee case DTRACEACT_UADDR:
17592777Stomee case DTRACEACT_UMOD:
17602777Stomee case DTRACEACT_SYM:
17612777Stomee case DTRACEACT_MOD:
17622777Stomee jobj = dtj_new_tuple_symbol_record(aggdata, rec, s, jc);
17632777Stomee break;
17641449Stomee default:
17651449Stomee jobj = dtj_recdata(jc, rec->dtrd_size,
17661449Stomee (aggdata->dtada_data + rec->dtrd_offset));
17671449Stomee }
17681449Stomee
17691449Stomee if (!jobj) {
17701449Stomee /* java exception pending */
17711449Stomee return (DTRACE_HANDLE_ABORT);
17721449Stomee }
17731449Stomee
17741449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_tuple,
17751449Stomee g_tupleadd_jm, jobj);
17761449Stomee (*jenv)->DeleteLocalRef(jenv, jobj);
17771449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
17781449Stomee WRAP_EXCEPTION(jenv);
17791449Stomee return (DTRACE_HANDLE_ABORT);
17801449Stomee }
17811449Stomee } else if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGVAL) {
17821449Stomee /*
17831449Stomee * Record value is that of an aggregating action. The printa()
17841449Stomee * format string may place the tuple ahead of the aggregation
17851449Stomee * value(s), so we can't be sure we have the tuple until we get
17861449Stomee * the AGGLAST flag indicating the last callback associated with
17871449Stomee * the current tuple. Save the aggregation value or values
17881449Stomee * (multiple values if more than one aggregation is passed to
17891449Stomee * printa()) until then.
17901449Stomee */
17911449Stomee dtj_aggval_t *aggval;
17921449Stomee
17931449Stomee jstring jvalue = NULL;
17941449Stomee
17951449Stomee jvalue = dtj_new_aggval(jc, aggdata, rec);
17961449Stomee if (!jvalue) {
17971449Stomee /* java exception pending */
17981449Stomee WRAP_EXCEPTION(jenv);
17991449Stomee return (DTRACE_HANDLE_ABORT);
18001449Stomee }
18011449Stomee aggval = dtj_aggval_create(jenv, jvalue, aggdesc->dtagd_name,
18021449Stomee aggid);
18031449Stomee if (!aggval) {
18041449Stomee /* OutOfMemoryError pending */
18051449Stomee (*jenv)->DeleteLocalRef(jenv, jvalue);
18061449Stomee return (DTRACE_HANDLE_ABORT);
18071449Stomee }
18081449Stomee if (!dtj_list_add(jc->dtjj_aggval_list, aggval)) {
18091449Stomee /* deletes jvalue reference */
18101449Stomee dtj_aggval_destroy(aggval, jenv);
18111449Stomee dtj_throw_out_of_memory(jenv, "Failed to add aggval");
18121449Stomee return (DTRACE_HANDLE_ABORT);
18131449Stomee }
18141449Stomee }
18151449Stomee
18161449Stomee if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGLAST) {
18171449Stomee /* No more values associated with the current tuple. */
18181449Stomee
18191449Stomee dtj_aggval_t *aggval;
18201449Stomee uu_list_walk_t *itr;
18211449Stomee int tuple_member_count;
18221449Stomee
18231449Stomee jobject jrec = NULL;
18241449Stomee jstring jname = NULL;
18251449Stomee
18261449Stomee if (jc->dtjj_consumer->dtjc_expected == 0) {
18271449Stomee /*
18281449Stomee * singleton aggregation declared in D with no square
18291449Stomee * brackets
18301449Stomee */
18311449Stomee jc->dtjj_tuple = (*jenv)->GetStaticObjectField(jenv,
18321449Stomee g_tuple_jc, g_tuple_EMPTY_jsf);
18331449Stomee if (jc->dtjj_tuple == NULL) {
18341449Stomee dtj_throw_out_of_memory(jenv,
18351449Stomee "Failed to reference Tuple.EMPTY");
18361449Stomee return (DTRACE_HANDLE_ABORT);
18371449Stomee }
18381449Stomee }
18391449Stomee
18401449Stomee if (jc->dtjj_tuple == NULL) {
18411449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
18421449Stomee g_pdatainvalidate_printa_jm);
18433350Stomee goto printa_output;
18441449Stomee }
18451449Stomee
18461449Stomee tuple_member_count = (*jenv)->CallIntMethod(jenv,
18471449Stomee jc->dtjj_tuple, g_tuplesize_jm);
18481449Stomee if (tuple_member_count <
18491449Stomee jc->dtjj_consumer->dtjc_expected) {
18501449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
18511449Stomee g_pdatainvalidate_printa_jm);
18521449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple);
18531449Stomee jc->dtjj_tuple = NULL;
18541449Stomee goto printa_output;
18551449Stomee }
18561449Stomee
18571449Stomee itr = uu_list_walk_start(jc->dtjj_aggval_list, 0);
18581449Stomee while ((aggval = uu_list_walk_next(itr)) != NULL) {
18591449Stomee /*
18601449Stomee * new AggregationRecord: Combine the aggregation value
18611449Stomee * with the saved tuple and add it to the current
18621449Stomee * Aggregate or PrintaRecord.
18631449Stomee */
18641449Stomee jrec = (*jenv)->NewObject(jenv, g_aggrec_jc,
18651449Stomee g_aggrecinit_jm, jc->dtjj_tuple,
18661449Stomee aggval->dtja_value);
18671449Stomee (*jenv)->DeleteLocalRef(jenv, aggval->dtja_value);
18681449Stomee aggval->dtja_value = NULL;
18691449Stomee if (!jrec) {
18701449Stomee /* java exception pending */
18711449Stomee WRAP_EXCEPTION(jenv);
18721449Stomee return (DTRACE_HANDLE_ABORT);
18731449Stomee }
18741449Stomee
18751449Stomee /* aggregation name */
18761449Stomee jname = (*jenv)->NewStringUTF(jenv,
18771449Stomee aggval->dtja_aggname);
18781449Stomee if (!jname) {
18791449Stomee /* OutOfMemoryError pending */
18801449Stomee (*jenv)->DeleteLocalRef(jenv, jrec);
18811449Stomee return (DTRACE_HANDLE_ABORT);
18821449Stomee }
18831449Stomee
18841449Stomee /*
18851449Stomee * If the printa() format string specifies the value of
18861449Stomee * the aggregating action multiple times, PrintaRecord
18871449Stomee * ignores the attempt to add the duplicate record.
18881449Stomee */
18891449Stomee if (jc->dtjj_consumer->dtjc_printa_snaptime) {
18901449Stomee /* add to PrintaRecord */
18911449Stomee (*jenv)->CallVoidMethod(jenv,
18921449Stomee jc->dtjj_probedata,
18931449Stomee g_pdataadd_aggrec_jm,
18941449Stomee jname, aggval->dtja_aggid, jrec);
18951449Stomee } else {
18961449Stomee /* add to Aggregate */
18971449Stomee (*jenv)->CallVoidMethod(jenv,
18981449Stomee jc->dtjj_aggregate, g_aggaddrec_jm,
18991449Stomee jname, aggval->dtja_aggid, jrec);
19001449Stomee }
19011449Stomee
19021449Stomee (*jenv)->DeleteLocalRef(jenv, jrec);
19031449Stomee (*jenv)->DeleteLocalRef(jenv, jname);
19041449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
19051449Stomee WRAP_EXCEPTION(jenv);
19061449Stomee return (DTRACE_HANDLE_ABORT);
19071449Stomee }
19081449Stomee }
19091449Stomee uu_list_walk_end(itr);
19101449Stomee dtj_list_clear(jc->dtjj_aggval_list, dtj_aggval_destroy,
19111449Stomee jenv);
19121449Stomee
19131449Stomee printa_output:
19141449Stomee if (jc->dtjj_consumer->dtjc_printa_snaptime) {
19151449Stomee /*
19161449Stomee * Get the formatted string associated with the current
19171449Stomee * tuple if this is a printa() callback.
19181449Stomee */
19191449Stomee jstring jstr = (*jenv)->CallObjectMethod(jenv,
19201449Stomee jc->dtjj_printa_buffer, g_tostring_jm);
19211449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
19221449Stomee WRAP_EXCEPTION(jenv);
19231449Stomee return (DTRACE_HANDLE_ABORT);
19241449Stomee }
19251449Stomee /*
19263645Stomee * Clear the StringBuilder: this does not throw
19273645Stomee * exceptions. Reuse the StringBuilder until the end of
19281449Stomee * the current probedata then dispose of it.
19291449Stomee */
19301449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_printa_buffer,
19311449Stomee g_bufsetlen_jm, 0);
19321449Stomee /* Add formatted string to PrintaRecord */
19331449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
19341449Stomee g_pdataadd_printa_str_jm, jc->dtjj_tuple, jstr);
19351449Stomee (*jenv)->DeleteLocalRef(jenv, jstr);
19361449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
19371449Stomee WRAP_EXCEPTION(jenv);
19381449Stomee return (DTRACE_HANDLE_ABORT);
19391449Stomee }
19401449Stomee }
19411449Stomee
19421449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple);
19431449Stomee jc->dtjj_tuple = NULL;
19441449Stomee jc->dtjj_consumer->dtjc_expected = -1;
19451449Stomee }
19461449Stomee
19471449Stomee return (DTRACE_HANDLE_OK);
19481449Stomee }
19491449Stomee
19501449Stomee /*
19511449Stomee * Return B_TRUE if the aggregation is included, B_FALSE otherwise. Only in the
19521449Stomee * latter case might there be an exception pending.
19531449Stomee */
19541449Stomee static boolean_t
dtj_is_included(const dtrace_aggdata_t * data,dtj_java_consumer_t * jc)19551449Stomee dtj_is_included(const dtrace_aggdata_t *data, dtj_java_consumer_t *jc)
19561449Stomee {
19571449Stomee JNIEnv *jenv = jc->dtjj_jenv;
19581449Stomee
19591449Stomee if (jc->dtjj_aggregate_spec) {
19601449Stomee jboolean included;
19611449Stomee jstring aggname = NULL;
19621449Stomee
19631449Stomee const dtrace_aggdesc_t *aggdesc = data->dtada_desc;
19641449Stomee aggname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name);
19651449Stomee if (!aggname) {
19661449Stomee /* java exception pending */
19671449Stomee return (B_FALSE);
19681449Stomee }
19691449Stomee
19701449Stomee included = (*jenv)->CallBooleanMethod(jenv,
19711449Stomee jc->dtjj_aggregate_spec, g_aggspec_included_jm,
19721449Stomee aggname);
19731449Stomee (*jenv)->DeleteLocalRef(jenv, aggname);
19741449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
19751449Stomee WRAP_EXCEPTION(jenv);
19761449Stomee return (B_FALSE);
19771449Stomee }
19781449Stomee
19791449Stomee return (included);
19801449Stomee }
19811449Stomee
19821449Stomee return (B_TRUE);
19831449Stomee }
19841449Stomee
19851449Stomee /*
19861449Stomee * Return NULL if a java exception is pending, otherwise return a new
19871449Stomee * AggregationValue instance.
19881449Stomee */
19891449Stomee static jobject
dtj_new_aggval(dtj_java_consumer_t * jc,const dtrace_aggdata_t * data,const dtrace_recdesc_t * rec)19901449Stomee dtj_new_aggval(dtj_java_consumer_t *jc, const dtrace_aggdata_t *data,
19911449Stomee const dtrace_recdesc_t *rec)
19921449Stomee {
19931449Stomee JNIEnv *jenv = jc->dtjj_jenv;
19941449Stomee
19951449Stomee jobject jvalue = NULL; /* return value */
19961449Stomee
19971449Stomee dtrace_actkind_t act;
19981449Stomee uint64_t normal;
19991449Stomee caddr_t addr;
20001449Stomee int64_t value;
20011449Stomee
20021449Stomee act = rec->dtrd_action;
20031449Stomee normal = data->dtada_normal;
20041449Stomee addr = data->dtada_data + rec->dtrd_offset;
20051449Stomee if (act == DTRACEAGG_AVG) {
20061449Stomee value = dtj_average(addr, normal);
20071449Stomee } else {
20081449Stomee /* LINTED - alignment */
20091449Stomee value = (*((int64_t *)addr)) / normal;
20101449Stomee }
20111449Stomee
20121449Stomee if (act == DTRACEAGG_QUANTIZE || act == DTRACEAGG_LQUANTIZE) {
20131449Stomee jvalue = dtj_new_distribution(data, rec, jc);
20141449Stomee } else {
20151449Stomee switch (act) {
20161449Stomee case DTRACEAGG_COUNT:
20171449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggcount_jc,
20181449Stomee g_aggcountinit_jm, value);
20191449Stomee break;
20201449Stomee case DTRACEAGG_SUM:
20211449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggsum_jc,
20221449Stomee g_aggsuminit_jm, value);
20231449Stomee break;
20241449Stomee case DTRACEAGG_AVG:
20251449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggavg_jc,
20261449Stomee g_aggavginit_jm, value, dtj_avg_total(addr,
20271449Stomee normal), dtj_avg_count(addr));
20281449Stomee break;
20291449Stomee case DTRACEAGG_MIN:
20301449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggmin_jc,
20311449Stomee g_aggmininit_jm, value);
20321449Stomee break;
20331449Stomee case DTRACEAGG_MAX:
20341449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggmax_jc,
20351449Stomee g_aggmaxinit_jm, value);
20361449Stomee break;
2037*6136Stomee case DTRACEAGG_STDDEV:
2038*6136Stomee jvalue = dtj_stddev(jenv, addr, normal);
2039*6136Stomee break;
20401449Stomee default:
20411449Stomee jvalue = NULL;
20421449Stomee dtj_throw_illegal_argument(jenv,
20431449Stomee "unexpected aggregation action: %d", act);
20441449Stomee }
20451449Stomee }
20461449Stomee
20471449Stomee return (jvalue);
20481449Stomee }
20491449Stomee
20501449Stomee /*
20511449Stomee * Stops the given consumer if it is running. Throws DTraceException if
20521449Stomee * dtrace_stop() fails and no other exception is already pending. Clears and
20531449Stomee * rethrows any pending exception in order to grab the global lock safely.
20541449Stomee */
20551449Stomee void
dtj_stop(dtj_java_consumer_t * jc)20561449Stomee dtj_stop(dtj_java_consumer_t *jc)
20571449Stomee {
20581449Stomee JNIEnv *jenv;
20591449Stomee int rc;
20601449Stomee jthrowable e;
20611449Stomee
20621449Stomee switch (jc->dtjj_consumer->dtjc_state) {
20631449Stomee case DTJ_CONSUMER_GO:
20641449Stomee case DTJ_CONSUMER_START:
20651449Stomee break;
20661449Stomee default:
20671449Stomee return;
20681449Stomee }
20691449Stomee
20701449Stomee jenv = jc->dtjj_jenv;
20711449Stomee e = (*jenv)->ExceptionOccurred(jenv);
20721449Stomee if (e) {
20731449Stomee (*jenv)->ExceptionClear(jenv);
20741449Stomee }
20751449Stomee
20761449Stomee (*jenv)->MonitorEnter(jenv, g_caller_jc);
20771449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
20781449Stomee goto rethrow;
20791449Stomee }
20801449Stomee
20811449Stomee rc = dtrace_status(jc->dtjj_consumer->dtjc_dtp);
20821449Stomee if (rc != DTRACE_STATUS_STOPPED) {
20831449Stomee rc = dtrace_stop(jc->dtjj_consumer->dtjc_dtp);
20841449Stomee }
20851449Stomee
20861449Stomee (*jenv)->MonitorExit(jenv, g_caller_jc);
20871449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
20881449Stomee goto rethrow;
20891449Stomee }
20901449Stomee
20911449Stomee if (rc == -1) {
20921449Stomee (*jenv)->MonitorEnter(jenv, g_caller_jc);
20931449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
20941449Stomee goto rethrow;
20951449Stomee }
20961449Stomee /* Do not wrap DTraceException */
20971449Stomee dtj_throw_dtrace_exception(jc,
20981449Stomee "couldn't stop tracing: %s",
20991449Stomee dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp,
21001449Stomee dtrace_errno(jc->dtjj_consumer->dtjc_dtp)));
21011449Stomee /* safe to call with pending exception */
21021449Stomee (*jenv)->MonitorExit(jenv, g_caller_jc);
21031449Stomee } else {
21041449Stomee jc->dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP;
21051449Stomee }
21061449Stomee
21071449Stomee rethrow:
21081449Stomee if (e) {
21091449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
21101449Stomee /*
21111449Stomee * Favor earlier pending exception over
21121449Stomee * exception thrown in this function.
21131449Stomee */
21141449Stomee (*jenv)->ExceptionClear(jenv);
21151449Stomee }
21161449Stomee (*jenv)->Throw(jenv, e);
21171449Stomee (*jenv)->DeleteLocalRef(jenv, e);
21181449Stomee }
21191449Stomee }
21201449Stomee
21211449Stomee /*
21221449Stomee * Return Aggregate instance, or null if java exception pending.
21231449Stomee */
21241449Stomee jobject
dtj_get_aggregate(dtj_java_consumer_t * jc)21251449Stomee dtj_get_aggregate(dtj_java_consumer_t *jc)
21261449Stomee {
21271449Stomee JNIEnv *jenv = jc->dtjj_jenv;
21281449Stomee hrtime_t snaptime;
21291449Stomee int rc;
21301449Stomee
21311449Stomee jobject aggregate = NULL;
21321449Stomee
21333645Stomee /* Must not call MonitorEnter with a pending exception */
21343645Stomee if ((*jenv)->ExceptionCheck(jenv)) {
21353645Stomee WRAP_EXCEPTION(jenv);
21363645Stomee return (NULL);
21373645Stomee }
21383645Stomee
21391449Stomee /*
21401449Stomee * Aggregations must be snapped, walked, and cleared atomically,
21411449Stomee * otherwise clearing loses data accumulated since the most recent snap.
21421449Stomee * This per-consumer lock prevents dtrace_work() from snapping or
21431449Stomee * clearing aggregations while we're in the middle of this atomic
21441449Stomee * operation, so we continue to hold it until done clearing.
21451449Stomee */
21461449Stomee (*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock);
21471449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
21481449Stomee WRAP_EXCEPTION(jenv);
21491449Stomee return (NULL);
21501449Stomee }
21511449Stomee
21521449Stomee dtj_aggwalk_init(jc);
21531449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
21541449Stomee WRAP_EXCEPTION(jenv);
21551449Stomee /* release per-consumer lock */
21561449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
21571449Stomee return (NULL);
21581449Stomee }
21591449Stomee
21601449Stomee /*
21611449Stomee * Snap aggregations
21621449Stomee *
21631449Stomee * We need to record the snaptime here for the caller. Leaving it to
21641449Stomee * the caller to record the snaptime before calling getAggregate() may
21651449Stomee * be inaccurate because of the indeterminate delay waiting on the
21661449Stomee * consumer lock before calling dtrace_aggregate_snap().
21671449Stomee */
21681449Stomee snaptime = gethrtime();
21691449Stomee if (dtrace_aggregate_snap(jc->dtjj_consumer->dtjc_dtp) != 0) {
21701449Stomee dtj_error_t e;
21713645Stomee
21723645Stomee /*
21733645Stomee * The dataDropped() ConsumerListener method can throw an
21743645Stomee * exception in the getAggregate() thread if the drop handler is
21753645Stomee * invoked during dtrace_aggregate_snap().
21763645Stomee */
21773645Stomee if ((*jenv)->ExceptionCheck(jenv)) {
21783645Stomee /* Do not wrap exception thrown from ConsumerListener */
21793645Stomee /* release per-consumer lock */
21803645Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
21813645Stomee return (NULL);
21823645Stomee }
21833645Stomee
21841449Stomee if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) {
21851449Stomee /* Do not wrap DTraceException */
21861449Stomee dtj_throw_dtrace_exception(jc, e.dtje_message);
21871449Stomee }
21881449Stomee /* release per-consumer lock */
21891449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
21901449Stomee return (NULL);
21911449Stomee }
21921449Stomee
21933645Stomee if ((*jenv)->ExceptionCheck(jenv)) {
21943645Stomee /*
21953645Stomee * Wrap the exception thrown from ConsumerListener in this case,
21963645Stomee * so we can see that it unexpectedly reached this spot in
21973645Stomee * native code (dtrace_aggregate_snap should have returned
21983645Stomee * non-zero).
21993645Stomee */
22003645Stomee WRAP_EXCEPTION(jenv);
22013645Stomee /* release per-consumer lock */
22023645Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22033645Stomee return (NULL);
22043645Stomee }
22053645Stomee
22061449Stomee /* Create the Java representation of the aggregate snapshot. */
22071449Stomee aggregate = (*jenv)->NewObject(jenv, g_agg_jc, g_agginit_jm,
22081449Stomee snaptime);
22091449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
22101449Stomee WRAP_EXCEPTION(jenv);
22111449Stomee /* release per-consumer lock */
22121449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22131449Stomee return (NULL);
22141449Stomee }
22151449Stomee jc->dtjj_aggregate = aggregate;
22161449Stomee
22171449Stomee /*
2218*6136Stomee * Walk the aggregate, converting the data into Java Objects. Traverse
2219*6136Stomee * in the order determined by libdtrace, respecting the various
2220*6136Stomee * "aggsort" options, just as dtrace_work does when generating
2221*6136Stomee * aggregations for the printa() action. libdtrace ordering is preserved
2222*6136Stomee * in the "ordinal" property of AggregationRecord, since it would
2223*6136Stomee * otherwise be lost when the records are hashed into the Aggregation's
2224*6136Stomee * map. Neither the consumer loop nor the competing getAggregate()
2225*6136Stomee * thread should depend on any particular record ordering (such as
2226*6136Stomee * ordering by tuple key) to process records correctly.
22271449Stomee *
22281449Stomee * It is impractical to hold the global lock around
22291449Stomee * dtrace_aggregate_print(), since it may take a long time (e.g. an
22301449Stomee * entire second) if it performs expensive conversions such as that
22311449Stomee * needed for user stack traces. Most libdtrace functions are not
22321449Stomee * guaranteed to be MT-safe, even when each thread has its own dtrace
22331449Stomee * handle; or even if they are safe, there is no guarantee that future
22341449Stomee * changes may not make them unsafe. Fortunately in this case, however,
22351449Stomee * only a per-consumer lock is necessary to avoid conflict with
22361449Stomee * dtrace_work() running in another thread (the consumer loop).
22371449Stomee */
2238*6136Stomee rc = dtrace_aggregate_print(jc->dtjj_consumer->dtjc_dtp, NULL, NULL);
22391449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
22401449Stomee WRAP_EXCEPTION(jenv);
22411449Stomee /* release per-consumer lock */
22421449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22431449Stomee return (NULL);
22441449Stomee }
22451449Stomee if (rc != 0) {
22461449Stomee dtj_error_t e;
22471449Stomee if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) {
22481449Stomee /* release per-consumer lock */
22491449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22501449Stomee return (NULL);
22511449Stomee }
22521449Stomee
22531449Stomee if (e.dtje_number != EINTR) {
22541449Stomee /* Do not wrap DTraceException */
22551449Stomee dtj_throw_dtrace_exception(jc, e.dtje_message);
22561449Stomee /* release per-consumer lock */
22571449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22581449Stomee return (NULL);
22591449Stomee }
22601449Stomee }
22611449Stomee
22621449Stomee dtj_aggwalk_init(jc);
22631449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
22641449Stomee WRAP_EXCEPTION(jenv);
22651449Stomee /* release per-consumer lock */
22661449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22671449Stomee return (NULL);
22681449Stomee }
22691449Stomee
22701449Stomee /*
22711449Stomee * dtrace_aggregate_clear() clears all aggregations, and we need to
22721449Stomee * clear aggregations selectively. It also fails to preserve the
22731449Stomee * lquantize() range and step size; using aggregate_walk() to clear
22741449Stomee * aggregations does not have this problem.
22751449Stomee */
22761449Stomee rc = dtrace_aggregate_walk(jc->dtjj_consumer->dtjc_dtp, dtj_clear, jc);
22771449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
22781449Stomee WRAP_EXCEPTION(jenv);
22791449Stomee /* release per-consumer lock */
22801449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22811449Stomee return (NULL);
22821449Stomee }
22831449Stomee if (rc != 0) {
22841449Stomee dtj_error_t e;
22851449Stomee if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) {
22861449Stomee /* Do not wrap DTraceException */
22871449Stomee dtj_throw_dtrace_exception(jc, e.dtje_message);
22881449Stomee }
22891449Stomee /* release per-consumer lock */
22901449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22911449Stomee return (NULL);
22921449Stomee }
22931449Stomee
22941449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22951449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
22961449Stomee WRAP_EXCEPTION(jenv);
22971449Stomee return (NULL);
22981449Stomee }
22991449Stomee
23001449Stomee aggregate = jc->dtjj_aggregate;
23011449Stomee jc->dtjj_aggregate = NULL;
23021449Stomee
23031449Stomee return (aggregate);
23041449Stomee }
23051449Stomee
23061449Stomee /*
23071449Stomee * Process any requests, such as the setting of runtime options, enqueued during
23081449Stomee * dtrace_sleep(). A Java exception is pending if this function returns
23091449Stomee * DTJ_ERR.
23101449Stomee */
23111449Stomee static dtj_status_t
dtj_process_requests(dtj_java_consumer_t * jc)23121449Stomee dtj_process_requests(dtj_java_consumer_t *jc)
23131449Stomee {
23141449Stomee dtj_request_t *r;
23151449Stomee uu_list_t *list = jc->dtjj_consumer->dtjc_request_list;
23161449Stomee pthread_mutex_t *list_lock = &jc->dtjj_consumer->
23171449Stomee dtjc_request_list_lock;
23181449Stomee const char *opt;
23191449Stomee const char *val;
23201449Stomee
23211449Stomee (void) pthread_mutex_lock(list_lock);
23221449Stomee while (!dtj_list_empty(list)) {
23231449Stomee r = uu_list_first(list);
23241449Stomee uu_list_remove(list, r);
23251449Stomee
23261449Stomee switch (r->dtjr_type) {
23271449Stomee case DTJ_REQUEST_OPTION:
23281449Stomee opt = dtj_string_list_first(r->dtjr_args);
23291449Stomee val = dtj_string_list_last(r->dtjr_args);
23301449Stomee if (dtrace_setopt(jc->dtjj_consumer->dtjc_dtp, opt,
23311449Stomee val) == -1) {
23321449Stomee /* Do not wrap DTraceException */
23331449Stomee dtj_throw_dtrace_exception(jc,
23341449Stomee "failed to set %s: %s", opt,
23351449Stomee dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp,
23361449Stomee dtrace_errno(jc->dtjj_consumer->dtjc_dtp)));
23371449Stomee dtj_request_destroy(r, NULL);
23381449Stomee (void) pthread_mutex_unlock(list_lock);
23391449Stomee return (DTJ_ERR);
23401449Stomee }
23411449Stomee break;
23421449Stomee }
23431449Stomee dtj_request_destroy(r, NULL);
23441449Stomee }
23451449Stomee (void) pthread_mutex_unlock(list_lock);
23461449Stomee return (DTJ_OK);
23471449Stomee }
23481449Stomee
23491449Stomee /*
23501449Stomee * Return DTJ_OK if the consumer loop is stopped normally by either the exit()
23511449Stomee * action or the Consumer stop() method. Otherwise return DTJ_ERR if the
23521449Stomee * consumer loop terminates abnormally with an exception pending.
23531449Stomee */
23541449Stomee dtj_status_t
dtj_consume(dtj_java_consumer_t * jc)23551449Stomee dtj_consume(dtj_java_consumer_t *jc)
23561449Stomee {
23571449Stomee JNIEnv *jenv = jc->dtjj_jenv;
23581449Stomee dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp;
23591449Stomee boolean_t done = B_FALSE;
23601449Stomee dtj_error_t e;
23611449Stomee
23621449Stomee do {
23631449Stomee if (!jc->dtjj_consumer->dtjc_interrupt) {
23641449Stomee dtrace_sleep(dtp);
23651449Stomee }
23661449Stomee
23671449Stomee if (jc->dtjj_consumer->dtjc_interrupt) {
23681449Stomee done = B_TRUE;
23691449Stomee dtj_stop(jc);
23701449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
23711449Stomee /*
23721449Stomee * Exception left pending by Consumer
23731449Stomee * getAggregate() method.
23741449Stomee */
23751449Stomee return (DTJ_ERR);
23761449Stomee }
23771449Stomee } else if (jc->dtjj_consumer->dtjc_process_list != NULL) {
23781449Stomee int nprocs = uu_list_numnodes(jc->dtjj_consumer->
23791449Stomee dtjc_process_list);
23801449Stomee if (jc->dtjj_consumer->dtjc_procs_ended == nprocs) {
23811449Stomee done = B_TRUE;
23821449Stomee dtj_stop(jc);
23831449Stomee }
23841449Stomee }
23851449Stomee
23861449Stomee /*
23871449Stomee * Functions like dtrace_setopt() are not safe to call during
23881449Stomee * dtrace_sleep(). Check the request list every time we wake up
23891449Stomee * from dtrace_sleep().
23901449Stomee */
23911449Stomee if (!done) {
23921449Stomee if (dtj_process_requests(jc) != DTJ_OK) {
23931449Stomee /* Do not wrap DTraceException */
23941449Stomee return (DTJ_ERR);
23951449Stomee }
23961449Stomee }
23971449Stomee
23983645Stomee /* Must not call MonitorEnter with a pending exception */
23993645Stomee if ((*jenv)->ExceptionCheck(jenv)) {
24003645Stomee WRAP_EXCEPTION(jenv);
24013645Stomee return (DTJ_ERR);
24023645Stomee }
24033645Stomee
24041449Stomee /*
24051449Stomee * Use the per-consumer lock to avoid conflict with
24061449Stomee * get_aggregate() called from another thread.
24071449Stomee */
24081449Stomee (*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock);
24091449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
24101449Stomee WRAP_EXCEPTION(jenv);
24111449Stomee return (DTJ_ERR);
24121449Stomee }
24131449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller,
24141449Stomee g_interval_began_jm);
24151449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
24161716Stomee /* Don't wrap exception thrown from ConsumerListener */
24171449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
24181449Stomee return (DTJ_ERR);
24191449Stomee }
24201449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = gethrtime();
24211449Stomee switch (dtrace_work(dtp, NULL, dtj_chew, dtj_chewrec, jc)) {
24221449Stomee case DTRACE_WORKSTATUS_DONE:
24231449Stomee done = B_TRUE;
24241449Stomee break;
24251449Stomee case DTRACE_WORKSTATUS_OKAY:
24261449Stomee break;
24271449Stomee default:
24281449Stomee /*
24291449Stomee * Check for a pending exception that got us to this
24301449Stomee * error workstatus case.
24311449Stomee */
24321449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
24331449Stomee /*
24341449Stomee * Ensure valid initial state before releasing
24351449Stomee * the consumer lock
24361449Stomee */
24371449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0;
24381449Stomee /* Do not wrap DTraceException */
24391449Stomee /* Release per-consumer lock */
24401449Stomee (*jenv)->MonitorExit(jenv,
24411449Stomee jc->dtjj_consumer_lock);
24421449Stomee return (DTJ_ERR);
24431449Stomee }
24441449Stomee
24451449Stomee if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) {
24461449Stomee /* java exception pending */
24471449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0;
24481449Stomee /* Release per-consumer lock */
24491449Stomee (*jenv)->MonitorExit(jenv,
24501449Stomee jc->dtjj_consumer_lock);
24511449Stomee return (DTJ_ERR);
24521449Stomee }
24531449Stomee
24541449Stomee if (e.dtje_number != EINTR) {
24551449Stomee /* Do not wrap DTraceException */
24561449Stomee dtj_throw_dtrace_exception(jc, e.dtje_message);
24571449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0;
24581449Stomee /* Release per-consumer lock */
24591449Stomee (*jenv)->MonitorExit(jenv,
24601449Stomee jc->dtjj_consumer_lock);
24611449Stomee return (DTJ_ERR);
24621449Stomee }
24631449Stomee }
24641449Stomee /*
24651449Stomee * Check for ConsumerException before doing anything else with
24661449Stomee * the JNIEnv.
24671449Stomee */
24681449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
24691449Stomee /*
24701449Stomee * Do not wrap exception thrown from ConsumerListener.
24711449Stomee */
24721449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0;
24731449Stomee /* Release per-consumer lock */
24741449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
24751449Stomee return (DTJ_ERR);
24761449Stomee }
24771449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0;
24781449Stomee /*
24791449Stomee * Notify ConsumerListeners the the dtrace_work() interval ended
24801449Stomee * before releasing the lock.
24811449Stomee */
24821449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller,
24831449Stomee g_interval_ended_jm);
24841449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
24851449Stomee if ((*jenv)->ExceptionCheck(jenv)) {
24861716Stomee /* Don't wrap exception thrown from ConsumerListener */
24871449Stomee return (DTJ_ERR);
24881449Stomee }
24891449Stomee
24901449Stomee /*
24911449Stomee * Check for a temporarily cleared exception set by a handler
24921449Stomee * that could not safely leave the exception pending because it
24931449Stomee * could not return an abort signal. Rethrow it now that it's
24941449Stomee * safe to do so (when it's possible to ensure that no JNI calls
24951449Stomee * will be made that are unsafe while an exception is pending).
24961449Stomee */
24971449Stomee if (jc->dtjj_exception) {
24981449Stomee (*jenv)->Throw(jenv, jc->dtjj_exception);
24991449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_exception);
25001449Stomee jc->dtjj_exception = NULL;
25011449Stomee return (DTJ_ERR);
25021449Stomee }
25031449Stomee } while (!done);
25041449Stomee
25051449Stomee return (DTJ_OK);
25061449Stomee }
2507