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 {
1240fb3fb4f3Stomee /* 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
1251e77b06d2Stomee /* 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 /*
2237fb3fb4f3Stomee * 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