1fb3fb4f3Stomee /*
2fb3fb4f3Stomee  * CDDL HEADER START
3fb3fb4f3Stomee  *
4fb3fb4f3Stomee  * The contents of this file are subject to the terms of the
5fb3fb4f3Stomee  * Common Development and Distribution License (the "License").
6fb3fb4f3Stomee  * You may not use this file except in compliance with the License.
7fb3fb4f3Stomee  *
8fb3fb4f3Stomee  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fb3fb4f3Stomee  * or http://www.opensolaris.org/os/licensing.
10fb3fb4f3Stomee  * See the License for the specific language governing permissions
11fb3fb4f3Stomee  * and limitations under the License.
12fb3fb4f3Stomee  *
13fb3fb4f3Stomee  * When distributing Covered Code, include this CDDL HEADER in each
14fb3fb4f3Stomee  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fb3fb4f3Stomee  * If applicable, add the following below this CDDL HEADER, with the
16fb3fb4f3Stomee  * fields enclosed by brackets "[]" replaced with your own identifying
17fb3fb4f3Stomee  * information: Portions Copyright [yyyy] [name of copyright owner]
18fb3fb4f3Stomee  *
19fb3fb4f3Stomee  * CDDL HEADER END
20fb3fb4f3Stomee  */
21fb3fb4f3Stomee 
22fb3fb4f3Stomee /*
23e77b06d2Stomee  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24fb3fb4f3Stomee  * Use is subject to license terms.
25fb3fb4f3Stomee  */
26fb3fb4f3Stomee 
27fb3fb4f3Stomee #include <stdio.h>
28fb3fb4f3Stomee #include <ctype.h>
29fb3fb4f3Stomee #include <limits.h>
30fb3fb4f3Stomee #include <errno.h>
31fb3fb4f3Stomee #include <stdlib.h>
32fb3fb4f3Stomee #include <unistd.h>
33fb3fb4f3Stomee #include <strings.h>
34fb3fb4f3Stomee #include <sys/wait.h>
35fb3fb4f3Stomee #include <limits.h>
36fb3fb4f3Stomee #include <signal.h>
37fb3fb4f3Stomee #include <libproc.h>
38fb3fb4f3Stomee #include <pthread.h>
39fb3fb4f3Stomee #include <dtrace_jni.h>
40fb3fb4f3Stomee 
41fb3fb4f3Stomee /*
42fb3fb4f3Stomee  * Implements the work done in the running consumer loop.  The native Java
43fb3fb4f3Stomee  * methods (JNI layer) are implemented in dtrace_jni.c.
44fb3fb4f3Stomee  */
45fb3fb4f3Stomee 
46fb3fb4f3Stomee /* Record handler passed to dtrace_work() */
47fb3fb4f3Stomee static int dtj_chewrec(const dtrace_probedata_t *, const dtrace_recdesc_t *,
48fb3fb4f3Stomee     void *);
49fb3fb4f3Stomee /* Probe data handler passed to dtrace_work() */
50fb3fb4f3Stomee static int dtj_chew(const dtrace_probedata_t *, void *);
51fb3fb4f3Stomee 
52fb3fb4f3Stomee /* Processes requests from LocalConsumer enqueued during dtrace_sleep() */
53fb3fb4f3Stomee static dtj_status_t dtj_process_requests(dtj_java_consumer_t *);
54fb3fb4f3Stomee 
55fb3fb4f3Stomee /*
56fb3fb4f3Stomee  * Callback handlers set in dtj_set_callback_handlers(), called from libdtrace
57fb3fb4f3Stomee  * in the consumer loop (from dtrace_work())
58fb3fb4f3Stomee  */
59fb3fb4f3Stomee static int dtj_drophandler(const dtrace_dropdata_t *, void *);
60fb3fb4f3Stomee static int dtj_errhandler(const dtrace_errdata_t *, void *);
61fb3fb4f3Stomee static void dtj_prochandler(struct ps_prochandle *, const char *, void *);
62fb3fb4f3Stomee static int dtj_setopthandler(const dtrace_setoptdata_t *, void *);
63fb3fb4f3Stomee /*
64fb3fb4f3Stomee  * Buffered output handler called from libdtrace in both the consumer loop (from
65fb3fb4f3Stomee  * dtrace_work()) and the get_aggregate() function (from
66fb3fb4f3Stomee  * dtrace_aggregate_print()).
67fb3fb4f3Stomee  */
68fb3fb4f3Stomee static int dtj_bufhandler(const dtrace_bufdata_t *, void *);
69fb3fb4f3Stomee 
70fb3fb4f3Stomee /* Conversion of libdtrace data into Java Objects */
71fb3fb4f3Stomee static jobject dtj_recdata(dtj_java_consumer_t *, uint32_t, caddr_t);
72fb3fb4f3Stomee static jobject dtj_bytedata(JNIEnv *, uint32_t, caddr_t);
73fb3fb4f3Stomee static jobject dtj_new_stack_record(const caddr_t, const dtrace_recdesc_t *,
74fb3fb4f3Stomee     dtj_java_consumer_t *);
75fb3fb4f3Stomee static jobject dtj_new_probedata_stack_record(const dtrace_probedata_t *,
76fb3fb4f3Stomee     const dtrace_recdesc_t *, dtj_java_consumer_t *);
77127bbe13Stomee static jobject dtj_new_symbol_record(const caddr_t, const dtrace_recdesc_t *,
78127bbe13Stomee     dtj_java_consumer_t *);
79127bbe13Stomee static jobject dtj_new_probedata_symbol_record(const dtrace_probedata_t *,
80127bbe13Stomee     const dtrace_recdesc_t *, dtj_java_consumer_t *);
81fb3fb4f3Stomee /* Aggregation data */
82fb3fb4f3Stomee static jobject dtj_new_tuple_stack_record(const dtrace_aggdata_t *,
83fb3fb4f3Stomee     const dtrace_recdesc_t *, const char *, dtj_java_consumer_t *);
84127bbe13Stomee static jobject dtj_new_tuple_symbol_record(const dtrace_aggdata_t *,
85127bbe13Stomee     const dtrace_recdesc_t *, const char *, dtj_java_consumer_t *);
86fb3fb4f3Stomee static jobject dtj_new_distribution(const dtrace_aggdata_t *,
87fb3fb4f3Stomee     const dtrace_recdesc_t *, dtj_java_consumer_t *);
88fb3fb4f3Stomee static jobject dtj_new_aggval(dtj_java_consumer_t *, const dtrace_aggdata_t *,
89fb3fb4f3Stomee     const dtrace_recdesc_t *);
90fb3fb4f3Stomee static int64_t dtj_average(caddr_t, uint64_t);
91fb3fb4f3Stomee static int64_t dtj_avg_total(caddr_t, uint64_t);
92fb3fb4f3Stomee static int64_t dtj_avg_count(caddr_t);
93e77b06d2Stomee static jobject dtj_stddev(JNIEnv *, caddr_t, uint64_t);
94fb3fb4f3Stomee 
95fb3fb4f3Stomee /* Aggregation functions */
96fb3fb4f3Stomee static void dtj_aggwalk_init(dtj_java_consumer_t *);
97fb3fb4f3Stomee static int dtj_agghandler(const dtrace_bufdata_t *, dtj_java_consumer_t *);
98fb3fb4f3Stomee static boolean_t dtj_is_included(const dtrace_aggdata_t *,
99fb3fb4f3Stomee     dtj_java_consumer_t *);
100127bbe13Stomee static void dtj_attach_frames(dtj_java_consumer_t *, jobject, jobjectArray);
101127bbe13Stomee static void dtj_attach_name(dtj_java_consumer_t *, jobject, jstring);
102fb3fb4f3Stomee static boolean_t dtj_is_stack_action(dtrace_actkind_t);
103127bbe13Stomee static boolean_t dtj_is_symbol_action(dtrace_actkind_t);
104fb3fb4f3Stomee static int dtj_clear(const dtrace_aggdata_t *, void *);
105fb3fb4f3Stomee 
106fb3fb4f3Stomee /*
107fb3fb4f3Stomee  * The consumer loop needs to protect calls to libdtrace functions with a global
108fb3fb4f3Stomee  * lock.  JNI native method calls in dtrace_jni.c are already protected and do
109fb3fb4f3Stomee  * not need this function.
110fb3fb4f3Stomee  */
111fb3fb4f3Stomee dtj_status_t
dtj_get_dtrace_error(dtj_java_consumer_t * jc,dtj_error_t * e)112fb3fb4f3Stomee dtj_get_dtrace_error(dtj_java_consumer_t *jc, dtj_error_t *e)
113fb3fb4f3Stomee {
114fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
115fb3fb4f3Stomee 	dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp;
116fb3fb4f3Stomee 
1174ae67516Stomee 	/* Must not call MonitorEnter with a pending exception */
1184ae67516Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
1194ae67516Stomee 		WRAP_EXCEPTION(jenv);
1204ae67516Stomee 		return (DTJ_ERR);
1214ae67516Stomee 	}
122fb3fb4f3Stomee 	/* Grab global lock */
123fb3fb4f3Stomee 	(*jenv)->MonitorEnter(jenv, g_caller_jc);
124fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
125fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
126fb3fb4f3Stomee 		return (DTJ_ERR);
127fb3fb4f3Stomee 	}
128fb3fb4f3Stomee 	e->dtje_number = dtrace_errno(dtp);
129fb3fb4f3Stomee 	e->dtje_message = dtrace_errmsg(dtp, e->dtje_number);
130fb3fb4f3Stomee 	(*jenv)->MonitorExit(jenv, g_caller_jc);
131fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
132fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
133fb3fb4f3Stomee 		return (DTJ_ERR);
134fb3fb4f3Stomee 	}
135fb3fb4f3Stomee 	return (DTJ_OK);
136fb3fb4f3Stomee }
137fb3fb4f3Stomee 
138fb3fb4f3Stomee /*
139fb3fb4f3Stomee  * Protected by global lock (LocalConsumer.class) that protects call to
140fb3fb4f3Stomee  * Java_org_opensolaris_os_dtrace_LocalConsumer__1go()
141fb3fb4f3Stomee  */
142fb3fb4f3Stomee dtj_status_t
dtj_set_callback_handlers(dtj_java_consumer_t * jc)143fb3fb4f3Stomee dtj_set_callback_handlers(dtj_java_consumer_t *jc)
144fb3fb4f3Stomee {
145fb3fb4f3Stomee 	dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp;
146fb3fb4f3Stomee 	dtrace_optval_t optval;
147fb3fb4f3Stomee 
148fb3fb4f3Stomee 	if (dtrace_handle_buffered(dtp, &dtj_bufhandler, NULL) == -1) {
149fb3fb4f3Stomee 		dtj_throw_dtrace_exception(jc,
150fb3fb4f3Stomee 		    "failed to establish buffered handler: %s",
151fb3fb4f3Stomee 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
152fb3fb4f3Stomee 		return (DTJ_ERR);
153fb3fb4f3Stomee 	}
154fb3fb4f3Stomee 
155fb3fb4f3Stomee 	if (dtrace_handle_drop(dtp, &dtj_drophandler, NULL) == -1) {
156fb3fb4f3Stomee 		dtj_throw_dtrace_exception(jc,
157fb3fb4f3Stomee 		    "failed to establish drop handler: %s",
158fb3fb4f3Stomee 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
159fb3fb4f3Stomee 		return (DTJ_ERR);
160fb3fb4f3Stomee 	}
161fb3fb4f3Stomee 
162fb3fb4f3Stomee 	if (dtrace_handle_err(dtp, &dtj_errhandler, NULL) == -1) {
163fb3fb4f3Stomee 		dtj_throw_dtrace_exception(jc,
164fb3fb4f3Stomee 		    "failed to establish error handler: %s",
165fb3fb4f3Stomee 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
166fb3fb4f3Stomee 		return (DTJ_ERR);
167fb3fb4f3Stomee 	}
168fb3fb4f3Stomee 
169fb3fb4f3Stomee 	if (dtrace_handle_proc(dtp, &dtj_prochandler, NULL) == -1) {
170fb3fb4f3Stomee 		dtj_throw_dtrace_exception(jc,
171fb3fb4f3Stomee 		    "failed to establish proc handler: %s",
172fb3fb4f3Stomee 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
173fb3fb4f3Stomee 		return (DTJ_ERR);
174fb3fb4f3Stomee 	}
175fb3fb4f3Stomee 
176fb3fb4f3Stomee 	if (dtrace_getopt(dtp, "flowindent", &optval) == -1) {
177fb3fb4f3Stomee 		dtj_throw_dtrace_exception(jc,
178fb3fb4f3Stomee 		    "couldn't get option %s: %s", "flowindent",
179fb3fb4f3Stomee 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
180fb3fb4f3Stomee 		return (DTJ_ERR);
181fb3fb4f3Stomee 	}
182fb3fb4f3Stomee 
183fb3fb4f3Stomee 	jc->dtjj_consumer->dtjc_flow = (optval != DTRACEOPT_UNSET);
184fb3fb4f3Stomee 
185fb3fb4f3Stomee 	if (dtrace_handle_setopt(dtp, &dtj_setopthandler, NULL) == -1) {
186fb3fb4f3Stomee 		dtj_throw_dtrace_exception(jc,
187fb3fb4f3Stomee 		    "failed to establish setopt handler: %s",
188fb3fb4f3Stomee 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
189fb3fb4f3Stomee 		return (DTJ_ERR);
190fb3fb4f3Stomee 	}
191fb3fb4f3Stomee 
192fb3fb4f3Stomee 	return (DTJ_OK);
193fb3fb4f3Stomee }
194fb3fb4f3Stomee 
195fb3fb4f3Stomee static int
196fb3fb4f3Stomee /* ARGSUSED */
dtj_drophandler(const dtrace_dropdata_t * data,void * arg)197fb3fb4f3Stomee dtj_drophandler(const dtrace_dropdata_t *data, void *arg)
198fb3fb4f3Stomee {
199fb3fb4f3Stomee 	dtj_java_consumer_t *jc;
200fb3fb4f3Stomee 	JNIEnv *jenv;
201fb3fb4f3Stomee 
202fb3fb4f3Stomee 	const char *dropkind;
203fb3fb4f3Stomee 
204fb3fb4f3Stomee 	jstring msg = NULL;
205fb3fb4f3Stomee 	jstring kind = NULL;
206fb3fb4f3Stomee 	jobject drop = NULL;
207fb3fb4f3Stomee 
208fb3fb4f3Stomee 	jc = pthread_getspecific(g_dtj_consumer_key);
209fb3fb4f3Stomee 	jenv = jc->dtjj_jenv;
210fb3fb4f3Stomee 
211fb3fb4f3Stomee 	msg = dtj_NewStringNative(jenv, data->dtdda_msg);
212fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
213fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
214fb3fb4f3Stomee 	}
215fb3fb4f3Stomee 	switch (data->dtdda_kind) {
216fb3fb4f3Stomee 	case DTRACEDROP_PRINCIPAL:
217fb3fb4f3Stomee 		dropkind = "PRINCIPAL";
218fb3fb4f3Stomee 		break;
219fb3fb4f3Stomee 	case DTRACEDROP_AGGREGATION:
220fb3fb4f3Stomee 		dropkind = "AGGREGATION";
221fb3fb4f3Stomee 		break;
222fb3fb4f3Stomee 	case DTRACEDROP_DYNAMIC:
223fb3fb4f3Stomee 		dropkind = "DYNAMIC";
224fb3fb4f3Stomee 		break;
225fb3fb4f3Stomee 	case DTRACEDROP_DYNRINSE:
226fb3fb4f3Stomee 		dropkind = "DYNRINSE";
227fb3fb4f3Stomee 		break;
228fb3fb4f3Stomee 	case DTRACEDROP_DYNDIRTY:
229fb3fb4f3Stomee 		dropkind = "DYNDIRTY";
230fb3fb4f3Stomee 		break;
231fb3fb4f3Stomee 	case DTRACEDROP_SPEC:
232fb3fb4f3Stomee 		dropkind = "SPEC";
233fb3fb4f3Stomee 		break;
234fb3fb4f3Stomee 	case DTRACEDROP_SPECBUSY:
235fb3fb4f3Stomee 		dropkind = "SPECBUSY";
236fb3fb4f3Stomee 		break;
237fb3fb4f3Stomee 	case DTRACEDROP_SPECUNAVAIL:
238fb3fb4f3Stomee 		dropkind = "SPECUNAVAIL";
239fb3fb4f3Stomee 		break;
240fb3fb4f3Stomee 	case DTRACEDROP_STKSTROVERFLOW:
241fb3fb4f3Stomee 		dropkind = "STKSTROVERFLOW";
242fb3fb4f3Stomee 		break;
243fb3fb4f3Stomee 	case DTRACEDROP_DBLERROR:
244fb3fb4f3Stomee 		dropkind = "DBLERROR";
245fb3fb4f3Stomee 		break;
246fb3fb4f3Stomee 	default:
247fb3fb4f3Stomee 		dropkind = "UNKNOWN";
248fb3fb4f3Stomee 	}
249fb3fb4f3Stomee 	kind = (*jenv)->NewStringUTF(jenv, dropkind);
250fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
251fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, msg);
252fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
253fb3fb4f3Stomee 	}
254fb3fb4f3Stomee 	drop = (*jenv)->NewObject(jenv, g_drop_jc, g_dropinit_jm,
255fb3fb4f3Stomee 	    data->dtdda_cpu, kind, data->dtdda_drops, data->dtdda_total, msg);
256fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, kind);
257fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, msg);
258fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
259fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
260fb3fb4f3Stomee 	}
261fb3fb4f3Stomee 	(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_drop_jm, drop);
262fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, drop);
263fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
264fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
265fb3fb4f3Stomee 	}
266fb3fb4f3Stomee 
267fb3fb4f3Stomee 	return (DTRACE_HANDLE_OK);
268fb3fb4f3Stomee }
269fb3fb4f3Stomee 
270fb3fb4f3Stomee static int
271fb3fb4f3Stomee /* ARGSUSED */
dtj_errhandler(const dtrace_errdata_t * data,void * arg)272fb3fb4f3Stomee dtj_errhandler(const dtrace_errdata_t *data, void *arg)
273fb3fb4f3Stomee {
274fb3fb4f3Stomee 	dtj_java_consumer_t *jc;
275fb3fb4f3Stomee 	JNIEnv *jenv;
276fb3fb4f3Stomee 
277fb3fb4f3Stomee 	const char *f;
278fb3fb4f3Stomee 	int64_t addr;
279fb3fb4f3Stomee 
280fb3fb4f3Stomee 	jobject probe = NULL;
281fb3fb4f3Stomee 	jstring fault = NULL;
282fb3fb4f3Stomee 	jstring msg = NULL;
283fb3fb4f3Stomee 	jobject error = NULL;
284fb3fb4f3Stomee 
285fb3fb4f3Stomee 	jc = pthread_getspecific(g_dtj_consumer_key);
286fb3fb4f3Stomee 	jenv = jc->dtjj_jenv;
287fb3fb4f3Stomee 
288fb3fb4f3Stomee 	probe = dtj_new_probedesc(jc, data->dteda_pdesc);
289fb3fb4f3Stomee 	if (!probe) {
290fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
291fb3fb4f3Stomee 	}
292fb3fb4f3Stomee 	f = dtj_get_fault_name(data->dteda_fault);
293fb3fb4f3Stomee 	if (f) {
294fb3fb4f3Stomee 		fault = (*jenv)->NewStringUTF(jenv, f);
295fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
296fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, probe);
297fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
298fb3fb4f3Stomee 		}
299fb3fb4f3Stomee 	}
300fb3fb4f3Stomee 	switch (data->dteda_fault) {
301fb3fb4f3Stomee 	case DTRACEFLT_BADADDR:
302fb3fb4f3Stomee 	case DTRACEFLT_BADALIGN:
303b8fac8e1Sjhaslam 	case DTRACEFLT_BADSTACK:
304fb3fb4f3Stomee 		addr = data->dteda_addr;
305fb3fb4f3Stomee 		break;
306fb3fb4f3Stomee 	default:
307fb3fb4f3Stomee 		addr = -1;
308fb3fb4f3Stomee 	}
309fb3fb4f3Stomee 	msg = dtj_NewStringNative(jenv, data->dteda_msg);
310fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
311fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, probe);
312fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, fault);
313fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
314fb3fb4f3Stomee 	}
315fb3fb4f3Stomee 	error = (*jenv)->NewObject(jenv, g_error_jc, g_errinit_jm,
316fb3fb4f3Stomee 	    probe,
317fb3fb4f3Stomee 	    data->dteda_edesc->dtepd_epid,
318fb3fb4f3Stomee 	    data->dteda_cpu,
319fb3fb4f3Stomee 	    data->dteda_action,
320fb3fb4f3Stomee 	    data->dteda_offset,
321fb3fb4f3Stomee 	    fault, addr, msg);
322fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, msg);
323fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, fault);
324fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, probe);
325fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
326fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
327fb3fb4f3Stomee 	}
328fb3fb4f3Stomee 	(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_error_jm, error);
329fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, error);
330fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
331fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
332fb3fb4f3Stomee 	}
333fb3fb4f3Stomee 
334fb3fb4f3Stomee 	return (DTRACE_HANDLE_OK);
335fb3fb4f3Stomee }
336fb3fb4f3Stomee 
337fb3fb4f3Stomee /*
338fb3fb4f3Stomee  * Since the function signature does not allow us to return an abort signal, we
339fb3fb4f3Stomee  * need to temporarily clear any pending exception before returning, since
340fb3fb4f3Stomee  * without the abort we can't guarantee that the exception will be checked in
341fb3fb4f3Stomee  * time to prevent invalid JNI function calls.
342fb3fb4f3Stomee  */
343fb3fb4f3Stomee static void
344fb3fb4f3Stomee /* ARGSUSED */
dtj_prochandler(struct ps_prochandle * P,const char * msg,void * arg)345fb3fb4f3Stomee dtj_prochandler(struct ps_prochandle *P, const char *msg, void *arg)
346fb3fb4f3Stomee {
347fb3fb4f3Stomee 	dtj_java_consumer_t *jc;
348fb3fb4f3Stomee 	JNIEnv *jenv;
349fb3fb4f3Stomee 
350fb3fb4f3Stomee 	const psinfo_t *prp = Ppsinfo(P);
351fb3fb4f3Stomee 	int pid = Pstatus(P)->pr_pid;
352fb3fb4f3Stomee 	int signal = -1;
353fb3fb4f3Stomee 	char signame[SIG2STR_MAX];
354fb3fb4f3Stomee 	const char *statusname;
355fb3fb4f3Stomee 	int exit = INT_MAX; /* invalid initial status */
356fb3fb4f3Stomee 
357fb3fb4f3Stomee 	jstring status = NULL;
358fb3fb4f3Stomee 	jstring signalName = NULL;
359fb3fb4f3Stomee 	jstring message = NULL;
360fb3fb4f3Stomee 	jobject process = NULL;
361fb3fb4f3Stomee 
362fb3fb4f3Stomee 	jc = pthread_getspecific(g_dtj_consumer_key);
363fb3fb4f3Stomee 	jenv = jc->dtjj_jenv;
364fb3fb4f3Stomee 
365fb3fb4f3Stomee 	switch (Pstate(P)) {
366fb3fb4f3Stomee 	case PS_RUN:
367fb3fb4f3Stomee 		statusname = "RUN";
368fb3fb4f3Stomee 		break;
369fb3fb4f3Stomee 	case PS_STOP:
370fb3fb4f3Stomee 		statusname = "STOP";
371fb3fb4f3Stomee 		break;
372fb3fb4f3Stomee 	case PS_UNDEAD:
373fb3fb4f3Stomee 		statusname = "UNDEAD";
374fb3fb4f3Stomee 		if (prp != NULL) {
375fb3fb4f3Stomee 			exit = WEXITSTATUS(prp->pr_wstat);
376fb3fb4f3Stomee 		}
377fb3fb4f3Stomee 		if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) {
378fb3fb4f3Stomee 			signal = WTERMSIG(prp->pr_wstat);
379fb3fb4f3Stomee 			(void) proc_signame(signal, signame, sizeof (signame));
380fb3fb4f3Stomee 			signalName = (*jenv)->NewStringUTF(jenv, signame);
381fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
382fb3fb4f3Stomee 				goto proc_end;
383fb3fb4f3Stomee 			}
384fb3fb4f3Stomee 		}
385fb3fb4f3Stomee 		++jc->dtjj_consumer->dtjc_procs_ended;
386fb3fb4f3Stomee 		break;
387fb3fb4f3Stomee 	case PS_LOST:
388fb3fb4f3Stomee 		statusname = "LOST";
389fb3fb4f3Stomee 		++jc->dtjj_consumer->dtjc_procs_ended;
390fb3fb4f3Stomee 		break;
391fb3fb4f3Stomee 	case PS_DEAD:
392fb3fb4f3Stomee 		/*
393fb3fb4f3Stomee 		 * PS_DEAD not handled by dtrace.c prochandler, still this is a
394fb3fb4f3Stomee 		 * case of process termination and it can't hurt to handle it.
395fb3fb4f3Stomee 		 */
396fb3fb4f3Stomee 		statusname = "DEAD";
397fb3fb4f3Stomee 		++jc->dtjj_consumer->dtjc_procs_ended;
398fb3fb4f3Stomee 		break;
399fb3fb4f3Stomee 	default:
400fb3fb4f3Stomee 		/*
401fb3fb4f3Stomee 		 * Unexpected, but erring on the side of tolerance by not
402fb3fb4f3Stomee 		 * crashing the consumer.  Failure to notify listeners of
403fb3fb4f3Stomee 		 * process state not handled by the dtrace.c prochandler does
404fb3fb4f3Stomee 		 * not seem serious.
405fb3fb4f3Stomee 		 */
406fb3fb4f3Stomee 		return;
407fb3fb4f3Stomee 	}
408fb3fb4f3Stomee 
409fb3fb4f3Stomee 	status = (*jenv)->NewStringUTF(jenv, statusname);
410fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
411fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, signalName);
412fb3fb4f3Stomee 		goto proc_end;
413fb3fb4f3Stomee 	}
414fb3fb4f3Stomee 	if (msg) {
415fb3fb4f3Stomee 		message = dtj_NewStringNative(jenv, msg);
416fb3fb4f3Stomee 		if (!message) {
417fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, status);
418fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, signalName);
419fb3fb4f3Stomee 			goto proc_end;
420fb3fb4f3Stomee 		}
421fb3fb4f3Stomee 	}
422fb3fb4f3Stomee 	process = (*jenv)->NewObject(jenv, g_process_jc, g_procinit_jm,
423fb3fb4f3Stomee 	    pid, status, signal, signalName, NULL, message);
424fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, status);
425fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, signalName);
426fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, message);
427fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
428fb3fb4f3Stomee 		goto proc_end;
429fb3fb4f3Stomee 	}
430fb3fb4f3Stomee 	if (exit != INT_MAX) {
431fb3fb4f3Stomee 		/* valid exit status */
432fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, process, g_procexit_jm, exit);
433fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
434fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, process);
435fb3fb4f3Stomee 			goto proc_end;
436fb3fb4f3Stomee 		}
437fb3fb4f3Stomee 	}
438fb3fb4f3Stomee 	(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_proc_jm, process);
439fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, process);
440fb3fb4f3Stomee 
441fb3fb4f3Stomee proc_end:
442fb3fb4f3Stomee 
443fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
444fb3fb4f3Stomee 		/*
445fb3fb4f3Stomee 		 * Save the exception so we can rethrow it later when it's safe.
446fb3fb4f3Stomee 		 */
447fb3fb4f3Stomee 		if (!jc->dtjj_exception) {
448fb3fb4f3Stomee 			jthrowable e = (*jenv)->ExceptionOccurred(jenv);
449fb3fb4f3Stomee 			jc->dtjj_exception = e;
450fb3fb4f3Stomee 		}
451fb3fb4f3Stomee 		(*jenv)->ExceptionClear(jenv);
452fb3fb4f3Stomee 	}
453fb3fb4f3Stomee }
454fb3fb4f3Stomee 
455fb3fb4f3Stomee static int
456fb3fb4f3Stomee /* ARGSUSED */
dtj_setopthandler(const dtrace_setoptdata_t * data,void * arg)457fb3fb4f3Stomee dtj_setopthandler(const dtrace_setoptdata_t *data, void *arg)
458fb3fb4f3Stomee {
459fb3fb4f3Stomee 	dtj_java_consumer_t *jc;
460fb3fb4f3Stomee 
461fb3fb4f3Stomee 	jc = pthread_getspecific(g_dtj_consumer_key);
462fb3fb4f3Stomee 	if (strcmp(data->dtsda_option, "flowindent") == 0) {
463fb3fb4f3Stomee 		jc->dtjj_consumer->dtjc_flow =
464fb3fb4f3Stomee 		    (data->dtsda_newval != DTRACEOPT_UNSET);
465fb3fb4f3Stomee 	}
466fb3fb4f3Stomee 	return (DTRACE_HANDLE_OK);
467fb3fb4f3Stomee }
468fb3fb4f3Stomee 
469fb3fb4f3Stomee /*
470fb3fb4f3Stomee  * Most of this function lifted from libdtrace/common/dt_consume.c
471fb3fb4f3Stomee  * dt_print_bytes().
472fb3fb4f3Stomee  */
473fb3fb4f3Stomee static jobject
dtj_bytedata(JNIEnv * jenv,uint32_t nbytes,caddr_t addr)474fb3fb4f3Stomee dtj_bytedata(JNIEnv *jenv, uint32_t nbytes, caddr_t addr)
475fb3fb4f3Stomee {
476fb3fb4f3Stomee 	/*
477fb3fb4f3Stomee 	 * If the byte stream is a series of printable characters, followed by
478fb3fb4f3Stomee 	 * a terminating byte, we print it out as a string.  Otherwise, we
479fb3fb4f3Stomee 	 * assume that it's something else and just print the bytes.
480fb3fb4f3Stomee 	 */
481fb3fb4f3Stomee 	int i, j;
482fb3fb4f3Stomee 	char *c = addr;
483fb3fb4f3Stomee 
484fb3fb4f3Stomee 	jobject jobj = NULL; /* return value */
485fb3fb4f3Stomee 
486fb3fb4f3Stomee 	if (nbytes == 0) {
487fb3fb4f3Stomee 		return ((*jenv)->NewStringUTF(jenv, ""));
488fb3fb4f3Stomee 	}
489fb3fb4f3Stomee 
490fb3fb4f3Stomee 	for (i = 0; i < nbytes; i++) {
491fb3fb4f3Stomee 		/*
492fb3fb4f3Stomee 		 * We define a "printable character" to be one for which
493fb3fb4f3Stomee 		 * isprint(3C) returns non-zero, isspace(3C) returns non-zero,
494fb3fb4f3Stomee 		 * or a character which is either backspace or the bell.
495fb3fb4f3Stomee 		 * Backspace and the bell are regrettably special because
496fb3fb4f3Stomee 		 * they fail the first two tests -- and yet they are entirely
497fb3fb4f3Stomee 		 * printable.  These are the only two control characters that
498fb3fb4f3Stomee 		 * have meaning for the terminal and for which isprint(3C) and
499fb3fb4f3Stomee 		 * isspace(3C) return 0.
500fb3fb4f3Stomee 		 */
501fb3fb4f3Stomee 		if (isprint(c[i]) || isspace(c[i]) ||
502fb3fb4f3Stomee 		    c[i] == '\b' || c[i] == '\a')
503fb3fb4f3Stomee 			continue;
504fb3fb4f3Stomee 
505fb3fb4f3Stomee 		if (c[i] == '\0' && i > 0) {
506fb3fb4f3Stomee 			/*
507fb3fb4f3Stomee 			 * This looks like it might be a string.  Before we
508fb3fb4f3Stomee 			 * assume that it is indeed a string, check the
509fb3fb4f3Stomee 			 * remainder of the byte range; if it contains
510fb3fb4f3Stomee 			 * additional non-nul characters, we'll assume that
511fb3fb4f3Stomee 			 * it's a binary stream that just happens to look like
512fb3fb4f3Stomee 			 * a string.
513fb3fb4f3Stomee 			 */
514fb3fb4f3Stomee 			for (j = i + 1; j < nbytes; j++) {
515fb3fb4f3Stomee 				if (c[j] != '\0')
516fb3fb4f3Stomee 					break;
517fb3fb4f3Stomee 			}
518fb3fb4f3Stomee 
519fb3fb4f3Stomee 			if (j != nbytes)
520fb3fb4f3Stomee 				break;
521fb3fb4f3Stomee 
522fb3fb4f3Stomee 			/* It's a string */
523fb3fb4f3Stomee 			return (dtj_NewStringNative(jenv, (char *)addr));
524fb3fb4f3Stomee 		}
525fb3fb4f3Stomee 
526fb3fb4f3Stomee 		break;
527fb3fb4f3Stomee 	}
528fb3fb4f3Stomee 
529fb3fb4f3Stomee 	if (i == nbytes) {
530fb3fb4f3Stomee 		/*
531fb3fb4f3Stomee 		 * The byte range is all printable characters, but there is
532fb3fb4f3Stomee 		 * no trailing nul byte.  We'll assume that it's a string.
533fb3fb4f3Stomee 		 */
534fb3fb4f3Stomee 		char *s = malloc(nbytes + 1);
535fb3fb4f3Stomee 		if (!s) {
536fb3fb4f3Stomee 			dtj_throw_out_of_memory(jenv,
537fb3fb4f3Stomee 			    "failed to allocate string value");
538fb3fb4f3Stomee 			return (NULL);
539fb3fb4f3Stomee 		}
540fb3fb4f3Stomee 		(void) strncpy(s, c, nbytes);
541fb3fb4f3Stomee 		s[nbytes] = '\0';
542fb3fb4f3Stomee 		jobj = dtj_NewStringNative(jenv, s);
543fb3fb4f3Stomee 		free(s);
544fb3fb4f3Stomee 		return (jobj);
545fb3fb4f3Stomee 	}
546fb3fb4f3Stomee 
547fb3fb4f3Stomee 	/* return byte array */
548fb3fb4f3Stomee 	jobj = (*jenv)->NewByteArray(jenv, nbytes);
549fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
550fb3fb4f3Stomee 		return (NULL);
551fb3fb4f3Stomee 	}
552fb3fb4f3Stomee 	(*jenv)->SetByteArrayRegion(jenv, (jbyteArray)jobj, 0, nbytes,
553fb3fb4f3Stomee 	    (const jbyte *)c);
554fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
555fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
556fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jobj);
557fb3fb4f3Stomee 		return (NULL);
558fb3fb4f3Stomee 	}
559fb3fb4f3Stomee 	return (jobj);
560fb3fb4f3Stomee }
561fb3fb4f3Stomee 
562fb3fb4f3Stomee /*
563fb3fb4f3Stomee  * Return NULL if memory could not be allocated (OutOfMemoryError is thrown in
564fb3fb4f3Stomee  * that case).
565fb3fb4f3Stomee  */
566fb3fb4f3Stomee static jobject
dtj_recdata(dtj_java_consumer_t * jc,uint32_t size,caddr_t addr)567fb3fb4f3Stomee dtj_recdata(dtj_java_consumer_t *jc, uint32_t size, caddr_t addr)
568fb3fb4f3Stomee {
569fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
570fb3fb4f3Stomee 	jobject jobj;
571127bbe13Stomee 	jobject jrec;
572fb3fb4f3Stomee 
573fb3fb4f3Stomee 	switch (size) {
574fb3fb4f3Stomee 	case 1:
575127bbe13Stomee 		jobj = (*jenv)->NewObject(jenv, g_int_jc,
576127bbe13Stomee 		    g_intinit_jm, (int)(*((uint8_t *)addr)));
577fb3fb4f3Stomee 		break;
578fb3fb4f3Stomee 	case 2:
579127bbe13Stomee 		jobj = (*jenv)->NewObject(jenv, g_int_jc,
580fb3fb4f3Stomee 		    /* LINTED - alignment */
581127bbe13Stomee 		    g_intinit_jm, (int)(*((uint16_t *)addr)));
582fb3fb4f3Stomee 		break;
583fb3fb4f3Stomee 	case 4:
584fb3fb4f3Stomee 		jobj = (*jenv)->NewObject(jenv, g_int_jc,
585fb3fb4f3Stomee 		    /* LINTED - alignment */
586fb3fb4f3Stomee 		    g_intinit_jm, *((int32_t *)addr));
587fb3fb4f3Stomee 		break;
588fb3fb4f3Stomee 	case 8:
589fb3fb4f3Stomee 		jobj = (*jenv)->NewObject(jenv, g_long_jc,
590fb3fb4f3Stomee 		    /* LINTED - alignment */
591fb3fb4f3Stomee 		    g_longinit_jm, *((int64_t *)addr));
592fb3fb4f3Stomee 		break;
593fb3fb4f3Stomee 	default:
594fb3fb4f3Stomee 		jobj = dtj_bytedata(jenv, size, addr);
595fb3fb4f3Stomee 		break;
596fb3fb4f3Stomee 	}
597fb3fb4f3Stomee 
598127bbe13Stomee 	if (!jobj) {
599127bbe13Stomee 		return (NULL); /* OutOfMemoryError pending */
600127bbe13Stomee 	}
601127bbe13Stomee 
602127bbe13Stomee 	jrec = (*jenv)->NewObject(jenv, g_scalar_jc,
603127bbe13Stomee 	    g_scalarinit_jm, jobj, size);
604127bbe13Stomee 	(*jenv)->DeleteLocalRef(jenv, jobj);
605127bbe13Stomee 
606127bbe13Stomee 	return (jrec);
607fb3fb4f3Stomee }
608fb3fb4f3Stomee 
609fb3fb4f3Stomee /*
610fb3fb4f3Stomee  * This is the record handling function passed to dtrace_work().  It differs
611fb3fb4f3Stomee  * from the bufhandler registered with dtrace_handle_buffered() as follows:
612fb3fb4f3Stomee  *
613fb3fb4f3Stomee  * 1.  It does not have access to libdtrace formatted output.
614fb3fb4f3Stomee  * 2.  It is called once for every D program statement, not for every
615fb3fb4f3Stomee  *     output-producing D action or aggregation record.  A statement may be a
616fb3fb4f3Stomee  *     variable assignment, having no size and producing no output.
617fb3fb4f3Stomee  * 3.  It is called for the D exit() action; the bufhandler is not.
618fb3fb4f3Stomee  * 4.  In response to the printa() action, it is called with a record having an
619fb3fb4f3Stomee  *     action of type DTRACEACT_PRINTA.  The bufhandler never sees that action
620fb3fb4f3Stomee  *     value.  It only sees the output-producing aggregation records.
621fb3fb4f3Stomee  * 5.  It is called with a NULL record at the end of each probedata.
622fb3fb4f3Stomee  */
623fb3fb4f3Stomee static int
dtj_chewrec(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,void * arg)624fb3fb4f3Stomee dtj_chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec,
625fb3fb4f3Stomee     void *arg)
626fb3fb4f3Stomee {
627fb3fb4f3Stomee 	dtj_java_consumer_t *jc = arg;
628fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
629fb3fb4f3Stomee 
630fb3fb4f3Stomee 	const dtrace_eprobedesc_t *edesc = data->dtpda_edesc;
631fb3fb4f3Stomee 	dtrace_actkind_t act;
632fb3fb4f3Stomee 	int r;
633fb3fb4f3Stomee 
634fb3fb4f3Stomee 	/*
635fb3fb4f3Stomee 	 * Update the record index to that of the current record, or to that of
636fb3fb4f3Stomee 	 * the last record if rec is NULL (signalling end of probe data).
637fb3fb4f3Stomee 	 */
638fb3fb4f3Stomee 	if (rec == NULL) {
639fb3fb4f3Stomee 		r = edesc->dtepd_nrecs; /* end of probe data */
640fb3fb4f3Stomee 	} else {
641fb3fb4f3Stomee 		/*
642fb3fb4f3Stomee 		 * This record handler is called once for the printf() action,
643fb3fb4f3Stomee 		 * but there may be multiple records in the probedata
644fb3fb4f3Stomee 		 * corresponding to the unformatted elements of that printf().
645fb3fb4f3Stomee 		 * We don't know ahead of time how many probedata records
646fb3fb4f3Stomee 		 * libdtrace will consume to produce output for one printf()
647fb3fb4f3Stomee 		 * action, so we look back at the previous call to dtj_chewrec()
648fb3fb4f3Stomee 		 * to see how many probedata records were consumed.  All
649fb3fb4f3Stomee 		 * non-null elements in the range from the previous record index
650fb3fb4f3Stomee 		 * up to and not including the current record index are assumed
651fb3fb4f3Stomee 		 * to be unformatted printf() elements, and will be attached to
652fb3fb4f3Stomee 		 * the PrintfRecord from the previous call.  A null element in
653fb3fb4f3Stomee 		 * that range is the result of a D program statement preceding
654fb3fb4f3Stomee 		 * the printf() that is not a D action.  These generate
655fb3fb4f3Stomee 		 * probedata records accounted for by the null placeholder, but
656fb3fb4f3Stomee 		 * do not advance the probedata offset and are not part of the
657fb3fb4f3Stomee 		 * subsequent printf().
658fb3fb4f3Stomee 		 *
659fb3fb4f3Stomee 		 * If rec->dtrd_size == 0, the record represents a D program
660fb3fb4f3Stomee 		 * statement that is not a D action.  It has no size and does
661fb3fb4f3Stomee 		 * not advance the offset in the probedata.  Handle it normally
662fb3fb4f3Stomee 		 * without special-casing or premature return, since in all
663fb3fb4f3Stomee 		 * cases we look at the previous record later in this function.
664fb3fb4f3Stomee 		 */
665fb3fb4f3Stomee 		for (r = jc->dtjj_consumer->dtjc_probedata_rec_i;
666fb3fb4f3Stomee 		    ((r < edesc->dtepd_nrecs) &&
667fb3fb4f3Stomee 		    (edesc->dtepd_rec[r].dtrd_offset < rec->dtrd_offset));
668fb3fb4f3Stomee 		    ++r) {
669fb3fb4f3Stomee 		}
670fb3fb4f3Stomee 	}
671fb3fb4f3Stomee 
672fb3fb4f3Stomee 	/*
673fb3fb4f3Stomee 	 * Attach the Java representations of the libdtrace data elements
674fb3fb4f3Stomee 	 * pertaining to the previous call to this record handler to the
675fb3fb4f3Stomee 	 * previous Java Record.  (All data elements belonging to the current
676fb3fb4f3Stomee 	 * probedata are added to a single list by the probedata consumer
677fb3fb4f3Stomee 	 * function dtj_chew() before this record consumer function is ever
678fb3fb4f3Stomee 	 * called.) For example, if the previous Record was generated by the
679fb3fb4f3Stomee 	 * printf() action, and dtj_chew() listed 3 records for its 3
680fb3fb4f3Stomee 	 * unformatted elements, those 3 libdtrace records comprise 1
681fb3fb4f3Stomee 	 * PrintfRecord.  Note that we cannot know how many data elements apply
682fb3fb4f3Stomee 	 * to the current rec until we find out the data index where the next
683fb3fb4f3Stomee 	 * rec starts.  (The knowledge of how many probedata records to consume
684fb3fb4f3Stomee 	 * is private to libdtrace.)
685fb3fb4f3Stomee 	 */
686fb3fb4f3Stomee 	if (jc->dtjj_consumer->dtjc_probedata_act == DTRACEACT_PRINTF) {
687fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
688fb3fb4f3Stomee 		    g_pdataattach_jm,
689fb3fb4f3Stomee 		    jc->dtjj_consumer->dtjc_probedata_rec_i, r - 1);
690fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
691fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
692fb3fb4f3Stomee 			return (DTRACE_CONSUME_ABORT);
693fb3fb4f3Stomee 		}
694fb3fb4f3Stomee 	}
695fb3fb4f3Stomee 
696fb3fb4f3Stomee 	if (rec == NULL) {
697fb3fb4f3Stomee 		/*
698fb3fb4f3Stomee 		 * End of probe data.  Notify listeners of the new ProbeData
699fb3fb4f3Stomee 		 * instance.
700fb3fb4f3Stomee 		 */
701fb3fb4f3Stomee 		if (jc->dtjj_probedata) {
702fb3fb4f3Stomee 			/* previous probedata */
703fb3fb4f3Stomee 			(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
704fb3fb4f3Stomee 			    g_pdataclear_jm);
705fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
706fb3fb4f3Stomee 				WRAP_EXCEPTION(jenv);
707fb3fb4f3Stomee 				return (DTRACE_CONSUME_ABORT);
708fb3fb4f3Stomee 			}
709fb3fb4f3Stomee 			(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller,
710fb3fb4f3Stomee 			    g_pdatanext_jm, jc->dtjj_probedata);
711fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jc->dtjj_probedata);
712fb3fb4f3Stomee 			jc->dtjj_probedata = NULL;
713fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
714fb3fb4f3Stomee 				/*
715fb3fb4f3Stomee 				 * Do not wrap exception thrown from
716fb3fb4f3Stomee 				 * ConsumerListener.
717fb3fb4f3Stomee 				 */
718fb3fb4f3Stomee 				return (DTRACE_CONSUME_ABORT);
719fb3fb4f3Stomee 			}
720fb3fb4f3Stomee 		}
721fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jc->dtjj_printa_buffer);
722fb3fb4f3Stomee 		jc->dtjj_printa_buffer = NULL;
723fb3fb4f3Stomee 		return (DTRACE_CONSUME_NEXT);
724fb3fb4f3Stomee 	}
725fb3fb4f3Stomee 
726fb3fb4f3Stomee 	act = rec->dtrd_action;
727fb3fb4f3Stomee 
728fb3fb4f3Stomee 	/* Set previous record action and data index to current */
729fb3fb4f3Stomee 	jc->dtjj_consumer->dtjc_probedata_act = act;
730fb3fb4f3Stomee 	jc->dtjj_consumer->dtjc_probedata_rec_i = r;
731fb3fb4f3Stomee 
732fb3fb4f3Stomee 	switch (act) {
733fb3fb4f3Stomee 	case DTRACEACT_DIFEXPR:
734*1ea5f93dSBryan Cantrill 	case DTRACEACT_TRACEMEM:
735fb3fb4f3Stomee 		if (rec->dtrd_size == 0) {
736fb3fb4f3Stomee 			/*
737fb3fb4f3Stomee 			 * The current record is not a D action, but a program
738fb3fb4f3Stomee 			 * statement such as a variable assignment, not to be
739fb3fb4f3Stomee 			 * confused with the trace() action.
740fb3fb4f3Stomee 			 */
741fb3fb4f3Stomee 			break;
742fb3fb4f3Stomee 		}
743fb3fb4f3Stomee 		/*
744fb3fb4f3Stomee 		 * Add a Record for the trace() action that references the
745fb3fb4f3Stomee 		 * native probedata element listed at the current index.
746fb3fb4f3Stomee 		 */
747fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
748fb3fb4f3Stomee 		    g_pdataadd_trace_jm,
749fb3fb4f3Stomee 		    jc->dtjj_consumer->dtjc_probedata_rec_i);
750fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
751fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
752fb3fb4f3Stomee 			return (DTRACE_CONSUME_ABORT);
753fb3fb4f3Stomee 		}
754fb3fb4f3Stomee 		break;
755fb3fb4f3Stomee 	case DTRACEACT_PRINTF:
756fb3fb4f3Stomee 		/*
757fb3fb4f3Stomee 		 * Just add an empty PrintfRecord for now.  We'll attach the
758fb3fb4f3Stomee 		 * unformatted elements in a subsequent call to this function.
759fb3fb4f3Stomee 		 * (We don't know how many there will be.)
760fb3fb4f3Stomee 		 */
761fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
762fb3fb4f3Stomee 		    g_pdataadd_printf_jm);
763fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
764fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
765fb3fb4f3Stomee 			return (DTRACE_CONSUME_ABORT);
766fb3fb4f3Stomee 		}
767fb3fb4f3Stomee 		/* defer formatted string to dtj_bufhandler() */
768fb3fb4f3Stomee 		break;
769fb3fb4f3Stomee 	case DTRACEACT_PRINTA: {
770fb3fb4f3Stomee 		jobject jbuf = NULL;
771fb3fb4f3Stomee 
772fb3fb4f3Stomee 		dtj_aggwalk_init(jc);
773fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
774fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
775fb3fb4f3Stomee 			return (DTRACE_CONSUME_ABORT);
776fb3fb4f3Stomee 		}
777fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
778fb3fb4f3Stomee 		    g_pdataadd_printa_jm,
779fb3fb4f3Stomee 		    jc->dtjj_consumer->dtjc_printa_snaptime,
780fb3fb4f3Stomee 		    (rec->dtrd_format != 0));
781fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
782fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
783fb3fb4f3Stomee 			return (DTRACE_CONSUME_ABORT);
784fb3fb4f3Stomee 		}
785fb3fb4f3Stomee 		if (jc->dtjj_printa_buffer == NULL) {
786fb3fb4f3Stomee 			/*
7874ae67516Stomee 			 * Create a StringBuilder to collect the pieces of
788fb3fb4f3Stomee 			 * formatted output into a single String.
789fb3fb4f3Stomee 			 */
790fb3fb4f3Stomee 			jbuf = (*jenv)->NewObject(jenv, g_buf_jc,
791fb3fb4f3Stomee 			    g_bufinit_jm);
792fb3fb4f3Stomee 			if (!jbuf) {
793fb3fb4f3Stomee 				/* OutOfMemoryError pending */
794fb3fb4f3Stomee 				return (DTRACE_CONSUME_ABORT);
795fb3fb4f3Stomee 			}
796fb3fb4f3Stomee 			jc->dtjj_printa_buffer = jbuf;
797fb3fb4f3Stomee 		}
798fb3fb4f3Stomee 		/* defer aggregation records to dtj_bufhandler() */
799fb3fb4f3Stomee 		break;
800fb3fb4f3Stomee 	}
801fb3fb4f3Stomee 	case DTRACEACT_EXIT:
802fb3fb4f3Stomee 		/*
803fb3fb4f3Stomee 		 * Add a Record for the exit() action that references the native
804fb3fb4f3Stomee 		 * probedata element listed at the current index.
805fb3fb4f3Stomee 		 */
806fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
807fb3fb4f3Stomee 		    g_pdataadd_exit_jm,
808fb3fb4f3Stomee 		    jc->dtjj_consumer->dtjc_probedata_rec_i);
809fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
810fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
811fb3fb4f3Stomee 			return (DTRACE_CONSUME_ABORT);
812fb3fb4f3Stomee 		}
813fb3fb4f3Stomee 		return (DTRACE_CONSUME_NEXT);
814fb3fb4f3Stomee 	}
815fb3fb4f3Stomee 
816fb3fb4f3Stomee 	return (DTRACE_CONSUME_THIS);
817fb3fb4f3Stomee }
818fb3fb4f3Stomee 
819fb3fb4f3Stomee /*
820fb3fb4f3Stomee  * This is the probe handling function passed to dtrace_work().  It is is called
821fb3fb4f3Stomee  * once every time a probe fires.  It is the first of all the callbacks for the
822fb3fb4f3Stomee  * current probe.  It is followed by multiple callbacks to dtj_chewrec(), one
823fb3fb4f3Stomee  * for each probedata record.  Each call to dtj_chewrec() is followed by zero or
824fb3fb4f3Stomee  * more callbacks to the bufhandler, one for each output-producing action or
825fb3fb4f3Stomee  * aggregation record.
826fb3fb4f3Stomee  */
827fb3fb4f3Stomee static int
dtj_chew(const dtrace_probedata_t * data,void * arg)828fb3fb4f3Stomee dtj_chew(const dtrace_probedata_t *data, void *arg)
829fb3fb4f3Stomee {
830fb3fb4f3Stomee 	dtj_java_consumer_t *jc = arg;
831fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
832fb3fb4f3Stomee 
833fb3fb4f3Stomee 	dtrace_eprobedesc_t *edesc;
834fb3fb4f3Stomee 	dtrace_probedesc_t *pdesc;
835fb3fb4f3Stomee 	dtrace_recdesc_t *rec;
836fb3fb4f3Stomee 	int epid;
837fb3fb4f3Stomee 	int cpu;
838fb3fb4f3Stomee 	int nrecs;
839fb3fb4f3Stomee 	int i;
840fb3fb4f3Stomee 
841fb3fb4f3Stomee 	jobject jpdata = NULL;
842fb3fb4f3Stomee 	jobject jprobe = NULL;
843fb3fb4f3Stomee 	jobject jflow = NULL;
844fb3fb4f3Stomee 	jstring jflowkind = NULL;
845fb3fb4f3Stomee 	jobject jobj = NULL;
846fb3fb4f3Stomee 
847fb3fb4f3Stomee 	edesc = data->dtpda_edesc;
848fb3fb4f3Stomee 	epid = (int)edesc->dtepd_epid;
849fb3fb4f3Stomee 	pdesc = data->dtpda_pdesc;
850fb3fb4f3Stomee 	cpu = (int)data->dtpda_cpu;
851fb3fb4f3Stomee 	if ((jprobe = dtj_new_probedesc(jc, pdesc)) == NULL) {
852fb3fb4f3Stomee 		/* java exception pending */
853fb3fb4f3Stomee 		return (DTRACE_CONSUME_ABORT);
854fb3fb4f3Stomee 	}
855fb3fb4f3Stomee 	nrecs = edesc->dtepd_nrecs;
856fb3fb4f3Stomee 
857fb3fb4f3Stomee 	if (jc->dtjj_consumer->dtjc_flow) {
858fb3fb4f3Stomee 		const char *kind;
859fb3fb4f3Stomee 		switch (data->dtpda_flow) {
860fb3fb4f3Stomee 		case DTRACEFLOW_ENTRY:
861fb3fb4f3Stomee 			kind = "ENTRY";
862fb3fb4f3Stomee 			break;
863fb3fb4f3Stomee 		case DTRACEFLOW_RETURN:
864fb3fb4f3Stomee 			kind = "RETURN";
865fb3fb4f3Stomee 			break;
866fb3fb4f3Stomee 		case DTRACEFLOW_NONE:
867fb3fb4f3Stomee 			kind = "NONE";
868fb3fb4f3Stomee 			break;
869fb3fb4f3Stomee 		default:
870fb3fb4f3Stomee 			kind = NULL;
871fb3fb4f3Stomee 		}
872fb3fb4f3Stomee 		if (kind != NULL) {
873fb3fb4f3Stomee 			int depth;
874fb3fb4f3Stomee 			jflowkind = (*jenv)->NewStringUTF(jenv, kind);
875fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
876fb3fb4f3Stomee 				WRAP_EXCEPTION(jenv);
877fb3fb4f3Stomee 				(*jenv)->DeleteLocalRef(jenv, jprobe);
878fb3fb4f3Stomee 				return (DTRACE_CONSUME_ABORT);
879fb3fb4f3Stomee 			}
880fb3fb4f3Stomee 			/*
881fb3fb4f3Stomee 			 * Use the knowledge that libdtrace indents 2 spaces per
882fb3fb4f3Stomee 			 * level in the call stack to calculate the depth.
883fb3fb4f3Stomee 			 */
884fb3fb4f3Stomee 			depth = (data->dtpda_indent / 2);
885fb3fb4f3Stomee 			jflow = (*jenv)->NewObject(jenv, g_flow_jc,
886fb3fb4f3Stomee 			    g_flowinit_jm, jflowkind, depth);
887fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jflowkind);
888fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
889fb3fb4f3Stomee 				WRAP_EXCEPTION(jenv);
890fb3fb4f3Stomee 				(*jenv)->DeleteLocalRef(jenv, jprobe);
891fb3fb4f3Stomee 				return (DTRACE_CONSUME_ABORT);
892fb3fb4f3Stomee 			}
893fb3fb4f3Stomee 		}
894fb3fb4f3Stomee 	}
895fb3fb4f3Stomee 
896fb3fb4f3Stomee 	/* Create ProbeData instance */
897fb3fb4f3Stomee 	jpdata = (*jenv)->NewObject(jenv, g_pdata_jc, g_pdatainit_jm,
898fb3fb4f3Stomee 	    epid, cpu, jprobe, jflow, nrecs);
899fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, jprobe);
900fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, jflow);
901fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
902fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
903fb3fb4f3Stomee 		return (DTRACE_CONSUME_ABORT);
904fb3fb4f3Stomee 	}
905fb3fb4f3Stomee 
906fb3fb4f3Stomee 	/*
907fb3fb4f3Stomee 	 * Populate the ProbeData list of Java data elements in advance so we
908fb3fb4f3Stomee 	 * don't need to peek back in the record handler at libdtrace records
909fb3fb4f3Stomee 	 * that have already been consumed.  In the Java API, each ProbeData
910fb3fb4f3Stomee 	 * Record is generated by one D action, while in the native libdtrace
911fb3fb4f3Stomee 	 * there may be more than one probedata record (each a single data
912fb3fb4f3Stomee 	 * element) per D action.  For example PrintfRecord has multiple
913fb3fb4f3Stomee 	 * unformatted elements, each represented by a native probedata record,
914fb3fb4f3Stomee 	 * but combined by the API into a single PrintfRecord.
915fb3fb4f3Stomee 	 */
916fb3fb4f3Stomee 	for (i = 0; i < nrecs; ++i) {
917fb3fb4f3Stomee 		rec = &edesc->dtepd_rec[i];
918fb3fb4f3Stomee 		/*
919fb3fb4f3Stomee 		 * A statement that is not a D action, such as assignment to a
920fb3fb4f3Stomee 		 * variable, has no size.  Add a NULL placeholder to the scratch
921fb3fb4f3Stomee 		 * list of Java probedata elements in that case.
922fb3fb4f3Stomee 		 */
923fb3fb4f3Stomee 		jobj = NULL; /* initialize object reference to null */
924fb3fb4f3Stomee 		if (rec->dtrd_size > 0) {
925fb3fb4f3Stomee 			if (dtj_is_stack_action(rec->dtrd_action)) {
926fb3fb4f3Stomee 				jobj = dtj_new_probedata_stack_record(data,
927fb3fb4f3Stomee 				    rec, jc);
928127bbe13Stomee 			} else if (dtj_is_symbol_action(rec->dtrd_action)) {
929127bbe13Stomee 				jobj = dtj_new_probedata_symbol_record(data,
930127bbe13Stomee 				    rec, jc);
931fb3fb4f3Stomee 			} else {
932fb3fb4f3Stomee 				jobj = dtj_recdata(jc, rec->dtrd_size,
933fb3fb4f3Stomee 				    (data->dtpda_data + rec->dtrd_offset));
934fb3fb4f3Stomee 			}
935fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
936fb3fb4f3Stomee 				WRAP_EXCEPTION(jenv);
937fb3fb4f3Stomee 				(*jenv)->DeleteLocalRef(jenv, jpdata);
938fb3fb4f3Stomee 				return (DTRACE_CONSUME_ABORT);
939fb3fb4f3Stomee 			}
940fb3fb4f3Stomee 		}
941fb3fb4f3Stomee 
942fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jpdata, g_pdataadd_jm, jobj);
943fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jobj);
944fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
945fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
946fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jpdata);
947fb3fb4f3Stomee 			return (DTRACE_CONSUME_ABORT);
948fb3fb4f3Stomee 		}
949fb3fb4f3Stomee 	}
950fb3fb4f3Stomee 
951fb3fb4f3Stomee 	if (jc->dtjj_probedata != NULL) {
952fb3fb4f3Stomee 		dtj_throw_illegal_state(jenv, "unfinished probedata");
953fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
954fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jpdata);
955fb3fb4f3Stomee 		return (DTRACE_CONSUME_ABORT);
956fb3fb4f3Stomee 	}
957fb3fb4f3Stomee 	jc->dtjj_probedata = jpdata;
958fb3fb4f3Stomee 
959fb3fb4f3Stomee 	/* Initialize per-consumer probedata fields */
960fb3fb4f3Stomee 	jc->dtjj_consumer->dtjc_probedata_rec_i = 0;
961fb3fb4f3Stomee 	jc->dtjj_consumer->dtjc_probedata_act = DTRACEACT_NONE;
962fb3fb4f3Stomee 	dtj_aggwalk_init(jc);
963fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
964fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
965fb3fb4f3Stomee 		return (DTRACE_CONSUME_ABORT);
966fb3fb4f3Stomee 	}
967fb3fb4f3Stomee 
968fb3fb4f3Stomee 	return (DTRACE_CONSUME_THIS);
969fb3fb4f3Stomee }
970fb3fb4f3Stomee 
971fb3fb4f3Stomee /*
972fb3fb4f3Stomee  * This is the buffered output handler registered with dtrace_handle_buffered().
973fb3fb4f3Stomee  * It's purpose is to make the output of the libdtrace print routines available
974fb3fb4f3Stomee  * to this API, without writing any of it to a file (such as stdout).  This is
975fb3fb4f3Stomee  * needed for the stack(), ustack(), and jstack() actions to get human-readable
976fb3fb4f3Stomee  * stack values, since there is no public function in libdtrace to convert stack
977fb3fb4f3Stomee  * values to strings.  It is also used to get the formatted output of the D
978fb3fb4f3Stomee  * printf() and printa() actions.
979fb3fb4f3Stomee  *
980fb3fb4f3Stomee  * The bufhandler is called once for each output-producing, non-aggregating D
981fb3fb4f3Stomee  * action, such as trace() or printf(), and once for each libdtrace aggregation
982fb3fb4f3Stomee  * record (whether in response to the D printa() action, or the Consumer
983fb3fb4f3Stomee  * getAggregate() method).  In the simple printa() case that takes one
984fb3fb4f3Stomee  * aggregation and does not specify a format string, there is one libdtrace
985fb3fb4f3Stomee  * record per tuple element plus one for the corresponding value.  The complete
986fb3fb4f3Stomee  * tuple/value pair becomes a single AggregationRecord exported by the API.
987fb3fb4f3Stomee  * When multiple aggregations are passed to printa(), each tuple is associated
988fb3fb4f3Stomee  * with a list of values, one from each aggregation.  If a printa() format
989fb3fb4f3Stomee  * string does not specify placeholders for every aggregation value and tuple
990fb3fb4f3Stomee  * member, callbacks for those values and tuple members are omitted (and the
991fb3fb4f3Stomee  * data is omitted from the resulting PrintaRecord).
992fb3fb4f3Stomee  *
993fb3fb4f3Stomee  * Notes to characterize some non-obvious bufhandler behavior:
994fb3fb4f3Stomee  *
995fb3fb4f3Stomee  * 1. dtj_bufhandler() is never called with bufdata->dtbda_recdesc->dtrd_action
996fb3fb4f3Stomee  * DTRACEACT_PRINTA.  That action only appears in the probedata consumer
997fb3fb4f3Stomee  * functions dtj_chew() and dtj_chewrec() before the bufhandler is called with
998fb3fb4f3Stomee  * subsequent aggregation records.
999fb3fb4f3Stomee  *
1000fb3fb4f3Stomee  * 2. If printa() specifies a format string argument, then the bufhandler is
1001fb3fb4f3Stomee  * called only for those elements of the tuple/value pair that are included in
1002fb3fb4f3Stomee  * the format string.  If a stack() tuple member is omitted from the format
1003fb3fb4f3Stomee  * string, its human-readable representation will not be available to this API,
1004fb3fb4f3Stomee  * so the stack frame array is also omitted from the resulting
1005fb3fb4f3Stomee  * AggregationRecord.  The bufhandler is also called once for each string of
1006fb3fb4f3Stomee  * characters surrounding printa() format string placeholders.  For example,
1007fb3fb4f3Stomee  * "  %@d %d stack%k\n" results in the following callbacks:
1008fb3fb4f3Stomee  *  - two spaces
1009fb3fb4f3Stomee  *  - the aggregation value
1010fb3fb4f3Stomee  *  - a single space
1011fb3fb4f3Stomee  *  - the first tuple member (an integer)
1012fb3fb4f3Stomee  *  - " stack"
1013fb3fb4f3Stomee  *  - the second tuple member (a stack)
1014fb3fb4f3Stomee  *  - a newline
1015fb3fb4f3Stomee  * A NULL record (NULL dtbda_recdesc) distinguishes a callback with interstitial
1016fb3fb4f3Stomee  * format string characters from a callback with a tuple member or aggregation
1017fb3fb4f3Stomee  * value (which has a non-NULL recdesc).  The contents are also distinguished by
1018fb3fb4f3Stomee  * the following flags:
1019fb3fb4f3Stomee  *  DTRACE_BUFDATA_AGGKEY
1020fb3fb4f3Stomee  *  DTRACE_BUFDATA_AGGVAL
1021fb3fb4f3Stomee  *  DTRACE_BUFDATA_AGGFORMAT
1022fb3fb4f3Stomee  *  DTRACE_BUFDATA_AGGLAST
1023fb3fb4f3Stomee  *
1024fb3fb4f3Stomee  * There is no final callback with the complete formatted string, so that must
1025fb3fb4f3Stomee  * be concatenated across multiple callbacks to the bufhandler.
1026fb3fb4f3Stomee  *
1027fb3fb4f3Stomee  * 3. bufdata->dtbda_probe->dtpda_data may be overwritten by libdtrace print
1028fb3fb4f3Stomee  * routines.  The address is cached in the dtj_chew() function in case it is
1029fb3fb4f3Stomee  * needed in the bufhandler.
1030fb3fb4f3Stomee  */
1031fb3fb4f3Stomee static int
1032fb3fb4f3Stomee /* ARGSUSED */
dtj_bufhandler(const dtrace_bufdata_t * bufdata,void * arg)1033fb3fb4f3Stomee dtj_bufhandler(const dtrace_bufdata_t *bufdata, void *arg)
1034fb3fb4f3Stomee {
1035fb3fb4f3Stomee 	dtj_java_consumer_t *jc;
1036fb3fb4f3Stomee 	JNIEnv *jenv;
1037fb3fb4f3Stomee 	const dtrace_recdesc_t *rec;
1038fb3fb4f3Stomee 	dtrace_actkind_t act = DTRACEACT_NONE;
1039fb3fb4f3Stomee 	const char *s;
1040fb3fb4f3Stomee 
1041fb3fb4f3Stomee 	jobject jstr = NULL;
1042fb3fb4f3Stomee 
1043fb3fb4f3Stomee 	/*
1044fb3fb4f3Stomee 	 * Get the thread-specific java consumer.  The bufhandler needs access
1045fb3fb4f3Stomee 	 * to the correct JNI state specific to either the consumer loop or the
1046fb3fb4f3Stomee 	 * getAggregate() call (aggregation snapshots can be requested
1047fb3fb4f3Stomee 	 * asynchronously while the consumer loop generates PrintaRecords in
1048fb3fb4f3Stomee 	 * dtrace_work() for ConsumerListeners).
1049fb3fb4f3Stomee 	 */
1050fb3fb4f3Stomee 	jc = pthread_getspecific(g_dtj_consumer_key);
1051fb3fb4f3Stomee 	jenv = jc->dtjj_jenv;
1052fb3fb4f3Stomee 
1053fb3fb4f3Stomee 	/*
1054fb3fb4f3Stomee 	 * In at least one corner case (printa with multiple aggregations and a
1055fb3fb4f3Stomee 	 * format string that does not completely specify the tuple), returning
1056fb3fb4f3Stomee 	 * DTRACE_HANDLE_ABORT does not prevent a subsequent callback to this
1057fb3fb4f3Stomee 	 * bufhandler.  This check ensures that the invalid call is ignored.
1058fb3fb4f3Stomee 	 */
1059fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
1060fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
1061fb3fb4f3Stomee 	}
1062fb3fb4f3Stomee 
1063fb3fb4f3Stomee 	if (bufdata->dtbda_aggdata) {
1064fb3fb4f3Stomee 		return (dtj_agghandler(bufdata, jc));
1065fb3fb4f3Stomee 	}
1066fb3fb4f3Stomee 
1067fb3fb4f3Stomee 	s = bufdata->dtbda_buffered;
1068fb3fb4f3Stomee 	if (s == NULL) {
1069fb3fb4f3Stomee 		return (DTRACE_HANDLE_OK);
1070fb3fb4f3Stomee 	}
1071fb3fb4f3Stomee 
1072fb3fb4f3Stomee 	rec = bufdata->dtbda_recdesc;
1073fb3fb4f3Stomee 	if (rec) {
1074fb3fb4f3Stomee 		act = rec->dtrd_action;
1075fb3fb4f3Stomee 	}
1076fb3fb4f3Stomee 
1077fb3fb4f3Stomee 	switch (act) {
1078fb3fb4f3Stomee 	case DTRACEACT_DIFEXPR:
1079*1ea5f93dSBryan Cantrill 	case DTRACEACT_TRACEMEM:
1080fb3fb4f3Stomee 		/* trace() action */
1081fb3fb4f3Stomee 		break;
1082fb3fb4f3Stomee 	case DTRACEACT_PRINTF:
1083fb3fb4f3Stomee 		/*
1084fb3fb4f3Stomee 		 * Only the formatted string was not available to dtj_chewrec(),
1085fb3fb4f3Stomee 		 * so we attach that now.
1086fb3fb4f3Stomee 		 */
1087fb3fb4f3Stomee 		jstr = dtj_NewStringNative(jenv, s);
1088fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1089fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1090fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1091fb3fb4f3Stomee 		}
1092fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
1093fb3fb4f3Stomee 		    g_pdataset_formatted_jm, jstr);
1094fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jstr);
1095fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1096fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1097fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1098fb3fb4f3Stomee 		}
1099fb3fb4f3Stomee 		break;
1100fb3fb4f3Stomee 	case DTRACEACT_STACK:
1101fb3fb4f3Stomee 	case DTRACEACT_USTACK:
1102fb3fb4f3Stomee 	case DTRACEACT_JSTACK:
1103fb3fb4f3Stomee 		/* stand-alone stack(), ustack(), or jstack() action */
1104fb3fb4f3Stomee 		jstr = (*jenv)->NewStringUTF(jenv, s);
1105fb3fb4f3Stomee 		if (!jstr) {
1106fb3fb4f3Stomee 			/* OutOfMemoryError pending */
1107fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1108fb3fb4f3Stomee 		}
1109fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
1110fb3fb4f3Stomee 		    g_pdataadd_stack_jm,
1111fb3fb4f3Stomee 		    jc->dtjj_consumer->dtjc_probedata_rec_i, jstr);
1112fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jstr);
1113fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1114fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1115fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1116fb3fb4f3Stomee 		}
1117fb3fb4f3Stomee 		break;
1118127bbe13Stomee 	case DTRACEACT_USYM:
1119127bbe13Stomee 	case DTRACEACT_UADDR:
1120127bbe13Stomee 	case DTRACEACT_UMOD:
1121127bbe13Stomee 	case DTRACEACT_SYM:
1122127bbe13Stomee 	case DTRACEACT_MOD:
1123127bbe13Stomee 		/* stand-alone symbol lookup action */
1124127bbe13Stomee 		jstr = (*jenv)->NewStringUTF(jenv, s);
1125127bbe13Stomee 		if (!jstr) {
1126127bbe13Stomee 			/* OutOfMemoryError pending */
1127127bbe13Stomee 			return (DTRACE_HANDLE_ABORT);
1128127bbe13Stomee 		}
1129127bbe13Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
1130127bbe13Stomee 		    g_pdataadd_symbol_jm,
1131127bbe13Stomee 		    jc->dtjj_consumer->dtjc_probedata_rec_i, jstr);
1132127bbe13Stomee 		(*jenv)->DeleteLocalRef(jenv, jstr);
1133127bbe13Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1134127bbe13Stomee 			WRAP_EXCEPTION(jenv);
1135127bbe13Stomee 			return (DTRACE_HANDLE_ABORT);
1136127bbe13Stomee 		}
1137127bbe13Stomee 		break;
1138fb3fb4f3Stomee 	default:
1139fb3fb4f3Stomee 		/*
1140fb3fb4f3Stomee 		 * The record handler dtj_chewrec() defers nothing else to this
1141fb3fb4f3Stomee 		 * bufhandler.
1142fb3fb4f3Stomee 		 */
1143fb3fb4f3Stomee 		break;
1144fb3fb4f3Stomee 	}
1145fb3fb4f3Stomee 
1146fb3fb4f3Stomee 	return (DTRACE_HANDLE_OK);
1147fb3fb4f3Stomee }
1148fb3fb4f3Stomee 
1149fb3fb4f3Stomee static boolean_t
dtj_is_stack_action(dtrace_actkind_t act)1150fb3fb4f3Stomee dtj_is_stack_action(dtrace_actkind_t act)
1151fb3fb4f3Stomee {
1152fb3fb4f3Stomee 	boolean_t stack_action;
1153fb3fb4f3Stomee 	switch (act) {
1154fb3fb4f3Stomee 	case DTRACEACT_STACK:
1155fb3fb4f3Stomee 	case DTRACEACT_USTACK:
1156fb3fb4f3Stomee 	case DTRACEACT_JSTACK:
1157fb3fb4f3Stomee 		stack_action = B_TRUE;
1158fb3fb4f3Stomee 		break;
1159fb3fb4f3Stomee 	default:
1160fb3fb4f3Stomee 		stack_action = B_FALSE;
1161fb3fb4f3Stomee 	}
1162fb3fb4f3Stomee 	return (stack_action);
1163fb3fb4f3Stomee }
1164fb3fb4f3Stomee 
1165127bbe13Stomee static boolean_t
dtj_is_symbol_action(dtrace_actkind_t act)1166127bbe13Stomee dtj_is_symbol_action(dtrace_actkind_t act)
1167127bbe13Stomee {
1168127bbe13Stomee 	boolean_t symbol_action;
1169127bbe13Stomee 	switch (act) {
1170127bbe13Stomee 	case DTRACEACT_USYM:
1171127bbe13Stomee 	case DTRACEACT_UADDR:
1172127bbe13Stomee 	case DTRACEACT_UMOD:
1173127bbe13Stomee 	case DTRACEACT_SYM:
1174127bbe13Stomee 	case DTRACEACT_MOD:
1175127bbe13Stomee 		symbol_action = B_TRUE;
1176127bbe13Stomee 		break;
1177127bbe13Stomee 	default:
1178127bbe13Stomee 		symbol_action = B_FALSE;
1179127bbe13Stomee 	}
1180127bbe13Stomee 	return (symbol_action);
1181127bbe13Stomee }
1182127bbe13Stomee 
1183fb3fb4f3Stomee /*
1184fb3fb4f3Stomee  * Called by get_aggregate() to clear only those aggregations specified by the
1185fb3fb4f3Stomee  * caller.
1186fb3fb4f3Stomee  */
1187fb3fb4f3Stomee static int
dtj_clear(const dtrace_aggdata_t * data,void * arg)1188fb3fb4f3Stomee dtj_clear(const dtrace_aggdata_t *data, void *arg)
1189fb3fb4f3Stomee {
1190fb3fb4f3Stomee 	dtj_java_consumer_t *jc = arg;
1191fb3fb4f3Stomee 	jboolean cleared = JNI_FALSE;
1192fb3fb4f3Stomee 
1193fb3fb4f3Stomee 	jstring jname = NULL;
1194fb3fb4f3Stomee 
1195fb3fb4f3Stomee 	if (jc->dtjj_aggregate_spec) {
1196fb3fb4f3Stomee 		JNIEnv *jenv = jc->dtjj_jenv;
1197fb3fb4f3Stomee 
1198fb3fb4f3Stomee 		dtrace_aggdesc_t *aggdesc = data->dtada_desc;
1199fb3fb4f3Stomee 
1200fb3fb4f3Stomee 		jname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name);
1201fb3fb4f3Stomee 		if (!jname) {
1202fb3fb4f3Stomee 			/* java exception pending */
1203fb3fb4f3Stomee 			return (DTRACE_AGGWALK_ABORT);
1204fb3fb4f3Stomee 		}
1205fb3fb4f3Stomee 
1206fb3fb4f3Stomee 		cleared = (*jenv)->CallBooleanMethod(jenv,
1207fb3fb4f3Stomee 		    jc->dtjj_aggregate_spec, g_aggspec_cleared_jm, jname);
1208fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jname);
1209fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1210fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1211fb3fb4f3Stomee 			return (DTRACE_AGGWALK_ABORT);
1212fb3fb4f3Stomee 		}
1213fb3fb4f3Stomee 	}
1214fb3fb4f3Stomee 
1215fb3fb4f3Stomee 	return (cleared ? DTRACE_AGGWALK_CLEAR : DTRACE_AGGWALK_NEXT);
1216fb3fb4f3Stomee }
1217fb3fb4f3Stomee 
1218fb3fb4f3Stomee static int64_t
dtj_average(caddr_t addr,uint64_t normal)1219fb3fb4f3Stomee dtj_average(caddr_t addr, uint64_t normal)
1220fb3fb4f3Stomee {
1221fb3fb4f3Stomee 	/* LINTED - alignment */
1222e77b06d2Stomee 	int64_t *data = (int64_t *)addr;
1223fb3fb4f3Stomee 
1224fb3fb4f3Stomee 	return (data[0] ?
1225e77b06d2Stomee 	    (data[1] / (int64_t)normal / data[0]) : 0);
1226fb3fb4f3Stomee }
1227fb3fb4f3Stomee 
1228fb3fb4f3Stomee static int64_t
dtj_avg_total(caddr_t addr,uint64_t normal)1229fb3fb4f3Stomee dtj_avg_total(caddr_t addr, uint64_t normal)
1230fb3fb4f3Stomee {
1231fb3fb4f3Stomee 	/* LINTED - alignment */
1232e77b06d2Stomee 	int64_t *data = (int64_t *)addr;
1233fb3fb4f3Stomee 
1234e77b06d2Stomee 	return (data[1] / (int64_t)normal);
1235fb3fb4f3Stomee }
1236fb3fb4f3Stomee 
1237fb3fb4f3Stomee static int64_t
dtj_avg_count(caddr_t addr)1238fb3fb4f3Stomee dtj_avg_count(caddr_t addr)
1239fb3fb4f3Stomee {
1240e77b06d2Stomee 	/* LINTED - alignment */
1241e77b06d2Stomee 	int64_t *data = (int64_t *)addr;
1242e77b06d2Stomee 
1243e77b06d2Stomee 	return (data[0]);
1244e77b06d2Stomee }
1245e77b06d2Stomee 
1246e77b06d2Stomee static jobject
dtj_stddev_total_squares(JNIEnv * jenv,caddr_t addr,uint64_t normal)1247e77b06d2Stomee dtj_stddev_total_squares(JNIEnv *jenv, caddr_t addr, uint64_t normal)
1248e77b06d2Stomee {
1249e77b06d2Stomee 	jobject val128;
1250e77b06d2Stomee 
1251fb3fb4f3Stomee 	/* LINTED - alignment */
1252fb3fb4f3Stomee 	uint64_t *data = (uint64_t *)addr;
1253fb3fb4f3Stomee 
1254e77b06d2Stomee 	if (data[0] == 0) {
1255e77b06d2Stomee 		val128 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc,
1256e77b06d2Stomee 		    g_bigint_val_jsm, (uint64_t)0);
1257e77b06d2Stomee 	} else {
1258e77b06d2Stomee 		val128 = dtj_int128(jenv, data[3], data[2]);
1259e77b06d2Stomee 
1260e77b06d2Stomee 		if (normal != 1) {
1261e77b06d2Stomee 			jobject divisor;
1262e77b06d2Stomee 			jobject tmp;
1263e77b06d2Stomee 
1264e77b06d2Stomee 			divisor = (*jenv)->CallStaticObjectMethod(jenv,
1265e77b06d2Stomee 			    g_bigint_jc, g_bigint_val_jsm, normal);
1266e77b06d2Stomee 			tmp = val128;
1267e77b06d2Stomee 			val128 = (*jenv)->CallObjectMethod(jenv, tmp,
1268e77b06d2Stomee 			    g_bigint_div_jm, divisor);
1269e77b06d2Stomee 			(*jenv)->DeleteLocalRef(jenv, tmp);
1270e77b06d2Stomee 			(*jenv)->DeleteLocalRef(jenv, divisor);
1271e77b06d2Stomee 		}
1272e77b06d2Stomee 	}
1273e77b06d2Stomee 
1274e77b06d2Stomee 	return (val128);
1275e77b06d2Stomee }
1276e77b06d2Stomee 
1277e77b06d2Stomee /*
1278e77b06d2Stomee  * Return NULL if a java exception is pending, otherwise return a new
1279e77b06d2Stomee  * StddevValue instance.
1280e77b06d2Stomee  */
1281e77b06d2Stomee static jobject
dtj_stddev(JNIEnv * jenv,caddr_t addr,uint64_t normal)1282e77b06d2Stomee dtj_stddev(JNIEnv *jenv, caddr_t addr, uint64_t normal)
1283e77b06d2Stomee {
1284e77b06d2Stomee 	jobject total_squares;
1285e77b06d2Stomee 	jobject stddev;
1286e77b06d2Stomee 
1287e77b06d2Stomee 	total_squares = dtj_stddev_total_squares(jenv, addr, normal);
1288e77b06d2Stomee 	stddev = (*jenv)->NewObject(jenv, g_aggstddev_jc, g_aggstddevinit_jm,
1289e77b06d2Stomee 	    dtj_avg_count(addr), dtj_avg_total(addr, normal), total_squares);
1290e77b06d2Stomee 	(*jenv)->DeleteLocalRef(jenv, total_squares);
1291e77b06d2Stomee 
1292e77b06d2Stomee 	return (stddev);
1293fb3fb4f3Stomee }
1294fb3fb4f3Stomee 
1295fb3fb4f3Stomee static jobject
dtj_new_probedata_stack_record(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)1296fb3fb4f3Stomee dtj_new_probedata_stack_record(const dtrace_probedata_t *data,
1297fb3fb4f3Stomee     const dtrace_recdesc_t *rec, dtj_java_consumer_t *jc)
1298fb3fb4f3Stomee {
1299fb3fb4f3Stomee 	caddr_t addr;
1300fb3fb4f3Stomee 
1301fb3fb4f3Stomee 	/* Get raw stack data */
1302fb3fb4f3Stomee 	addr = data->dtpda_data + rec->dtrd_offset;
1303fb3fb4f3Stomee 	return (dtj_new_stack_record(addr, rec, jc));
1304fb3fb4f3Stomee }
1305fb3fb4f3Stomee 
1306fb3fb4f3Stomee 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)1307fb3fb4f3Stomee dtj_new_tuple_stack_record(const dtrace_aggdata_t *data,
1308fb3fb4f3Stomee     const dtrace_recdesc_t *rec, const char *s, dtj_java_consumer_t *jc)
1309fb3fb4f3Stomee {
1310fb3fb4f3Stomee 	caddr_t addr;
1311fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
1312fb3fb4f3Stomee 
1313fb3fb4f3Stomee 	jobjectArray frames = NULL;
1314fb3fb4f3Stomee 	jobject jobj = NULL; /* tuple element */
1315fb3fb4f3Stomee 	jstring jstr = NULL;
1316fb3fb4f3Stomee 
1317fb3fb4f3Stomee 	/* Get raw stack data */
1318fb3fb4f3Stomee 	addr = data->dtada_data + rec->dtrd_offset;
1319fb3fb4f3Stomee 	jobj = dtj_new_stack_record(addr, rec, jc);
1320fb3fb4f3Stomee 	if (!jobj) {
1321fb3fb4f3Stomee 		return (NULL); /* java exception pending */
1322fb3fb4f3Stomee 	}
1323fb3fb4f3Stomee 
1324fb3fb4f3Stomee 	jstr = dtj_NewStringNative(jenv, s);
1325fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
1326fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jobj);
1327fb3fb4f3Stomee 		return (NULL);
1328fb3fb4f3Stomee 	}
1329fb3fb4f3Stomee 	frames = (*jenv)->CallStaticObjectMethod(jenv, g_stack_jc,
1330fb3fb4f3Stomee 	    g_parsestack_jsm, jstr);
1331fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, jstr);
1332fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
1333fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jobj);
1334fb3fb4f3Stomee 		return (NULL);
1335fb3fb4f3Stomee 	}
1336fb3fb4f3Stomee 	dtj_attach_frames(jc, jobj, frames);
1337fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, frames);
1338fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
1339127bbe13Stomee 		WRAP_EXCEPTION(jenv);
1340127bbe13Stomee 		return (NULL);
1341127bbe13Stomee 	}
1342127bbe13Stomee 
1343127bbe13Stomee 	return (jobj);
1344127bbe13Stomee }
1345127bbe13Stomee 
1346127bbe13Stomee static jobject
dtj_new_probedata_symbol_record(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)1347127bbe13Stomee dtj_new_probedata_symbol_record(const dtrace_probedata_t *data,
1348127bbe13Stomee     const dtrace_recdesc_t *rec, dtj_java_consumer_t *jc)
1349127bbe13Stomee {
1350127bbe13Stomee 	caddr_t addr;
1351127bbe13Stomee 
1352127bbe13Stomee 	addr = data->dtpda_data + rec->dtrd_offset;
1353127bbe13Stomee 	return (dtj_new_symbol_record(addr, rec, jc));
1354127bbe13Stomee }
1355127bbe13Stomee 
1356127bbe13Stomee 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)1357127bbe13Stomee dtj_new_tuple_symbol_record(const dtrace_aggdata_t *data,
1358127bbe13Stomee     const dtrace_recdesc_t *rec, const char *s, dtj_java_consumer_t *jc)
1359127bbe13Stomee {
1360127bbe13Stomee 	caddr_t addr;
1361127bbe13Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
1362127bbe13Stomee 
1363127bbe13Stomee 	jobject jobj = NULL; /* tuple element */
1364127bbe13Stomee 	jstring jstr = NULL; /* lookup value */
1365127bbe13Stomee 	jstring tstr = NULL; /* trimmed lookup value */
1366127bbe13Stomee 
1367127bbe13Stomee 	addr = data->dtada_data + rec->dtrd_offset;
1368127bbe13Stomee 	jobj = dtj_new_symbol_record(addr, rec, jc);
1369127bbe13Stomee 	if (!jobj) {
1370127bbe13Stomee 		return (NULL); /* java exception pending */
1371127bbe13Stomee 	}
1372127bbe13Stomee 
1373127bbe13Stomee 	/* Get symbol lookup */
1374127bbe13Stomee 	jstr = (*jenv)->NewStringUTF(jenv, s);
1375127bbe13Stomee 	if (!jstr) {
1376127bbe13Stomee 		/* OutOfMemoryError pending */
1377127bbe13Stomee 		(*jenv)->DeleteLocalRef(jenv, jobj);
1378127bbe13Stomee 		return (NULL);
1379127bbe13Stomee 	}
1380127bbe13Stomee 	/* Trim leading and trailing whitespace */
1381127bbe13Stomee 	tstr = (*jenv)->CallObjectMethod(jenv, jstr, g_trim_jm);
1382127bbe13Stomee 	/* trim() returns a new string; don't leak the old one */
1383127bbe13Stomee 	(*jenv)->DeleteLocalRef(jenv, jstr);
1384127bbe13Stomee 	jstr = tstr;
1385127bbe13Stomee 	tstr = NULL;
1386127bbe13Stomee 
1387127bbe13Stomee 	dtj_attach_name(jc, jobj, jstr);
1388127bbe13Stomee 	(*jenv)->DeleteLocalRef(jenv, jstr);
1389127bbe13Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
1390127bbe13Stomee 		WRAP_EXCEPTION(jenv);
1391fb3fb4f3Stomee 		return (NULL);
1392fb3fb4f3Stomee 	}
1393fb3fb4f3Stomee 
1394fb3fb4f3Stomee 	return (jobj);
1395fb3fb4f3Stomee }
1396fb3fb4f3Stomee 
1397fb3fb4f3Stomee /* Caller must be holding per-consumer lock */
1398fb3fb4f3Stomee static void
dtj_aggwalk_init(dtj_java_consumer_t * jc)1399fb3fb4f3Stomee dtj_aggwalk_init(dtj_java_consumer_t *jc)
1400fb3fb4f3Stomee {
1401fb3fb4f3Stomee 	jc->dtjj_consumer->dtjc_aggid = -1;
1402fb3fb4f3Stomee 	jc->dtjj_consumer->dtjc_expected = -1;
1403fb3fb4f3Stomee 	if (jc->dtjj_tuple != NULL) {
1404fb3fb4f3Stomee 		/* assert without crashing */
1405fb3fb4f3Stomee 		dtj_throw_illegal_state(jc->dtjj_jenv,
1406fb3fb4f3Stomee 		    "stale aggregation tuple");
1407fb3fb4f3Stomee 	}
1408fb3fb4f3Stomee }
1409fb3fb4f3Stomee 
1410fb3fb4f3Stomee static jobject
dtj_new_stack_record(const caddr_t addr,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)1411127bbe13Stomee dtj_new_stack_record(const caddr_t addr, const dtrace_recdesc_t *rec,
1412fb3fb4f3Stomee     dtj_java_consumer_t *jc)
1413fb3fb4f3Stomee {
1414fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
1415fb3fb4f3Stomee 
1416fb3fb4f3Stomee 	dtrace_actkind_t act;
1417fb3fb4f3Stomee 	uint64_t *pc;
1418fb3fb4f3Stomee 	pid_t pid = -1;
1419fb3fb4f3Stomee 	int size; /* size of raw bytes not including trailing zeros */
1420fb3fb4f3Stomee 	int i; /* index of last non-zero byte */
1421fb3fb4f3Stomee 
1422fb3fb4f3Stomee 	jbyteArray raw = NULL;
1423fb3fb4f3Stomee 	jobject stack = NULL; /* return value */
1424fb3fb4f3Stomee 
1425fb3fb4f3Stomee 	/* trim trailing zeros */
1426fb3fb4f3Stomee 	for (i = rec->dtrd_size - 1; (i >= 0) && !addr[i]; --i) {
1427fb3fb4f3Stomee 	}
1428fb3fb4f3Stomee 	size = (i + 1);
1429fb3fb4f3Stomee 	raw = (*jenv)->NewByteArray(jenv, size);
1430fb3fb4f3Stomee 	if (!raw) {
1431fb3fb4f3Stomee 		return (NULL); /* OutOfMemoryError pending */
1432fb3fb4f3Stomee 	}
1433fb3fb4f3Stomee 	(*jenv)->SetByteArrayRegion(jenv, raw, 0, size,
1434fb3fb4f3Stomee 	    (const jbyte *)addr);
1435fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
1436fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
1437fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, raw);
1438fb3fb4f3Stomee 		return (NULL);
1439fb3fb4f3Stomee 	}
1440fb3fb4f3Stomee 
1441fb3fb4f3Stomee 	/* Create StackValueRecord instance from raw stack data */
1442fb3fb4f3Stomee 	act = rec->dtrd_action;
1443fb3fb4f3Stomee 	switch (act) {
1444fb3fb4f3Stomee 	case DTRACEACT_STACK:
1445fb3fb4f3Stomee 		stack = (*jenv)->NewObject(jenv, g_stack_jc,
1446fb3fb4f3Stomee 		    g_stackinit_jm, raw);
1447fb3fb4f3Stomee 		break;
1448fb3fb4f3Stomee 	case DTRACEACT_USTACK:
1449fb3fb4f3Stomee 	case DTRACEACT_JSTACK:
1450fb3fb4f3Stomee 		/* Get pid of user process */
1451fb3fb4f3Stomee 		pc = (uint64_t *)(uintptr_t)addr;
1452fb3fb4f3Stomee 		pid = (pid_t)*pc;
1453fb3fb4f3Stomee 		stack = (*jenv)->NewObject(jenv, g_ustack_jc,
1454fb3fb4f3Stomee 		    g_ustackinit_jm, pid, raw);
1455fb3fb4f3Stomee 		break;
1456fb3fb4f3Stomee 	default:
1457fb3fb4f3Stomee 		dtj_throw_illegal_argument(jenv,
1458fb3fb4f3Stomee 		    "Expected stack action, got %d\n", act);
1459fb3fb4f3Stomee 	}
1460fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, raw);
1461fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
1462fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
1463fb3fb4f3Stomee 		return (NULL);
1464fb3fb4f3Stomee 	}
1465fb3fb4f3Stomee 	return (stack);
1466fb3fb4f3Stomee }
1467fb3fb4f3Stomee 
1468127bbe13Stomee static jobject
dtj_new_symbol_record(const caddr_t addr,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)1469127bbe13Stomee dtj_new_symbol_record(const caddr_t addr, const dtrace_recdesc_t *rec,
1470127bbe13Stomee     dtj_java_consumer_t *jc)
1471127bbe13Stomee {
1472127bbe13Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
1473127bbe13Stomee 
1474127bbe13Stomee 	dtrace_actkind_t act;
1475127bbe13Stomee 	uint64_t *pc;
1476127bbe13Stomee 	pid_t pid = -1;
1477127bbe13Stomee 
1478127bbe13Stomee 	jobject symbol = NULL; /* return value */
1479127bbe13Stomee 
1480127bbe13Stomee 	act = rec->dtrd_action;
1481127bbe13Stomee 	switch (act) {
1482127bbe13Stomee 	case DTRACEACT_SYM:
1483127bbe13Stomee 	case DTRACEACT_MOD:
1484127bbe13Stomee 		/* LINTED - alignment */
1485127bbe13Stomee 		pc = (uint64_t *)addr;
1486127bbe13Stomee 		symbol = (*jenv)->NewObject(jenv, g_symbol_jc,
1487127bbe13Stomee 		    g_symbolinit_jm, *pc);
1488127bbe13Stomee 		break;
1489127bbe13Stomee 	case DTRACEACT_USYM:
1490127bbe13Stomee 	case DTRACEACT_UADDR:
1491127bbe13Stomee 	case DTRACEACT_UMOD:
1492127bbe13Stomee 		/* Get pid of user process */
1493127bbe13Stomee 		pc = (uint64_t *)(uintptr_t)addr;
1494127bbe13Stomee 		pid = (pid_t)*pc;
1495127bbe13Stomee 		++pc;
1496127bbe13Stomee 		symbol = (*jenv)->NewObject(jenv, g_usymbol_jc,
1497127bbe13Stomee 		    g_usymbolinit_jm, pid, *pc);
1498127bbe13Stomee 		break;
1499127bbe13Stomee 	default:
1500127bbe13Stomee 		dtj_throw_illegal_argument(jenv,
1501127bbe13Stomee 		    "Expected stack action, got %d\n", act);
1502127bbe13Stomee 	}
1503127bbe13Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
1504127bbe13Stomee 		WRAP_EXCEPTION(jenv);
1505127bbe13Stomee 		return (NULL);
1506127bbe13Stomee 	}
1507127bbe13Stomee 	return (symbol);
1508127bbe13Stomee }
1509127bbe13Stomee 
1510fb3fb4f3Stomee /*
1511fb3fb4f3Stomee  * Return NULL if java exception pending, otherwise return Distribution value.
1512fb3fb4f3Stomee  */
1513fb3fb4f3Stomee static jobject
dtj_new_distribution(const dtrace_aggdata_t * data,const dtrace_recdesc_t * rec,dtj_java_consumer_t * jc)1514fb3fb4f3Stomee dtj_new_distribution(const dtrace_aggdata_t *data, const dtrace_recdesc_t *rec,
1515fb3fb4f3Stomee     dtj_java_consumer_t *jc)
1516fb3fb4f3Stomee {
1517fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
1518fb3fb4f3Stomee 
1519fb3fb4f3Stomee 	jlongArray jbuckets = NULL;
1520fb3fb4f3Stomee 	jobject jdist = NULL; /* return value */
1521fb3fb4f3Stomee 
1522fb3fb4f3Stomee 	dtrace_actkind_t act = rec->dtrd_action;
1523fb3fb4f3Stomee 	/* LINTED - alignment */
1524fb3fb4f3Stomee 	int64_t *aggbuckets = (int64_t *)
1525fb3fb4f3Stomee 	    (data->dtada_data + rec->dtrd_offset);
1526fb3fb4f3Stomee 	size_t size = rec->dtrd_size;
1527fb3fb4f3Stomee 	int64_t value;
1528fb3fb4f3Stomee 	uint64_t normal = data->dtada_normal;
1529fb3fb4f3Stomee 	int64_t base, step;
1530fb3fb4f3Stomee 	int levels;
1531fb3fb4f3Stomee 	int n; /* number of buckets */
1532fb3fb4f3Stomee 
1533fb3fb4f3Stomee 	/* distribution */
1534ae94d716SRichard Lowe 	switch (act) {
1535ae94d716SRichard Lowe 	case DTRACEAGG_LQUANTIZE:
1536fb3fb4f3Stomee 		/* first "bucket" used for range and step */
1537fb3fb4f3Stomee 		value = *aggbuckets++;
1538fb3fb4f3Stomee 		base = DTRACE_LQUANTIZE_BASE(value);
1539fb3fb4f3Stomee 		step = DTRACE_LQUANTIZE_STEP(value);
1540fb3fb4f3Stomee 		levels = DTRACE_LQUANTIZE_LEVELS(value);
1541fb3fb4f3Stomee 		size -= sizeof (int64_t); /* exclude non-bucket */
1542fb3fb4f3Stomee 		/*
1543fb3fb4f3Stomee 		 * Add one for the base bucket and one for the bucket of values
1544fb3fb4f3Stomee 		 * less than the base.
1545fb3fb4f3Stomee 		 */
1546fb3fb4f3Stomee 		n = levels + 2;
1547ae94d716SRichard Lowe 		break;
1548ae94d716SRichard Lowe 	case DTRACEAGG_LLQUANTIZE:
1549ae94d716SRichard Lowe 		value = *aggbuckets++;
1550ae94d716SRichard Lowe 		size -= sizeof (int64_t);
1551ae94d716SRichard Lowe 		levels = size / sizeof (int64_t);
1552ae94d716SRichard Lowe 		n = levels;
1553ae94d716SRichard Lowe 		break;
1554ae94d716SRichard Lowe 	case DTRACEAGG_QUANTIZE:
1555fb3fb4f3Stomee 		n = DTRACE_QUANTIZE_NBUCKETS;
1556fb3fb4f3Stomee 		levels = n - 1; /* levels excludes base */
1557ae94d716SRichard Lowe 		break;
1558fb3fb4f3Stomee 	}
1559ae94d716SRichard Lowe 
1560fb3fb4f3Stomee 	if (size != (n * sizeof (uint64_t)) || n < 1) {
1561fb3fb4f3Stomee 		dtj_throw_illegal_state(jenv,
1562fb3fb4f3Stomee 		    "size mismatch: record %d, buckets %d", size,
1563fb3fb4f3Stomee 		    (n * sizeof (uint64_t)));
1564fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
1565fb3fb4f3Stomee 		return (NULL);
1566fb3fb4f3Stomee 	}
1567fb3fb4f3Stomee 
1568fb3fb4f3Stomee 	jbuckets = (*jenv)->NewLongArray(jenv, n);
1569fb3fb4f3Stomee 	if (!jbuckets) {
1570fb3fb4f3Stomee 		return (NULL); /* exception pending */
1571fb3fb4f3Stomee 	}
1572ae94d716SRichard Lowe 
1573fb3fb4f3Stomee 	if (n > 0) {
1574fb3fb4f3Stomee 		(*jenv)->SetLongArrayRegion(jenv, jbuckets, 0, n, aggbuckets);
1575fb3fb4f3Stomee 		/* check for ArrayIndexOutOfBounds */
1576fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1577fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1578fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jbuckets);
1579fb3fb4f3Stomee 			return (NULL);
1580fb3fb4f3Stomee 		}
1581fb3fb4f3Stomee 	}
1582fb3fb4f3Stomee 
1583ae94d716SRichard Lowe 	switch (act) {
1584ae94d716SRichard Lowe 	case DTRACEAGG_LQUANTIZE:
1585fb3fb4f3Stomee 		/* Must pass 64-bit base and step or constructor gets junk. */
1586fb3fb4f3Stomee 		jdist = (*jenv)->NewObject(jenv, g_ldist_jc, g_ldistinit_jm,
1587fb3fb4f3Stomee 		    base, step, jbuckets);
1588ae94d716SRichard Lowe 		break;
1589ae94d716SRichard Lowe 	case DTRACEAGG_QUANTIZE:
1590fb3fb4f3Stomee 		jdist = (*jenv)->NewObject(jenv, g_dist_jc, g_distinit_jm,
1591fb3fb4f3Stomee 		    jbuckets);
1592ae94d716SRichard Lowe 		break;
1593ae94d716SRichard Lowe 	case DTRACEAGG_LLQUANTIZE:
1594ae94d716SRichard Lowe 		jdist = (*jenv)->NewObject(jenv, g_lldist_jc, g_lldistinit_jm,
1595ae94d716SRichard Lowe 		    value, jbuckets);
1596ae94d716SRichard Lowe 		break;
1597fb3fb4f3Stomee 	}
1598fb3fb4f3Stomee 
1599fb3fb4f3Stomee 	(*jenv)->DeleteLocalRef(jenv, jbuckets);
1600fb3fb4f3Stomee 	if (!jdist) {
1601fb3fb4f3Stomee 		return (NULL); /* exception pending */
1602fb3fb4f3Stomee 	}
1603fb3fb4f3Stomee 
1604fb3fb4f3Stomee 	if (normal != 1) {
1605fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jdist, g_dist_normal_jm, normal);
1606fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1607fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1608fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jdist);
1609fb3fb4f3Stomee 			return (NULL);
1610fb3fb4f3Stomee 		}
1611fb3fb4f3Stomee 	}
1612fb3fb4f3Stomee 	return (jdist);
1613fb3fb4f3Stomee }
1614fb3fb4f3Stomee 
1615fb3fb4f3Stomee static void
dtj_attach_frames(dtj_java_consumer_t * jc,jobject stack,jobjectArray frames)1616fb3fb4f3Stomee dtj_attach_frames(dtj_java_consumer_t *jc, jobject stack,
1617fb3fb4f3Stomee     jobjectArray frames)
1618fb3fb4f3Stomee {
1619fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
1620fb3fb4f3Stomee 
1621fb3fb4f3Stomee 	if ((*jenv)->IsInstanceOf(jenv, stack, g_stack_jc)) {
1622fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, stack, g_stackset_frames_jm,
1623fb3fb4f3Stomee 		    frames);
1624fb3fb4f3Stomee 	} else if ((*jenv)->IsInstanceOf(jenv, stack, g_ustack_jc)) {
1625fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, stack, g_ustackset_frames_jm,
1626fb3fb4f3Stomee 		    frames);
1627fb3fb4f3Stomee 	}
1628fb3fb4f3Stomee }
1629fb3fb4f3Stomee 
1630127bbe13Stomee static void
dtj_attach_name(dtj_java_consumer_t * jc,jobject symbol,jstring s)1631127bbe13Stomee dtj_attach_name(dtj_java_consumer_t *jc, jobject symbol, jstring s)
1632127bbe13Stomee {
1633127bbe13Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
1634127bbe13Stomee 
1635127bbe13Stomee 	if ((*jenv)->IsInstanceOf(jenv, symbol, g_symbol_jc)) {
1636127bbe13Stomee 		(*jenv)->CallVoidMethod(jenv, symbol, g_symbolset_name_jm, s);
1637127bbe13Stomee 	} else if ((*jenv)->IsInstanceOf(jenv, symbol, g_usymbol_jc)) {
1638127bbe13Stomee 		(*jenv)->CallVoidMethod(jenv, symbol, g_usymbolset_name_jm, s);
1639127bbe13Stomee 	}
1640127bbe13Stomee }
1641127bbe13Stomee 
1642fb3fb4f3Stomee /*
1643fb3fb4f3Stomee  * Note: It is not valid to look outside the current libdtrace record in the
1644fb3fb4f3Stomee  * given aggdata (except to get the aggregation ID from the first record).
1645fb3fb4f3Stomee  *
1646fb3fb4f3Stomee  * Return DTRACE_HANDLE_ABORT if java exception pending, otherwise
1647fb3fb4f3Stomee  * DTRACE_HANDLE_OK.
1648fb3fb4f3Stomee  */
1649fb3fb4f3Stomee static int
dtj_agghandler(const dtrace_bufdata_t * bufdata,dtj_java_consumer_t * jc)1650fb3fb4f3Stomee dtj_agghandler(const dtrace_bufdata_t *bufdata, dtj_java_consumer_t *jc)
1651fb3fb4f3Stomee {
1652fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
1653fb3fb4f3Stomee 
1654fb3fb4f3Stomee 	const dtrace_aggdata_t *aggdata = bufdata->dtbda_aggdata;
1655fb3fb4f3Stomee 	const dtrace_aggdesc_t *aggdesc;
1656fb3fb4f3Stomee 	const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc;
1657fb3fb4f3Stomee 	const char *s = bufdata->dtbda_buffered;
1658fb3fb4f3Stomee 	dtrace_actkind_t act = DTRACEACT_NONE;
1659fb3fb4f3Stomee 	int64_t aggid;
1660fb3fb4f3Stomee 
1661fb3fb4f3Stomee 	jobject jobj = NULL;
1662fb3fb4f3Stomee 
1663fb3fb4f3Stomee 	if (aggdata == NULL) {
1664fb3fb4f3Stomee 		/* Assert without crashing */
1665fb3fb4f3Stomee 		dtj_throw_illegal_state(jenv, "null aggdata");
1666fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
1667fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
1668fb3fb4f3Stomee 	}
1669fb3fb4f3Stomee 	aggdesc = aggdata->dtada_desc;
1670fb3fb4f3Stomee 
1671fb3fb4f3Stomee 	/*
1672fb3fb4f3Stomee 	 * Get the aggregation ID from the first record.
1673fb3fb4f3Stomee 	 */
1674fb3fb4f3Stomee 	/* LINTED - alignment */
1675fb3fb4f3Stomee 	aggid = *((int64_t *)(aggdata->dtada_data +
1676fb3fb4f3Stomee 	    aggdesc->dtagd_rec[0].dtrd_offset));
1677fb3fb4f3Stomee 	if (aggid < 0) {
1678fb3fb4f3Stomee 		/* Assert without crashing */
1679fb3fb4f3Stomee 		dtj_throw_illegal_argument(jenv, "negative aggregation ID");
1680fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
1681fb3fb4f3Stomee 		return (DTRACE_HANDLE_ABORT);
1682fb3fb4f3Stomee 	}
1683fb3fb4f3Stomee 
1684fb3fb4f3Stomee 	if (jc->dtjj_consumer->dtjc_printa_snaptime) {
1685fb3fb4f3Stomee 		/* Append buffered output if this is a printa() callback. */
1686fb3fb4f3Stomee 		jstring jstr = dtj_NewStringNative(jenv, s);
1687fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1688fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1689fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1690fb3fb4f3Stomee 		}
1691fb3fb4f3Stomee 		/*
16924ae67516Stomee 		 * StringBuilder append() returns a reference to the
16934ae67516Stomee 		 * StringBuilder; must not leak the returned reference.
1694fb3fb4f3Stomee 		 */
1695fb3fb4f3Stomee 		jobj = (*jenv)->CallObjectMethod(jenv,
1696fb3fb4f3Stomee 		    jc->dtjj_printa_buffer, g_buf_append_str_jm, jstr);
1697fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jstr);
1698fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jobj);
1699fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1700fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1701fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1702fb3fb4f3Stomee 		}
1703fb3fb4f3Stomee 	} else {
1704fb3fb4f3Stomee 		/*
1705fb3fb4f3Stomee 		 * Test whether to include the aggregation if this is a
1706e77b06d2Stomee 		 * getAggregate() call.  Optimization: perform the inclusion
1707fb3fb4f3Stomee 		 * test only when the aggregation has changed.
1708fb3fb4f3Stomee 		 */
1709fb3fb4f3Stomee 		if (aggid != jc->dtjj_consumer->dtjc_aggid) {
1710fb3fb4f3Stomee 			jc->dtjj_consumer->dtjc_included =
1711fb3fb4f3Stomee 			    dtj_is_included(aggdata, jc);
1712fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
1713fb3fb4f3Stomee 				WRAP_EXCEPTION(jenv);
1714fb3fb4f3Stomee 				return (DTRACE_HANDLE_ABORT);
1715fb3fb4f3Stomee 			}
1716fb3fb4f3Stomee 		}
1717fb3fb4f3Stomee 		if (!jc->dtjj_consumer->dtjc_included) {
1718fb3fb4f3Stomee 			return (DTRACE_HANDLE_OK);
1719fb3fb4f3Stomee 		}
1720fb3fb4f3Stomee 	}
1721fb3fb4f3Stomee 	jc->dtjj_consumer->dtjc_aggid = aggid;
1722fb3fb4f3Stomee 
1723fb3fb4f3Stomee 	/*
1724fb3fb4f3Stomee 	 * Determine the expected number of tuple members.  While it is not
1725fb3fb4f3Stomee 	 * technically valid to look outside the current record in the current
1726fb3fb4f3Stomee 	 * aggdata, this implementation does so without a known failure case.
1727fb3fb4f3Stomee 	 * Any method relying only on the current callback record makes riskier
1728fb3fb4f3Stomee 	 * assumptions and still does not cover every corner case (for example,
1729fb3fb4f3Stomee 	 * counting the records from index 1 up to and not including the index
1730fb3fb4f3Stomee 	 * of the current DTRACE_BUFDATA_AGGVAL record, which fails when a
1731fb3fb4f3Stomee 	 * format string specifies the value ahead of one or more tuple
1732fb3fb4f3Stomee 	 * elements).  Knowing that the calculation of the expected tuple size
1733fb3fb4f3Stomee 	 * is technically invalid (because it looks outside the current record),
1734fb3fb4f3Stomee 	 * we make the calculation at the earliest opportunity, before anything
1735fb3fb4f3Stomee 	 * might happen to invalidate any part of the aggdata.  It ought to be
1736fb3fb4f3Stomee 	 * safe in any case: dtrd_action and dtrd_size do not appear ever to be
1737fb3fb4f3Stomee 	 * overwritten, and dtrd_offset is not used outside the current record.
1738fb3fb4f3Stomee 	 *
1739fb3fb4f3Stomee 	 * It is possible (if the assumptions here ever prove untrue) that the
1740fb3fb4f3Stomee 	 * libdtrace buffered output handler may need to be enhanced to provide
1741fb3fb4f3Stomee 	 * the expected number of tuple members.
1742fb3fb4f3Stomee 	 */
1743fb3fb4f3Stomee 	if (jc->dtjj_consumer->dtjc_expected < 0) {
1744fb3fb4f3Stomee 		int r;
1745fb3fb4f3Stomee 		for (r = 1; r < aggdesc->dtagd_nrecs; ++r) {
1746fb3fb4f3Stomee 			act = aggdesc->dtagd_rec[r].dtrd_action;
1747fb3fb4f3Stomee 			if (DTRACEACT_ISAGG(act) ||
1748fb3fb4f3Stomee 			    aggdesc->dtagd_rec[r].dtrd_size == 0) {
1749fb3fb4f3Stomee 				break;
1750fb3fb4f3Stomee 			}
1751fb3fb4f3Stomee 		}
1752fb3fb4f3Stomee 		jc->dtjj_consumer->dtjc_expected = r - 1;
1753fb3fb4f3Stomee 	}
1754fb3fb4f3Stomee 
1755fb3fb4f3Stomee 	if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGKEY) {
1756fb3fb4f3Stomee 		/* record value is a tuple member */
1757fb3fb4f3Stomee 
1758fb3fb4f3Stomee 		if (jc->dtjj_tuple == NULL) {
1759fb3fb4f3Stomee 			jc->dtjj_tuple = (*jenv)->NewObject(jenv,
1760fb3fb4f3Stomee 			    g_tuple_jc, g_tupleinit_jm);
1761fb3fb4f3Stomee 			if (!jc->dtjj_tuple) {
1762fb3fb4f3Stomee 				/* java exception pending */
1763fb3fb4f3Stomee 				return (DTRACE_HANDLE_ABORT);
1764fb3fb4f3Stomee 			}
1765fb3fb4f3Stomee 		}
1766fb3fb4f3Stomee 
1767fb3fb4f3Stomee 		act = rec->dtrd_action;
1768fb3fb4f3Stomee 
1769fb3fb4f3Stomee 		switch (act) {
1770fb3fb4f3Stomee 		case DTRACEACT_STACK:
1771fb3fb4f3Stomee 		case DTRACEACT_USTACK:
1772fb3fb4f3Stomee 		case DTRACEACT_JSTACK:
1773fb3fb4f3Stomee 			jobj = dtj_new_tuple_stack_record(aggdata, rec, s, jc);
1774fb3fb4f3Stomee 			break;
1775127bbe13Stomee 		case DTRACEACT_USYM:
1776127bbe13Stomee 		case DTRACEACT_UADDR:
1777127bbe13Stomee 		case DTRACEACT_UMOD:
1778127bbe13Stomee 		case DTRACEACT_SYM:
1779127bbe13Stomee 		case DTRACEACT_MOD:
1780127bbe13Stomee 			jobj = dtj_new_tuple_symbol_record(aggdata, rec, s, jc);
1781127bbe13Stomee 			break;
1782fb3fb4f3Stomee 		default:
1783fb3fb4f3Stomee 			jobj = dtj_recdata(jc, rec->dtrd_size,
1784fb3fb4f3Stomee 			    (aggdata->dtada_data + rec->dtrd_offset));
1785fb3fb4f3Stomee 		}
1786fb3fb4f3Stomee 
1787fb3fb4f3Stomee 		if (!jobj) {
1788fb3fb4f3Stomee 			/* java exception pending */
1789fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1790fb3fb4f3Stomee 		}
1791fb3fb4f3Stomee 
1792fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_tuple,
1793fb3fb4f3Stomee 		    g_tupleadd_jm, jobj);
1794fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jobj);
1795fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1796fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1797fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1798fb3fb4f3Stomee 		}
1799fb3fb4f3Stomee 	} else if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGVAL) {
1800fb3fb4f3Stomee 		/*
1801fb3fb4f3Stomee 		 * Record value is that of an aggregating action.  The printa()
1802fb3fb4f3Stomee 		 * format string may place the tuple ahead of the aggregation
1803fb3fb4f3Stomee 		 * value(s), so we can't be sure we have the tuple until we get
1804fb3fb4f3Stomee 		 * the AGGLAST flag indicating the last callback associated with
1805fb3fb4f3Stomee 		 * the current tuple.  Save the aggregation value or values
1806fb3fb4f3Stomee 		 * (multiple values if more than one aggregation is passed to
1807fb3fb4f3Stomee 		 * printa()) until then.
1808fb3fb4f3Stomee 		 */
1809fb3fb4f3Stomee 		dtj_aggval_t *aggval;
1810fb3fb4f3Stomee 
1811fb3fb4f3Stomee 		jstring jvalue = NULL;
1812fb3fb4f3Stomee 
1813fb3fb4f3Stomee 		jvalue = dtj_new_aggval(jc, aggdata, rec);
1814fb3fb4f3Stomee 		if (!jvalue) {
1815fb3fb4f3Stomee 			/* java exception pending */
1816fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1817fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1818fb3fb4f3Stomee 		}
1819fb3fb4f3Stomee 		aggval = dtj_aggval_create(jenv, jvalue, aggdesc->dtagd_name,
1820fb3fb4f3Stomee 		    aggid);
1821fb3fb4f3Stomee 		if (!aggval) {
1822fb3fb4f3Stomee 			/* OutOfMemoryError pending */
1823fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jvalue);
1824fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1825fb3fb4f3Stomee 		}
1826fb3fb4f3Stomee 		if (!dtj_list_add(jc->dtjj_aggval_list, aggval)) {
1827fb3fb4f3Stomee 			/* deletes jvalue reference */
1828fb3fb4f3Stomee 			dtj_aggval_destroy(aggval, jenv);
1829fb3fb4f3Stomee 			dtj_throw_out_of_memory(jenv, "Failed to add aggval");
1830fb3fb4f3Stomee 			return (DTRACE_HANDLE_ABORT);
1831fb3fb4f3Stomee 		}
1832fb3fb4f3Stomee 	}
1833fb3fb4f3Stomee 
1834fb3fb4f3Stomee 	if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGLAST) {
1835fb3fb4f3Stomee 		/* No more values associated with the current tuple. */
1836fb3fb4f3Stomee 
1837fb3fb4f3Stomee 		dtj_aggval_t *aggval;
1838fb3fb4f3Stomee 		uu_list_walk_t *itr;
1839fb3fb4f3Stomee 		int tuple_member_count;
1840fb3fb4f3Stomee 
1841fb3fb4f3Stomee 		jobject jrec = NULL;
1842fb3fb4f3Stomee 		jstring jname = NULL;
1843fb3fb4f3Stomee 
1844fb3fb4f3Stomee 		if (jc->dtjj_consumer->dtjc_expected == 0) {
1845fb3fb4f3Stomee 			/*
1846fb3fb4f3Stomee 			 * singleton aggregation declared in D with no square
1847fb3fb4f3Stomee 			 * brackets
1848fb3fb4f3Stomee 			 */
1849fb3fb4f3Stomee 			jc->dtjj_tuple = (*jenv)->GetStaticObjectField(jenv,
1850fb3fb4f3Stomee 			    g_tuple_jc, g_tuple_EMPTY_jsf);
1851fb3fb4f3Stomee 			if (jc->dtjj_tuple == NULL) {
1852fb3fb4f3Stomee 				dtj_throw_out_of_memory(jenv,
1853fb3fb4f3Stomee 				    "Failed to reference Tuple.EMPTY");
1854fb3fb4f3Stomee 				return (DTRACE_HANDLE_ABORT);
1855fb3fb4f3Stomee 			}
1856fb3fb4f3Stomee 		}
1857fb3fb4f3Stomee 
1858fb3fb4f3Stomee 		if (jc->dtjj_tuple == NULL) {
1859fb3fb4f3Stomee 			(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
1860fb3fb4f3Stomee 			    g_pdatainvalidate_printa_jm);
186181621461Stomee 			goto printa_output;
1862fb3fb4f3Stomee 		}
1863fb3fb4f3Stomee 
1864fb3fb4f3Stomee 		tuple_member_count = (*jenv)->CallIntMethod(jenv,
1865fb3fb4f3Stomee 		    jc->dtjj_tuple, g_tuplesize_jm);
1866fb3fb4f3Stomee 		if (tuple_member_count <
1867fb3fb4f3Stomee 		    jc->dtjj_consumer->dtjc_expected) {
1868fb3fb4f3Stomee 			(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
1869fb3fb4f3Stomee 			    g_pdatainvalidate_printa_jm);
1870fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple);
1871fb3fb4f3Stomee 			jc->dtjj_tuple = NULL;
1872fb3fb4f3Stomee 			goto printa_output;
1873fb3fb4f3Stomee 		}
1874fb3fb4f3Stomee 
1875fb3fb4f3Stomee 		itr = uu_list_walk_start(jc->dtjj_aggval_list, 0);
1876fb3fb4f3Stomee 		while ((aggval = uu_list_walk_next(itr)) != NULL) {
1877fb3fb4f3Stomee 			/*
1878fb3fb4f3Stomee 			 * new AggregationRecord:  Combine the aggregation value
1879fb3fb4f3Stomee 			 * with the saved tuple and add it to the current
1880fb3fb4f3Stomee 			 * Aggregate or PrintaRecord.
1881fb3fb4f3Stomee 			 */
1882fb3fb4f3Stomee 			jrec = (*jenv)->NewObject(jenv, g_aggrec_jc,
1883fb3fb4f3Stomee 			    g_aggrecinit_jm, jc->dtjj_tuple,
1884fb3fb4f3Stomee 			    aggval->dtja_value);
1885fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, aggval->dtja_value);
1886fb3fb4f3Stomee 			aggval->dtja_value = NULL;
1887fb3fb4f3Stomee 			if (!jrec) {
1888fb3fb4f3Stomee 				/* java exception pending */
1889fb3fb4f3Stomee 				WRAP_EXCEPTION(jenv);
1890fb3fb4f3Stomee 				return (DTRACE_HANDLE_ABORT);
1891fb3fb4f3Stomee 			}
1892fb3fb4f3Stomee 
1893fb3fb4f3Stomee 			/* aggregation name */
1894fb3fb4f3Stomee 			jname = (*jenv)->NewStringUTF(jenv,
1895fb3fb4f3Stomee 			    aggval->dtja_aggname);
1896fb3fb4f3Stomee 			if (!jname) {
1897fb3fb4f3Stomee 				/* OutOfMemoryError pending */
1898fb3fb4f3Stomee 				(*jenv)->DeleteLocalRef(jenv, jrec);
1899fb3fb4f3Stomee 				return (DTRACE_HANDLE_ABORT);
1900fb3fb4f3Stomee 			}
1901fb3fb4f3Stomee 
1902fb3fb4f3Stomee 			/*
1903fb3fb4f3Stomee 			 * If the printa() format string specifies the value of
1904fb3fb4f3Stomee 			 * the aggregating action multiple times, PrintaRecord
1905fb3fb4f3Stomee 			 * ignores the attempt to add the duplicate record.
1906fb3fb4f3Stomee 			 */
1907fb3fb4f3Stomee 			if (jc->dtjj_consumer->dtjc_printa_snaptime) {
1908fb3fb4f3Stomee 				/* add to PrintaRecord */
1909fb3fb4f3Stomee 				(*jenv)->CallVoidMethod(jenv,
1910fb3fb4f3Stomee 				    jc->dtjj_probedata,
1911fb3fb4f3Stomee 				    g_pdataadd_aggrec_jm,
1912fb3fb4f3Stomee 				    jname, aggval->dtja_aggid, jrec);
1913fb3fb4f3Stomee 			} else {
1914fb3fb4f3Stomee 				/* add to Aggregate */
1915fb3fb4f3Stomee 				(*jenv)->CallVoidMethod(jenv,
1916fb3fb4f3Stomee 				    jc->dtjj_aggregate, g_aggaddrec_jm,
1917fb3fb4f3Stomee 				    jname, aggval->dtja_aggid, jrec);
1918fb3fb4f3Stomee 			}
1919fb3fb4f3Stomee 
1920fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jrec);
1921fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jname);
1922fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
1923fb3fb4f3Stomee 				WRAP_EXCEPTION(jenv);
1924fb3fb4f3Stomee 				return (DTRACE_HANDLE_ABORT);
1925fb3fb4f3Stomee 			}
1926fb3fb4f3Stomee 		}
1927fb3fb4f3Stomee 		uu_list_walk_end(itr);
1928fb3fb4f3Stomee 		dtj_list_clear(jc->dtjj_aggval_list, dtj_aggval_destroy,
1929fb3fb4f3Stomee 		    jenv);
1930fb3fb4f3Stomee 
1931fb3fb4f3Stomee printa_output:
1932fb3fb4f3Stomee 		if (jc->dtjj_consumer->dtjc_printa_snaptime) {
1933fb3fb4f3Stomee 			/*
1934fb3fb4f3Stomee 			 * Get the formatted string associated with the current
1935fb3fb4f3Stomee 			 * tuple if this is a printa() callback.
1936fb3fb4f3Stomee 			 */
1937fb3fb4f3Stomee 			jstring jstr = (*jenv)->CallObjectMethod(jenv,
1938fb3fb4f3Stomee 			    jc->dtjj_printa_buffer, g_tostring_jm);
1939fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
1940fb3fb4f3Stomee 				WRAP_EXCEPTION(jenv);
1941fb3fb4f3Stomee 				return (DTRACE_HANDLE_ABORT);
1942fb3fb4f3Stomee 			}
1943fb3fb4f3Stomee 			/*
19444ae67516Stomee 			 * Clear the StringBuilder: this does not throw
19454ae67516Stomee 			 * exceptions.  Reuse the StringBuilder until the end of
1946fb3fb4f3Stomee 			 * the current probedata then dispose of it.
1947fb3fb4f3Stomee 			 */
1948fb3fb4f3Stomee 			(*jenv)->CallVoidMethod(jenv, jc->dtjj_printa_buffer,
1949fb3fb4f3Stomee 			    g_bufsetlen_jm, 0);
1950fb3fb4f3Stomee 			/* Add formatted string to PrintaRecord */
1951fb3fb4f3Stomee 			(*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata,
1952fb3fb4f3Stomee 			    g_pdataadd_printa_str_jm, jc->dtjj_tuple, jstr);
1953fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jstr);
1954fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
1955fb3fb4f3Stomee 				WRAP_EXCEPTION(jenv);
1956fb3fb4f3Stomee 				return (DTRACE_HANDLE_ABORT);
1957fb3fb4f3Stomee 			}
1958fb3fb4f3Stomee 		}
1959fb3fb4f3Stomee 
1960fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple);
1961fb3fb4f3Stomee 		jc->dtjj_tuple = NULL;
1962fb3fb4f3Stomee 		jc->dtjj_consumer->dtjc_expected = -1;
1963fb3fb4f3Stomee 	}
1964fb3fb4f3Stomee 
1965fb3fb4f3Stomee 	return (DTRACE_HANDLE_OK);
1966fb3fb4f3Stomee }
1967fb3fb4f3Stomee 
1968fb3fb4f3Stomee /*
1969fb3fb4f3Stomee  * Return B_TRUE if the aggregation is included, B_FALSE otherwise.  Only in the
1970fb3fb4f3Stomee  * latter case might there be an exception pending.
1971fb3fb4f3Stomee  */
1972fb3fb4f3Stomee static boolean_t
dtj_is_included(const dtrace_aggdata_t * data,dtj_java_consumer_t * jc)1973fb3fb4f3Stomee dtj_is_included(const dtrace_aggdata_t *data, dtj_java_consumer_t *jc)
1974fb3fb4f3Stomee {
1975fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
1976fb3fb4f3Stomee 
1977fb3fb4f3Stomee 	if (jc->dtjj_aggregate_spec) {
1978fb3fb4f3Stomee 		jboolean included;
1979fb3fb4f3Stomee 		jstring aggname = NULL;
1980fb3fb4f3Stomee 
1981fb3fb4f3Stomee 		const dtrace_aggdesc_t *aggdesc = data->dtada_desc;
1982fb3fb4f3Stomee 		aggname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name);
1983fb3fb4f3Stomee 		if (!aggname) {
1984fb3fb4f3Stomee 			/* java exception pending */
1985fb3fb4f3Stomee 			return (B_FALSE);
1986fb3fb4f3Stomee 		}
1987fb3fb4f3Stomee 
1988fb3fb4f3Stomee 		included = (*jenv)->CallBooleanMethod(jenv,
1989fb3fb4f3Stomee 		    jc->dtjj_aggregate_spec, g_aggspec_included_jm,
1990fb3fb4f3Stomee 		    aggname);
1991fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, aggname);
1992fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
1993fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
1994fb3fb4f3Stomee 			return (B_FALSE);
1995fb3fb4f3Stomee 		}
1996fb3fb4f3Stomee 
1997fb3fb4f3Stomee 		return (included);
1998fb3fb4f3Stomee 	}
1999fb3fb4f3Stomee 
2000fb3fb4f3Stomee 	return (B_TRUE);
2001fb3fb4f3Stomee }
2002fb3fb4f3Stomee 
2003fb3fb4f3Stomee /*
2004fb3fb4f3Stomee  * Return NULL if a java exception is pending, otherwise return a new
2005fb3fb4f3Stomee  * AggregationValue instance.
2006fb3fb4f3Stomee  */
2007fb3fb4f3Stomee static jobject
dtj_new_aggval(dtj_java_consumer_t * jc,const dtrace_aggdata_t * data,const dtrace_recdesc_t * rec)2008fb3fb4f3Stomee dtj_new_aggval(dtj_java_consumer_t *jc, const dtrace_aggdata_t *data,
2009fb3fb4f3Stomee     const dtrace_recdesc_t *rec)
2010fb3fb4f3Stomee {
2011fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
2012fb3fb4f3Stomee 
2013fb3fb4f3Stomee 	jobject jvalue = NULL; /* return value */
2014fb3fb4f3Stomee 
2015fb3fb4f3Stomee 	dtrace_actkind_t act;
2016fb3fb4f3Stomee 	uint64_t normal;
2017fb3fb4f3Stomee 	caddr_t addr;
2018fb3fb4f3Stomee 	int64_t value;
2019fb3fb4f3Stomee 
2020fb3fb4f3Stomee 	act = rec->dtrd_action;
2021fb3fb4f3Stomee 	normal = data->dtada_normal;
2022fb3fb4f3Stomee 	addr = data->dtada_data + rec->dtrd_offset;
2023fb3fb4f3Stomee 	if (act == DTRACEAGG_AVG) {
2024fb3fb4f3Stomee 		value = dtj_average(addr, normal);
2025fb3fb4f3Stomee 	} else {
2026fb3fb4f3Stomee 		/* LINTED - alignment */
2027fb3fb4f3Stomee 		value = (*((int64_t *)addr)) / normal;
2028fb3fb4f3Stomee 	}
2029fb3fb4f3Stomee 
2030ae94d716SRichard Lowe 	if ((act == DTRACEAGG_QUANTIZE) || (act == DTRACEAGG_LQUANTIZE) ||
2031ae94d716SRichard Lowe 	    (act == DTRACEAGG_LLQUANTIZE)) {
2032fb3fb4f3Stomee 		jvalue = dtj_new_distribution(data, rec, jc);
2033fb3fb4f3Stomee 	} else {
2034fb3fb4f3Stomee 		switch (act) {
2035fb3fb4f3Stomee 		case DTRACEAGG_COUNT:
2036fb3fb4f3Stomee 			jvalue = (*jenv)->NewObject(jenv, g_aggcount_jc,
2037fb3fb4f3Stomee 			    g_aggcountinit_jm, value);
2038fb3fb4f3Stomee 			break;
2039fb3fb4f3Stomee 		case DTRACEAGG_SUM:
2040fb3fb4f3Stomee 			jvalue = (*jenv)->NewObject(jenv, g_aggsum_jc,
2041fb3fb4f3Stomee 			    g_aggsuminit_jm, value);
2042fb3fb4f3Stomee 			break;
2043fb3fb4f3Stomee 		case DTRACEAGG_AVG:
2044fb3fb4f3Stomee 			jvalue = (*jenv)->NewObject(jenv, g_aggavg_jc,
2045fb3fb4f3Stomee 			    g_aggavginit_jm, value, dtj_avg_total(addr,
2046fb3fb4f3Stomee 			    normal), dtj_avg_count(addr));
2047fb3fb4f3Stomee 			break;
2048fb3fb4f3Stomee 		case DTRACEAGG_MIN:
2049fb3fb4f3Stomee 			jvalue = (*jenv)->NewObject(jenv, g_aggmin_jc,
2050fb3fb4f3Stomee 			    g_aggmininit_jm, value);
2051fb3fb4f3Stomee 			break;
2052fb3fb4f3Stomee 		case DTRACEAGG_MAX:
2053fb3fb4f3Stomee 			jvalue = (*jenv)->NewObject(jenv, g_aggmax_jc,
2054fb3fb4f3Stomee 			    g_aggmaxinit_jm, value);
2055fb3fb4f3Stomee 			break;
2056e77b06d2Stomee 		case DTRACEAGG_STDDEV:
2057e77b06d2Stomee 			jvalue = dtj_stddev(jenv, addr, normal);
2058e77b06d2Stomee 			break;
2059fb3fb4f3Stomee 		default:
2060fb3fb4f3Stomee 			jvalue = NULL;
2061fb3fb4f3Stomee 			dtj_throw_illegal_argument(jenv,
2062fb3fb4f3Stomee 			    "unexpected aggregation action: %d", act);
2063fb3fb4f3Stomee 		}
2064fb3fb4f3Stomee 	}
2065fb3fb4f3Stomee 
2066fb3fb4f3Stomee 	return (jvalue);
2067fb3fb4f3Stomee }
2068fb3fb4f3Stomee 
2069fb3fb4f3Stomee /*
2070fb3fb4f3Stomee  * Stops the given consumer if it is running.  Throws DTraceException if
2071fb3fb4f3Stomee  * dtrace_stop() fails and no other exception is already pending.  Clears and
2072fb3fb4f3Stomee  * rethrows any pending exception in order to grab the global lock safely.
2073fb3fb4f3Stomee  */
2074fb3fb4f3Stomee void
dtj_stop(dtj_java_consumer_t * jc)2075fb3fb4f3Stomee dtj_stop(dtj_java_consumer_t *jc)
2076fb3fb4f3Stomee {
2077fb3fb4f3Stomee 	JNIEnv *jenv;
2078fb3fb4f3Stomee 	int rc;
2079fb3fb4f3Stomee 	jthrowable e;
2080fb3fb4f3Stomee 
2081fb3fb4f3Stomee 	switch (jc->dtjj_consumer->dtjc_state) {
2082fb3fb4f3Stomee 	case DTJ_CONSUMER_GO:
2083fb3fb4f3Stomee 	case DTJ_CONSUMER_START:
2084fb3fb4f3Stomee 		break;
2085fb3fb4f3Stomee 	default:
2086fb3fb4f3Stomee 		return;
2087fb3fb4f3Stomee 	}
2088fb3fb4f3Stomee 
2089fb3fb4f3Stomee 	jenv = jc->dtjj_jenv;
2090fb3fb4f3Stomee 	e = (*jenv)->ExceptionOccurred(jenv);
2091fb3fb4f3Stomee 	if (e) {
2092fb3fb4f3Stomee 		(*jenv)->ExceptionClear(jenv);
2093fb3fb4f3Stomee 	}
2094fb3fb4f3Stomee 
2095fb3fb4f3Stomee 	(*jenv)->MonitorEnter(jenv, g_caller_jc);
2096fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
2097fb3fb4f3Stomee 		goto rethrow;
2098fb3fb4f3Stomee 	}
2099fb3fb4f3Stomee 
2100fb3fb4f3Stomee 	rc = dtrace_status(jc->dtjj_consumer->dtjc_dtp);
2101fb3fb4f3Stomee 	if (rc != DTRACE_STATUS_STOPPED) {
2102fb3fb4f3Stomee 		rc = dtrace_stop(jc->dtjj_consumer->dtjc_dtp);
2103fb3fb4f3Stomee 	}
2104fb3fb4f3Stomee 
2105fb3fb4f3Stomee 	(*jenv)->MonitorExit(jenv, g_caller_jc);
2106fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
2107fb3fb4f3Stomee 		goto rethrow;
2108fb3fb4f3Stomee 	}
2109fb3fb4f3Stomee 
2110fb3fb4f3Stomee 	if (rc == -1) {
2111fb3fb4f3Stomee 		(*jenv)->MonitorEnter(jenv, g_caller_jc);
2112fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
2113fb3fb4f3Stomee 			goto rethrow;
2114fb3fb4f3Stomee 		}
2115fb3fb4f3Stomee 		/* Do not wrap DTraceException */
2116fb3fb4f3Stomee 		dtj_throw_dtrace_exception(jc,
2117fb3fb4f3Stomee 		    "couldn't stop tracing: %s",
2118fb3fb4f3Stomee 		    dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp,
2119fb3fb4f3Stomee 		    dtrace_errno(jc->dtjj_consumer->dtjc_dtp)));
2120fb3fb4f3Stomee 		/* safe to call with pending exception */
2121fb3fb4f3Stomee 		(*jenv)->MonitorExit(jenv, g_caller_jc);
2122fb3fb4f3Stomee 	} else {
2123fb3fb4f3Stomee 		jc->dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP;
2124fb3fb4f3Stomee 	}
2125fb3fb4f3Stomee 
2126fb3fb4f3Stomee rethrow:
2127fb3fb4f3Stomee 	if (e) {
2128fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
2129fb3fb4f3Stomee 			/*
2130fb3fb4f3Stomee 			 * Favor earlier pending exception over
2131fb3fb4f3Stomee 			 * exception thrown in this function.
2132fb3fb4f3Stomee 			 */
2133fb3fb4f3Stomee 			(*jenv)->ExceptionClear(jenv);
2134fb3fb4f3Stomee 		}
2135fb3fb4f3Stomee 		(*jenv)->Throw(jenv, e);
2136fb3fb4f3Stomee 		(*jenv)->DeleteLocalRef(jenv, e);
2137fb3fb4f3Stomee 	}
2138fb3fb4f3Stomee }
2139fb3fb4f3Stomee 
2140fb3fb4f3Stomee /*
2141fb3fb4f3Stomee  * Return Aggregate instance, or null if java exception pending.
2142fb3fb4f3Stomee  */
2143fb3fb4f3Stomee jobject
dtj_get_aggregate(dtj_java_consumer_t * jc)2144fb3fb4f3Stomee dtj_get_aggregate(dtj_java_consumer_t *jc)
2145fb3fb4f3Stomee {
2146fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
2147fb3fb4f3Stomee 	hrtime_t snaptime;
2148fb3fb4f3Stomee 	int rc;
2149fb3fb4f3Stomee 
2150fb3fb4f3Stomee 	jobject aggregate = NULL;
2151fb3fb4f3Stomee 
21524ae67516Stomee 	/* Must not call MonitorEnter with a pending exception */
21534ae67516Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
21544ae67516Stomee 		WRAP_EXCEPTION(jenv);
21554ae67516Stomee 		return (NULL);
21564ae67516Stomee 	}
21574ae67516Stomee 
2158fb3fb4f3Stomee 	/*
2159fb3fb4f3Stomee 	 * Aggregations must be snapped, walked, and cleared atomically,
2160fb3fb4f3Stomee 	 * otherwise clearing loses data accumulated since the most recent snap.
2161fb3fb4f3Stomee 	 * This per-consumer lock prevents dtrace_work() from snapping or
2162fb3fb4f3Stomee 	 * clearing aggregations while we're in the middle of this atomic
2163fb3fb4f3Stomee 	 * operation, so we continue to hold it until done clearing.
2164fb3fb4f3Stomee 	 */
2165fb3fb4f3Stomee 	(*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock);
2166fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
2167fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
2168fb3fb4f3Stomee 		return (NULL);
2169fb3fb4f3Stomee 	}
2170fb3fb4f3Stomee 
2171fb3fb4f3Stomee 	dtj_aggwalk_init(jc);
2172fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
2173fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
2174fb3fb4f3Stomee 		/* release per-consumer lock */
2175fb3fb4f3Stomee 		(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2176fb3fb4f3Stomee 		return (NULL);
2177fb3fb4f3Stomee 	}
2178fb3fb4f3Stomee 
2179fb3fb4f3Stomee 	/*
2180fb3fb4f3Stomee 	 * Snap aggregations
2181fb3fb4f3Stomee 	 *
2182fb3fb4f3Stomee 	 * We need to record the snaptime here for the caller.  Leaving it to
2183fb3fb4f3Stomee 	 * the caller to record the snaptime before calling getAggregate() may
2184fb3fb4f3Stomee 	 * be inaccurate because of the indeterminate delay waiting on the
2185fb3fb4f3Stomee 	 * consumer lock before calling dtrace_aggregate_snap().
2186fb3fb4f3Stomee 	 */
2187fb3fb4f3Stomee 	snaptime = gethrtime();
2188fb3fb4f3Stomee 	if (dtrace_aggregate_snap(jc->dtjj_consumer->dtjc_dtp) != 0) {
2189fb3fb4f3Stomee 		dtj_error_t e;
21904ae67516Stomee 
21914ae67516Stomee 		/*
21924ae67516Stomee 		 * The dataDropped() ConsumerListener method can throw an
21934ae67516Stomee 		 * exception in the getAggregate() thread if the drop handler is
21944ae67516Stomee 		 * invoked during dtrace_aggregate_snap().
21954ae67516Stomee 		 */
21964ae67516Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
21974ae67516Stomee 			/* Do not wrap exception thrown from ConsumerListener */
21984ae67516Stomee 			/* release per-consumer lock */
21994ae67516Stomee 			(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22004ae67516Stomee 			return (NULL);
22014ae67516Stomee 		}
22024ae67516Stomee 
2203fb3fb4f3Stomee 		if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) {
2204fb3fb4f3Stomee 			/* Do not wrap DTraceException */
2205fb3fb4f3Stomee 			dtj_throw_dtrace_exception(jc, e.dtje_message);
2206fb3fb4f3Stomee 		}
2207fb3fb4f3Stomee 		/* release per-consumer lock */
2208fb3fb4f3Stomee 		(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2209fb3fb4f3Stomee 		return (NULL);
2210fb3fb4f3Stomee 	}
2211fb3fb4f3Stomee 
22124ae67516Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
22134ae67516Stomee 		/*
22144ae67516Stomee 		 * Wrap the exception thrown from ConsumerListener in this case,
22154ae67516Stomee 		 * so we can see that it unexpectedly reached this spot in
22164ae67516Stomee 		 * native code (dtrace_aggregate_snap should have returned
22174ae67516Stomee 		 * non-zero).
22184ae67516Stomee 		 */
22194ae67516Stomee 		WRAP_EXCEPTION(jenv);
22204ae67516Stomee 		/* release per-consumer lock */
22214ae67516Stomee 		(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
22224ae67516Stomee 		return (NULL);
22234ae67516Stomee 	}
22244ae67516Stomee 
2225fb3fb4f3Stomee 	/* Create the Java representation of the aggregate snapshot. */
2226fb3fb4f3Stomee 	aggregate = (*jenv)->NewObject(jenv, g_agg_jc, g_agginit_jm,
2227fb3fb4f3Stomee 	    snaptime);
2228fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
2229fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
2230fb3fb4f3Stomee 		/* release per-consumer lock */
2231fb3fb4f3Stomee 		(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2232fb3fb4f3Stomee 		return (NULL);
2233fb3fb4f3Stomee 	}
2234fb3fb4f3Stomee 	jc->dtjj_aggregate = aggregate;
2235fb3fb4f3Stomee 
2236fb3fb4f3Stomee 	/*
2237e77b06d2Stomee 	 * Walk the aggregate, converting the data into Java Objects. Traverse
2238e77b06d2Stomee 	 * in the order determined by libdtrace, respecting the various
2239e77b06d2Stomee 	 * "aggsort" options, just as dtrace_work does when generating
2240e77b06d2Stomee 	 * aggregations for the printa() action. libdtrace ordering is preserved
2241e77b06d2Stomee 	 * in the "ordinal" property of AggregationRecord, since it would
2242e77b06d2Stomee 	 * otherwise be lost when the records are hashed into the Aggregation's
2243e77b06d2Stomee 	 * map. Neither the consumer loop nor the competing getAggregate()
2244e77b06d2Stomee 	 * thread should depend on any particular record ordering (such as
2245e77b06d2Stomee 	 * ordering by tuple key) to process records correctly.
2246fb3fb4f3Stomee 	 *
2247fb3fb4f3Stomee 	 * It is impractical to hold the global lock around
2248fb3fb4f3Stomee 	 * dtrace_aggregate_print(), since it may take a long time (e.g. an
2249fb3fb4f3Stomee 	 * entire second) if it performs expensive conversions such as that
2250fb3fb4f3Stomee 	 * needed for user stack traces.  Most libdtrace functions are not
2251fb3fb4f3Stomee 	 * guaranteed to be MT-safe, even when each thread has its own dtrace
2252fb3fb4f3Stomee 	 * handle; or even if they are safe, there is no guarantee that future
2253fb3fb4f3Stomee 	 * changes may not make them unsafe.  Fortunately in this case, however,
2254fb3fb4f3Stomee 	 * only a per-consumer lock is necessary to avoid conflict with
2255fb3fb4f3Stomee 	 * dtrace_work() running in another thread (the consumer loop).
2256fb3fb4f3Stomee 	 */
2257e77b06d2Stomee 	rc = dtrace_aggregate_print(jc->dtjj_consumer->dtjc_dtp, NULL, NULL);
2258fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
2259fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
2260fb3fb4f3Stomee 		/* release per-consumer lock */
2261fb3fb4f3Stomee 		(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2262fb3fb4f3Stomee 		return (NULL);
2263fb3fb4f3Stomee 	}
2264fb3fb4f3Stomee 	if (rc != 0) {
2265fb3fb4f3Stomee 		dtj_error_t e;
2266fb3fb4f3Stomee 		if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) {
2267fb3fb4f3Stomee 			/* release per-consumer lock */
2268fb3fb4f3Stomee 			(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2269fb3fb4f3Stomee 			return (NULL);
2270fb3fb4f3Stomee 		}
2271fb3fb4f3Stomee 
2272fb3fb4f3Stomee 		if (e.dtje_number != EINTR) {
2273fb3fb4f3Stomee 			/* Do not wrap DTraceException */
2274fb3fb4f3Stomee 			dtj_throw_dtrace_exception(jc, e.dtje_message);
2275fb3fb4f3Stomee 			/* release per-consumer lock */
2276fb3fb4f3Stomee 			(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2277fb3fb4f3Stomee 			return (NULL);
2278fb3fb4f3Stomee 		}
2279fb3fb4f3Stomee 	}
2280fb3fb4f3Stomee 
2281fb3fb4f3Stomee 	dtj_aggwalk_init(jc);
2282fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
2283fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
2284fb3fb4f3Stomee 		/* release per-consumer lock */
2285fb3fb4f3Stomee 		(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2286fb3fb4f3Stomee 		return (NULL);
2287fb3fb4f3Stomee 	}
2288fb3fb4f3Stomee 
2289fb3fb4f3Stomee 	/*
2290fb3fb4f3Stomee 	 * dtrace_aggregate_clear() clears all aggregations, and we need to
2291fb3fb4f3Stomee 	 * clear aggregations selectively.  It also fails to preserve the
2292fb3fb4f3Stomee 	 * lquantize() range and step size; using aggregate_walk() to clear
2293fb3fb4f3Stomee 	 * aggregations does not have this problem.
2294fb3fb4f3Stomee 	 */
2295fb3fb4f3Stomee 	rc = dtrace_aggregate_walk(jc->dtjj_consumer->dtjc_dtp, dtj_clear, jc);
2296fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
2297fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
2298fb3fb4f3Stomee 		/* release per-consumer lock */
2299fb3fb4f3Stomee 		(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2300fb3fb4f3Stomee 		return (NULL);
2301fb3fb4f3Stomee 	}
2302fb3fb4f3Stomee 	if (rc != 0) {
2303fb3fb4f3Stomee 		dtj_error_t e;
2304fb3fb4f3Stomee 		if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) {
2305fb3fb4f3Stomee 			/* Do not wrap DTraceException */
2306fb3fb4f3Stomee 			dtj_throw_dtrace_exception(jc, e.dtje_message);
2307fb3fb4f3Stomee 		}
2308fb3fb4f3Stomee 		/* release per-consumer lock */
2309fb3fb4f3Stomee 		(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2310fb3fb4f3Stomee 		return (NULL);
2311fb3fb4f3Stomee 	}
2312fb3fb4f3Stomee 
2313fb3fb4f3Stomee 	(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2314fb3fb4f3Stomee 	if ((*jenv)->ExceptionCheck(jenv)) {
2315fb3fb4f3Stomee 		WRAP_EXCEPTION(jenv);
2316fb3fb4f3Stomee 		return (NULL);
2317fb3fb4f3Stomee 	}
2318fb3fb4f3Stomee 
2319fb3fb4f3Stomee 	aggregate = jc->dtjj_aggregate;
2320fb3fb4f3Stomee 	jc->dtjj_aggregate = NULL;
2321fb3fb4f3Stomee 
2322fb3fb4f3Stomee 	return (aggregate);
2323fb3fb4f3Stomee }
2324fb3fb4f3Stomee 
2325fb3fb4f3Stomee /*
2326fb3fb4f3Stomee  * Process any requests, such as the setting of runtime options, enqueued during
2327fb3fb4f3Stomee  * dtrace_sleep().  A Java exception is pending if this function returns
2328fb3fb4f3Stomee  * DTJ_ERR.
2329fb3fb4f3Stomee  */
2330fb3fb4f3Stomee static dtj_status_t
dtj_process_requests(dtj_java_consumer_t * jc)2331fb3fb4f3Stomee dtj_process_requests(dtj_java_consumer_t *jc)
2332fb3fb4f3Stomee {
2333fb3fb4f3Stomee 	dtj_request_t *r;
2334fb3fb4f3Stomee 	uu_list_t *list = jc->dtjj_consumer->dtjc_request_list;
2335fb3fb4f3Stomee 	pthread_mutex_t *list_lock = &jc->dtjj_consumer->
2336fb3fb4f3Stomee 	    dtjc_request_list_lock;
2337fb3fb4f3Stomee 	const char *opt;
2338fb3fb4f3Stomee 	const char *val;
2339fb3fb4f3Stomee 
2340fb3fb4f3Stomee 	(void) pthread_mutex_lock(list_lock);
2341fb3fb4f3Stomee 	while (!dtj_list_empty(list)) {
2342fb3fb4f3Stomee 		r = uu_list_first(list);
2343fb3fb4f3Stomee 		uu_list_remove(list, r);
2344fb3fb4f3Stomee 
2345fb3fb4f3Stomee 		switch (r->dtjr_type) {
2346fb3fb4f3Stomee 		case DTJ_REQUEST_OPTION:
2347fb3fb4f3Stomee 			opt = dtj_string_list_first(r->dtjr_args);
2348fb3fb4f3Stomee 			val = dtj_string_list_last(r->dtjr_args);
2349fb3fb4f3Stomee 			if (dtrace_setopt(jc->dtjj_consumer->dtjc_dtp, opt,
2350fb3fb4f3Stomee 			    val) == -1) {
2351fb3fb4f3Stomee 				/* Do not wrap DTraceException */
2352fb3fb4f3Stomee 				dtj_throw_dtrace_exception(jc,
2353fb3fb4f3Stomee 				    "failed to set %s: %s", opt,
2354fb3fb4f3Stomee 				    dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp,
2355fb3fb4f3Stomee 				    dtrace_errno(jc->dtjj_consumer->dtjc_dtp)));
2356fb3fb4f3Stomee 				dtj_request_destroy(r, NULL);
2357fb3fb4f3Stomee 				(void) pthread_mutex_unlock(list_lock);
2358fb3fb4f3Stomee 				return (DTJ_ERR);
2359fb3fb4f3Stomee 			}
2360fb3fb4f3Stomee 			break;
2361fb3fb4f3Stomee 		}
2362fb3fb4f3Stomee 		dtj_request_destroy(r, NULL);
2363fb3fb4f3Stomee 	}
2364fb3fb4f3Stomee 	(void) pthread_mutex_unlock(list_lock);
2365fb3fb4f3Stomee 	return (DTJ_OK);
2366fb3fb4f3Stomee }
2367fb3fb4f3Stomee 
2368fb3fb4f3Stomee /*
2369fb3fb4f3Stomee  * Return DTJ_OK if the consumer loop is stopped normally by either the exit()
2370fb3fb4f3Stomee  * action or the Consumer stop() method.  Otherwise return DTJ_ERR if the
2371fb3fb4f3Stomee  * consumer loop terminates abnormally with an exception pending.
2372fb3fb4f3Stomee  */
2373fb3fb4f3Stomee dtj_status_t
dtj_consume(dtj_java_consumer_t * jc)2374fb3fb4f3Stomee dtj_consume(dtj_java_consumer_t *jc)
2375fb3fb4f3Stomee {
2376fb3fb4f3Stomee 	JNIEnv *jenv = jc->dtjj_jenv;
2377fb3fb4f3Stomee 	dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp;
2378fb3fb4f3Stomee 	boolean_t done = B_FALSE;
2379fb3fb4f3Stomee 	dtj_error_t e;
2380fb3fb4f3Stomee 
2381fb3fb4f3Stomee 	do {
2382fb3fb4f3Stomee 		if (!jc->dtjj_consumer->dtjc_interrupt) {
2383fb3fb4f3Stomee 			dtrace_sleep(dtp);
2384fb3fb4f3Stomee 		}
2385fb3fb4f3Stomee 
2386fb3fb4f3Stomee 		if (jc->dtjj_consumer->dtjc_interrupt) {
2387fb3fb4f3Stomee 			done = B_TRUE;
2388fb3fb4f3Stomee 			dtj_stop(jc);
2389fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
2390fb3fb4f3Stomee 				/*
2391fb3fb4f3Stomee 				 * Exception left pending by Consumer
2392fb3fb4f3Stomee 				 * getAggregate() method.
2393fb3fb4f3Stomee 				 */
2394fb3fb4f3Stomee 				return (DTJ_ERR);
2395fb3fb4f3Stomee 			}
2396fb3fb4f3Stomee 		} else if (jc->dtjj_consumer->dtjc_process_list != NULL) {
2397fb3fb4f3Stomee 			int nprocs = uu_list_numnodes(jc->dtjj_consumer->
2398fb3fb4f3Stomee 			    dtjc_process_list);
2399fb3fb4f3Stomee 			if (jc->dtjj_consumer->dtjc_procs_ended == nprocs) {
2400fb3fb4f3Stomee 				done = B_TRUE;
2401fb3fb4f3Stomee 				dtj_stop(jc);
2402fb3fb4f3Stomee 			}
2403fb3fb4f3Stomee 		}
2404fb3fb4f3Stomee 
2405fb3fb4f3Stomee 		/*
2406fb3fb4f3Stomee 		 * Functions like dtrace_setopt() are not safe to call during
2407fb3fb4f3Stomee 		 * dtrace_sleep().  Check the request list every time we wake up
2408fb3fb4f3Stomee 		 * from dtrace_sleep().
2409fb3fb4f3Stomee 		 */
2410fb3fb4f3Stomee 		if (!done) {
2411fb3fb4f3Stomee 			if (dtj_process_requests(jc) != DTJ_OK) {
2412fb3fb4f3Stomee 				/* Do not wrap DTraceException */
2413fb3fb4f3Stomee 				return (DTJ_ERR);
2414fb3fb4f3Stomee 			}
2415fb3fb4f3Stomee 		}
2416fb3fb4f3Stomee 
24174ae67516Stomee 		/* Must not call MonitorEnter with a pending exception */
24184ae67516Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
24194ae67516Stomee 			WRAP_EXCEPTION(jenv);
24204ae67516Stomee 			return (DTJ_ERR);
24214ae67516Stomee 		}
24224ae67516Stomee 
2423fb3fb4f3Stomee 		/*
2424fb3fb4f3Stomee 		 * Use the per-consumer lock to avoid conflict with
2425fb3fb4f3Stomee 		 * get_aggregate() called from another thread.
2426fb3fb4f3Stomee 		 */
2427fb3fb4f3Stomee 		(*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock);
2428fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
2429fb3fb4f3Stomee 			WRAP_EXCEPTION(jenv);
2430fb3fb4f3Stomee 			return (DTJ_ERR);
2431fb3fb4f3Stomee 		}
2432fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller,
2433fb3fb4f3Stomee 		    g_interval_began_jm);
2434fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
243552aacb45Stomee 			/* Don't wrap exception thrown from ConsumerListener */
2436fb3fb4f3Stomee 			(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2437fb3fb4f3Stomee 			return (DTJ_ERR);
2438fb3fb4f3Stomee 		}
2439fb3fb4f3Stomee 		jc->dtjj_consumer->dtjc_printa_snaptime = gethrtime();
2440fb3fb4f3Stomee 		switch (dtrace_work(dtp, NULL, dtj_chew, dtj_chewrec, jc)) {
2441fb3fb4f3Stomee 		case DTRACE_WORKSTATUS_DONE:
2442fb3fb4f3Stomee 			done = B_TRUE;
2443fb3fb4f3Stomee 			break;
2444fb3fb4f3Stomee 		case DTRACE_WORKSTATUS_OKAY:
2445fb3fb4f3Stomee 			break;
2446fb3fb4f3Stomee 		default:
2447fb3fb4f3Stomee 			/*
2448fb3fb4f3Stomee 			 * Check for a pending exception that got us to this
2449fb3fb4f3Stomee 			 * error workstatus case.
2450fb3fb4f3Stomee 			 */
2451fb3fb4f3Stomee 			if ((*jenv)->ExceptionCheck(jenv)) {
2452fb3fb4f3Stomee 				/*
2453fb3fb4f3Stomee 				 * Ensure valid initial state before releasing
2454fb3fb4f3Stomee 				 * the consumer lock
2455fb3fb4f3Stomee 				 */
2456fb3fb4f3Stomee 				jc->dtjj_consumer->dtjc_printa_snaptime = 0;
2457fb3fb4f3Stomee 				/* Do not wrap DTraceException */
2458fb3fb4f3Stomee 				/* Release per-consumer lock */
2459fb3fb4f3Stomee 				(*jenv)->MonitorExit(jenv,
2460fb3fb4f3Stomee 				    jc->dtjj_consumer_lock);
2461fb3fb4f3Stomee 				return (DTJ_ERR);
2462fb3fb4f3Stomee 			}
2463fb3fb4f3Stomee 
2464fb3fb4f3Stomee 			if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) {
2465fb3fb4f3Stomee 				/* java exception pending */
2466fb3fb4f3Stomee 				jc->dtjj_consumer->dtjc_printa_snaptime = 0;
2467fb3fb4f3Stomee 				/* Release per-consumer lock */
2468fb3fb4f3Stomee 				(*jenv)->MonitorExit(jenv,
2469fb3fb4f3Stomee 				    jc->dtjj_consumer_lock);
2470fb3fb4f3Stomee 				return (DTJ_ERR);
2471fb3fb4f3Stomee 			}
2472fb3fb4f3Stomee 
2473fb3fb4f3Stomee 			if (e.dtje_number != EINTR) {
2474fb3fb4f3Stomee 				/* Do not wrap DTraceException */
2475fb3fb4f3Stomee 				dtj_throw_dtrace_exception(jc, e.dtje_message);
2476fb3fb4f3Stomee 				jc->dtjj_consumer->dtjc_printa_snaptime = 0;
2477fb3fb4f3Stomee 				/* Release per-consumer lock */
2478fb3fb4f3Stomee 				(*jenv)->MonitorExit(jenv,
2479fb3fb4f3Stomee 				    jc->dtjj_consumer_lock);
2480fb3fb4f3Stomee 				return (DTJ_ERR);
2481fb3fb4f3Stomee 			}
2482fb3fb4f3Stomee 		}
2483fb3fb4f3Stomee 		/*
2484fb3fb4f3Stomee 		 * Check for ConsumerException before doing anything else with
2485fb3fb4f3Stomee 		 * the JNIEnv.
2486fb3fb4f3Stomee 		 */
2487fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
2488fb3fb4f3Stomee 			/*
2489fb3fb4f3Stomee 			 * Do not wrap exception thrown from ConsumerListener.
2490fb3fb4f3Stomee 			 */
2491fb3fb4f3Stomee 			jc->dtjj_consumer->dtjc_printa_snaptime = 0;
2492fb3fb4f3Stomee 			/* Release per-consumer lock */
2493fb3fb4f3Stomee 			(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2494fb3fb4f3Stomee 			return (DTJ_ERR);
2495fb3fb4f3Stomee 		}
2496fb3fb4f3Stomee 		jc->dtjj_consumer->dtjc_printa_snaptime = 0;
2497fb3fb4f3Stomee 		/*
2498fb3fb4f3Stomee 		 * Notify ConsumerListeners the the dtrace_work() interval ended
2499fb3fb4f3Stomee 		 * before releasing the lock.
2500fb3fb4f3Stomee 		 */
2501fb3fb4f3Stomee 		(*jenv)->CallVoidMethod(jenv, jc->dtjj_caller,
2502fb3fb4f3Stomee 		    g_interval_ended_jm);
2503fb3fb4f3Stomee 		(*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock);
2504fb3fb4f3Stomee 		if ((*jenv)->ExceptionCheck(jenv)) {
250552aacb45Stomee 			/* Don't wrap exception thrown from ConsumerListener */
2506fb3fb4f3Stomee 			return (DTJ_ERR);
2507fb3fb4f3Stomee 		}
2508fb3fb4f3Stomee 
2509fb3fb4f3Stomee 		/*
2510fb3fb4f3Stomee 		 * Check for a temporarily cleared exception set by a handler
2511fb3fb4f3Stomee 		 * that could not safely leave the exception pending because it
2512fb3fb4f3Stomee 		 * could not return an abort signal.  Rethrow it now that it's
2513fb3fb4f3Stomee 		 * safe to do so (when it's possible to ensure that no JNI calls
2514fb3fb4f3Stomee 		 * will be made that are unsafe while an exception is pending).
2515fb3fb4f3Stomee 		 */
2516fb3fb4f3Stomee 		if (jc->dtjj_exception) {
2517fb3fb4f3Stomee 			(*jenv)->Throw(jenv, jc->dtjj_exception);
2518fb3fb4f3Stomee 			(*jenv)->DeleteLocalRef(jenv, jc->dtjj_exception);
2519fb3fb4f3Stomee 			jc->dtjj_exception = NULL;
2520fb3fb4f3Stomee 			return (DTJ_ERR);
2521fb3fb4f3Stomee 		}
2522fb3fb4f3Stomee 	} while (!done);
2523fb3fb4f3Stomee 
2524fb3fb4f3Stomee 	return (DTJ_OK);
2525fb3fb4f3Stomee }
2526