11449Stomee /* 21449Stomee * CDDL HEADER START 31449Stomee * 41449Stomee * The contents of this file are subject to the terms of the 51449Stomee * Common Development and Distribution License (the "License"). 61449Stomee * You may not use this file except in compliance with the License. 71449Stomee * 81449Stomee * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91449Stomee * or http://www.opensolaris.org/os/licensing. 101449Stomee * See the License for the specific language governing permissions 111449Stomee * and limitations under the License. 121449Stomee * 131449Stomee * When distributing Covered Code, include this CDDL HEADER in each 141449Stomee * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151449Stomee * If applicable, add the following below this CDDL HEADER, with the 161449Stomee * fields enclosed by brackets "[]" replaced with your own identifying 171449Stomee * information: Portions Copyright [yyyy] [name of copyright owner] 181449Stomee * 191449Stomee * CDDL HEADER END 201449Stomee */ 211449Stomee 221449Stomee /* 231449Stomee * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 241449Stomee * Use is subject to license terms. 251449Stomee */ 261449Stomee 271449Stomee #pragma ident "%Z%%M% %I% %E% SMI" 281449Stomee 291449Stomee #include <stdio.h> 301449Stomee #include <ctype.h> 311449Stomee #include <limits.h> 321449Stomee #include <errno.h> 331449Stomee #include <stdlib.h> 341449Stomee #include <unistd.h> 351449Stomee #include <strings.h> 361449Stomee #include <sys/wait.h> 371449Stomee #include <limits.h> 381449Stomee #include <signal.h> 391449Stomee #include <libproc.h> 401449Stomee #include <pthread.h> 411449Stomee #include <dtrace_jni.h> 421449Stomee 431449Stomee /* 441449Stomee * Implements the work done in the running consumer loop. The native Java 451449Stomee * methods (JNI layer) are implemented in dtrace_jni.c. 461449Stomee */ 471449Stomee 481449Stomee /* Record handler passed to dtrace_work() */ 491449Stomee static int dtj_chewrec(const dtrace_probedata_t *, const dtrace_recdesc_t *, 501449Stomee void *); 511449Stomee /* Probe data handler passed to dtrace_work() */ 521449Stomee static int dtj_chew(const dtrace_probedata_t *, void *); 531449Stomee 541449Stomee /* Processes requests from LocalConsumer enqueued during dtrace_sleep() */ 551449Stomee static dtj_status_t dtj_process_requests(dtj_java_consumer_t *); 561449Stomee 571449Stomee /* 581449Stomee * Callback handlers set in dtj_set_callback_handlers(), called from libdtrace 591449Stomee * in the consumer loop (from dtrace_work()) 601449Stomee */ 611449Stomee static int dtj_drophandler(const dtrace_dropdata_t *, void *); 621449Stomee static int dtj_errhandler(const dtrace_errdata_t *, void *); 631449Stomee static void dtj_prochandler(struct ps_prochandle *, const char *, void *); 641449Stomee static int dtj_setopthandler(const dtrace_setoptdata_t *, void *); 651449Stomee /* 661449Stomee * Buffered output handler called from libdtrace in both the consumer loop (from 671449Stomee * dtrace_work()) and the get_aggregate() function (from 681449Stomee * dtrace_aggregate_print()). 691449Stomee */ 701449Stomee static int dtj_bufhandler(const dtrace_bufdata_t *, void *); 711449Stomee 721449Stomee /* Conversion of libdtrace data into Java Objects */ 731449Stomee static jobject dtj_recdata(dtj_java_consumer_t *, uint32_t, caddr_t); 741449Stomee static jobject dtj_bytedata(JNIEnv *, uint32_t, caddr_t); 751449Stomee static jobject dtj_new_stack_record(const caddr_t, const dtrace_recdesc_t *, 761449Stomee dtj_java_consumer_t *); 771449Stomee static jobject dtj_new_probedata_stack_record(const dtrace_probedata_t *, 781449Stomee const dtrace_recdesc_t *, dtj_java_consumer_t *); 79*2777Stomee static jobject dtj_new_symbol_record(const caddr_t, const dtrace_recdesc_t *, 80*2777Stomee dtj_java_consumer_t *); 81*2777Stomee static jobject dtj_new_probedata_symbol_record(const dtrace_probedata_t *, 82*2777Stomee const dtrace_recdesc_t *, dtj_java_consumer_t *); 831449Stomee /* Aggregation data */ 841449Stomee static jobject dtj_new_tuple_stack_record(const dtrace_aggdata_t *, 851449Stomee const dtrace_recdesc_t *, const char *, dtj_java_consumer_t *); 86*2777Stomee static jobject dtj_new_tuple_symbol_record(const dtrace_aggdata_t *, 87*2777Stomee const dtrace_recdesc_t *, const char *, dtj_java_consumer_t *); 881449Stomee static jobject dtj_new_distribution(const dtrace_aggdata_t *, 891449Stomee const dtrace_recdesc_t *, dtj_java_consumer_t *); 901449Stomee static jobject dtj_new_aggval(dtj_java_consumer_t *, const dtrace_aggdata_t *, 911449Stomee const dtrace_recdesc_t *); 921449Stomee static int64_t dtj_average(caddr_t, uint64_t); 931449Stomee static int64_t dtj_avg_total(caddr_t, uint64_t); 941449Stomee static int64_t dtj_avg_count(caddr_t); 951449Stomee 961449Stomee /* Aggregation functions */ 971449Stomee static void dtj_aggwalk_init(dtj_java_consumer_t *); 981449Stomee static int dtj_agghandler(const dtrace_bufdata_t *, dtj_java_consumer_t *); 991449Stomee static boolean_t dtj_is_included(const dtrace_aggdata_t *, 1001449Stomee dtj_java_consumer_t *); 101*2777Stomee static void dtj_attach_frames(dtj_java_consumer_t *, jobject, jobjectArray); 102*2777Stomee static void dtj_attach_name(dtj_java_consumer_t *, jobject, jstring); 1031449Stomee static boolean_t dtj_is_stack_action(dtrace_actkind_t); 104*2777Stomee static boolean_t dtj_is_symbol_action(dtrace_actkind_t); 1051449Stomee static int dtj_clear(const dtrace_aggdata_t *, void *); 1061449Stomee 1071449Stomee /* 1081449Stomee * The consumer loop needs to protect calls to libdtrace functions with a global 1091449Stomee * lock. JNI native method calls in dtrace_jni.c are already protected and do 1101449Stomee * not need this function. 1111449Stomee */ 1121449Stomee dtj_status_t 1131449Stomee dtj_get_dtrace_error(dtj_java_consumer_t *jc, dtj_error_t *e) 1141449Stomee { 1151449Stomee JNIEnv *jenv = jc->dtjj_jenv; 1161449Stomee dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp; 1171449Stomee 1181449Stomee /* Grab global lock */ 1191449Stomee (*jenv)->MonitorEnter(jenv, g_caller_jc); 1201449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 1211449Stomee WRAP_EXCEPTION(jenv); 1221449Stomee return (DTJ_ERR); 1231449Stomee } 1241449Stomee e->dtje_number = dtrace_errno(dtp); 1251449Stomee e->dtje_message = dtrace_errmsg(dtp, e->dtje_number); 1261449Stomee (*jenv)->MonitorExit(jenv, g_caller_jc); 1271449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 1281449Stomee WRAP_EXCEPTION(jenv); 1291449Stomee return (DTJ_ERR); 1301449Stomee } 1311449Stomee return (DTJ_OK); 1321449Stomee } 1331449Stomee 1341449Stomee /* 1351449Stomee * Protected by global lock (LocalConsumer.class) that protects call to 1361449Stomee * Java_org_opensolaris_os_dtrace_LocalConsumer__1go() 1371449Stomee */ 1381449Stomee dtj_status_t 1391449Stomee dtj_set_callback_handlers(dtj_java_consumer_t *jc) 1401449Stomee { 1411449Stomee dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp; 1421449Stomee dtrace_optval_t optval; 1431449Stomee 1441449Stomee if (dtrace_handle_buffered(dtp, &dtj_bufhandler, NULL) == -1) { 1451449Stomee dtj_throw_dtrace_exception(jc, 1461449Stomee "failed to establish buffered handler: %s", 1471449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 1481449Stomee return (DTJ_ERR); 1491449Stomee } 1501449Stomee 1511449Stomee if (dtrace_handle_drop(dtp, &dtj_drophandler, NULL) == -1) { 1521449Stomee dtj_throw_dtrace_exception(jc, 1531449Stomee "failed to establish drop handler: %s", 1541449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 1551449Stomee return (DTJ_ERR); 1561449Stomee } 1571449Stomee 1581449Stomee if (dtrace_handle_err(dtp, &dtj_errhandler, NULL) == -1) { 1591449Stomee dtj_throw_dtrace_exception(jc, 1601449Stomee "failed to establish error handler: %s", 1611449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 1621449Stomee return (DTJ_ERR); 1631449Stomee } 1641449Stomee 1651449Stomee if (dtrace_handle_proc(dtp, &dtj_prochandler, NULL) == -1) { 1661449Stomee dtj_throw_dtrace_exception(jc, 1671449Stomee "failed to establish proc handler: %s", 1681449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 1691449Stomee return (DTJ_ERR); 1701449Stomee } 1711449Stomee 1721449Stomee if (dtrace_getopt(dtp, "flowindent", &optval) == -1) { 1731449Stomee dtj_throw_dtrace_exception(jc, 1741449Stomee "couldn't get option %s: %s", "flowindent", 1751449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 1761449Stomee return (DTJ_ERR); 1771449Stomee } 1781449Stomee 1791449Stomee jc->dtjj_consumer->dtjc_flow = (optval != DTRACEOPT_UNSET); 1801449Stomee 1811449Stomee if (dtrace_handle_setopt(dtp, &dtj_setopthandler, NULL) == -1) { 1821449Stomee dtj_throw_dtrace_exception(jc, 1831449Stomee "failed to establish setopt handler: %s", 1841449Stomee dtrace_errmsg(dtp, dtrace_errno(dtp))); 1851449Stomee return (DTJ_ERR); 1861449Stomee } 1871449Stomee 1881449Stomee return (DTJ_OK); 1891449Stomee } 1901449Stomee 1911449Stomee static int 1921449Stomee /* ARGSUSED */ 1931449Stomee dtj_drophandler(const dtrace_dropdata_t *data, void *arg) 1941449Stomee { 1951449Stomee dtj_java_consumer_t *jc; 1961449Stomee JNIEnv *jenv; 1971449Stomee 1981449Stomee const char *dropkind; 1991449Stomee 2001449Stomee jstring msg = NULL; 2011449Stomee jstring kind = NULL; 2021449Stomee jobject drop = NULL; 2031449Stomee 2041449Stomee jc = pthread_getspecific(g_dtj_consumer_key); 2051449Stomee jenv = jc->dtjj_jenv; 2061449Stomee 2071449Stomee msg = dtj_NewStringNative(jenv, data->dtdda_msg); 2081449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 2091449Stomee return (DTRACE_HANDLE_ABORT); 2101449Stomee } 2111449Stomee switch (data->dtdda_kind) { 2121449Stomee case DTRACEDROP_PRINCIPAL: 2131449Stomee dropkind = "PRINCIPAL"; 2141449Stomee break; 2151449Stomee case DTRACEDROP_AGGREGATION: 2161449Stomee dropkind = "AGGREGATION"; 2171449Stomee break; 2181449Stomee case DTRACEDROP_DYNAMIC: 2191449Stomee dropkind = "DYNAMIC"; 2201449Stomee break; 2211449Stomee case DTRACEDROP_DYNRINSE: 2221449Stomee dropkind = "DYNRINSE"; 2231449Stomee break; 2241449Stomee case DTRACEDROP_DYNDIRTY: 2251449Stomee dropkind = "DYNDIRTY"; 2261449Stomee break; 2271449Stomee case DTRACEDROP_SPEC: 2281449Stomee dropkind = "SPEC"; 2291449Stomee break; 2301449Stomee case DTRACEDROP_SPECBUSY: 2311449Stomee dropkind = "SPECBUSY"; 2321449Stomee break; 2331449Stomee case DTRACEDROP_SPECUNAVAIL: 2341449Stomee dropkind = "SPECUNAVAIL"; 2351449Stomee break; 2361449Stomee case DTRACEDROP_STKSTROVERFLOW: 2371449Stomee dropkind = "STKSTROVERFLOW"; 2381449Stomee break; 2391449Stomee case DTRACEDROP_DBLERROR: 2401449Stomee dropkind = "DBLERROR"; 2411449Stomee break; 2421449Stomee default: 2431449Stomee dropkind = "UNKNOWN"; 2441449Stomee } 2451449Stomee kind = (*jenv)->NewStringUTF(jenv, dropkind); 2461449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 2471449Stomee (*jenv)->DeleteLocalRef(jenv, msg); 2481449Stomee return (DTRACE_HANDLE_ABORT); 2491449Stomee } 2501449Stomee drop = (*jenv)->NewObject(jenv, g_drop_jc, g_dropinit_jm, 2511449Stomee data->dtdda_cpu, kind, data->dtdda_drops, data->dtdda_total, msg); 2521449Stomee (*jenv)->DeleteLocalRef(jenv, kind); 2531449Stomee (*jenv)->DeleteLocalRef(jenv, msg); 2541449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 2551449Stomee return (DTRACE_HANDLE_ABORT); 2561449Stomee } 2571449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_drop_jm, drop); 2581449Stomee (*jenv)->DeleteLocalRef(jenv, drop); 2591449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 2601449Stomee return (DTRACE_HANDLE_ABORT); 2611449Stomee } 2621449Stomee 2631449Stomee return (DTRACE_HANDLE_OK); 2641449Stomee } 2651449Stomee 2661449Stomee static int 2671449Stomee /* ARGSUSED */ 2681449Stomee dtj_errhandler(const dtrace_errdata_t *data, void *arg) 2691449Stomee { 2701449Stomee dtj_java_consumer_t *jc; 2711449Stomee JNIEnv *jenv; 2721449Stomee 2731449Stomee const char *f; 2741449Stomee int64_t addr; 2751449Stomee 2761449Stomee jobject probe = NULL; 2771449Stomee jstring fault = NULL; 2781449Stomee jstring msg = NULL; 2791449Stomee jobject error = NULL; 2801449Stomee 2811449Stomee jc = pthread_getspecific(g_dtj_consumer_key); 2821449Stomee jenv = jc->dtjj_jenv; 2831449Stomee 2841449Stomee probe = dtj_new_probedesc(jc, data->dteda_pdesc); 2851449Stomee if (!probe) { 2861449Stomee return (DTRACE_HANDLE_ABORT); 2871449Stomee } 2881449Stomee f = dtj_get_fault_name(data->dteda_fault); 2891449Stomee if (f) { 2901449Stomee fault = (*jenv)->NewStringUTF(jenv, f); 2911449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 2921449Stomee (*jenv)->DeleteLocalRef(jenv, probe); 2931449Stomee return (DTRACE_HANDLE_ABORT); 2941449Stomee } 2951449Stomee } 2961449Stomee switch (data->dteda_fault) { 2971449Stomee case DTRACEFLT_BADADDR: 2981449Stomee case DTRACEFLT_BADALIGN: 2991449Stomee addr = data->dteda_addr; 3001449Stomee break; 3011449Stomee default: 3021449Stomee addr = -1; 3031449Stomee } 3041449Stomee msg = dtj_NewStringNative(jenv, data->dteda_msg); 3051449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 3061449Stomee (*jenv)->DeleteLocalRef(jenv, probe); 3071449Stomee (*jenv)->DeleteLocalRef(jenv, fault); 3081449Stomee return (DTRACE_HANDLE_ABORT); 3091449Stomee } 3101449Stomee error = (*jenv)->NewObject(jenv, g_error_jc, g_errinit_jm, 3111449Stomee probe, 3121449Stomee data->dteda_edesc->dtepd_epid, 3131449Stomee data->dteda_cpu, 3141449Stomee data->dteda_action, 3151449Stomee data->dteda_offset, 3161449Stomee fault, addr, msg); 3171449Stomee (*jenv)->DeleteLocalRef(jenv, msg); 3181449Stomee (*jenv)->DeleteLocalRef(jenv, fault); 3191449Stomee (*jenv)->DeleteLocalRef(jenv, probe); 3201449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 3211449Stomee return (DTRACE_HANDLE_ABORT); 3221449Stomee } 3231449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_error_jm, error); 3241449Stomee (*jenv)->DeleteLocalRef(jenv, error); 3251449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 3261449Stomee return (DTRACE_HANDLE_ABORT); 3271449Stomee } 3281449Stomee 3291449Stomee return (DTRACE_HANDLE_OK); 3301449Stomee } 3311449Stomee 3321449Stomee /* 3331449Stomee * Since the function signature does not allow us to return an abort signal, we 3341449Stomee * need to temporarily clear any pending exception before returning, since 3351449Stomee * without the abort we can't guarantee that the exception will be checked in 3361449Stomee * time to prevent invalid JNI function calls. 3371449Stomee */ 3381449Stomee static void 3391449Stomee /* ARGSUSED */ 3401449Stomee dtj_prochandler(struct ps_prochandle *P, const char *msg, void *arg) 3411449Stomee { 3421449Stomee dtj_java_consumer_t *jc; 3431449Stomee JNIEnv *jenv; 3441449Stomee 3451449Stomee const psinfo_t *prp = Ppsinfo(P); 3461449Stomee int pid = Pstatus(P)->pr_pid; 3471449Stomee int signal = -1; 3481449Stomee char signame[SIG2STR_MAX]; 3491449Stomee const char *statusname; 3501449Stomee int exit = INT_MAX; /* invalid initial status */ 3511449Stomee 3521449Stomee jstring status = NULL; 3531449Stomee jstring signalName = NULL; 3541449Stomee jstring message = NULL; 3551449Stomee jobject process = NULL; 3561449Stomee 3571449Stomee jc = pthread_getspecific(g_dtj_consumer_key); 3581449Stomee jenv = jc->dtjj_jenv; 3591449Stomee 3601449Stomee switch (Pstate(P)) { 3611449Stomee case PS_RUN: 3621449Stomee statusname = "RUN"; 3631449Stomee break; 3641449Stomee case PS_STOP: 3651449Stomee statusname = "STOP"; 3661449Stomee break; 3671449Stomee case PS_UNDEAD: 3681449Stomee statusname = "UNDEAD"; 3691449Stomee if (prp != NULL) { 3701449Stomee exit = WEXITSTATUS(prp->pr_wstat); 3711449Stomee } 3721449Stomee if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) { 3731449Stomee signal = WTERMSIG(prp->pr_wstat); 3741449Stomee (void) proc_signame(signal, signame, sizeof (signame)); 3751449Stomee signalName = (*jenv)->NewStringUTF(jenv, signame); 3761449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 3771449Stomee goto proc_end; 3781449Stomee } 3791449Stomee } 3801449Stomee ++jc->dtjj_consumer->dtjc_procs_ended; 3811449Stomee break; 3821449Stomee case PS_LOST: 3831449Stomee statusname = "LOST"; 3841449Stomee ++jc->dtjj_consumer->dtjc_procs_ended; 3851449Stomee break; 3861449Stomee case PS_DEAD: 3871449Stomee /* 3881449Stomee * PS_DEAD not handled by dtrace.c prochandler, still this is a 3891449Stomee * case of process termination and it can't hurt to handle it. 3901449Stomee */ 3911449Stomee statusname = "DEAD"; 3921449Stomee ++jc->dtjj_consumer->dtjc_procs_ended; 3931449Stomee break; 3941449Stomee default: 3951449Stomee /* 3961449Stomee * Unexpected, but erring on the side of tolerance by not 3971449Stomee * crashing the consumer. Failure to notify listeners of 3981449Stomee * process state not handled by the dtrace.c prochandler does 3991449Stomee * not seem serious. 4001449Stomee */ 4011449Stomee return; 4021449Stomee } 4031449Stomee 4041449Stomee status = (*jenv)->NewStringUTF(jenv, statusname); 4051449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 4061449Stomee (*jenv)->DeleteLocalRef(jenv, signalName); 4071449Stomee goto proc_end; 4081449Stomee } 4091449Stomee if (msg) { 4101449Stomee message = dtj_NewStringNative(jenv, msg); 4111449Stomee if (!message) { 4121449Stomee (*jenv)->DeleteLocalRef(jenv, status); 4131449Stomee (*jenv)->DeleteLocalRef(jenv, signalName); 4141449Stomee goto proc_end; 4151449Stomee } 4161449Stomee } 4171449Stomee process = (*jenv)->NewObject(jenv, g_process_jc, g_procinit_jm, 4181449Stomee pid, status, signal, signalName, NULL, message); 4191449Stomee (*jenv)->DeleteLocalRef(jenv, status); 4201449Stomee (*jenv)->DeleteLocalRef(jenv, signalName); 4211449Stomee (*jenv)->DeleteLocalRef(jenv, message); 4221449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 4231449Stomee goto proc_end; 4241449Stomee } 4251449Stomee if (exit != INT_MAX) { 4261449Stomee /* valid exit status */ 4271449Stomee (*jenv)->CallVoidMethod(jenv, process, g_procexit_jm, exit); 4281449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 4291449Stomee (*jenv)->DeleteLocalRef(jenv, process); 4301449Stomee goto proc_end; 4311449Stomee } 4321449Stomee } 4331449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_proc_jm, process); 4341449Stomee (*jenv)->DeleteLocalRef(jenv, process); 4351449Stomee 4361449Stomee proc_end: 4371449Stomee 4381449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 4391449Stomee /* 4401449Stomee * Save the exception so we can rethrow it later when it's safe. 4411449Stomee */ 4421449Stomee if (!jc->dtjj_exception) { 4431449Stomee jthrowable e = (*jenv)->ExceptionOccurred(jenv); 4441449Stomee jc->dtjj_exception = e; 4451449Stomee } 4461449Stomee (*jenv)->ExceptionClear(jenv); 4471449Stomee } 4481449Stomee } 4491449Stomee 4501449Stomee static int 4511449Stomee /* ARGSUSED */ 4521449Stomee dtj_setopthandler(const dtrace_setoptdata_t *data, void *arg) 4531449Stomee { 4541449Stomee dtj_java_consumer_t *jc; 4551449Stomee 4561449Stomee jc = pthread_getspecific(g_dtj_consumer_key); 4571449Stomee if (strcmp(data->dtsda_option, "flowindent") == 0) { 4581449Stomee jc->dtjj_consumer->dtjc_flow = 4591449Stomee (data->dtsda_newval != DTRACEOPT_UNSET); 4601449Stomee } 4611449Stomee return (DTRACE_HANDLE_OK); 4621449Stomee } 4631449Stomee 4641449Stomee /* 4651449Stomee * Most of this function lifted from libdtrace/common/dt_consume.c 4661449Stomee * dt_print_bytes(). 4671449Stomee */ 4681449Stomee static jobject 4691449Stomee dtj_bytedata(JNIEnv *jenv, uint32_t nbytes, caddr_t addr) 4701449Stomee { 4711449Stomee /* 4721449Stomee * If the byte stream is a series of printable characters, followed by 4731449Stomee * a terminating byte, we print it out as a string. Otherwise, we 4741449Stomee * assume that it's something else and just print the bytes. 4751449Stomee */ 4761449Stomee int i, j; 4771449Stomee char *c = addr; 4781449Stomee 4791449Stomee jobject jobj = NULL; /* return value */ 4801449Stomee 4811449Stomee if (nbytes == 0) { 4821449Stomee return ((*jenv)->NewStringUTF(jenv, "")); 4831449Stomee } 4841449Stomee 4851449Stomee for (i = 0; i < nbytes; i++) { 4861449Stomee /* 4871449Stomee * We define a "printable character" to be one for which 4881449Stomee * isprint(3C) returns non-zero, isspace(3C) returns non-zero, 4891449Stomee * or a character which is either backspace or the bell. 4901449Stomee * Backspace and the bell are regrettably special because 4911449Stomee * they fail the first two tests -- and yet they are entirely 4921449Stomee * printable. These are the only two control characters that 4931449Stomee * have meaning for the terminal and for which isprint(3C) and 4941449Stomee * isspace(3C) return 0. 4951449Stomee */ 4961449Stomee if (isprint(c[i]) || isspace(c[i]) || 4971449Stomee c[i] == '\b' || c[i] == '\a') 4981449Stomee continue; 4991449Stomee 5001449Stomee if (c[i] == '\0' && i > 0) { 5011449Stomee /* 5021449Stomee * This looks like it might be a string. Before we 5031449Stomee * assume that it is indeed a string, check the 5041449Stomee * remainder of the byte range; if it contains 5051449Stomee * additional non-nul characters, we'll assume that 5061449Stomee * it's a binary stream that just happens to look like 5071449Stomee * a string. 5081449Stomee */ 5091449Stomee for (j = i + 1; j < nbytes; j++) { 5101449Stomee if (c[j] != '\0') 5111449Stomee break; 5121449Stomee } 5131449Stomee 5141449Stomee if (j != nbytes) 5151449Stomee break; 5161449Stomee 5171449Stomee /* It's a string */ 5181449Stomee return (dtj_NewStringNative(jenv, (char *)addr)); 5191449Stomee } 5201449Stomee 5211449Stomee break; 5221449Stomee } 5231449Stomee 5241449Stomee if (i == nbytes) { 5251449Stomee /* 5261449Stomee * The byte range is all printable characters, but there is 5271449Stomee * no trailing nul byte. We'll assume that it's a string. 5281449Stomee */ 5291449Stomee char *s = malloc(nbytes + 1); 5301449Stomee if (!s) { 5311449Stomee dtj_throw_out_of_memory(jenv, 5321449Stomee "failed to allocate string value"); 5331449Stomee return (NULL); 5341449Stomee } 5351449Stomee (void) strncpy(s, c, nbytes); 5361449Stomee s[nbytes] = '\0'; 5371449Stomee jobj = dtj_NewStringNative(jenv, s); 5381449Stomee free(s); 5391449Stomee return (jobj); 5401449Stomee } 5411449Stomee 5421449Stomee /* return byte array */ 5431449Stomee jobj = (*jenv)->NewByteArray(jenv, nbytes); 5441449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 5451449Stomee return (NULL); 5461449Stomee } 5471449Stomee (*jenv)->SetByteArrayRegion(jenv, (jbyteArray)jobj, 0, nbytes, 5481449Stomee (const jbyte *)c); 5491449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 5501449Stomee WRAP_EXCEPTION(jenv); 5511449Stomee (*jenv)->DeleteLocalRef(jenv, jobj); 5521449Stomee return (NULL); 5531449Stomee } 5541449Stomee return (jobj); 5551449Stomee } 5561449Stomee 5571449Stomee /* 5581449Stomee * Return NULL if memory could not be allocated (OutOfMemoryError is thrown in 5591449Stomee * that case). 5601449Stomee */ 5611449Stomee static jobject 5621449Stomee dtj_recdata(dtj_java_consumer_t *jc, uint32_t size, caddr_t addr) 5631449Stomee { 5641449Stomee JNIEnv *jenv = jc->dtjj_jenv; 5651449Stomee jobject jobj; 566*2777Stomee jobject jrec; 5671449Stomee 5681449Stomee switch (size) { 5691449Stomee case 1: 570*2777Stomee jobj = (*jenv)->NewObject(jenv, g_int_jc, 571*2777Stomee g_intinit_jm, (int)(*((uint8_t *)addr))); 5721449Stomee break; 5731449Stomee case 2: 574*2777Stomee jobj = (*jenv)->NewObject(jenv, g_int_jc, 5751449Stomee /* LINTED - alignment */ 576*2777Stomee g_intinit_jm, (int)(*((uint16_t *)addr))); 5771449Stomee break; 5781449Stomee case 4: 5791449Stomee jobj = (*jenv)->NewObject(jenv, g_int_jc, 5801449Stomee /* LINTED - alignment */ 5811449Stomee g_intinit_jm, *((int32_t *)addr)); 5821449Stomee break; 5831449Stomee case 8: 5841449Stomee jobj = (*jenv)->NewObject(jenv, g_long_jc, 5851449Stomee /* LINTED - alignment */ 5861449Stomee g_longinit_jm, *((int64_t *)addr)); 5871449Stomee break; 5881449Stomee default: 5891449Stomee jobj = dtj_bytedata(jenv, size, addr); 5901449Stomee break; 5911449Stomee } 5921449Stomee 593*2777Stomee if (!jobj) { 594*2777Stomee return (NULL); /* OutOfMemoryError pending */ 595*2777Stomee } 596*2777Stomee 597*2777Stomee jrec = (*jenv)->NewObject(jenv, g_scalar_jc, 598*2777Stomee g_scalarinit_jm, jobj, size); 599*2777Stomee (*jenv)->DeleteLocalRef(jenv, jobj); 600*2777Stomee 601*2777Stomee return (jrec); 6021449Stomee } 6031449Stomee 6041449Stomee /* 6051449Stomee * This is the record handling function passed to dtrace_work(). It differs 6061449Stomee * from the bufhandler registered with dtrace_handle_buffered() as follows: 6071449Stomee * 6081449Stomee * 1. It does not have access to libdtrace formatted output. 6091449Stomee * 2. It is called once for every D program statement, not for every 6101449Stomee * output-producing D action or aggregation record. A statement may be a 6111449Stomee * variable assignment, having no size and producing no output. 6121449Stomee * 3. It is called for the D exit() action; the bufhandler is not. 6131449Stomee * 4. In response to the printa() action, it is called with a record having an 6141449Stomee * action of type DTRACEACT_PRINTA. The bufhandler never sees that action 6151449Stomee * value. It only sees the output-producing aggregation records. 6161449Stomee * 5. It is called with a NULL record at the end of each probedata. 6171449Stomee */ 6181449Stomee static int 6191449Stomee dtj_chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, 6201449Stomee void *arg) 6211449Stomee { 6221449Stomee dtj_java_consumer_t *jc = arg; 6231449Stomee JNIEnv *jenv = jc->dtjj_jenv; 6241449Stomee 6251449Stomee const dtrace_eprobedesc_t *edesc = data->dtpda_edesc; 6261449Stomee dtrace_actkind_t act; 6271449Stomee int r; 6281449Stomee 6291449Stomee /* 6301449Stomee * Update the record index to that of the current record, or to that of 6311449Stomee * the last record if rec is NULL (signalling end of probe data). 6321449Stomee */ 6331449Stomee if (rec == NULL) { 6341449Stomee r = edesc->dtepd_nrecs; /* end of probe data */ 6351449Stomee } else { 6361449Stomee /* 6371449Stomee * This record handler is called once for the printf() action, 6381449Stomee * but there may be multiple records in the probedata 6391449Stomee * corresponding to the unformatted elements of that printf(). 6401449Stomee * We don't know ahead of time how many probedata records 6411449Stomee * libdtrace will consume to produce output for one printf() 6421449Stomee * action, so we look back at the previous call to dtj_chewrec() 6431449Stomee * to see how many probedata records were consumed. All 6441449Stomee * non-null elements in the range from the previous record index 6451449Stomee * up to and not including the current record index are assumed 6461449Stomee * to be unformatted printf() elements, and will be attached to 6471449Stomee * the PrintfRecord from the previous call. A null element in 6481449Stomee * that range is the result of a D program statement preceding 6491449Stomee * the printf() that is not a D action. These generate 6501449Stomee * probedata records accounted for by the null placeholder, but 6511449Stomee * do not advance the probedata offset and are not part of the 6521449Stomee * subsequent printf(). 6531449Stomee * 6541449Stomee * If rec->dtrd_size == 0, the record represents a D program 6551449Stomee * statement that is not a D action. It has no size and does 6561449Stomee * not advance the offset in the probedata. Handle it normally 6571449Stomee * without special-casing or premature return, since in all 6581449Stomee * cases we look at the previous record later in this function. 6591449Stomee */ 6601449Stomee for (r = jc->dtjj_consumer->dtjc_probedata_rec_i; 6611449Stomee ((r < edesc->dtepd_nrecs) && 6621449Stomee (edesc->dtepd_rec[r].dtrd_offset < rec->dtrd_offset)); 6631449Stomee ++r) { 6641449Stomee } 6651449Stomee } 6661449Stomee 6671449Stomee /* 6681449Stomee * Attach the Java representations of the libdtrace data elements 6691449Stomee * pertaining to the previous call to this record handler to the 6701449Stomee * previous Java Record. (All data elements belonging to the current 6711449Stomee * probedata are added to a single list by the probedata consumer 6721449Stomee * function dtj_chew() before this record consumer function is ever 6731449Stomee * called.) For example, if the previous Record was generated by the 6741449Stomee * printf() action, and dtj_chew() listed 3 records for its 3 6751449Stomee * unformatted elements, those 3 libdtrace records comprise 1 6761449Stomee * PrintfRecord. Note that we cannot know how many data elements apply 6771449Stomee * to the current rec until we find out the data index where the next 6781449Stomee * rec starts. (The knowledge of how many probedata records to consume 6791449Stomee * is private to libdtrace.) 6801449Stomee */ 6811449Stomee if (jc->dtjj_consumer->dtjc_probedata_act == DTRACEACT_PRINTF) { 6821449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 6831449Stomee g_pdataattach_jm, 6841449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i, r - 1); 6851449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 6861449Stomee WRAP_EXCEPTION(jenv); 6871449Stomee return (DTRACE_CONSUME_ABORT); 6881449Stomee } 6891449Stomee } 6901449Stomee 6911449Stomee if (rec == NULL) { 6921449Stomee /* 6931449Stomee * End of probe data. Notify listeners of the new ProbeData 6941449Stomee * instance. 6951449Stomee */ 6961449Stomee if (jc->dtjj_probedata) { 6971449Stomee /* previous probedata */ 6981449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 6991449Stomee g_pdataclear_jm); 7001449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 7011449Stomee WRAP_EXCEPTION(jenv); 7021449Stomee return (DTRACE_CONSUME_ABORT); 7031449Stomee } 7041449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, 7051449Stomee g_pdatanext_jm, jc->dtjj_probedata); 7061449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_probedata); 7071449Stomee jc->dtjj_probedata = NULL; 7081449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 7091449Stomee /* 7101449Stomee * Do not wrap exception thrown from 7111449Stomee * ConsumerListener. 7121449Stomee */ 7131449Stomee return (DTRACE_CONSUME_ABORT); 7141449Stomee } 7151449Stomee } 7161449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_printa_buffer); 7171449Stomee jc->dtjj_printa_buffer = NULL; 7181449Stomee return (DTRACE_CONSUME_NEXT); 7191449Stomee } 7201449Stomee 7211449Stomee act = rec->dtrd_action; 7221449Stomee 7231449Stomee /* Set previous record action and data index to current */ 7241449Stomee jc->dtjj_consumer->dtjc_probedata_act = act; 7251449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i = r; 7261449Stomee 7271449Stomee switch (act) { 7281449Stomee case DTRACEACT_DIFEXPR: 7291449Stomee if (rec->dtrd_size == 0) { 7301449Stomee /* 7311449Stomee * The current record is not a D action, but a program 7321449Stomee * statement such as a variable assignment, not to be 7331449Stomee * confused with the trace() action. 7341449Stomee */ 7351449Stomee break; 7361449Stomee } 7371449Stomee /* 7381449Stomee * Add a Record for the trace() action that references the 7391449Stomee * native probedata element listed at the current index. 7401449Stomee */ 7411449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 7421449Stomee g_pdataadd_trace_jm, 7431449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i); 7441449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 7451449Stomee WRAP_EXCEPTION(jenv); 7461449Stomee return (DTRACE_CONSUME_ABORT); 7471449Stomee } 7481449Stomee break; 7491449Stomee case DTRACEACT_PRINTF: 7501449Stomee /* 7511449Stomee * Just add an empty PrintfRecord for now. We'll attach the 7521449Stomee * unformatted elements in a subsequent call to this function. 7531449Stomee * (We don't know how many there will be.) 7541449Stomee */ 7551449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 7561449Stomee g_pdataadd_printf_jm); 7571449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 7581449Stomee WRAP_EXCEPTION(jenv); 7591449Stomee return (DTRACE_CONSUME_ABORT); 7601449Stomee } 7611449Stomee /* defer formatted string to dtj_bufhandler() */ 7621449Stomee break; 7631449Stomee case DTRACEACT_PRINTA: { 7641449Stomee jobject jbuf = NULL; 7651449Stomee 7661449Stomee dtj_aggwalk_init(jc); 7671449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 7681449Stomee WRAP_EXCEPTION(jenv); 7691449Stomee return (DTRACE_CONSUME_ABORT); 7701449Stomee } 7711449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 7721449Stomee g_pdataadd_printa_jm, 7731449Stomee jc->dtjj_consumer->dtjc_printa_snaptime, 7741449Stomee (rec->dtrd_format != 0)); 7751449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 7761449Stomee WRAP_EXCEPTION(jenv); 7771449Stomee return (DTRACE_CONSUME_ABORT); 7781449Stomee } 7791449Stomee if (jc->dtjj_printa_buffer == NULL) { 7801449Stomee /* 7811449Stomee * Create a StringBuffer to collect the pieces of 7821449Stomee * formatted output into a single String. 7831449Stomee */ 7841449Stomee jbuf = (*jenv)->NewObject(jenv, g_buf_jc, 7851449Stomee g_bufinit_jm); 7861449Stomee if (!jbuf) { 7871449Stomee /* OutOfMemoryError pending */ 7881449Stomee return (DTRACE_CONSUME_ABORT); 7891449Stomee } 7901449Stomee jc->dtjj_printa_buffer = jbuf; 7911449Stomee } 7921449Stomee /* defer aggregation records to dtj_bufhandler() */ 7931449Stomee break; 7941449Stomee } 7951449Stomee case DTRACEACT_EXIT: 7961449Stomee /* 7971449Stomee * Add a Record for the exit() action that references the native 7981449Stomee * probedata element listed at the current index. 7991449Stomee */ 8001449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 8011449Stomee g_pdataadd_exit_jm, 8021449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i); 8031449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 8041449Stomee WRAP_EXCEPTION(jenv); 8051449Stomee return (DTRACE_CONSUME_ABORT); 8061449Stomee } 8071449Stomee return (DTRACE_CONSUME_NEXT); 8081449Stomee } 8091449Stomee 8101449Stomee return (DTRACE_CONSUME_THIS); 8111449Stomee } 8121449Stomee 8131449Stomee /* 8141449Stomee * This is the probe handling function passed to dtrace_work(). It is is called 8151449Stomee * once every time a probe fires. It is the first of all the callbacks for the 8161449Stomee * current probe. It is followed by multiple callbacks to dtj_chewrec(), one 8171449Stomee * for each probedata record. Each call to dtj_chewrec() is followed by zero or 8181449Stomee * more callbacks to the bufhandler, one for each output-producing action or 8191449Stomee * aggregation record. 8201449Stomee */ 8211449Stomee static int 8221449Stomee dtj_chew(const dtrace_probedata_t *data, void *arg) 8231449Stomee { 8241449Stomee dtj_java_consumer_t *jc = arg; 8251449Stomee JNIEnv *jenv = jc->dtjj_jenv; 8261449Stomee 8271449Stomee dtrace_eprobedesc_t *edesc; 8281449Stomee dtrace_probedesc_t *pdesc; 8291449Stomee dtrace_recdesc_t *rec; 8301449Stomee int epid; 8311449Stomee int cpu; 8321449Stomee int nrecs; 8331449Stomee int i; 8341449Stomee 8351449Stomee jobject jpdata = NULL; 8361449Stomee jobject jprobe = NULL; 8371449Stomee jobject jflow = NULL; 8381449Stomee jstring jflowkind = NULL; 8391449Stomee jobject jobj = NULL; 8401449Stomee 8411449Stomee edesc = data->dtpda_edesc; 8421449Stomee epid = (int)edesc->dtepd_epid; 8431449Stomee pdesc = data->dtpda_pdesc; 8441449Stomee cpu = (int)data->dtpda_cpu; 8451449Stomee if ((jprobe = dtj_new_probedesc(jc, pdesc)) == NULL) { 8461449Stomee /* java exception pending */ 8471449Stomee return (DTRACE_CONSUME_ABORT); 8481449Stomee } 8491449Stomee nrecs = edesc->dtepd_nrecs; 8501449Stomee 8511449Stomee if (jc->dtjj_consumer->dtjc_flow) { 8521449Stomee const char *kind; 8531449Stomee switch (data->dtpda_flow) { 8541449Stomee case DTRACEFLOW_ENTRY: 8551449Stomee kind = "ENTRY"; 8561449Stomee break; 8571449Stomee case DTRACEFLOW_RETURN: 8581449Stomee kind = "RETURN"; 8591449Stomee break; 8601449Stomee case DTRACEFLOW_NONE: 8611449Stomee kind = "NONE"; 8621449Stomee break; 8631449Stomee default: 8641449Stomee kind = NULL; 8651449Stomee } 8661449Stomee if (kind != NULL) { 8671449Stomee int depth; 8681449Stomee jflowkind = (*jenv)->NewStringUTF(jenv, kind); 8691449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 8701449Stomee WRAP_EXCEPTION(jenv); 8711449Stomee (*jenv)->DeleteLocalRef(jenv, jprobe); 8721449Stomee return (DTRACE_CONSUME_ABORT); 8731449Stomee } 8741449Stomee /* 8751449Stomee * Use the knowledge that libdtrace indents 2 spaces per 8761449Stomee * level in the call stack to calculate the depth. 8771449Stomee */ 8781449Stomee depth = (data->dtpda_indent / 2); 8791449Stomee jflow = (*jenv)->NewObject(jenv, g_flow_jc, 8801449Stomee g_flowinit_jm, jflowkind, depth); 8811449Stomee (*jenv)->DeleteLocalRef(jenv, jflowkind); 8821449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 8831449Stomee WRAP_EXCEPTION(jenv); 8841449Stomee (*jenv)->DeleteLocalRef(jenv, jprobe); 8851449Stomee return (DTRACE_CONSUME_ABORT); 8861449Stomee } 8871449Stomee } 8881449Stomee } 8891449Stomee 8901449Stomee /* Create ProbeData instance */ 8911449Stomee jpdata = (*jenv)->NewObject(jenv, g_pdata_jc, g_pdatainit_jm, 8921449Stomee epid, cpu, jprobe, jflow, nrecs); 8931449Stomee (*jenv)->DeleteLocalRef(jenv, jprobe); 8941449Stomee (*jenv)->DeleteLocalRef(jenv, jflow); 8951449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 8961449Stomee WRAP_EXCEPTION(jenv); 8971449Stomee return (DTRACE_CONSUME_ABORT); 8981449Stomee } 8991449Stomee 9001449Stomee /* 9011449Stomee * Populate the ProbeData list of Java data elements in advance so we 9021449Stomee * don't need to peek back in the record handler at libdtrace records 9031449Stomee * that have already been consumed. In the Java API, each ProbeData 9041449Stomee * Record is generated by one D action, while in the native libdtrace 9051449Stomee * there may be more than one probedata record (each a single data 9061449Stomee * element) per D action. For example PrintfRecord has multiple 9071449Stomee * unformatted elements, each represented by a native probedata record, 9081449Stomee * but combined by the API into a single PrintfRecord. 9091449Stomee */ 9101449Stomee for (i = 0; i < nrecs; ++i) { 9111449Stomee rec = &edesc->dtepd_rec[i]; 9121449Stomee /* 9131449Stomee * A statement that is not a D action, such as assignment to a 9141449Stomee * variable, has no size. Add a NULL placeholder to the scratch 9151449Stomee * list of Java probedata elements in that case. 9161449Stomee */ 9171449Stomee jobj = NULL; /* initialize object reference to null */ 9181449Stomee if (rec->dtrd_size > 0) { 9191449Stomee if (dtj_is_stack_action(rec->dtrd_action)) { 9201449Stomee jobj = dtj_new_probedata_stack_record(data, 9211449Stomee rec, jc); 922*2777Stomee } else if (dtj_is_symbol_action(rec->dtrd_action)) { 923*2777Stomee jobj = dtj_new_probedata_symbol_record(data, 924*2777Stomee rec, jc); 9251449Stomee } else { 9261449Stomee jobj = dtj_recdata(jc, rec->dtrd_size, 9271449Stomee (data->dtpda_data + rec->dtrd_offset)); 9281449Stomee } 9291449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 9301449Stomee WRAP_EXCEPTION(jenv); 9311449Stomee (*jenv)->DeleteLocalRef(jenv, jpdata); 9321449Stomee return (DTRACE_CONSUME_ABORT); 9331449Stomee } 9341449Stomee } 9351449Stomee 9361449Stomee (*jenv)->CallVoidMethod(jenv, jpdata, g_pdataadd_jm, jobj); 9371449Stomee (*jenv)->DeleteLocalRef(jenv, jobj); 9381449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 9391449Stomee WRAP_EXCEPTION(jenv); 9401449Stomee (*jenv)->DeleteLocalRef(jenv, jpdata); 9411449Stomee return (DTRACE_CONSUME_ABORT); 9421449Stomee } 9431449Stomee } 9441449Stomee 9451449Stomee if (jc->dtjj_probedata != NULL) { 9461449Stomee dtj_throw_illegal_state(jenv, "unfinished probedata"); 9471449Stomee WRAP_EXCEPTION(jenv); 9481449Stomee (*jenv)->DeleteLocalRef(jenv, jpdata); 9491449Stomee return (DTRACE_CONSUME_ABORT); 9501449Stomee } 9511449Stomee jc->dtjj_probedata = jpdata; 9521449Stomee 9531449Stomee /* Initialize per-consumer probedata fields */ 9541449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i = 0; 9551449Stomee jc->dtjj_consumer->dtjc_probedata_act = DTRACEACT_NONE; 9561449Stomee dtj_aggwalk_init(jc); 9571449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 9581449Stomee WRAP_EXCEPTION(jenv); 9591449Stomee return (DTRACE_CONSUME_ABORT); 9601449Stomee } 9611449Stomee 9621449Stomee return (DTRACE_CONSUME_THIS); 9631449Stomee } 9641449Stomee 9651449Stomee /* 9661449Stomee * This is the buffered output handler registered with dtrace_handle_buffered(). 9671449Stomee * It's purpose is to make the output of the libdtrace print routines available 9681449Stomee * to this API, without writing any of it to a file (such as stdout). This is 9691449Stomee * needed for the stack(), ustack(), and jstack() actions to get human-readable 9701449Stomee * stack values, since there is no public function in libdtrace to convert stack 9711449Stomee * values to strings. It is also used to get the formatted output of the D 9721449Stomee * printf() and printa() actions. 9731449Stomee * 9741449Stomee * The bufhandler is called once for each output-producing, non-aggregating D 9751449Stomee * action, such as trace() or printf(), and once for each libdtrace aggregation 9761449Stomee * record (whether in response to the D printa() action, or the Consumer 9771449Stomee * getAggregate() method). In the simple printa() case that takes one 9781449Stomee * aggregation and does not specify a format string, there is one libdtrace 9791449Stomee * record per tuple element plus one for the corresponding value. The complete 9801449Stomee * tuple/value pair becomes a single AggregationRecord exported by the API. 9811449Stomee * When multiple aggregations are passed to printa(), each tuple is associated 9821449Stomee * with a list of values, one from each aggregation. If a printa() format 9831449Stomee * string does not specify placeholders for every aggregation value and tuple 9841449Stomee * member, callbacks for those values and tuple members are omitted (and the 9851449Stomee * data is omitted from the resulting PrintaRecord). 9861449Stomee * 9871449Stomee * Notes to characterize some non-obvious bufhandler behavior: 9881449Stomee * 9891449Stomee * 1. dtj_bufhandler() is never called with bufdata->dtbda_recdesc->dtrd_action 9901449Stomee * DTRACEACT_PRINTA. That action only appears in the probedata consumer 9911449Stomee * functions dtj_chew() and dtj_chewrec() before the bufhandler is called with 9921449Stomee * subsequent aggregation records. 9931449Stomee * 9941449Stomee * 2. If printa() specifies a format string argument, then the bufhandler is 9951449Stomee * called only for those elements of the tuple/value pair that are included in 9961449Stomee * the format string. If a stack() tuple member is omitted from the format 9971449Stomee * string, its human-readable representation will not be available to this API, 9981449Stomee * so the stack frame array is also omitted from the resulting 9991449Stomee * AggregationRecord. The bufhandler is also called once for each string of 10001449Stomee * characters surrounding printa() format string placeholders. For example, 10011449Stomee * " %@d %d stack%k\n" results in the following callbacks: 10021449Stomee * - two spaces 10031449Stomee * - the aggregation value 10041449Stomee * - a single space 10051449Stomee * - the first tuple member (an integer) 10061449Stomee * - " stack" 10071449Stomee * - the second tuple member (a stack) 10081449Stomee * - a newline 10091449Stomee * A NULL record (NULL dtbda_recdesc) distinguishes a callback with interstitial 10101449Stomee * format string characters from a callback with a tuple member or aggregation 10111449Stomee * value (which has a non-NULL recdesc). The contents are also distinguished by 10121449Stomee * the following flags: 10131449Stomee * DTRACE_BUFDATA_AGGKEY 10141449Stomee * DTRACE_BUFDATA_AGGVAL 10151449Stomee * DTRACE_BUFDATA_AGGFORMAT 10161449Stomee * DTRACE_BUFDATA_AGGLAST 10171449Stomee * 10181449Stomee * There is no final callback with the complete formatted string, so that must 10191449Stomee * be concatenated across multiple callbacks to the bufhandler. 10201449Stomee * 10211449Stomee * 3. bufdata->dtbda_probe->dtpda_data may be overwritten by libdtrace print 10221449Stomee * routines. The address is cached in the dtj_chew() function in case it is 10231449Stomee * needed in the bufhandler. 10241449Stomee */ 10251449Stomee static int 10261449Stomee /* ARGSUSED */ 10271449Stomee dtj_bufhandler(const dtrace_bufdata_t *bufdata, void *arg) 10281449Stomee { 10291449Stomee dtj_java_consumer_t *jc; 10301449Stomee JNIEnv *jenv; 10311449Stomee const dtrace_recdesc_t *rec; 10321449Stomee dtrace_actkind_t act = DTRACEACT_NONE; 10331449Stomee const char *s; 10341449Stomee 10351449Stomee jobject jstr = NULL; 10361449Stomee 10371449Stomee /* 10381449Stomee * Get the thread-specific java consumer. The bufhandler needs access 10391449Stomee * to the correct JNI state specific to either the consumer loop or the 10401449Stomee * getAggregate() call (aggregation snapshots can be requested 10411449Stomee * asynchronously while the consumer loop generates PrintaRecords in 10421449Stomee * dtrace_work() for ConsumerListeners). 10431449Stomee */ 10441449Stomee jc = pthread_getspecific(g_dtj_consumer_key); 10451449Stomee jenv = jc->dtjj_jenv; 10461449Stomee 10471449Stomee /* 10481449Stomee * In at least one corner case (printa with multiple aggregations and a 10491449Stomee * format string that does not completely specify the tuple), returning 10501449Stomee * DTRACE_HANDLE_ABORT does not prevent a subsequent callback to this 10511449Stomee * bufhandler. This check ensures that the invalid call is ignored. 10521449Stomee */ 10531449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 10541449Stomee return (DTRACE_HANDLE_ABORT); 10551449Stomee } 10561449Stomee 10571449Stomee if (bufdata->dtbda_aggdata) { 10581449Stomee return (dtj_agghandler(bufdata, jc)); 10591449Stomee } 10601449Stomee 10611449Stomee s = bufdata->dtbda_buffered; 10621449Stomee if (s == NULL) { 10631449Stomee return (DTRACE_HANDLE_OK); 10641449Stomee } 10651449Stomee 10661449Stomee rec = bufdata->dtbda_recdesc; 10671449Stomee if (rec) { 10681449Stomee act = rec->dtrd_action; 10691449Stomee } 10701449Stomee 10711449Stomee switch (act) { 10721449Stomee case DTRACEACT_DIFEXPR: 10731449Stomee /* trace() action */ 10741449Stomee break; 10751449Stomee case DTRACEACT_PRINTF: 10761449Stomee /* 10771449Stomee * Only the formatted string was not available to dtj_chewrec(), 10781449Stomee * so we attach that now. 10791449Stomee */ 10801449Stomee jstr = dtj_NewStringNative(jenv, s); 10811449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 10821449Stomee WRAP_EXCEPTION(jenv); 10831449Stomee return (DTRACE_HANDLE_ABORT); 10841449Stomee } 10851449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 10861449Stomee g_pdataset_formatted_jm, jstr); 10871449Stomee (*jenv)->DeleteLocalRef(jenv, jstr); 10881449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 10891449Stomee WRAP_EXCEPTION(jenv); 10901449Stomee return (DTRACE_HANDLE_ABORT); 10911449Stomee } 10921449Stomee break; 10931449Stomee case DTRACEACT_STACK: 10941449Stomee case DTRACEACT_USTACK: 10951449Stomee case DTRACEACT_JSTACK: 10961449Stomee /* stand-alone stack(), ustack(), or jstack() action */ 10971449Stomee jstr = (*jenv)->NewStringUTF(jenv, s); 10981449Stomee if (!jstr) { 10991449Stomee /* OutOfMemoryError pending */ 11001449Stomee return (DTRACE_HANDLE_ABORT); 11011449Stomee } 11021449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 11031449Stomee g_pdataadd_stack_jm, 11041449Stomee jc->dtjj_consumer->dtjc_probedata_rec_i, jstr); 11051449Stomee (*jenv)->DeleteLocalRef(jenv, jstr); 11061449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 11071449Stomee WRAP_EXCEPTION(jenv); 11081449Stomee return (DTRACE_HANDLE_ABORT); 11091449Stomee } 11101449Stomee break; 1111*2777Stomee case DTRACEACT_USYM: 1112*2777Stomee case DTRACEACT_UADDR: 1113*2777Stomee case DTRACEACT_UMOD: 1114*2777Stomee case DTRACEACT_SYM: 1115*2777Stomee case DTRACEACT_MOD: 1116*2777Stomee /* stand-alone symbol lookup action */ 1117*2777Stomee jstr = (*jenv)->NewStringUTF(jenv, s); 1118*2777Stomee if (!jstr) { 1119*2777Stomee /* OutOfMemoryError pending */ 1120*2777Stomee return (DTRACE_HANDLE_ABORT); 1121*2777Stomee } 1122*2777Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 1123*2777Stomee g_pdataadd_symbol_jm, 1124*2777Stomee jc->dtjj_consumer->dtjc_probedata_rec_i, jstr); 1125*2777Stomee (*jenv)->DeleteLocalRef(jenv, jstr); 1126*2777Stomee if ((*jenv)->ExceptionCheck(jenv)) { 1127*2777Stomee WRAP_EXCEPTION(jenv); 1128*2777Stomee return (DTRACE_HANDLE_ABORT); 1129*2777Stomee } 1130*2777Stomee break; 11311449Stomee default: 11321449Stomee /* 11331449Stomee * The record handler dtj_chewrec() defers nothing else to this 11341449Stomee * bufhandler. 11351449Stomee */ 11361449Stomee break; 11371449Stomee } 11381449Stomee 11391449Stomee return (DTRACE_HANDLE_OK); 11401449Stomee } 11411449Stomee 11421449Stomee static boolean_t 11431449Stomee dtj_is_stack_action(dtrace_actkind_t act) 11441449Stomee { 11451449Stomee boolean_t stack_action; 11461449Stomee switch (act) { 11471449Stomee case DTRACEACT_STACK: 11481449Stomee case DTRACEACT_USTACK: 11491449Stomee case DTRACEACT_JSTACK: 11501449Stomee stack_action = B_TRUE; 11511449Stomee break; 11521449Stomee default: 11531449Stomee stack_action = B_FALSE; 11541449Stomee } 11551449Stomee return (stack_action); 11561449Stomee } 11571449Stomee 1158*2777Stomee static boolean_t 1159*2777Stomee dtj_is_symbol_action(dtrace_actkind_t act) 1160*2777Stomee { 1161*2777Stomee boolean_t symbol_action; 1162*2777Stomee switch (act) { 1163*2777Stomee case DTRACEACT_USYM: 1164*2777Stomee case DTRACEACT_UADDR: 1165*2777Stomee case DTRACEACT_UMOD: 1166*2777Stomee case DTRACEACT_SYM: 1167*2777Stomee case DTRACEACT_MOD: 1168*2777Stomee symbol_action = B_TRUE; 1169*2777Stomee break; 1170*2777Stomee default: 1171*2777Stomee symbol_action = B_FALSE; 1172*2777Stomee } 1173*2777Stomee return (symbol_action); 1174*2777Stomee } 1175*2777Stomee 11761449Stomee /* 11771449Stomee * Called by get_aggregate() to clear only those aggregations specified by the 11781449Stomee * caller. 11791449Stomee */ 11801449Stomee static int 11811449Stomee dtj_clear(const dtrace_aggdata_t *data, void *arg) 11821449Stomee { 11831449Stomee dtj_java_consumer_t *jc = arg; 11841449Stomee jboolean cleared = JNI_FALSE; 11851449Stomee 11861449Stomee jstring jname = NULL; 11871449Stomee 11881449Stomee if (jc->dtjj_aggregate_spec) { 11891449Stomee JNIEnv *jenv = jc->dtjj_jenv; 11901449Stomee 11911449Stomee dtrace_aggdesc_t *aggdesc = data->dtada_desc; 11921449Stomee 11931449Stomee jname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name); 11941449Stomee if (!jname) { 11951449Stomee /* java exception pending */ 11961449Stomee return (DTRACE_AGGWALK_ABORT); 11971449Stomee } 11981449Stomee 11991449Stomee cleared = (*jenv)->CallBooleanMethod(jenv, 12001449Stomee jc->dtjj_aggregate_spec, g_aggspec_cleared_jm, jname); 12011449Stomee (*jenv)->DeleteLocalRef(jenv, jname); 12021449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 12031449Stomee WRAP_EXCEPTION(jenv); 12041449Stomee return (DTRACE_AGGWALK_ABORT); 12051449Stomee } 12061449Stomee } 12071449Stomee 12081449Stomee return (cleared ? DTRACE_AGGWALK_CLEAR : DTRACE_AGGWALK_NEXT); 12091449Stomee } 12101449Stomee 12111449Stomee static int64_t 12121449Stomee dtj_average(caddr_t addr, uint64_t normal) 12131449Stomee { 12141449Stomee /* LINTED - alignment */ 12151449Stomee uint64_t *data = (uint64_t *)addr; 12161449Stomee 12171449Stomee return (data[0] ? 12181449Stomee (long long)(data[1] / normal / data[0]) : 0); 12191449Stomee } 12201449Stomee 12211449Stomee static int64_t 12221449Stomee dtj_avg_total(caddr_t addr, uint64_t normal) 12231449Stomee { 12241449Stomee /* LINTED - alignment */ 12251449Stomee uint64_t *data = (uint64_t *)addr; 12261449Stomee 12271449Stomee return ((long long)(data[1] / normal)); 12281449Stomee } 12291449Stomee 12301449Stomee static int64_t 12311449Stomee dtj_avg_count(caddr_t addr) 12321449Stomee { 12331449Stomee /* LINTED - alignment */ 12341449Stomee uint64_t *data = (uint64_t *)addr; 12351449Stomee 12361449Stomee return ((long long)data[0]); 12371449Stomee } 12381449Stomee 12391449Stomee static jobject 12401449Stomee dtj_new_probedata_stack_record(const dtrace_probedata_t *data, 12411449Stomee const dtrace_recdesc_t *rec, dtj_java_consumer_t *jc) 12421449Stomee { 12431449Stomee caddr_t addr; 12441449Stomee 12451449Stomee /* Get raw stack data */ 12461449Stomee addr = data->dtpda_data + rec->dtrd_offset; 12471449Stomee return (dtj_new_stack_record(addr, rec, jc)); 12481449Stomee } 12491449Stomee 12501449Stomee static jobject 12511449Stomee dtj_new_tuple_stack_record(const dtrace_aggdata_t *data, 12521449Stomee const dtrace_recdesc_t *rec, const char *s, dtj_java_consumer_t *jc) 12531449Stomee { 12541449Stomee caddr_t addr; 12551449Stomee JNIEnv *jenv = jc->dtjj_jenv; 12561449Stomee 12571449Stomee jobjectArray frames = NULL; 12581449Stomee jobject jobj = NULL; /* tuple element */ 12591449Stomee jstring jstr = NULL; 12601449Stomee 12611449Stomee /* Get raw stack data */ 12621449Stomee addr = data->dtada_data + rec->dtrd_offset; 12631449Stomee jobj = dtj_new_stack_record(addr, rec, jc); 12641449Stomee if (!jobj) { 12651449Stomee return (NULL); /* java exception pending */ 12661449Stomee } 12671449Stomee 12681449Stomee jstr = dtj_NewStringNative(jenv, s); 12691449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 12701449Stomee (*jenv)->DeleteLocalRef(jenv, jobj); 12711449Stomee return (NULL); 12721449Stomee } 12731449Stomee frames = (*jenv)->CallStaticObjectMethod(jenv, g_stack_jc, 12741449Stomee g_parsestack_jsm, jstr); 12751449Stomee (*jenv)->DeleteLocalRef(jenv, jstr); 12761449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 12771449Stomee (*jenv)->DeleteLocalRef(jenv, jobj); 12781449Stomee return (NULL); 12791449Stomee } 12801449Stomee dtj_attach_frames(jc, jobj, frames); 12811449Stomee (*jenv)->DeleteLocalRef(jenv, frames); 12821449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 1283*2777Stomee WRAP_EXCEPTION(jenv); 1284*2777Stomee return (NULL); 1285*2777Stomee } 1286*2777Stomee 1287*2777Stomee return (jobj); 1288*2777Stomee } 1289*2777Stomee 1290*2777Stomee static jobject 1291*2777Stomee dtj_new_probedata_symbol_record(const dtrace_probedata_t *data, 1292*2777Stomee const dtrace_recdesc_t *rec, dtj_java_consumer_t *jc) 1293*2777Stomee { 1294*2777Stomee caddr_t addr; 1295*2777Stomee 1296*2777Stomee addr = data->dtpda_data + rec->dtrd_offset; 1297*2777Stomee return (dtj_new_symbol_record(addr, rec, jc)); 1298*2777Stomee } 1299*2777Stomee 1300*2777Stomee static jobject 1301*2777Stomee dtj_new_tuple_symbol_record(const dtrace_aggdata_t *data, 1302*2777Stomee const dtrace_recdesc_t *rec, const char *s, dtj_java_consumer_t *jc) 1303*2777Stomee { 1304*2777Stomee caddr_t addr; 1305*2777Stomee JNIEnv *jenv = jc->dtjj_jenv; 1306*2777Stomee 1307*2777Stomee jobject jobj = NULL; /* tuple element */ 1308*2777Stomee jstring jstr = NULL; /* lookup value */ 1309*2777Stomee jstring tstr = NULL; /* trimmed lookup value */ 1310*2777Stomee 1311*2777Stomee addr = data->dtada_data + rec->dtrd_offset; 1312*2777Stomee jobj = dtj_new_symbol_record(addr, rec, jc); 1313*2777Stomee if (!jobj) { 1314*2777Stomee return (NULL); /* java exception pending */ 1315*2777Stomee } 1316*2777Stomee 1317*2777Stomee /* Get symbol lookup */ 1318*2777Stomee jstr = (*jenv)->NewStringUTF(jenv, s); 1319*2777Stomee if (!jstr) { 1320*2777Stomee /* OutOfMemoryError pending */ 1321*2777Stomee (*jenv)->DeleteLocalRef(jenv, jobj); 1322*2777Stomee return (NULL); 1323*2777Stomee } 1324*2777Stomee /* Trim leading and trailing whitespace */ 1325*2777Stomee tstr = (*jenv)->CallObjectMethod(jenv, jstr, g_trim_jm); 1326*2777Stomee /* trim() returns a new string; don't leak the old one */ 1327*2777Stomee (*jenv)->DeleteLocalRef(jenv, jstr); 1328*2777Stomee jstr = tstr; 1329*2777Stomee tstr = NULL; 1330*2777Stomee 1331*2777Stomee dtj_attach_name(jc, jobj, jstr); 1332*2777Stomee (*jenv)->DeleteLocalRef(jenv, jstr); 1333*2777Stomee if ((*jenv)->ExceptionCheck(jenv)) { 1334*2777Stomee WRAP_EXCEPTION(jenv); 13351449Stomee return (NULL); 13361449Stomee } 13371449Stomee 13381449Stomee return (jobj); 13391449Stomee } 13401449Stomee 13411449Stomee /* Caller must be holding per-consumer lock */ 13421449Stomee static void 13431449Stomee dtj_aggwalk_init(dtj_java_consumer_t *jc) 13441449Stomee { 13451449Stomee jc->dtjj_consumer->dtjc_aggid = -1; 13461449Stomee jc->dtjj_consumer->dtjc_expected = -1; 13471449Stomee if (jc->dtjj_tuple != NULL) { 13481449Stomee /* assert without crashing */ 13491449Stomee dtj_throw_illegal_state(jc->dtjj_jenv, 13501449Stomee "stale aggregation tuple"); 13511449Stomee } 13521449Stomee } 13531449Stomee 13541449Stomee static jobject 1355*2777Stomee dtj_new_stack_record(const caddr_t addr, const dtrace_recdesc_t *rec, 13561449Stomee dtj_java_consumer_t *jc) 13571449Stomee { 13581449Stomee JNIEnv *jenv = jc->dtjj_jenv; 13591449Stomee 13601449Stomee dtrace_actkind_t act; 13611449Stomee uint64_t *pc; 13621449Stomee pid_t pid = -1; 13631449Stomee int size; /* size of raw bytes not including trailing zeros */ 13641449Stomee int i; /* index of last non-zero byte */ 13651449Stomee 13661449Stomee jbyteArray raw = NULL; 13671449Stomee jobject stack = NULL; /* return value */ 13681449Stomee 13691449Stomee /* trim trailing zeros */ 13701449Stomee for (i = rec->dtrd_size - 1; (i >= 0) && !addr[i]; --i) { 13711449Stomee } 13721449Stomee size = (i + 1); 13731449Stomee raw = (*jenv)->NewByteArray(jenv, size); 13741449Stomee if (!raw) { 13751449Stomee return (NULL); /* OutOfMemoryError pending */ 13761449Stomee } 13771449Stomee (*jenv)->SetByteArrayRegion(jenv, raw, 0, size, 13781449Stomee (const jbyte *)addr); 13791449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 13801449Stomee WRAP_EXCEPTION(jenv); 13811449Stomee (*jenv)->DeleteLocalRef(jenv, raw); 13821449Stomee return (NULL); 13831449Stomee } 13841449Stomee 13851449Stomee /* Create StackValueRecord instance from raw stack data */ 13861449Stomee act = rec->dtrd_action; 13871449Stomee switch (act) { 13881449Stomee case DTRACEACT_STACK: 13891449Stomee stack = (*jenv)->NewObject(jenv, g_stack_jc, 13901449Stomee g_stackinit_jm, raw); 13911449Stomee break; 13921449Stomee case DTRACEACT_USTACK: 13931449Stomee case DTRACEACT_JSTACK: 13941449Stomee /* Get pid of user process */ 13951449Stomee pc = (uint64_t *)(uintptr_t)addr; 13961449Stomee pid = (pid_t)*pc; 13971449Stomee stack = (*jenv)->NewObject(jenv, g_ustack_jc, 13981449Stomee g_ustackinit_jm, pid, raw); 13991449Stomee break; 14001449Stomee default: 14011449Stomee dtj_throw_illegal_argument(jenv, 14021449Stomee "Expected stack action, got %d\n", act); 14031449Stomee } 14041449Stomee (*jenv)->DeleteLocalRef(jenv, raw); 14051449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 14061449Stomee WRAP_EXCEPTION(jenv); 14071449Stomee return (NULL); 14081449Stomee } 14091449Stomee return (stack); 14101449Stomee } 14111449Stomee 1412*2777Stomee static jobject 1413*2777Stomee dtj_new_symbol_record(const caddr_t addr, const dtrace_recdesc_t *rec, 1414*2777Stomee dtj_java_consumer_t *jc) 1415*2777Stomee { 1416*2777Stomee JNIEnv *jenv = jc->dtjj_jenv; 1417*2777Stomee 1418*2777Stomee dtrace_actkind_t act; 1419*2777Stomee uint64_t *pc; 1420*2777Stomee pid_t pid = -1; 1421*2777Stomee 1422*2777Stomee jobject symbol = NULL; /* return value */ 1423*2777Stomee 1424*2777Stomee act = rec->dtrd_action; 1425*2777Stomee switch (act) { 1426*2777Stomee case DTRACEACT_SYM: 1427*2777Stomee case DTRACEACT_MOD: 1428*2777Stomee /* LINTED - alignment */ 1429*2777Stomee pc = (uint64_t *)addr; 1430*2777Stomee symbol = (*jenv)->NewObject(jenv, g_symbol_jc, 1431*2777Stomee g_symbolinit_jm, *pc); 1432*2777Stomee break; 1433*2777Stomee case DTRACEACT_USYM: 1434*2777Stomee case DTRACEACT_UADDR: 1435*2777Stomee case DTRACEACT_UMOD: 1436*2777Stomee /* Get pid of user process */ 1437*2777Stomee pc = (uint64_t *)(uintptr_t)addr; 1438*2777Stomee pid = (pid_t)*pc; 1439*2777Stomee ++pc; 1440*2777Stomee symbol = (*jenv)->NewObject(jenv, g_usymbol_jc, 1441*2777Stomee g_usymbolinit_jm, pid, *pc); 1442*2777Stomee break; 1443*2777Stomee default: 1444*2777Stomee dtj_throw_illegal_argument(jenv, 1445*2777Stomee "Expected stack action, got %d\n", act); 1446*2777Stomee } 1447*2777Stomee if ((*jenv)->ExceptionCheck(jenv)) { 1448*2777Stomee WRAP_EXCEPTION(jenv); 1449*2777Stomee return (NULL); 1450*2777Stomee } 1451*2777Stomee return (symbol); 1452*2777Stomee } 1453*2777Stomee 14541449Stomee /* 14551449Stomee * Return NULL if java exception pending, otherwise return Distribution value. 14561449Stomee */ 14571449Stomee static jobject 14581449Stomee dtj_new_distribution(const dtrace_aggdata_t *data, const dtrace_recdesc_t *rec, 14591449Stomee dtj_java_consumer_t *jc) 14601449Stomee { 14611449Stomee JNIEnv *jenv = jc->dtjj_jenv; 14621449Stomee 14631449Stomee jlongArray jbuckets = NULL; 14641449Stomee jobject jdist = NULL; /* return value */ 14651449Stomee 14661449Stomee dtrace_actkind_t act = rec->dtrd_action; 14671449Stomee /* LINTED - alignment */ 14681449Stomee int64_t *aggbuckets = (int64_t *) 14691449Stomee (data->dtada_data + rec->dtrd_offset); 14701449Stomee size_t size = rec->dtrd_size; 14711449Stomee int64_t value; 14721449Stomee uint64_t normal = data->dtada_normal; 14731449Stomee int64_t base, step; 14741449Stomee int levels; 14751449Stomee int n; /* number of buckets */ 14761449Stomee 14771449Stomee /* distribution */ 14781449Stomee if (act == DTRACEAGG_LQUANTIZE) { 14791449Stomee /* first "bucket" used for range and step */ 14801449Stomee value = *aggbuckets++; 14811449Stomee base = DTRACE_LQUANTIZE_BASE(value); 14821449Stomee step = DTRACE_LQUANTIZE_STEP(value); 14831449Stomee levels = DTRACE_LQUANTIZE_LEVELS(value); 14841449Stomee size -= sizeof (int64_t); /* exclude non-bucket */ 14851449Stomee /* 14861449Stomee * Add one for the base bucket and one for the bucket of values 14871449Stomee * less than the base. 14881449Stomee */ 14891449Stomee n = levels + 2; 14901449Stomee } else { 14911449Stomee n = DTRACE_QUANTIZE_NBUCKETS; 14921449Stomee levels = n - 1; /* levels excludes base */ 14931449Stomee } 14941449Stomee if (size != (n * sizeof (uint64_t)) || n < 1) { 14951449Stomee dtj_throw_illegal_state(jenv, 14961449Stomee "size mismatch: record %d, buckets %d", size, 14971449Stomee (n * sizeof (uint64_t))); 14981449Stomee WRAP_EXCEPTION(jenv); 14991449Stomee return (NULL); 15001449Stomee } 15011449Stomee 15021449Stomee jbuckets = (*jenv)->NewLongArray(jenv, n); 15031449Stomee if (!jbuckets) { 15041449Stomee return (NULL); /* exception pending */ 15051449Stomee } 15061449Stomee if (n > 0) { 15071449Stomee (*jenv)->SetLongArrayRegion(jenv, jbuckets, 0, n, aggbuckets); 15081449Stomee /* check for ArrayIndexOutOfBounds */ 15091449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 15101449Stomee WRAP_EXCEPTION(jenv); 15111449Stomee (*jenv)->DeleteLocalRef(jenv, jbuckets); 15121449Stomee return (NULL); 15131449Stomee } 15141449Stomee } 15151449Stomee 15161449Stomee if (act == DTRACEAGG_LQUANTIZE) { 15171449Stomee /* Must pass 64-bit base and step or constructor gets junk. */ 15181449Stomee jdist = (*jenv)->NewObject(jenv, g_ldist_jc, g_ldistinit_jm, 15191449Stomee base, step, jbuckets); 15201449Stomee } else { 15211449Stomee jdist = (*jenv)->NewObject(jenv, g_dist_jc, g_distinit_jm, 15221449Stomee jbuckets); 15231449Stomee } 15241449Stomee 15251449Stomee (*jenv)->DeleteLocalRef(jenv, jbuckets); 15261449Stomee if (!jdist) { 15271449Stomee return (NULL); /* exception pending */ 15281449Stomee } 15291449Stomee 15301449Stomee if (normal != 1) { 15311449Stomee (*jenv)->CallVoidMethod(jenv, jdist, g_dist_normal_jm, normal); 15321449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 15331449Stomee WRAP_EXCEPTION(jenv); 15341449Stomee (*jenv)->DeleteLocalRef(jenv, jdist); 15351449Stomee return (NULL); 15361449Stomee } 15371449Stomee } 15381449Stomee return (jdist); 15391449Stomee } 15401449Stomee 15411449Stomee static void 15421449Stomee dtj_attach_frames(dtj_java_consumer_t *jc, jobject stack, 15431449Stomee jobjectArray frames) 15441449Stomee { 15451449Stomee JNIEnv *jenv = jc->dtjj_jenv; 15461449Stomee 15471449Stomee if ((*jenv)->IsInstanceOf(jenv, stack, g_stack_jc)) { 15481449Stomee (*jenv)->CallVoidMethod(jenv, stack, g_stackset_frames_jm, 15491449Stomee frames); 15501449Stomee } else if ((*jenv)->IsInstanceOf(jenv, stack, g_ustack_jc)) { 15511449Stomee (*jenv)->CallVoidMethod(jenv, stack, g_ustackset_frames_jm, 15521449Stomee frames); 15531449Stomee } 15541449Stomee } 15551449Stomee 1556*2777Stomee static void 1557*2777Stomee dtj_attach_name(dtj_java_consumer_t *jc, jobject symbol, jstring s) 1558*2777Stomee { 1559*2777Stomee JNIEnv *jenv = jc->dtjj_jenv; 1560*2777Stomee 1561*2777Stomee if ((*jenv)->IsInstanceOf(jenv, symbol, g_symbol_jc)) { 1562*2777Stomee (*jenv)->CallVoidMethod(jenv, symbol, g_symbolset_name_jm, s); 1563*2777Stomee } else if ((*jenv)->IsInstanceOf(jenv, symbol, g_usymbol_jc)) { 1564*2777Stomee (*jenv)->CallVoidMethod(jenv, symbol, g_usymbolset_name_jm, s); 1565*2777Stomee } 1566*2777Stomee } 1567*2777Stomee 15681449Stomee /* 15691449Stomee * Note: It is not valid to look outside the current libdtrace record in the 15701449Stomee * given aggdata (except to get the aggregation ID from the first record). 15711449Stomee * 15721449Stomee * Return DTRACE_HANDLE_ABORT if java exception pending, otherwise 15731449Stomee * DTRACE_HANDLE_OK. 15741449Stomee */ 15751449Stomee static int 15761449Stomee dtj_agghandler(const dtrace_bufdata_t *bufdata, dtj_java_consumer_t *jc) 15771449Stomee { 15781449Stomee JNIEnv *jenv = jc->dtjj_jenv; 15791449Stomee 15801449Stomee const dtrace_aggdata_t *aggdata = bufdata->dtbda_aggdata; 15811449Stomee const dtrace_aggdesc_t *aggdesc; 15821449Stomee const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc; 15831449Stomee const char *s = bufdata->dtbda_buffered; 15841449Stomee dtrace_actkind_t act = DTRACEACT_NONE; 15851449Stomee int64_t aggid; 15861449Stomee 15871449Stomee jobject jobj = NULL; 15881449Stomee 15891449Stomee if (aggdata == NULL) { 15901449Stomee /* Assert without crashing */ 15911449Stomee dtj_throw_illegal_state(jenv, "null aggdata"); 15921449Stomee WRAP_EXCEPTION(jenv); 15931449Stomee return (DTRACE_HANDLE_ABORT); 15941449Stomee } 15951449Stomee aggdesc = aggdata->dtada_desc; 15961449Stomee 15971449Stomee /* 15981449Stomee * Get the aggregation ID from the first record. 15991449Stomee */ 16001449Stomee /* LINTED - alignment */ 16011449Stomee aggid = *((int64_t *)(aggdata->dtada_data + 16021449Stomee aggdesc->dtagd_rec[0].dtrd_offset)); 16031449Stomee if (aggid < 0) { 16041449Stomee /* Assert without crashing */ 16051449Stomee dtj_throw_illegal_argument(jenv, "negative aggregation ID"); 16061449Stomee WRAP_EXCEPTION(jenv); 16071449Stomee return (DTRACE_HANDLE_ABORT); 16081449Stomee } 16091449Stomee 16101449Stomee if (jc->dtjj_consumer->dtjc_printa_snaptime) { 16111449Stomee /* Append buffered output if this is a printa() callback. */ 16121449Stomee jstring jstr = dtj_NewStringNative(jenv, s); 16131449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 16141449Stomee WRAP_EXCEPTION(jenv); 16151449Stomee return (DTRACE_HANDLE_ABORT); 16161449Stomee } 16171449Stomee /* 16181449Stomee * StringBuffer append() returns a reference to the 16191449Stomee * StringBuffer; must not leak the returned reference. 16201449Stomee */ 16211449Stomee jobj = (*jenv)->CallObjectMethod(jenv, 16221449Stomee jc->dtjj_printa_buffer, g_buf_append_str_jm, jstr); 16231449Stomee (*jenv)->DeleteLocalRef(jenv, jstr); 16241449Stomee (*jenv)->DeleteLocalRef(jenv, jobj); 16251449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 16261449Stomee WRAP_EXCEPTION(jenv); 16271449Stomee return (DTRACE_HANDLE_ABORT); 16281449Stomee } 16291449Stomee } else { 16301449Stomee /* 16311449Stomee * Test whether to include the aggregation if this is a 16321449Stomee * getAggregate() callback. Optimization: perform the inclusion 16331449Stomee * test only when the aggregation has changed. 16341449Stomee */ 16351449Stomee if (aggid != jc->dtjj_consumer->dtjc_aggid) { 16361449Stomee jc->dtjj_consumer->dtjc_included = 16371449Stomee dtj_is_included(aggdata, jc); 16381449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 16391449Stomee WRAP_EXCEPTION(jenv); 16401449Stomee return (DTRACE_HANDLE_ABORT); 16411449Stomee } 16421449Stomee } 16431449Stomee if (!jc->dtjj_consumer->dtjc_included) { 16441449Stomee return (DTRACE_HANDLE_OK); 16451449Stomee } 16461449Stomee } 16471449Stomee jc->dtjj_consumer->dtjc_aggid = aggid; 16481449Stomee 16491449Stomee /* 16501449Stomee * Determine the expected number of tuple members. While it is not 16511449Stomee * technically valid to look outside the current record in the current 16521449Stomee * aggdata, this implementation does so without a known failure case. 16531449Stomee * Any method relying only on the current callback record makes riskier 16541449Stomee * assumptions and still does not cover every corner case (for example, 16551449Stomee * counting the records from index 1 up to and not including the index 16561449Stomee * of the current DTRACE_BUFDATA_AGGVAL record, which fails when a 16571449Stomee * format string specifies the value ahead of one or more tuple 16581449Stomee * elements). Knowing that the calculation of the expected tuple size 16591449Stomee * is technically invalid (because it looks outside the current record), 16601449Stomee * we make the calculation at the earliest opportunity, before anything 16611449Stomee * might happen to invalidate any part of the aggdata. It ought to be 16621449Stomee * safe in any case: dtrd_action and dtrd_size do not appear ever to be 16631449Stomee * overwritten, and dtrd_offset is not used outside the current record. 16641449Stomee * 16651449Stomee * It is possible (if the assumptions here ever prove untrue) that the 16661449Stomee * libdtrace buffered output handler may need to be enhanced to provide 16671449Stomee * the expected number of tuple members. 16681449Stomee */ 16691449Stomee if (jc->dtjj_consumer->dtjc_expected < 0) { 16701449Stomee int r; 16711449Stomee for (r = 1; r < aggdesc->dtagd_nrecs; ++r) { 16721449Stomee act = aggdesc->dtagd_rec[r].dtrd_action; 16731449Stomee if (DTRACEACT_ISAGG(act) || 16741449Stomee aggdesc->dtagd_rec[r].dtrd_size == 0) { 16751449Stomee break; 16761449Stomee } 16771449Stomee } 16781449Stomee jc->dtjj_consumer->dtjc_expected = r - 1; 16791449Stomee } 16801449Stomee 16811449Stomee if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGKEY) { 16821449Stomee /* record value is a tuple member */ 16831449Stomee 16841449Stomee if (jc->dtjj_tuple == NULL) { 16851449Stomee jc->dtjj_tuple = (*jenv)->NewObject(jenv, 16861449Stomee g_tuple_jc, g_tupleinit_jm); 16871449Stomee if (!jc->dtjj_tuple) { 16881449Stomee /* java exception pending */ 16891449Stomee return (DTRACE_HANDLE_ABORT); 16901449Stomee } 16911449Stomee } 16921449Stomee 16931449Stomee act = rec->dtrd_action; 16941449Stomee 16951449Stomee switch (act) { 16961449Stomee case DTRACEACT_STACK: 16971449Stomee case DTRACEACT_USTACK: 16981449Stomee case DTRACEACT_JSTACK: 16991449Stomee jobj = dtj_new_tuple_stack_record(aggdata, rec, s, jc); 17001449Stomee break; 1701*2777Stomee case DTRACEACT_USYM: 1702*2777Stomee case DTRACEACT_UADDR: 1703*2777Stomee case DTRACEACT_UMOD: 1704*2777Stomee case DTRACEACT_SYM: 1705*2777Stomee case DTRACEACT_MOD: 1706*2777Stomee jobj = dtj_new_tuple_symbol_record(aggdata, rec, s, jc); 1707*2777Stomee break; 17081449Stomee default: 17091449Stomee jobj = dtj_recdata(jc, rec->dtrd_size, 17101449Stomee (aggdata->dtada_data + rec->dtrd_offset)); 17111449Stomee } 17121449Stomee 17131449Stomee if (!jobj) { 17141449Stomee /* java exception pending */ 17151449Stomee return (DTRACE_HANDLE_ABORT); 17161449Stomee } 17171449Stomee 17181449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_tuple, 17191449Stomee g_tupleadd_jm, jobj); 17201449Stomee (*jenv)->DeleteLocalRef(jenv, jobj); 17211449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 17221449Stomee WRAP_EXCEPTION(jenv); 17231449Stomee return (DTRACE_HANDLE_ABORT); 17241449Stomee } 17251449Stomee } else if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGVAL) { 17261449Stomee /* 17271449Stomee * Record value is that of an aggregating action. The printa() 17281449Stomee * format string may place the tuple ahead of the aggregation 17291449Stomee * value(s), so we can't be sure we have the tuple until we get 17301449Stomee * the AGGLAST flag indicating the last callback associated with 17311449Stomee * the current tuple. Save the aggregation value or values 17321449Stomee * (multiple values if more than one aggregation is passed to 17331449Stomee * printa()) until then. 17341449Stomee */ 17351449Stomee dtj_aggval_t *aggval; 17361449Stomee 17371449Stomee jstring jvalue = NULL; 17381449Stomee 17391449Stomee jvalue = dtj_new_aggval(jc, aggdata, rec); 17401449Stomee if (!jvalue) { 17411449Stomee /* java exception pending */ 17421449Stomee WRAP_EXCEPTION(jenv); 17431449Stomee return (DTRACE_HANDLE_ABORT); 17441449Stomee } 17451449Stomee aggval = dtj_aggval_create(jenv, jvalue, aggdesc->dtagd_name, 17461449Stomee aggid); 17471449Stomee if (!aggval) { 17481449Stomee /* OutOfMemoryError pending */ 17491449Stomee (*jenv)->DeleteLocalRef(jenv, jvalue); 17501449Stomee return (DTRACE_HANDLE_ABORT); 17511449Stomee } 17521449Stomee if (!dtj_list_add(jc->dtjj_aggval_list, aggval)) { 17531449Stomee /* deletes jvalue reference */ 17541449Stomee dtj_aggval_destroy(aggval, jenv); 17551449Stomee dtj_throw_out_of_memory(jenv, "Failed to add aggval"); 17561449Stomee return (DTRACE_HANDLE_ABORT); 17571449Stomee } 17581449Stomee } 17591449Stomee 17601449Stomee if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGLAST) { 17611449Stomee /* No more values associated with the current tuple. */ 17621449Stomee 17631449Stomee dtj_aggval_t *aggval; 17641449Stomee uu_list_walk_t *itr; 17651449Stomee int tuple_member_count; 17661449Stomee 17671449Stomee jobject jrec = NULL; 17681449Stomee jstring jname = NULL; 17691449Stomee 17701449Stomee if (jc->dtjj_consumer->dtjc_expected == 0) { 17711449Stomee /* 17721449Stomee * singleton aggregation declared in D with no square 17731449Stomee * brackets 17741449Stomee */ 17751449Stomee jc->dtjj_tuple = (*jenv)->GetStaticObjectField(jenv, 17761449Stomee g_tuple_jc, g_tuple_EMPTY_jsf); 17771449Stomee if (jc->dtjj_tuple == NULL) { 17781449Stomee dtj_throw_out_of_memory(jenv, 17791449Stomee "Failed to reference Tuple.EMPTY"); 17801449Stomee return (DTRACE_HANDLE_ABORT); 17811449Stomee } 17821449Stomee } 17831449Stomee 17841449Stomee if (jc->dtjj_tuple == NULL) { 17851449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 17861449Stomee g_pdatainvalidate_printa_jm); 17871449Stomee } 17881449Stomee 17891449Stomee tuple_member_count = (*jenv)->CallIntMethod(jenv, 17901449Stomee jc->dtjj_tuple, g_tuplesize_jm); 17911449Stomee if (tuple_member_count < 17921449Stomee jc->dtjj_consumer->dtjc_expected) { 17931449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 17941449Stomee g_pdatainvalidate_printa_jm); 17951449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple); 17961449Stomee jc->dtjj_tuple = NULL; 17971449Stomee } 17981449Stomee 17991449Stomee if (jc->dtjj_tuple == NULL) { 18001449Stomee goto printa_output; 18011449Stomee } 18021449Stomee 18031449Stomee itr = uu_list_walk_start(jc->dtjj_aggval_list, 0); 18041449Stomee while ((aggval = uu_list_walk_next(itr)) != NULL) { 18051449Stomee /* 18061449Stomee * new AggregationRecord: Combine the aggregation value 18071449Stomee * with the saved tuple and add it to the current 18081449Stomee * Aggregate or PrintaRecord. 18091449Stomee */ 18101449Stomee jrec = (*jenv)->NewObject(jenv, g_aggrec_jc, 18111449Stomee g_aggrecinit_jm, jc->dtjj_tuple, 18121449Stomee aggval->dtja_value); 18131449Stomee (*jenv)->DeleteLocalRef(jenv, aggval->dtja_value); 18141449Stomee aggval->dtja_value = NULL; 18151449Stomee if (!jrec) { 18161449Stomee /* java exception pending */ 18171449Stomee WRAP_EXCEPTION(jenv); 18181449Stomee return (DTRACE_HANDLE_ABORT); 18191449Stomee } 18201449Stomee 18211449Stomee /* aggregation name */ 18221449Stomee jname = (*jenv)->NewStringUTF(jenv, 18231449Stomee aggval->dtja_aggname); 18241449Stomee if (!jname) { 18251449Stomee /* OutOfMemoryError pending */ 18261449Stomee (*jenv)->DeleteLocalRef(jenv, jrec); 18271449Stomee return (DTRACE_HANDLE_ABORT); 18281449Stomee } 18291449Stomee 18301449Stomee /* 18311449Stomee * If the printa() format string specifies the value of 18321449Stomee * the aggregating action multiple times, PrintaRecord 18331449Stomee * ignores the attempt to add the duplicate record. 18341449Stomee */ 18351449Stomee if (jc->dtjj_consumer->dtjc_printa_snaptime) { 18361449Stomee /* add to PrintaRecord */ 18371449Stomee (*jenv)->CallVoidMethod(jenv, 18381449Stomee jc->dtjj_probedata, 18391449Stomee g_pdataadd_aggrec_jm, 18401449Stomee jname, aggval->dtja_aggid, jrec); 18411449Stomee } else { 18421449Stomee /* add to Aggregate */ 18431449Stomee (*jenv)->CallVoidMethod(jenv, 18441449Stomee jc->dtjj_aggregate, g_aggaddrec_jm, 18451449Stomee jname, aggval->dtja_aggid, jrec); 18461449Stomee } 18471449Stomee 18481449Stomee (*jenv)->DeleteLocalRef(jenv, jrec); 18491449Stomee (*jenv)->DeleteLocalRef(jenv, jname); 18501449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 18511449Stomee WRAP_EXCEPTION(jenv); 18521449Stomee return (DTRACE_HANDLE_ABORT); 18531449Stomee } 18541449Stomee } 18551449Stomee uu_list_walk_end(itr); 18561449Stomee dtj_list_clear(jc->dtjj_aggval_list, dtj_aggval_destroy, 18571449Stomee jenv); 18581449Stomee 18591449Stomee printa_output: 18601449Stomee if (jc->dtjj_consumer->dtjc_printa_snaptime) { 18611449Stomee /* 18621449Stomee * Get the formatted string associated with the current 18631449Stomee * tuple if this is a printa() callback. 18641449Stomee */ 18651449Stomee jstring jstr = (*jenv)->CallObjectMethod(jenv, 18661449Stomee jc->dtjj_printa_buffer, g_tostring_jm); 18671449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 18681449Stomee WRAP_EXCEPTION(jenv); 18691449Stomee return (DTRACE_HANDLE_ABORT); 18701449Stomee } 18711449Stomee /* 18721449Stomee * Clear the StringBuffer: this does not throw 18731449Stomee * exceptions. Reuse the StringBuffer until the end of 18741449Stomee * the current probedata then dispose of it. 18751449Stomee */ 18761449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_printa_buffer, 18771449Stomee g_bufsetlen_jm, 0); 18781449Stomee /* Add formatted string to PrintaRecord */ 18791449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, 18801449Stomee g_pdataadd_printa_str_jm, jc->dtjj_tuple, jstr); 18811449Stomee (*jenv)->DeleteLocalRef(jenv, jstr); 18821449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 18831449Stomee WRAP_EXCEPTION(jenv); 18841449Stomee return (DTRACE_HANDLE_ABORT); 18851449Stomee } 18861449Stomee } 18871449Stomee 18881449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple); 18891449Stomee jc->dtjj_tuple = NULL; 18901449Stomee jc->dtjj_consumer->dtjc_expected = -1; 18911449Stomee } 18921449Stomee 18931449Stomee return (DTRACE_HANDLE_OK); 18941449Stomee } 18951449Stomee 18961449Stomee /* 18971449Stomee * Return B_TRUE if the aggregation is included, B_FALSE otherwise. Only in the 18981449Stomee * latter case might there be an exception pending. 18991449Stomee */ 19001449Stomee static boolean_t 19011449Stomee dtj_is_included(const dtrace_aggdata_t *data, dtj_java_consumer_t *jc) 19021449Stomee { 19031449Stomee JNIEnv *jenv = jc->dtjj_jenv; 19041449Stomee 19051449Stomee if (jc->dtjj_aggregate_spec) { 19061449Stomee jboolean included; 19071449Stomee jstring aggname = NULL; 19081449Stomee 19091449Stomee const dtrace_aggdesc_t *aggdesc = data->dtada_desc; 19101449Stomee aggname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name); 19111449Stomee if (!aggname) { 19121449Stomee /* java exception pending */ 19131449Stomee return (B_FALSE); 19141449Stomee } 19151449Stomee 19161449Stomee included = (*jenv)->CallBooleanMethod(jenv, 19171449Stomee jc->dtjj_aggregate_spec, g_aggspec_included_jm, 19181449Stomee aggname); 19191449Stomee (*jenv)->DeleteLocalRef(jenv, aggname); 19201449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 19211449Stomee WRAP_EXCEPTION(jenv); 19221449Stomee return (B_FALSE); 19231449Stomee } 19241449Stomee 19251449Stomee return (included); 19261449Stomee } 19271449Stomee 19281449Stomee return (B_TRUE); 19291449Stomee } 19301449Stomee 19311449Stomee /* 19321449Stomee * Return NULL if a java exception is pending, otherwise return a new 19331449Stomee * AggregationValue instance. 19341449Stomee */ 19351449Stomee static jobject 19361449Stomee dtj_new_aggval(dtj_java_consumer_t *jc, const dtrace_aggdata_t *data, 19371449Stomee const dtrace_recdesc_t *rec) 19381449Stomee { 19391449Stomee JNIEnv *jenv = jc->dtjj_jenv; 19401449Stomee 19411449Stomee jobject jvalue = NULL; /* return value */ 19421449Stomee 19431449Stomee dtrace_actkind_t act; 19441449Stomee uint64_t normal; 19451449Stomee caddr_t addr; 19461449Stomee int64_t value; 19471449Stomee 19481449Stomee act = rec->dtrd_action; 19491449Stomee normal = data->dtada_normal; 19501449Stomee addr = data->dtada_data + rec->dtrd_offset; 19511449Stomee if (act == DTRACEAGG_AVG) { 19521449Stomee value = dtj_average(addr, normal); 19531449Stomee } else { 19541449Stomee /* LINTED - alignment */ 19551449Stomee value = (*((int64_t *)addr)) / normal; 19561449Stomee } 19571449Stomee 19581449Stomee if (act == DTRACEAGG_QUANTIZE || act == DTRACEAGG_LQUANTIZE) { 19591449Stomee jvalue = dtj_new_distribution(data, rec, jc); 19601449Stomee } else { 19611449Stomee switch (act) { 19621449Stomee case DTRACEAGG_COUNT: 19631449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggcount_jc, 19641449Stomee g_aggcountinit_jm, value); 19651449Stomee break; 19661449Stomee case DTRACEAGG_SUM: 19671449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggsum_jc, 19681449Stomee g_aggsuminit_jm, value); 19691449Stomee break; 19701449Stomee case DTRACEAGG_AVG: 19711449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggavg_jc, 19721449Stomee g_aggavginit_jm, value, dtj_avg_total(addr, 19731449Stomee normal), dtj_avg_count(addr)); 19741449Stomee break; 19751449Stomee case DTRACEAGG_MIN: 19761449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggmin_jc, 19771449Stomee g_aggmininit_jm, value); 19781449Stomee break; 19791449Stomee case DTRACEAGG_MAX: 19801449Stomee jvalue = (*jenv)->NewObject(jenv, g_aggmax_jc, 19811449Stomee g_aggmaxinit_jm, value); 19821449Stomee break; 19831449Stomee default: 19841449Stomee jvalue = NULL; 19851449Stomee dtj_throw_illegal_argument(jenv, 19861449Stomee "unexpected aggregation action: %d", act); 19871449Stomee } 19881449Stomee } 19891449Stomee 19901449Stomee return (jvalue); 19911449Stomee } 19921449Stomee 19931449Stomee /* 19941449Stomee * Stops the given consumer if it is running. Throws DTraceException if 19951449Stomee * dtrace_stop() fails and no other exception is already pending. Clears and 19961449Stomee * rethrows any pending exception in order to grab the global lock safely. 19971449Stomee */ 19981449Stomee void 19991449Stomee dtj_stop(dtj_java_consumer_t *jc) 20001449Stomee { 20011449Stomee JNIEnv *jenv; 20021449Stomee int rc; 20031449Stomee jthrowable e; 20041449Stomee 20051449Stomee switch (jc->dtjj_consumer->dtjc_state) { 20061449Stomee case DTJ_CONSUMER_GO: 20071449Stomee case DTJ_CONSUMER_START: 20081449Stomee break; 20091449Stomee default: 20101449Stomee return; 20111449Stomee } 20121449Stomee 20131449Stomee jenv = jc->dtjj_jenv; 20141449Stomee e = (*jenv)->ExceptionOccurred(jenv); 20151449Stomee if (e) { 20161449Stomee (*jenv)->ExceptionClear(jenv); 20171449Stomee } 20181449Stomee 20191449Stomee (*jenv)->MonitorEnter(jenv, g_caller_jc); 20201449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 20211449Stomee goto rethrow; 20221449Stomee } 20231449Stomee 20241449Stomee rc = dtrace_status(jc->dtjj_consumer->dtjc_dtp); 20251449Stomee if (rc != DTRACE_STATUS_STOPPED) { 20261449Stomee rc = dtrace_stop(jc->dtjj_consumer->dtjc_dtp); 20271449Stomee } 20281449Stomee 20291449Stomee (*jenv)->MonitorExit(jenv, g_caller_jc); 20301449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 20311449Stomee goto rethrow; 20321449Stomee } 20331449Stomee 20341449Stomee if (rc == -1) { 20351449Stomee (*jenv)->MonitorEnter(jenv, g_caller_jc); 20361449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 20371449Stomee goto rethrow; 20381449Stomee } 20391449Stomee /* Do not wrap DTraceException */ 20401449Stomee dtj_throw_dtrace_exception(jc, 20411449Stomee "couldn't stop tracing: %s", 20421449Stomee dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp, 20431449Stomee dtrace_errno(jc->dtjj_consumer->dtjc_dtp))); 20441449Stomee /* safe to call with pending exception */ 20451449Stomee (*jenv)->MonitorExit(jenv, g_caller_jc); 20461449Stomee } else { 20471449Stomee jc->dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP; 20481449Stomee } 20491449Stomee 20501449Stomee rethrow: 20511449Stomee if (e) { 20521449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 20531449Stomee /* 20541449Stomee * Favor earlier pending exception over 20551449Stomee * exception thrown in this function. 20561449Stomee */ 20571449Stomee (*jenv)->ExceptionClear(jenv); 20581449Stomee } 20591449Stomee (*jenv)->Throw(jenv, e); 20601449Stomee (*jenv)->DeleteLocalRef(jenv, e); 20611449Stomee } 20621449Stomee } 20631449Stomee 20641449Stomee /* 20651449Stomee * Return Aggregate instance, or null if java exception pending. 20661449Stomee */ 20671449Stomee jobject 20681449Stomee dtj_get_aggregate(dtj_java_consumer_t *jc) 20691449Stomee { 20701449Stomee JNIEnv *jenv = jc->dtjj_jenv; 20711449Stomee hrtime_t snaptime; 20721449Stomee int rc; 20731449Stomee 20741449Stomee jobject aggregate = NULL; 20751449Stomee 20761449Stomee /* 20771449Stomee * Aggregations must be snapped, walked, and cleared atomically, 20781449Stomee * otherwise clearing loses data accumulated since the most recent snap. 20791449Stomee * This per-consumer lock prevents dtrace_work() from snapping or 20801449Stomee * clearing aggregations while we're in the middle of this atomic 20811449Stomee * operation, so we continue to hold it until done clearing. 20821449Stomee */ 20831449Stomee (*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock); 20841449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 20851449Stomee WRAP_EXCEPTION(jenv); 20861449Stomee return (NULL); 20871449Stomee } 20881449Stomee 20891449Stomee dtj_aggwalk_init(jc); 20901449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 20911449Stomee WRAP_EXCEPTION(jenv); 20921449Stomee /* release per-consumer lock */ 20931449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 20941449Stomee return (NULL); 20951449Stomee } 20961449Stomee 20971449Stomee /* 20981449Stomee * Snap aggregations 20991449Stomee * 21001449Stomee * We need to record the snaptime here for the caller. Leaving it to 21011449Stomee * the caller to record the snaptime before calling getAggregate() may 21021449Stomee * be inaccurate because of the indeterminate delay waiting on the 21031449Stomee * consumer lock before calling dtrace_aggregate_snap(). 21041449Stomee */ 21051449Stomee snaptime = gethrtime(); 21061449Stomee if (dtrace_aggregate_snap(jc->dtjj_consumer->dtjc_dtp) != 0) { 21071449Stomee dtj_error_t e; 21081449Stomee if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) { 21091449Stomee /* Do not wrap DTraceException */ 21101449Stomee dtj_throw_dtrace_exception(jc, e.dtje_message); 21111449Stomee } 21121449Stomee /* release per-consumer lock */ 21131449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 21141449Stomee return (NULL); 21151449Stomee } 21161449Stomee 21171449Stomee /* Create the Java representation of the aggregate snapshot. */ 21181449Stomee aggregate = (*jenv)->NewObject(jenv, g_agg_jc, g_agginit_jm, 21191449Stomee snaptime); 21201449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 21211449Stomee WRAP_EXCEPTION(jenv); 21221449Stomee /* release per-consumer lock */ 21231449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 21241449Stomee return (NULL); 21251449Stomee } 21261449Stomee jc->dtjj_aggregate = aggregate; 21271449Stomee 21281449Stomee /* 21291449Stomee * Walk the aggregate, converting the data into Java Objects. Traverse 21301449Stomee * in order by aggregation ID first and tuple second by using 21311449Stomee * dtrace_aggregate_walk_keysorted (uses varkeycmp). We cannot do the 21321449Stomee * same for aggregations generated by the printa() action, since 21331449Stomee * dtrace_work() traverses aggregation data in the order determined by 21341449Stomee * the various "aggsort" options. Functions used by both the consumer 21351449Stomee * loop and the competing getAggregate() thread must not depend on the 21361449Stomee * ordering of records by tuple key. 21371449Stomee * 21381449Stomee * It is impractical to hold the global lock around 21391449Stomee * dtrace_aggregate_print(), since it may take a long time (e.g. an 21401449Stomee * entire second) if it performs expensive conversions such as that 21411449Stomee * needed for user stack traces. Most libdtrace functions are not 21421449Stomee * guaranteed to be MT-safe, even when each thread has its own dtrace 21431449Stomee * handle; or even if they are safe, there is no guarantee that future 21441449Stomee * changes may not make them unsafe. Fortunately in this case, however, 21451449Stomee * only a per-consumer lock is necessary to avoid conflict with 21461449Stomee * dtrace_work() running in another thread (the consumer loop). 21471449Stomee */ 21481449Stomee rc = dtrace_aggregate_print(jc->dtjj_consumer->dtjc_dtp, NULL, 21491449Stomee dtrace_aggregate_walk_keysorted); 21501449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 21511449Stomee WRAP_EXCEPTION(jenv); 21521449Stomee /* release per-consumer lock */ 21531449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 21541449Stomee return (NULL); 21551449Stomee } 21561449Stomee if (rc != 0) { 21571449Stomee dtj_error_t e; 21581449Stomee if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) { 21591449Stomee /* release per-consumer lock */ 21601449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 21611449Stomee return (NULL); 21621449Stomee } 21631449Stomee 21641449Stomee if (e.dtje_number != EINTR) { 21651449Stomee /* Do not wrap DTraceException */ 21661449Stomee dtj_throw_dtrace_exception(jc, e.dtje_message); 21671449Stomee /* release per-consumer lock */ 21681449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 21691449Stomee return (NULL); 21701449Stomee } 21711449Stomee } 21721449Stomee 21731449Stomee dtj_aggwalk_init(jc); 21741449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 21751449Stomee WRAP_EXCEPTION(jenv); 21761449Stomee /* release per-consumer lock */ 21771449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 21781449Stomee return (NULL); 21791449Stomee } 21801449Stomee 21811449Stomee /* 21821449Stomee * dtrace_aggregate_clear() clears all aggregations, and we need to 21831449Stomee * clear aggregations selectively. It also fails to preserve the 21841449Stomee * lquantize() range and step size; using aggregate_walk() to clear 21851449Stomee * aggregations does not have this problem. 21861449Stomee */ 21871449Stomee rc = dtrace_aggregate_walk(jc->dtjj_consumer->dtjc_dtp, dtj_clear, jc); 21881449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 21891449Stomee WRAP_EXCEPTION(jenv); 21901449Stomee /* release per-consumer lock */ 21911449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 21921449Stomee return (NULL); 21931449Stomee } 21941449Stomee if (rc != 0) { 21951449Stomee dtj_error_t e; 21961449Stomee if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) { 21971449Stomee /* Do not wrap DTraceException */ 21981449Stomee dtj_throw_dtrace_exception(jc, e.dtje_message); 21991449Stomee } 22001449Stomee /* release per-consumer lock */ 22011449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 22021449Stomee return (NULL); 22031449Stomee } 22041449Stomee 22051449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 22061449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 22071449Stomee WRAP_EXCEPTION(jenv); 22081449Stomee return (NULL); 22091449Stomee } 22101449Stomee 22111449Stomee aggregate = jc->dtjj_aggregate; 22121449Stomee jc->dtjj_aggregate = NULL; 22131449Stomee 22141449Stomee return (aggregate); 22151449Stomee } 22161449Stomee 22171449Stomee /* 22181449Stomee * Process any requests, such as the setting of runtime options, enqueued during 22191449Stomee * dtrace_sleep(). A Java exception is pending if this function returns 22201449Stomee * DTJ_ERR. 22211449Stomee */ 22221449Stomee static dtj_status_t 22231449Stomee dtj_process_requests(dtj_java_consumer_t *jc) 22241449Stomee { 22251449Stomee dtj_request_t *r; 22261449Stomee uu_list_t *list = jc->dtjj_consumer->dtjc_request_list; 22271449Stomee pthread_mutex_t *list_lock = &jc->dtjj_consumer-> 22281449Stomee dtjc_request_list_lock; 22291449Stomee const char *opt; 22301449Stomee const char *val; 22311449Stomee 22321449Stomee (void) pthread_mutex_lock(list_lock); 22331449Stomee while (!dtj_list_empty(list)) { 22341449Stomee r = uu_list_first(list); 22351449Stomee uu_list_remove(list, r); 22361449Stomee 22371449Stomee switch (r->dtjr_type) { 22381449Stomee case DTJ_REQUEST_OPTION: 22391449Stomee opt = dtj_string_list_first(r->dtjr_args); 22401449Stomee val = dtj_string_list_last(r->dtjr_args); 22411449Stomee if (dtrace_setopt(jc->dtjj_consumer->dtjc_dtp, opt, 22421449Stomee val) == -1) { 22431449Stomee /* Do not wrap DTraceException */ 22441449Stomee dtj_throw_dtrace_exception(jc, 22451449Stomee "failed to set %s: %s", opt, 22461449Stomee dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp, 22471449Stomee dtrace_errno(jc->dtjj_consumer->dtjc_dtp))); 22481449Stomee dtj_request_destroy(r, NULL); 22491449Stomee (void) pthread_mutex_unlock(list_lock); 22501449Stomee return (DTJ_ERR); 22511449Stomee } 22521449Stomee break; 22531449Stomee } 22541449Stomee dtj_request_destroy(r, NULL); 22551449Stomee } 22561449Stomee (void) pthread_mutex_unlock(list_lock); 22571449Stomee return (DTJ_OK); 22581449Stomee } 22591449Stomee 22601449Stomee /* 22611449Stomee * Return DTJ_OK if the consumer loop is stopped normally by either the exit() 22621449Stomee * action or the Consumer stop() method. Otherwise return DTJ_ERR if the 22631449Stomee * consumer loop terminates abnormally with an exception pending. 22641449Stomee */ 22651449Stomee dtj_status_t 22661449Stomee dtj_consume(dtj_java_consumer_t *jc) 22671449Stomee { 22681449Stomee JNIEnv *jenv = jc->dtjj_jenv; 22691449Stomee dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp; 22701449Stomee boolean_t done = B_FALSE; 22711449Stomee dtj_error_t e; 22721449Stomee 22731449Stomee do { 22741449Stomee if (!jc->dtjj_consumer->dtjc_interrupt) { 22751449Stomee dtrace_sleep(dtp); 22761449Stomee } 22771449Stomee 22781449Stomee if (jc->dtjj_consumer->dtjc_interrupt) { 22791449Stomee done = B_TRUE; 22801449Stomee dtj_stop(jc); 22811449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 22821449Stomee /* 22831449Stomee * Exception left pending by Consumer 22841449Stomee * getAggregate() method. 22851449Stomee */ 22861449Stomee return (DTJ_ERR); 22871449Stomee } 22881449Stomee } else if (jc->dtjj_consumer->dtjc_process_list != NULL) { 22891449Stomee int nprocs = uu_list_numnodes(jc->dtjj_consumer-> 22901449Stomee dtjc_process_list); 22911449Stomee if (jc->dtjj_consumer->dtjc_procs_ended == nprocs) { 22921449Stomee done = B_TRUE; 22931449Stomee dtj_stop(jc); 22941449Stomee } 22951449Stomee } 22961449Stomee 22971449Stomee /* 22981449Stomee * Functions like dtrace_setopt() are not safe to call during 22991449Stomee * dtrace_sleep(). Check the request list every time we wake up 23001449Stomee * from dtrace_sleep(). 23011449Stomee */ 23021449Stomee if (!done) { 23031449Stomee if (dtj_process_requests(jc) != DTJ_OK) { 23041449Stomee /* Do not wrap DTraceException */ 23051449Stomee return (DTJ_ERR); 23061449Stomee } 23071449Stomee } 23081449Stomee 23091449Stomee /* 23101449Stomee * Use the per-consumer lock to avoid conflict with 23111449Stomee * get_aggregate() called from another thread. 23121449Stomee */ 23131449Stomee (*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock); 23141449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 23151449Stomee WRAP_EXCEPTION(jenv); 23161449Stomee return (DTJ_ERR); 23171449Stomee } 23181449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, 23191449Stomee g_interval_began_jm); 23201449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 23211716Stomee /* Don't wrap exception thrown from ConsumerListener */ 23221449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 23231449Stomee return (DTJ_ERR); 23241449Stomee } 23251449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = gethrtime(); 23261449Stomee switch (dtrace_work(dtp, NULL, dtj_chew, dtj_chewrec, jc)) { 23271449Stomee case DTRACE_WORKSTATUS_DONE: 23281449Stomee done = B_TRUE; 23291449Stomee break; 23301449Stomee case DTRACE_WORKSTATUS_OKAY: 23311449Stomee break; 23321449Stomee default: 23331449Stomee /* 23341449Stomee * Check for a pending exception that got us to this 23351449Stomee * error workstatus case. 23361449Stomee */ 23371449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 23381449Stomee /* 23391449Stomee * Ensure valid initial state before releasing 23401449Stomee * the consumer lock 23411449Stomee */ 23421449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0; 23431449Stomee /* Do not wrap DTraceException */ 23441449Stomee /* Release per-consumer lock */ 23451449Stomee (*jenv)->MonitorExit(jenv, 23461449Stomee jc->dtjj_consumer_lock); 23471449Stomee return (DTJ_ERR); 23481449Stomee } 23491449Stomee 23501449Stomee if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) { 23511449Stomee /* java exception pending */ 23521449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0; 23531449Stomee /* Release per-consumer lock */ 23541449Stomee (*jenv)->MonitorExit(jenv, 23551449Stomee jc->dtjj_consumer_lock); 23561449Stomee return (DTJ_ERR); 23571449Stomee } 23581449Stomee 23591449Stomee if (e.dtje_number != EINTR) { 23601449Stomee /* Do not wrap DTraceException */ 23611449Stomee dtj_throw_dtrace_exception(jc, e.dtje_message); 23621449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0; 23631449Stomee /* Release per-consumer lock */ 23641449Stomee (*jenv)->MonitorExit(jenv, 23651449Stomee jc->dtjj_consumer_lock); 23661449Stomee return (DTJ_ERR); 23671449Stomee } 23681449Stomee } 23691449Stomee /* 23701449Stomee * Check for ConsumerException before doing anything else with 23711449Stomee * the JNIEnv. 23721449Stomee */ 23731449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 23741449Stomee /* 23751449Stomee * Do not wrap exception thrown from ConsumerListener. 23761449Stomee */ 23771449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0; 23781449Stomee /* Release per-consumer lock */ 23791449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 23801449Stomee return (DTJ_ERR); 23811449Stomee } 23821449Stomee jc->dtjj_consumer->dtjc_printa_snaptime = 0; 23831449Stomee /* 23841449Stomee * Notify ConsumerListeners the the dtrace_work() interval ended 23851449Stomee * before releasing the lock. 23861449Stomee */ 23871449Stomee (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, 23881449Stomee g_interval_ended_jm); 23891449Stomee (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); 23901449Stomee if ((*jenv)->ExceptionCheck(jenv)) { 23911716Stomee /* Don't wrap exception thrown from ConsumerListener */ 23921449Stomee return (DTJ_ERR); 23931449Stomee } 23941449Stomee 23951449Stomee /* 23961449Stomee * Check for a temporarily cleared exception set by a handler 23971449Stomee * that could not safely leave the exception pending because it 23981449Stomee * could not return an abort signal. Rethrow it now that it's 23991449Stomee * safe to do so (when it's possible to ensure that no JNI calls 24001449Stomee * will be made that are unsafe while an exception is pending). 24011449Stomee */ 24021449Stomee if (jc->dtjj_exception) { 24031449Stomee (*jenv)->Throw(jenv, jc->dtjj_exception); 24041449Stomee (*jenv)->DeleteLocalRef(jenv, jc->dtjj_exception); 24051449Stomee jc->dtjj_exception = NULL; 24061449Stomee return (DTJ_ERR); 24071449Stomee } 24081449Stomee } while (!done); 24091449Stomee 24101449Stomee return (DTJ_OK); 24111449Stomee } 2412