xref: /onnv-gate/usr/src/lib/libdtrace_jni/common/dtj_consume.c (revision 6136:b1f0a0698377)
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