diff options
author | tomee <none@none> | 2006-02-16 12:15:27 -0800 |
---|---|---|
committer | tomee <none@none> | 2006-02-16 12:15:27 -0800 |
commit | fb3fb4f3d76d55b64440afd0af72775dfad3bd1d (patch) | |
tree | befe8fec5ad60327cead19dcdbe6773c3e7d00b5 /usr/src | |
parent | 5e985db5e665b4363a8154fb1870b3895ca33192 (diff) | |
download | illumos-gate-fb3fb4f3d76d55b64440afd0af72775dfad3bd1d.tar.gz |
PSARC 2006/054 DTrace JNI Binding
6384263 PSARC 2006/054 DTrace JNI Binding
Diffstat (limited to 'usr/src')
93 files changed, 23105 insertions, 16 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 02f2039133..567ef25964 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -85,6 +84,7 @@ SUBDIRS += \ libadm \ libctf \ libdtrace \ + libdtrace_jni \ libcurses \ libgen \ libgss \ @@ -307,6 +307,7 @@ HDRSUBDIRS= libaio \ libdhcpsvc \ libdhcputil \ libdtrace \ + libdtrace_jni \ libeti \ libgen \ libwanboot \ @@ -431,6 +432,7 @@ libdhcpsvc: libinetutil libdhcputil: libinetutil libdladm: libdlpi libdevinfo libdtrace: libproc libgen libctf +libdtrace_jni: libuutil libdtrace libefi: libuuid $(CLOSED_BUILD)$(CLOSED)/lib/libelfsign: \ $(CLOSED)/lib/libike libcryptoutil pkcs11 diff --git a/usr/src/lib/libdtrace_jni/Makefile b/usr/src/lib/libdtrace_jni/Makefile new file mode 100644 index 0000000000..d0d83dce97 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/Makefile @@ -0,0 +1,67 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber: java spec .WAIT $(SUBDIRS) + +install: java spec .WAIT $(SUBDIRS) + +lint: $(SUBDIRS) + +install_h: + +check: $(CHECKHDRS) + +java spec $(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +doc: + @cd java; pwd; make doc + +FRC: + +include ../Makefile.targ + +# +# Cross-reference customization: build the cross-reference only over the +# source directories, and ignore Makefiles and machine-generated source. +# +XRDIRS = common i386 sparc sparcv9 +XRDEL = Makefile* diff --git a/usr/src/lib/libdtrace_jni/Makefile.com b/usr/src/lib/libdtrace_jni/Makefile.com new file mode 100644 index 0000000000..e91ebe7a93 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/Makefile.com @@ -0,0 +1,94 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY = libdtrace_jni.a +VERS = .1 + +LIBSRCS = \ + dtj_util.c \ + dtj_jnitab.c \ + dtj_error.c \ + dtj_probe.c \ + dtj_attr.c \ + dtj_consume.c \ + dtrace_jni.c + +OBJECTS = $(MACHOBJS) $(LIBSRCS:%.c=%.o) + +include ../../Makefile.lib + +SRCS = $(LIBSRCS:%.c=../common/%.c) + +SRCDIR = ../common +SPECMAPFILE = $(MAPDIR)/mapfile + +CPPFLAGS += -I../common -I. +CPPFLAGS += -I$(JAVA_ROOT)/include -I$(JAVA_ROOT)/include/solaris +CPPFLAGS += -I../java/native +CFLAGS += $(CCVERBOSE) -K PIC +CFLAGS64 += $(CCVERBOSE) -K PIC +LDLIBS += -lc -luutil -ldtrace -lproc + +LINTLIB = + +LFLAGS = -t -v + +ROOTDLIBDIR = $(ROOT)/usr/lib/dtrace_jni +ROOTDLIBDIR64 = $(ROOT)/usr/lib/dtrace_jni/64 + +ROOTDLIBS = $(DLIBSRCS:%=$(ROOTDLIBDIR)/%) + +.KEEP_STATE: + +all: $(DYNLIB) + +lint: lintcheck + +%.o: ../common/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +$(ROOTDLIBDIR): + $(INS.dir) + +$(ROOTDLIBDIR64): $(ROOTDLIBDIR) + $(INS.dir) + +$(ROOTDLIBDIR)/%.o: %.o + $(INS.file) + +$(ROOTDLIBDIR64)/%.o: %.o + $(INS.file) + +$(ROOTDLIBS): $(ROOTDLIBDIR) + +$(ROOTDOBJS): $(ROOTDLIBDIR) + +$(ROOTDOBJS64): $(ROOTDLIBDIR64) + +include ../../Makefile.targ diff --git a/usr/src/lib/libdtrace_jni/amd64/Makefile b/usr/src/lib/libdtrace_jni/amd64/Makefile new file mode 100644 index 0000000000..6755e8b390 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/amd64/Makefile @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +MAPDIR = ../spec/amd64 + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libdtrace_jni/common/dtj_attr.c b/usr/src/lib/libdtrace_jni/common/dtj_attr.c new file mode 100644 index 0000000000..83edf12843 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtj_attr.c @@ -0,0 +1,181 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <dtrace_jni.h> + +/* + * This file creates instances of the Java class + * org.opensolaris.os.dtrace.InterfaceAttributes. + */ + +static const char * +dtj_stability_name(dtrace_stability_t stability) +{ + const char *name; + switch (stability) { + case DTRACE_STABILITY_INTERNAL: + name = "INTERNAL"; + break; + case DTRACE_STABILITY_PRIVATE: + name = "PRIVATE"; + break; + case DTRACE_STABILITY_OBSOLETE: + name = "OBSOLETE"; + break; + case DTRACE_STABILITY_EXTERNAL: + name = "EXTERNAL"; + break; + case DTRACE_STABILITY_UNSTABLE: + name = "UNSTABLE"; + break; + case DTRACE_STABILITY_EVOLVING: + name = "EVOLVING"; + break; + case DTRACE_STABILITY_STABLE: + name = "STABLE"; + break; + case DTRACE_STABILITY_STANDARD: + name = "STANDARD"; + break; + default: + name = NULL; + } + + return (name); +} + +static const char * +dtj_dependency_class_name(dtrace_class_t class) +{ + const char *name; + switch (class) { + case DTRACE_CLASS_UNKNOWN: + name = "UNKNOWN"; + break; + case DTRACE_CLASS_CPU: + name = "CPU"; + break; + case DTRACE_CLASS_PLATFORM: + name = "PLATFORM"; + break; + case DTRACE_CLASS_GROUP: + name = "GROUP"; + break; + case DTRACE_CLASS_ISA: + name = "ISA"; + break; + case DTRACE_CLASS_COMMON: + name = "COMMON"; + break; + default: + name = NULL; + } + + return (name); +} + +jobject +dtj_new_attribute(dtj_java_consumer_t *jc, const dtrace_attribute_t *attr) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + const char *name; + + jstring jname = NULL; + jobject jattr = NULL; /* return value */ + + jattr = (*jenv)->NewObject(jenv, g_attr_jc, g_attrinit_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + return (NULL); + } + + /* name stability */ + name = dtj_stability_name(attr->dtat_name); + if (!name) { + dtj_throw_illegal_argument(jenv, + "unexpected name stability value: %d", + attr->dtat_name); + (*jenv)->DeleteLocalRef(jenv, jattr); + return (NULL); + } + jname = (*jenv)->NewStringUTF(jenv, name); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, jattr); + return (NULL); + } + (*jenv)->CallVoidMethod(jenv, jattr, g_attrset_name_jm, jname); + (*jenv)->DeleteLocalRef(jenv, jname); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, jattr); + return (NULL); + } + + /* data stability */ + name = dtj_stability_name(attr->dtat_data); + if (!name) { + dtj_throw_illegal_argument(jenv, + "unexpected data stability value: %d", + attr->dtat_data); + (*jenv)->DeleteLocalRef(jenv, jattr); + return (NULL); + } + jname = (*jenv)->NewStringUTF(jenv, name); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, jattr); + return (NULL); + } + (*jenv)->CallVoidMethod(jenv, jattr, g_attrset_data_jm, jname); + (*jenv)->DeleteLocalRef(jenv, jname); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, jattr); + return (NULL); + } + + /* dependency class */ + name = dtj_dependency_class_name(attr->dtat_class); + if (!name) { + dtj_throw_illegal_argument(jenv, + "unexpected dependency class value: %d", + attr->dtat_class); + (*jenv)->DeleteLocalRef(jenv, jattr); + return (NULL); + } + jname = (*jenv)->NewStringUTF(jenv, name); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, jattr); + return (NULL); + } + (*jenv)->CallVoidMethod(jenv, jattr, g_attrset_class_jm, jname); + (*jenv)->DeleteLocalRef(jenv, jname); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, jattr); + return (NULL); + } + + return (jattr); +} diff --git a/usr/src/lib/libdtrace_jni/common/dtj_consume.c b/usr/src/lib/libdtrace_jni/common/dtj_consume.c new file mode 100644 index 0000000000..026bbea5a3 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtj_consume.c @@ -0,0 +1,2247 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <ctype.h> +#include <limits.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <strings.h> +#include <sys/wait.h> +#include <limits.h> +#include <signal.h> +#include <libproc.h> +#include <pthread.h> +#include <dtrace_jni.h> + +/* + * Implements the work done in the running consumer loop. The native Java + * methods (JNI layer) are implemented in dtrace_jni.c. + */ + +/* Record handler passed to dtrace_work() */ +static int dtj_chewrec(const dtrace_probedata_t *, const dtrace_recdesc_t *, + void *); +/* Probe data handler passed to dtrace_work() */ +static int dtj_chew(const dtrace_probedata_t *, void *); + +/* Processes requests from LocalConsumer enqueued during dtrace_sleep() */ +static dtj_status_t dtj_process_requests(dtj_java_consumer_t *); + +/* + * Callback handlers set in dtj_set_callback_handlers(), called from libdtrace + * in the consumer loop (from dtrace_work()) + */ +static int dtj_drophandler(const dtrace_dropdata_t *, void *); +static int dtj_errhandler(const dtrace_errdata_t *, void *); +static void dtj_prochandler(struct ps_prochandle *, const char *, void *); +static int dtj_setopthandler(const dtrace_setoptdata_t *, void *); +/* + * Buffered output handler called from libdtrace in both the consumer loop (from + * dtrace_work()) and the get_aggregate() function (from + * dtrace_aggregate_print()). + */ +static int dtj_bufhandler(const dtrace_bufdata_t *, void *); + +/* Conversion of libdtrace data into Java Objects */ +static jobject dtj_recdata(dtj_java_consumer_t *, uint32_t, caddr_t); +static jobject dtj_bytedata(JNIEnv *, uint32_t, caddr_t); +static jobject dtj_new_stack_record(const caddr_t, const dtrace_recdesc_t *, + dtj_java_consumer_t *); +static jobject dtj_new_probedata_stack_record(const dtrace_probedata_t *, + const dtrace_recdesc_t *, dtj_java_consumer_t *); +/* Aggregation data */ +static jobject dtj_new_tuple_stack_record(const dtrace_aggdata_t *, + const dtrace_recdesc_t *, const char *, dtj_java_consumer_t *); +static jobject dtj_new_distribution(const dtrace_aggdata_t *, + const dtrace_recdesc_t *, dtj_java_consumer_t *); +static jobject dtj_new_aggval(dtj_java_consumer_t *, const dtrace_aggdata_t *, + const dtrace_recdesc_t *); +static int64_t dtj_average(caddr_t, uint64_t); +static int64_t dtj_avg_total(caddr_t, uint64_t); +static int64_t dtj_avg_count(caddr_t); + +/* Aggregation functions */ +static void dtj_aggwalk_init(dtj_java_consumer_t *); +static int dtj_agghandler(const dtrace_bufdata_t *, dtj_java_consumer_t *); +static boolean_t dtj_is_included(const dtrace_aggdata_t *, + dtj_java_consumer_t *); +static void dtj_attach_frames(dtj_java_consumer_t *, jobject, + jobjectArray); +static boolean_t dtj_is_stack_action(dtrace_actkind_t); +static int dtj_clear(const dtrace_aggdata_t *, void *); + +/* + * The consumer loop needs to protect calls to libdtrace functions with a global + * lock. JNI native method calls in dtrace_jni.c are already protected and do + * not need this function. + */ +dtj_status_t +dtj_get_dtrace_error(dtj_java_consumer_t *jc, dtj_error_t *e) +{ + JNIEnv *jenv = jc->dtjj_jenv; + dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp; + + /* Grab global lock */ + (*jenv)->MonitorEnter(jenv, g_caller_jc); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTJ_ERR); + } + e->dtje_number = dtrace_errno(dtp); + e->dtje_message = dtrace_errmsg(dtp, e->dtje_number); + (*jenv)->MonitorExit(jenv, g_caller_jc); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTJ_ERR); + } + return (DTJ_OK); +} + +/* + * Protected by global lock (LocalConsumer.class) that protects call to + * Java_org_opensolaris_os_dtrace_LocalConsumer__1go() + */ +dtj_status_t +dtj_set_callback_handlers(dtj_java_consumer_t *jc) +{ + dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp; + dtrace_optval_t optval; + + /* + * The user argument to the bufhandler is the lookup key used to obtain + * the thread-specific java consumer. The java consumer contains JNI + * state specific to either the consumer loop or the getAggregate() + * call. + */ + if (dtrace_handle_buffered(dtp, &dtj_bufhandler, NULL) == -1) { + dtj_throw_dtrace_exception(jc, + "failed to establish buffered handler: %s", + dtrace_errmsg(dtp, dtrace_errno(dtp))); + return (DTJ_ERR); + } + + if (dtrace_handle_drop(dtp, &dtj_drophandler, NULL) == -1) { + dtj_throw_dtrace_exception(jc, + "failed to establish drop handler: %s", + dtrace_errmsg(dtp, dtrace_errno(dtp))); + return (DTJ_ERR); + } + + if (dtrace_handle_err(dtp, &dtj_errhandler, NULL) == -1) { + dtj_throw_dtrace_exception(jc, + "failed to establish error handler: %s", + dtrace_errmsg(dtp, dtrace_errno(dtp))); + return (DTJ_ERR); + } + + if (dtrace_handle_proc(dtp, &dtj_prochandler, NULL) == -1) { + dtj_throw_dtrace_exception(jc, + "failed to establish proc handler: %s", + dtrace_errmsg(dtp, dtrace_errno(dtp))); + return (DTJ_ERR); + } + + if (dtrace_getopt(dtp, "flowindent", &optval) == -1) { + dtj_throw_dtrace_exception(jc, + "couldn't get option %s: %s", "flowindent", + dtrace_errmsg(dtp, dtrace_errno(dtp))); + return (DTJ_ERR); + } + + jc->dtjj_consumer->dtjc_flow = (optval != DTRACEOPT_UNSET); + + if (dtrace_handle_setopt(dtp, &dtj_setopthandler, NULL) == -1) { + dtj_throw_dtrace_exception(jc, + "failed to establish setopt handler: %s", + dtrace_errmsg(dtp, dtrace_errno(dtp))); + return (DTJ_ERR); + } + + return (DTJ_OK); +} + +static int +/* ARGSUSED */ +dtj_drophandler(const dtrace_dropdata_t *data, void *arg) +{ + dtj_java_consumer_t *jc; + JNIEnv *jenv; + + const char *dropkind; + + jstring msg = NULL; + jstring kind = NULL; + jobject drop = NULL; + + jc = pthread_getspecific(g_dtj_consumer_key); + jenv = jc->dtjj_jenv; + + msg = dtj_NewStringNative(jenv, data->dtdda_msg); + if ((*jenv)->ExceptionCheck(jenv)) { + return (DTRACE_HANDLE_ABORT); + } + switch (data->dtdda_kind) { + case DTRACEDROP_PRINCIPAL: + dropkind = "PRINCIPAL"; + break; + case DTRACEDROP_AGGREGATION: + dropkind = "AGGREGATION"; + break; + case DTRACEDROP_DYNAMIC: + dropkind = "DYNAMIC"; + break; + case DTRACEDROP_DYNRINSE: + dropkind = "DYNRINSE"; + break; + case DTRACEDROP_DYNDIRTY: + dropkind = "DYNDIRTY"; + break; + case DTRACEDROP_SPEC: + dropkind = "SPEC"; + break; + case DTRACEDROP_SPECBUSY: + dropkind = "SPECBUSY"; + break; + case DTRACEDROP_SPECUNAVAIL: + dropkind = "SPECUNAVAIL"; + break; + case DTRACEDROP_STKSTROVERFLOW: + dropkind = "STKSTROVERFLOW"; + break; + case DTRACEDROP_DBLERROR: + dropkind = "DBLERROR"; + break; + default: + dropkind = "UNKNOWN"; + } + kind = (*jenv)->NewStringUTF(jenv, dropkind); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, msg); + return (DTRACE_HANDLE_ABORT); + } + drop = (*jenv)->NewObject(jenv, g_drop_jc, g_dropinit_jm, + data->dtdda_cpu, kind, data->dtdda_drops, data->dtdda_total, msg); + (*jenv)->DeleteLocalRef(jenv, kind); + (*jenv)->DeleteLocalRef(jenv, msg); + if ((*jenv)->ExceptionCheck(jenv)) { + return (DTRACE_HANDLE_ABORT); + } + (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_drop_jm, drop); + (*jenv)->DeleteLocalRef(jenv, drop); + if ((*jenv)->ExceptionCheck(jenv)) { + return (DTRACE_HANDLE_ABORT); + } + + return (DTRACE_HANDLE_OK); +} + +static int +/* ARGSUSED */ +dtj_errhandler(const dtrace_errdata_t *data, void *arg) +{ + dtj_java_consumer_t *jc; + JNIEnv *jenv; + + const char *f; + int64_t addr; + + jobject probe = NULL; + jstring fault = NULL; + jstring msg = NULL; + jobject error = NULL; + + jc = pthread_getspecific(g_dtj_consumer_key); + jenv = jc->dtjj_jenv; + + probe = dtj_new_probedesc(jc, data->dteda_pdesc); + if (!probe) { + return (DTRACE_HANDLE_ABORT); + } + f = dtj_get_fault_name(data->dteda_fault); + if (f) { + fault = (*jenv)->NewStringUTF(jenv, f); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, probe); + return (DTRACE_HANDLE_ABORT); + } + } + switch (data->dteda_fault) { + case DTRACEFLT_BADADDR: + case DTRACEFLT_BADALIGN: + addr = data->dteda_addr; + break; + default: + addr = -1; + } + msg = dtj_NewStringNative(jenv, data->dteda_msg); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, probe); + (*jenv)->DeleteLocalRef(jenv, fault); + return (DTRACE_HANDLE_ABORT); + } + error = (*jenv)->NewObject(jenv, g_error_jc, g_errinit_jm, + probe, + data->dteda_edesc->dtepd_epid, + data->dteda_cpu, + data->dteda_action, + data->dteda_offset, + fault, addr, msg); + (*jenv)->DeleteLocalRef(jenv, msg); + (*jenv)->DeleteLocalRef(jenv, fault); + (*jenv)->DeleteLocalRef(jenv, probe); + if ((*jenv)->ExceptionCheck(jenv)) { + return (DTRACE_HANDLE_ABORT); + } + (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_error_jm, error); + (*jenv)->DeleteLocalRef(jenv, error); + if ((*jenv)->ExceptionCheck(jenv)) { + return (DTRACE_HANDLE_ABORT); + } + + return (DTRACE_HANDLE_OK); +} + +/* + * Since the function signature does not allow us to return an abort signal, we + * need to temporarily clear any pending exception before returning, since + * without the abort we can't guarantee that the exception will be checked in + * time to prevent invalid JNI function calls. + */ +static void +/* ARGSUSED */ +dtj_prochandler(struct ps_prochandle *P, const char *msg, void *arg) +{ + dtj_java_consumer_t *jc; + JNIEnv *jenv; + + const psinfo_t *prp = Ppsinfo(P); + int pid = Pstatus(P)->pr_pid; + int signal = -1; + char signame[SIG2STR_MAX]; + const char *statusname; + int exit = INT_MAX; /* invalid initial status */ + + jstring status = NULL; + jstring signalName = NULL; + jstring message = NULL; + jobject process = NULL; + + jc = pthread_getspecific(g_dtj_consumer_key); + jenv = jc->dtjj_jenv; + + switch (Pstate(P)) { + case PS_RUN: + statusname = "RUN"; + break; + case PS_STOP: + statusname = "STOP"; + break; + case PS_UNDEAD: + statusname = "UNDEAD"; + if (prp != NULL) { + exit = WEXITSTATUS(prp->pr_wstat); + } + if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) { + signal = WTERMSIG(prp->pr_wstat); + (void) proc_signame(signal, signame, sizeof (signame)); + signalName = (*jenv)->NewStringUTF(jenv, signame); + if ((*jenv)->ExceptionCheck(jenv)) { + goto proc_end; + } + } + ++jc->dtjj_consumer->dtjc_procs_ended; + break; + case PS_LOST: + statusname = "LOST"; + ++jc->dtjj_consumer->dtjc_procs_ended; + break; + case PS_DEAD: + /* + * PS_DEAD not handled by dtrace.c prochandler, still this is a + * case of process termination and it can't hurt to handle it. + */ + statusname = "DEAD"; + ++jc->dtjj_consumer->dtjc_procs_ended; + break; + default: + /* + * Unexpected, but erring on the side of tolerance by not + * crashing the consumer. Failure to notify listeners of + * process state not handled by the dtrace.c prochandler does + * not seem serious. + */ + return; + } + + status = (*jenv)->NewStringUTF(jenv, statusname); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, signalName); + goto proc_end; + } + if (msg) { + message = dtj_NewStringNative(jenv, msg); + if (!message) { + (*jenv)->DeleteLocalRef(jenv, status); + (*jenv)->DeleteLocalRef(jenv, signalName); + goto proc_end; + } + } + process = (*jenv)->NewObject(jenv, g_process_jc, g_procinit_jm, + pid, status, signal, signalName, NULL, message); + (*jenv)->DeleteLocalRef(jenv, status); + (*jenv)->DeleteLocalRef(jenv, signalName); + (*jenv)->DeleteLocalRef(jenv, message); + if ((*jenv)->ExceptionCheck(jenv)) { + goto proc_end; + } + if (exit != INT_MAX) { + /* valid exit status */ + (*jenv)->CallVoidMethod(jenv, process, g_procexit_jm, exit); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, process); + goto proc_end; + } + } + (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, g_proc_jm, process); + (*jenv)->DeleteLocalRef(jenv, process); + +proc_end: + + if ((*jenv)->ExceptionCheck(jenv)) { + /* + * Save the exception so we can rethrow it later when it's safe. + */ + if (!jc->dtjj_exception) { + jthrowable e = (*jenv)->ExceptionOccurred(jenv); + jc->dtjj_exception = e; + } + (*jenv)->ExceptionClear(jenv); + } +} + +static int +/* ARGSUSED */ +dtj_setopthandler(const dtrace_setoptdata_t *data, void *arg) +{ + dtj_java_consumer_t *jc; + + jc = pthread_getspecific(g_dtj_consumer_key); + if (strcmp(data->dtsda_option, "flowindent") == 0) { + jc->dtjj_consumer->dtjc_flow = + (data->dtsda_newval != DTRACEOPT_UNSET); + } + return (DTRACE_HANDLE_OK); +} + +/* + * Most of this function lifted from libdtrace/common/dt_consume.c + * dt_print_bytes(). + */ +static jobject +dtj_bytedata(JNIEnv *jenv, uint32_t nbytes, caddr_t addr) +{ + /* + * If the byte stream is a series of printable characters, followed by + * a terminating byte, we print it out as a string. Otherwise, we + * assume that it's something else and just print the bytes. + */ + int i, j; + char *c = addr; + + jobject jobj = NULL; /* return value */ + + if (nbytes == 0) { + return ((*jenv)->NewStringUTF(jenv, "")); + } + + for (i = 0; i < nbytes; i++) { + /* + * We define a "printable character" to be one for which + * isprint(3C) returns non-zero, isspace(3C) returns non-zero, + * or a character which is either backspace or the bell. + * Backspace and the bell are regrettably special because + * they fail the first two tests -- and yet they are entirely + * printable. These are the only two control characters that + * have meaning for the terminal and for which isprint(3C) and + * isspace(3C) return 0. + */ + if (isprint(c[i]) || isspace(c[i]) || + c[i] == '\b' || c[i] == '\a') + continue; + + if (c[i] == '\0' && i > 0) { + /* + * This looks like it might be a string. Before we + * assume that it is indeed a string, check the + * remainder of the byte range; if it contains + * additional non-nul characters, we'll assume that + * it's a binary stream that just happens to look like + * a string. + */ + for (j = i + 1; j < nbytes; j++) { + if (c[j] != '\0') + break; + } + + if (j != nbytes) + break; + + /* It's a string */ + return (dtj_NewStringNative(jenv, (char *)addr)); + } + + break; + } + + if (i == nbytes) { + /* + * The byte range is all printable characters, but there is + * no trailing nul byte. We'll assume that it's a string. + */ + char *s = malloc(nbytes + 1); + if (!s) { + dtj_throw_out_of_memory(jenv, + "failed to allocate string value"); + return (NULL); + } + (void) strncpy(s, c, nbytes); + s[nbytes] = '\0'; + jobj = dtj_NewStringNative(jenv, s); + free(s); + return (jobj); + } + + /* return byte array */ + jobj = (*jenv)->NewByteArray(jenv, nbytes); + if ((*jenv)->ExceptionCheck(jenv)) { + return (NULL); + } + (*jenv)->SetByteArrayRegion(jenv, (jbyteArray)jobj, 0, nbytes, + (const jbyte *)c); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + (*jenv)->DeleteLocalRef(jenv, jobj); + return (NULL); + } + return (jobj); +} + +/* + * Return NULL if memory could not be allocated (OutOfMemoryError is thrown in + * that case). + */ +static jobject +dtj_recdata(dtj_java_consumer_t *jc, uint32_t size, caddr_t addr) +{ + JNIEnv *jenv = jc->dtjj_jenv; + jobject jobj; + + switch (size) { + case 1: + jobj = (*jenv)->NewObject(jenv, g_byte_jc, + g_byteinit_jm, *((char *)addr)); + break; + case 2: + jobj = (*jenv)->NewObject(jenv, g_short_jc, + /* LINTED - alignment */ + g_shortinit_jm, *((int16_t *)addr)); + break; + case 4: + jobj = (*jenv)->NewObject(jenv, g_int_jc, + /* LINTED - alignment */ + g_intinit_jm, *((int32_t *)addr)); + break; + case 8: + jobj = (*jenv)->NewObject(jenv, g_long_jc, + /* LINTED - alignment */ + g_longinit_jm, *((int64_t *)addr)); + break; + default: + jobj = dtj_bytedata(jenv, size, addr); + break; + } + + return (jobj); +} + +/* + * This is the record handling function passed to dtrace_work(). It differs + * from the bufhandler registered with dtrace_handle_buffered() as follows: + * + * 1. It does not have access to libdtrace formatted output. + * 2. It is called once for every D program statement, not for every + * output-producing D action or aggregation record. A statement may be a + * variable assignment, having no size and producing no output. + * 3. It is called for the D exit() action; the bufhandler is not. + * 4. In response to the printa() action, it is called with a record having an + * action of type DTRACEACT_PRINTA. The bufhandler never sees that action + * value. It only sees the output-producing aggregation records. + * 5. It is called with a NULL record at the end of each probedata. + */ +static int +dtj_chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, + void *arg) +{ + dtj_java_consumer_t *jc = arg; + JNIEnv *jenv = jc->dtjj_jenv; + + const dtrace_eprobedesc_t *edesc = data->dtpda_edesc; + dtrace_actkind_t act; + int r; + + /* + * Update the record index to that of the current record, or to that of + * the last record if rec is NULL (signalling end of probe data). + */ + if (rec == NULL) { + r = edesc->dtepd_nrecs; /* end of probe data */ + } else { + /* + * This record handler is called once for the printf() action, + * but there may be multiple records in the probedata + * corresponding to the unformatted elements of that printf(). + * We don't know ahead of time how many probedata records + * libdtrace will consume to produce output for one printf() + * action, so we look back at the previous call to dtj_chewrec() + * to see how many probedata records were consumed. All + * non-null elements in the range from the previous record index + * up to and not including the current record index are assumed + * to be unformatted printf() elements, and will be attached to + * the PrintfRecord from the previous call. A null element in + * that range is the result of a D program statement preceding + * the printf() that is not a D action. These generate + * probedata records accounted for by the null placeholder, but + * do not advance the probedata offset and are not part of the + * subsequent printf(). + * + * If rec->dtrd_size == 0, the record represents a D program + * statement that is not a D action. It has no size and does + * not advance the offset in the probedata. Handle it normally + * without special-casing or premature return, since in all + * cases we look at the previous record later in this function. + */ + for (r = jc->dtjj_consumer->dtjc_probedata_rec_i; + ((r < edesc->dtepd_nrecs) && + (edesc->dtepd_rec[r].dtrd_offset < rec->dtrd_offset)); + ++r) { + } + } + + /* + * Attach the Java representations of the libdtrace data elements + * pertaining to the previous call to this record handler to the + * previous Java Record. (All data elements belonging to the current + * probedata are added to a single list by the probedata consumer + * function dtj_chew() before this record consumer function is ever + * called.) For example, if the previous Record was generated by the + * printf() action, and dtj_chew() listed 3 records for its 3 + * unformatted elements, those 3 libdtrace records comprise 1 + * PrintfRecord. Note that we cannot know how many data elements apply + * to the current rec until we find out the data index where the next + * rec starts. (The knowledge of how many probedata records to consume + * is private to libdtrace.) + */ + if (jc->dtjj_consumer->dtjc_probedata_act == DTRACEACT_PRINTF) { + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdataattach_jm, + jc->dtjj_consumer->dtjc_probedata_rec_i, r - 1); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_CONSUME_ABORT); + } + } + + if (rec == NULL) { + /* + * End of probe data. Notify listeners of the new ProbeData + * instance. + */ + if (jc->dtjj_probedata) { + /* previous probedata */ + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdataclear_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_CONSUME_ABORT); + } + (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, + g_pdatanext_jm, jc->dtjj_probedata); + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_probedata); + jc->dtjj_probedata = NULL; + if ((*jenv)->ExceptionCheck(jenv)) { + /* + * Do not wrap exception thrown from + * ConsumerListener. + */ + return (DTRACE_CONSUME_ABORT); + } + } + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_printa_buffer); + jc->dtjj_printa_buffer = NULL; + return (DTRACE_CONSUME_NEXT); + } + + act = rec->dtrd_action; + + /* Set previous record action and data index to current */ + jc->dtjj_consumer->dtjc_probedata_act = act; + jc->dtjj_consumer->dtjc_probedata_rec_i = r; + + switch (act) { + case DTRACEACT_DIFEXPR: + if (rec->dtrd_size == 0) { + /* + * The current record is not a D action, but a program + * statement such as a variable assignment, not to be + * confused with the trace() action. + */ + break; + } + /* + * Add a Record for the trace() action that references the + * native probedata element listed at the current index. + */ + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdataadd_trace_jm, + jc->dtjj_consumer->dtjc_probedata_rec_i); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_CONSUME_ABORT); + } + break; + case DTRACEACT_PRINTF: + /* + * Just add an empty PrintfRecord for now. We'll attach the + * unformatted elements in a subsequent call to this function. + * (We don't know how many there will be.) + */ + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdataadd_printf_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_CONSUME_ABORT); + } + /* defer formatted string to dtj_bufhandler() */ + break; + case DTRACEACT_PRINTA: { + jobject jbuf = NULL; + + dtj_aggwalk_init(jc); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_CONSUME_ABORT); + } + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdataadd_printa_jm, + jc->dtjj_consumer->dtjc_printa_snaptime, + (rec->dtrd_format != 0)); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_CONSUME_ABORT); + } + if (jc->dtjj_printa_buffer == NULL) { + /* + * Create a StringBuffer to collect the pieces of + * formatted output into a single String. + */ + jbuf = (*jenv)->NewObject(jenv, g_buf_jc, + g_bufinit_jm); + if (!jbuf) { + /* OutOfMemoryError pending */ + return (DTRACE_CONSUME_ABORT); + } + jc->dtjj_printa_buffer = jbuf; + } + /* defer aggregation records to dtj_bufhandler() */ + break; + } + case DTRACEACT_EXIT: + /* + * Add a Record for the exit() action that references the native + * probedata element listed at the current index. + */ + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdataadd_exit_jm, + jc->dtjj_consumer->dtjc_probedata_rec_i); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_CONSUME_ABORT); + } + return (DTRACE_CONSUME_NEXT); + } + + return (DTRACE_CONSUME_THIS); +} + +/* + * This is the probe handling function passed to dtrace_work(). It is is called + * once every time a probe fires. It is the first of all the callbacks for the + * current probe. It is followed by multiple callbacks to dtj_chewrec(), one + * for each probedata record. Each call to dtj_chewrec() is followed by zero or + * more callbacks to the bufhandler, one for each output-producing action or + * aggregation record. + */ +static int +dtj_chew(const dtrace_probedata_t *data, void *arg) +{ + dtj_java_consumer_t *jc = arg; + JNIEnv *jenv = jc->dtjj_jenv; + + dtrace_eprobedesc_t *edesc; + dtrace_probedesc_t *pdesc; + dtrace_recdesc_t *rec; + int epid; + int cpu; + int nrecs; + int i; + + jobject jpdata = NULL; + jobject jprobe = NULL; + jobject jflow = NULL; + jstring jflowkind = NULL; + jobject jobj = NULL; + + edesc = data->dtpda_edesc; + epid = (int)edesc->dtepd_epid; + pdesc = data->dtpda_pdesc; + cpu = (int)data->dtpda_cpu; + if ((jprobe = dtj_new_probedesc(jc, pdesc)) == NULL) { + /* java exception pending */ + return (DTRACE_CONSUME_ABORT); + } + nrecs = edesc->dtepd_nrecs; + + if (jc->dtjj_consumer->dtjc_flow) { + const char *kind; + switch (data->dtpda_flow) { + case DTRACEFLOW_ENTRY: + kind = "ENTRY"; + break; + case DTRACEFLOW_RETURN: + kind = "RETURN"; + break; + case DTRACEFLOW_NONE: + kind = "NONE"; + break; + default: + kind = NULL; + } + if (kind != NULL) { + int depth; + jflowkind = (*jenv)->NewStringUTF(jenv, kind); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + (*jenv)->DeleteLocalRef(jenv, jprobe); + return (DTRACE_CONSUME_ABORT); + } + /* + * Use the knowledge that libdtrace indents 2 spaces per + * level in the call stack to calculate the depth. + */ + depth = (data->dtpda_indent / 2); + jflow = (*jenv)->NewObject(jenv, g_flow_jc, + g_flowinit_jm, jflowkind, depth); + (*jenv)->DeleteLocalRef(jenv, jflowkind); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + (*jenv)->DeleteLocalRef(jenv, jprobe); + return (DTRACE_CONSUME_ABORT); + } + } + } + + /* Create ProbeData instance */ + jpdata = (*jenv)->NewObject(jenv, g_pdata_jc, g_pdatainit_jm, + epid, cpu, jprobe, jflow, nrecs); + (*jenv)->DeleteLocalRef(jenv, jprobe); + (*jenv)->DeleteLocalRef(jenv, jflow); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_CONSUME_ABORT); + } + + /* + * Populate the ProbeData list of Java data elements in advance so we + * don't need to peek back in the record handler at libdtrace records + * that have already been consumed. In the Java API, each ProbeData + * Record is generated by one D action, while in the native libdtrace + * there may be more than one probedata record (each a single data + * element) per D action. For example PrintfRecord has multiple + * unformatted elements, each represented by a native probedata record, + * but combined by the API into a single PrintfRecord. + */ + for (i = 0; i < nrecs; ++i) { + rec = &edesc->dtepd_rec[i]; + /* + * A statement that is not a D action, such as assignment to a + * variable, has no size. Add a NULL placeholder to the scratch + * list of Java probedata elements in that case. + */ + jobj = NULL; /* initialize object reference to null */ + if (rec->dtrd_size > 0) { + if (dtj_is_stack_action(rec->dtrd_action)) { + jobj = dtj_new_probedata_stack_record(data, + rec, jc); + } else { + jobj = dtj_recdata(jc, rec->dtrd_size, + (data->dtpda_data + rec->dtrd_offset)); + } + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + (*jenv)->DeleteLocalRef(jenv, jpdata); + return (DTRACE_CONSUME_ABORT); + } + } + + (*jenv)->CallVoidMethod(jenv, jpdata, g_pdataadd_jm, jobj); + (*jenv)->DeleteLocalRef(jenv, jobj); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + (*jenv)->DeleteLocalRef(jenv, jpdata); + return (DTRACE_CONSUME_ABORT); + } + } + + if (jc->dtjj_probedata != NULL) { + dtj_throw_illegal_state(jenv, "unfinished probedata"); + WRAP_EXCEPTION(jenv); + (*jenv)->DeleteLocalRef(jenv, jpdata); + return (DTRACE_CONSUME_ABORT); + } + jc->dtjj_probedata = jpdata; + + /* Initialize per-consumer probedata fields */ + jc->dtjj_consumer->dtjc_probedata_rec_i = 0; + jc->dtjj_consumer->dtjc_probedata_act = DTRACEACT_NONE; + dtj_aggwalk_init(jc); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_CONSUME_ABORT); + } + + return (DTRACE_CONSUME_THIS); +} + +/* + * This is the buffered output handler registered with dtrace_handle_buffered(). + * It's purpose is to make the output of the libdtrace print routines available + * to this API, without writing any of it to a file (such as stdout). This is + * needed for the stack(), ustack(), and jstack() actions to get human-readable + * stack values, since there is no public function in libdtrace to convert stack + * values to strings. It is also used to get the formatted output of the D + * printf() and printa() actions. + * + * The bufhandler is called once for each output-producing, non-aggregating D + * action, such as trace() or printf(), and once for each libdtrace aggregation + * record (whether in response to the D printa() action, or the Consumer + * getAggregate() method). In the simple printa() case that takes one + * aggregation and does not specify a format string, there is one libdtrace + * record per tuple element plus one for the corresponding value. The complete + * tuple/value pair becomes a single AggregationRecord exported by the API. + * When multiple aggregations are passed to printa(), each tuple is associated + * with a list of values, one from each aggregation. If a printa() format + * string does not specify placeholders for every aggregation value and tuple + * member, callbacks for those values and tuple members are omitted (and the + * data is omitted from the resulting PrintaRecord). + * + * Notes to characterize some non-obvious bufhandler behavior: + * + * 1. dtj_bufhandler() is never called with bufdata->dtbda_recdesc->dtrd_action + * DTRACEACT_PRINTA. That action only appears in the probedata consumer + * functions dtj_chew() and dtj_chewrec() before the bufhandler is called with + * subsequent aggregation records. + * + * 2. If printa() specifies a format string argument, then the bufhandler is + * called only for those elements of the tuple/value pair that are included in + * the format string. If a stack() tuple member is omitted from the format + * string, its human-readable representation will not be available to this API, + * so the stack frame array is also omitted from the resulting + * AggregationRecord. The bufhandler is also called once for each string of + * characters surrounding printa() format string placeholders. For example, + * " %@d %d stack%k\n" results in the following callbacks: + * - two spaces + * - the aggregation value + * - a single space + * - the first tuple member (an integer) + * - " stack" + * - the second tuple member (a stack) + * - a newline + * A NULL record (NULL dtbda_recdesc) distinguishes a callback with interstitial + * format string characters from a callback with a tuple member or aggregation + * value (which has a non-NULL recdesc). The contents are also distinguished by + * the following flags: + * DTRACE_BUFDATA_AGGKEY + * DTRACE_BUFDATA_AGGVAL + * DTRACE_BUFDATA_AGGFORMAT + * DTRACE_BUFDATA_AGGLAST + * + * There is no final callback with the complete formatted string, so that must + * be concatenated across multiple callbacks to the bufhandler. + * + * 3. bufdata->dtbda_probe->dtpda_data may be overwritten by libdtrace print + * routines. The address is cached in the dtj_chew() function in case it is + * needed in the bufhandler. + */ +static int +/* ARGSUSED */ +dtj_bufhandler(const dtrace_bufdata_t *bufdata, void *arg) +{ + dtj_java_consumer_t *jc; + JNIEnv *jenv; + const dtrace_recdesc_t *rec; + dtrace_actkind_t act = DTRACEACT_NONE; + const char *s; + + jobject jstr = NULL; + + /* + * Get the thread-specific java consumer. The bufhandler needs access + * to the correct JNI state specific to either the consumer loop or the + * getAggregate() call (aggregation snapshots can be requested + * asynchronously while the consumer loop generates PrintaRecords in + * dtrace_work() for ConsumerListeners). + */ + jc = pthread_getspecific(g_dtj_consumer_key); + jenv = jc->dtjj_jenv; + + /* + * In at least one corner case (printa with multiple aggregations and a + * format string that does not completely specify the tuple), returning + * DTRACE_HANDLE_ABORT does not prevent a subsequent callback to this + * bufhandler. This check ensures that the invalid call is ignored. + */ + if ((*jenv)->ExceptionCheck(jenv)) { + return (DTRACE_HANDLE_ABORT); + } + + if (bufdata->dtbda_aggdata) { + return (dtj_agghandler(bufdata, jc)); + } + + s = bufdata->dtbda_buffered; + if (s == NULL) { + return (DTRACE_HANDLE_OK); + } + + rec = bufdata->dtbda_recdesc; + if (rec) { + act = rec->dtrd_action; + } + + switch (act) { + case DTRACEACT_DIFEXPR: + /* trace() action */ + break; + case DTRACEACT_PRINTF: + /* + * Only the formatted string was not available to dtj_chewrec(), + * so we attach that now. + */ + jstr = dtj_NewStringNative(jenv, s); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdataset_formatted_jm, jstr); + (*jenv)->DeleteLocalRef(jenv, jstr); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + break; + case DTRACEACT_STACK: + case DTRACEACT_USTACK: + case DTRACEACT_JSTACK: + /* stand-alone stack(), ustack(), or jstack() action */ + jstr = (*jenv)->NewStringUTF(jenv, s); + if (!jstr) { + /* OutOfMemoryError pending */ + return (DTRACE_HANDLE_ABORT); + } + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdataadd_stack_jm, + jc->dtjj_consumer->dtjc_probedata_rec_i, jstr); + (*jenv)->DeleteLocalRef(jenv, jstr); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + break; + default: + /* + * The record handler dtj_chewrec() defers nothing else to this + * bufhandler. + */ + break; + } + + return (DTRACE_HANDLE_OK); +} + +static boolean_t +dtj_is_stack_action(dtrace_actkind_t act) +{ + boolean_t stack_action; + switch (act) { + case DTRACEACT_STACK: + case DTRACEACT_USTACK: + case DTRACEACT_JSTACK: + stack_action = B_TRUE; + break; + default: + stack_action = B_FALSE; + } + return (stack_action); +} + +/* + * Called by get_aggregate() to clear only those aggregations specified by the + * caller. + */ +static int +dtj_clear(const dtrace_aggdata_t *data, void *arg) +{ + dtj_java_consumer_t *jc = arg; + jboolean cleared = JNI_FALSE; + + jstring jname = NULL; + + if (jc->dtjj_aggregate_spec) { + JNIEnv *jenv = jc->dtjj_jenv; + + dtrace_aggdesc_t *aggdesc = data->dtada_desc; + + jname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name); + if (!jname) { + /* java exception pending */ + return (DTRACE_AGGWALK_ABORT); + } + + cleared = (*jenv)->CallBooleanMethod(jenv, + jc->dtjj_aggregate_spec, g_aggspec_cleared_jm, jname); + (*jenv)->DeleteLocalRef(jenv, jname); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_AGGWALK_ABORT); + } + } + + return (cleared ? DTRACE_AGGWALK_CLEAR : DTRACE_AGGWALK_NEXT); +} + +static int64_t +dtj_average(caddr_t addr, uint64_t normal) +{ + /* LINTED - alignment */ + uint64_t *data = (uint64_t *)addr; + + return (data[0] ? + (long long)(data[1] / normal / data[0]) : 0); +} + +static int64_t +dtj_avg_total(caddr_t addr, uint64_t normal) +{ + /* LINTED - alignment */ + uint64_t *data = (uint64_t *)addr; + + return ((long long)(data[1] / normal)); +} + +static int64_t +dtj_avg_count(caddr_t addr) +{ + /* LINTED - alignment */ + uint64_t *data = (uint64_t *)addr; + + return ((long long)data[0]); +} + +static jobject +dtj_new_probedata_stack_record(const dtrace_probedata_t *data, + const dtrace_recdesc_t *rec, dtj_java_consumer_t *jc) +{ + caddr_t addr; + + /* Get raw stack data */ + addr = data->dtpda_data + rec->dtrd_offset; + return (dtj_new_stack_record(addr, rec, jc)); +} + +static jobject +dtj_new_tuple_stack_record(const dtrace_aggdata_t *data, + const dtrace_recdesc_t *rec, const char *s, dtj_java_consumer_t *jc) +{ + caddr_t addr; + JNIEnv *jenv = jc->dtjj_jenv; + + jobjectArray frames = NULL; + jobject jobj = NULL; /* tuple element */ + jstring jstr = NULL; + + /* Get raw stack data */ + addr = data->dtada_data + rec->dtrd_offset; + jobj = dtj_new_stack_record(addr, rec, jc); + if (!jobj) { + return (NULL); /* java exception pending */ + } + + jstr = dtj_NewStringNative(jenv, s); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, jobj); + return (NULL); + } + frames = (*jenv)->CallStaticObjectMethod(jenv, g_stack_jc, + g_parsestack_jsm, jstr); + (*jenv)->DeleteLocalRef(jenv, jstr); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, jobj); + return (NULL); + } + dtj_attach_frames(jc, jobj, frames); + (*jenv)->DeleteLocalRef(jenv, frames); + if ((*jenv)->ExceptionCheck(jenv)) { + return (NULL); + } + + return (jobj); +} + +/* Caller must be holding per-consumer lock */ +static void +dtj_aggwalk_init(dtj_java_consumer_t *jc) +{ + jc->dtjj_consumer->dtjc_aggid = -1; + jc->dtjj_consumer->dtjc_expected = -1; + if (jc->dtjj_tuple != NULL) { + /* assert without crashing */ + dtj_throw_illegal_state(jc->dtjj_jenv, + "stale aggregation tuple"); + } +} + +static jobject +dtj_new_stack_record(caddr_t addr, const dtrace_recdesc_t *rec, + dtj_java_consumer_t *jc) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + dtrace_actkind_t act; + uint64_t *pc; + pid_t pid = -1; + int size; /* size of raw bytes not including trailing zeros */ + int i; /* index of last non-zero byte */ + + jbyteArray raw = NULL; + jobject stack = NULL; /* return value */ + + /* trim trailing zeros */ + for (i = rec->dtrd_size - 1; (i >= 0) && !addr[i]; --i) { + } + size = (i + 1); + raw = (*jenv)->NewByteArray(jenv, size); + if (!raw) { + return (NULL); /* OutOfMemoryError pending */ + } + (*jenv)->SetByteArrayRegion(jenv, raw, 0, size, + (const jbyte *)addr); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + (*jenv)->DeleteLocalRef(jenv, raw); + return (NULL); + } + + /* Create StackValueRecord instance from raw stack data */ + act = rec->dtrd_action; + switch (act) { + case DTRACEACT_STACK: + stack = (*jenv)->NewObject(jenv, g_stack_jc, + g_stackinit_jm, raw); + break; + case DTRACEACT_USTACK: + case DTRACEACT_JSTACK: + /* Get pid of user process */ + pc = (uint64_t *)(uintptr_t)addr; + pid = (pid_t)*pc; + stack = (*jenv)->NewObject(jenv, g_ustack_jc, + g_ustackinit_jm, pid, raw); + break; + default: + dtj_throw_illegal_argument(jenv, + "Expected stack action, got %d\n", act); + } + (*jenv)->DeleteLocalRef(jenv, raw); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (NULL); + } + return (stack); +} + +/* + * Return NULL if java exception pending, otherwise return Distribution value. + */ +static jobject +dtj_new_distribution(const dtrace_aggdata_t *data, const dtrace_recdesc_t *rec, + dtj_java_consumer_t *jc) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + jlongArray jbuckets = NULL; + jobject jdist = NULL; /* return value */ + + dtrace_actkind_t act = rec->dtrd_action; + /* LINTED - alignment */ + int64_t *aggbuckets = (int64_t *) + (data->dtada_data + rec->dtrd_offset); + size_t size = rec->dtrd_size; + int64_t value; + uint64_t normal = data->dtada_normal; + int64_t base, step; + int levels; + int n; /* number of buckets */ + + /* distribution */ + if (act == DTRACEAGG_LQUANTIZE) { + /* first "bucket" used for range and step */ + value = *aggbuckets++; + base = DTRACE_LQUANTIZE_BASE(value); + step = DTRACE_LQUANTIZE_STEP(value); + levels = DTRACE_LQUANTIZE_LEVELS(value); + size -= sizeof (int64_t); /* exclude non-bucket */ + /* + * Add one for the base bucket and one for the bucket of values + * less than the base. + */ + n = levels + 2; + } else { + n = DTRACE_QUANTIZE_NBUCKETS; + levels = n - 1; /* levels excludes base */ + } + if (size != (n * sizeof (uint64_t)) || n < 1) { + dtj_throw_illegal_state(jenv, + "size mismatch: record %d, buckets %d", size, + (n * sizeof (uint64_t))); + WRAP_EXCEPTION(jenv); + return (NULL); + } + + jbuckets = (*jenv)->NewLongArray(jenv, n); + if (!jbuckets) { + return (NULL); /* exception pending */ + } + if (n > 0) { + (*jenv)->SetLongArrayRegion(jenv, jbuckets, 0, n, aggbuckets); + /* check for ArrayIndexOutOfBounds */ + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + (*jenv)->DeleteLocalRef(jenv, jbuckets); + return (NULL); + } + } + + if (act == DTRACEAGG_LQUANTIZE) { + /* Must pass 64-bit base and step or constructor gets junk. */ + jdist = (*jenv)->NewObject(jenv, g_ldist_jc, g_ldistinit_jm, + base, step, jbuckets); + } else { + jdist = (*jenv)->NewObject(jenv, g_dist_jc, g_distinit_jm, + jbuckets); + } + + (*jenv)->DeleteLocalRef(jenv, jbuckets); + if (!jdist) { + return (NULL); /* exception pending */ + } + + if (normal != 1) { + (*jenv)->CallVoidMethod(jenv, jdist, g_dist_normal_jm, normal); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + (*jenv)->DeleteLocalRef(jenv, jdist); + return (NULL); + } + } + return (jdist); +} + +static void +dtj_attach_frames(dtj_java_consumer_t *jc, jobject stack, + jobjectArray frames) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + if ((*jenv)->IsInstanceOf(jenv, stack, g_stack_jc)) { + (*jenv)->CallVoidMethod(jenv, stack, g_stackset_frames_jm, + frames); + } else if ((*jenv)->IsInstanceOf(jenv, stack, g_ustack_jc)) { + (*jenv)->CallVoidMethod(jenv, stack, g_ustackset_frames_jm, + frames); + } +} + +/* + * Note: It is not valid to look outside the current libdtrace record in the + * given aggdata (except to get the aggregation ID from the first record). + * + * Return DTRACE_HANDLE_ABORT if java exception pending, otherwise + * DTRACE_HANDLE_OK. + */ +static int +dtj_agghandler(const dtrace_bufdata_t *bufdata, dtj_java_consumer_t *jc) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + const dtrace_aggdata_t *aggdata = bufdata->dtbda_aggdata; + const dtrace_aggdesc_t *aggdesc; + const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc; + const char *s = bufdata->dtbda_buffered; + dtrace_actkind_t act = DTRACEACT_NONE; + int64_t aggid; + + jobject jobj = NULL; + + if (aggdata == NULL) { + /* Assert without crashing */ + dtj_throw_illegal_state(jenv, "null aggdata"); + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + aggdesc = aggdata->dtada_desc; + + /* + * Get the aggregation ID from the first record. + */ + /* LINTED - alignment */ + aggid = *((int64_t *)(aggdata->dtada_data + + aggdesc->dtagd_rec[0].dtrd_offset)); + if (aggid < 0) { + /* Assert without crashing */ + dtj_throw_illegal_argument(jenv, "negative aggregation ID"); + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + + if (jc->dtjj_consumer->dtjc_printa_snaptime) { + /* Append buffered output if this is a printa() callback. */ + jstring jstr = dtj_NewStringNative(jenv, s); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + /* + * StringBuffer append() returns a reference to the + * StringBuffer; must not leak the returned reference. + */ + jobj = (*jenv)->CallObjectMethod(jenv, + jc->dtjj_printa_buffer, g_buf_append_str_jm, jstr); + (*jenv)->DeleteLocalRef(jenv, jstr); + (*jenv)->DeleteLocalRef(jenv, jobj); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + } else { + /* + * Test whether to include the aggregation if this is a + * getAggregate() callback. Optimization: perform the inclusion + * test only when the aggregation has changed. + */ + if (aggid != jc->dtjj_consumer->dtjc_aggid) { + jc->dtjj_consumer->dtjc_included = + dtj_is_included(aggdata, jc); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + } + if (!jc->dtjj_consumer->dtjc_included) { + return (DTRACE_HANDLE_OK); + } + } + jc->dtjj_consumer->dtjc_aggid = aggid; + + /* + * Determine the expected number of tuple members. While it is not + * technically valid to look outside the current record in the current + * aggdata, this implementation does so without a known failure case. + * Any method relying only on the current callback record makes riskier + * assumptions and still does not cover every corner case (for example, + * counting the records from index 1 up to and not including the index + * of the current DTRACE_BUFDATA_AGGVAL record, which fails when a + * format string specifies the value ahead of one or more tuple + * elements). Knowing that the calculation of the expected tuple size + * is technically invalid (because it looks outside the current record), + * we make the calculation at the earliest opportunity, before anything + * might happen to invalidate any part of the aggdata. It ought to be + * safe in any case: dtrd_action and dtrd_size do not appear ever to be + * overwritten, and dtrd_offset is not used outside the current record. + * + * It is possible (if the assumptions here ever prove untrue) that the + * libdtrace buffered output handler may need to be enhanced to provide + * the expected number of tuple members. + */ + if (jc->dtjj_consumer->dtjc_expected < 0) { + int r; + for (r = 1; r < aggdesc->dtagd_nrecs; ++r) { + act = aggdesc->dtagd_rec[r].dtrd_action; + if (DTRACEACT_ISAGG(act) || + aggdesc->dtagd_rec[r].dtrd_size == 0) { + break; + } + } + jc->dtjj_consumer->dtjc_expected = r - 1; + } + + if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGKEY) { + /* record value is a tuple member */ + + if (jc->dtjj_tuple == NULL) { + jc->dtjj_tuple = (*jenv)->NewObject(jenv, + g_tuple_jc, g_tupleinit_jm); + if (!jc->dtjj_tuple) { + /* java exception pending */ + return (DTRACE_HANDLE_ABORT); + } + } + + act = rec->dtrd_action; + + switch (act) { + case DTRACEACT_STACK: + case DTRACEACT_USTACK: + case DTRACEACT_JSTACK: + jobj = dtj_new_tuple_stack_record(aggdata, rec, s, jc); + break; + default: + jobj = dtj_recdata(jc, rec->dtrd_size, + (aggdata->dtada_data + rec->dtrd_offset)); + } + + if (!jobj) { + /* java exception pending */ + return (DTRACE_HANDLE_ABORT); + } + + (*jenv)->CallVoidMethod(jenv, jc->dtjj_tuple, + g_tupleadd_jm, jobj); + (*jenv)->DeleteLocalRef(jenv, jobj); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + } else if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGVAL) { + /* + * Record value is that of an aggregating action. The printa() + * format string may place the tuple ahead of the aggregation + * value(s), so we can't be sure we have the tuple until we get + * the AGGLAST flag indicating the last callback associated with + * the current tuple. Save the aggregation value or values + * (multiple values if more than one aggregation is passed to + * printa()) until then. + */ + dtj_aggval_t *aggval; + + jstring jvalue = NULL; + + jvalue = dtj_new_aggval(jc, aggdata, rec); + if (!jvalue) { + /* java exception pending */ + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + aggval = dtj_aggval_create(jenv, jvalue, aggdesc->dtagd_name, + aggid); + if (!aggval) { + /* OutOfMemoryError pending */ + (*jenv)->DeleteLocalRef(jenv, jvalue); + return (DTRACE_HANDLE_ABORT); + } + if (!dtj_list_add(jc->dtjj_aggval_list, aggval)) { + /* deletes jvalue reference */ + dtj_aggval_destroy(aggval, jenv); + dtj_throw_out_of_memory(jenv, "Failed to add aggval"); + return (DTRACE_HANDLE_ABORT); + } + } + + if (bufdata->dtbda_flags & DTRACE_BUFDATA_AGGLAST) { + /* No more values associated with the current tuple. */ + + dtj_aggval_t *aggval; + uu_list_walk_t *itr; + int tuple_member_count; + + jobject jrec = NULL; + jstring jname = NULL; + + if (jc->dtjj_consumer->dtjc_expected == 0) { + /* + * singleton aggregation declared in D with no square + * brackets + */ + jc->dtjj_tuple = (*jenv)->GetStaticObjectField(jenv, + g_tuple_jc, g_tuple_EMPTY_jsf); + if (jc->dtjj_tuple == NULL) { + dtj_throw_out_of_memory(jenv, + "Failed to reference Tuple.EMPTY"); + return (DTRACE_HANDLE_ABORT); + } + } + + if (jc->dtjj_tuple == NULL) { + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdatainvalidate_printa_jm); + } + + tuple_member_count = (*jenv)->CallIntMethod(jenv, + jc->dtjj_tuple, g_tuplesize_jm); + if (tuple_member_count < + jc->dtjj_consumer->dtjc_expected) { + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdatainvalidate_printa_jm); + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple); + jc->dtjj_tuple = NULL; + } + + if (jc->dtjj_tuple == NULL) { + goto printa_output; + } + + itr = uu_list_walk_start(jc->dtjj_aggval_list, 0); + while ((aggval = uu_list_walk_next(itr)) != NULL) { + /* + * new AggregationRecord: Combine the aggregation value + * with the saved tuple and add it to the current + * Aggregate or PrintaRecord. + */ + jrec = (*jenv)->NewObject(jenv, g_aggrec_jc, + g_aggrecinit_jm, jc->dtjj_tuple, + aggval->dtja_value); + (*jenv)->DeleteLocalRef(jenv, aggval->dtja_value); + aggval->dtja_value = NULL; + if (!jrec) { + /* java exception pending */ + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + + /* aggregation name */ + jname = (*jenv)->NewStringUTF(jenv, + aggval->dtja_aggname); + if (!jname) { + /* OutOfMemoryError pending */ + (*jenv)->DeleteLocalRef(jenv, jrec); + return (DTRACE_HANDLE_ABORT); + } + + /* + * If the printa() format string specifies the value of + * the aggregating action multiple times, PrintaRecord + * ignores the attempt to add the duplicate record. + */ + if (jc->dtjj_consumer->dtjc_printa_snaptime) { + /* add to PrintaRecord */ + (*jenv)->CallVoidMethod(jenv, + jc->dtjj_probedata, + g_pdataadd_aggrec_jm, + jname, aggval->dtja_aggid, jrec); + } else { + /* add to Aggregate */ + (*jenv)->CallVoidMethod(jenv, + jc->dtjj_aggregate, g_aggaddrec_jm, + jname, aggval->dtja_aggid, jrec); + } + + (*jenv)->DeleteLocalRef(jenv, jrec); + (*jenv)->DeleteLocalRef(jenv, jname); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + } + uu_list_walk_end(itr); + dtj_list_clear(jc->dtjj_aggval_list, dtj_aggval_destroy, + jenv); + +printa_output: + if (jc->dtjj_consumer->dtjc_printa_snaptime) { + /* + * Get the formatted string associated with the current + * tuple if this is a printa() callback. + */ + jstring jstr = (*jenv)->CallObjectMethod(jenv, + jc->dtjj_printa_buffer, g_tostring_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + /* + * Clear the StringBuffer: this does not throw + * exceptions. Reuse the StringBuffer until the end of + * the current probedata then dispose of it. + */ + (*jenv)->CallVoidMethod(jenv, jc->dtjj_printa_buffer, + g_bufsetlen_jm, 0); + /* Add formatted string to PrintaRecord */ + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probedata, + g_pdataadd_printa_str_jm, jc->dtjj_tuple, jstr); + (*jenv)->DeleteLocalRef(jenv, jstr); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTRACE_HANDLE_ABORT); + } + } + + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple); + jc->dtjj_tuple = NULL; + jc->dtjj_consumer->dtjc_expected = -1; + } + + return (DTRACE_HANDLE_OK); +} + +/* + * Return B_TRUE if the aggregation is included, B_FALSE otherwise. Only in the + * latter case might there be an exception pending. + */ +static boolean_t +dtj_is_included(const dtrace_aggdata_t *data, dtj_java_consumer_t *jc) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + if (jc->dtjj_aggregate_spec) { + jboolean included; + jstring aggname = NULL; + + const dtrace_aggdesc_t *aggdesc = data->dtada_desc; + aggname = (*jenv)->NewStringUTF(jenv, aggdesc->dtagd_name); + if (!aggname) { + /* java exception pending */ + return (B_FALSE); + } + + included = (*jenv)->CallBooleanMethod(jenv, + jc->dtjj_aggregate_spec, g_aggspec_included_jm, + aggname); + (*jenv)->DeleteLocalRef(jenv, aggname); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (B_FALSE); + } + + return (included); + } + + return (B_TRUE); +} + +/* + * Return NULL if a java exception is pending, otherwise return a new + * AggregationValue instance. + */ +static jobject +dtj_new_aggval(dtj_java_consumer_t *jc, const dtrace_aggdata_t *data, + const dtrace_recdesc_t *rec) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + jobject jvalue = NULL; /* return value */ + + dtrace_actkind_t act; + uint64_t normal; + caddr_t addr; + int64_t value; + + act = rec->dtrd_action; + normal = data->dtada_normal; + addr = data->dtada_data + rec->dtrd_offset; + if (act == DTRACEAGG_AVG) { + value = dtj_average(addr, normal); + } else { + /* LINTED - alignment */ + value = (*((int64_t *)addr)) / normal; + } + + if (act == DTRACEAGG_QUANTIZE || act == DTRACEAGG_LQUANTIZE) { + jvalue = dtj_new_distribution(data, rec, jc); + } else { + switch (act) { + case DTRACEAGG_COUNT: + jvalue = (*jenv)->NewObject(jenv, g_aggcount_jc, + g_aggcountinit_jm, value); + break; + case DTRACEAGG_SUM: + jvalue = (*jenv)->NewObject(jenv, g_aggsum_jc, + g_aggsuminit_jm, value); + break; + case DTRACEAGG_AVG: + jvalue = (*jenv)->NewObject(jenv, g_aggavg_jc, + g_aggavginit_jm, value, dtj_avg_total(addr, + normal), dtj_avg_count(addr)); + break; + case DTRACEAGG_MIN: + jvalue = (*jenv)->NewObject(jenv, g_aggmin_jc, + g_aggmininit_jm, value); + break; + case DTRACEAGG_MAX: + jvalue = (*jenv)->NewObject(jenv, g_aggmax_jc, + g_aggmaxinit_jm, value); + break; + default: + jvalue = NULL; + dtj_throw_illegal_argument(jenv, + "unexpected aggregation action: %d", act); + } + } + + return (jvalue); +} + +/* + * Stops the given consumer if it is running. Throws DTraceException if + * dtrace_stop() fails and no other exception is already pending. Clears and + * rethrows any pending exception in order to grab the global lock safely. + */ +void +dtj_stop(dtj_java_consumer_t *jc) +{ + JNIEnv *jenv; + int rc; + jthrowable e; + + switch (jc->dtjj_consumer->dtjc_state) { + case DTJ_CONSUMER_GO: + case DTJ_CONSUMER_START: + break; + default: + return; + } + + jenv = jc->dtjj_jenv; + e = (*jenv)->ExceptionOccurred(jenv); + if (e) { + (*jenv)->ExceptionClear(jenv); + } + + (*jenv)->MonitorEnter(jenv, g_caller_jc); + if ((*jenv)->ExceptionCheck(jenv)) { + goto rethrow; + } + + rc = dtrace_status(jc->dtjj_consumer->dtjc_dtp); + if (rc != DTRACE_STATUS_STOPPED) { + rc = dtrace_stop(jc->dtjj_consumer->dtjc_dtp); + } + + (*jenv)->MonitorExit(jenv, g_caller_jc); + if ((*jenv)->ExceptionCheck(jenv)) { + goto rethrow; + } + + if (rc == -1) { + (*jenv)->MonitorEnter(jenv, g_caller_jc); + if ((*jenv)->ExceptionCheck(jenv)) { + goto rethrow; + } + /* Do not wrap DTraceException */ + dtj_throw_dtrace_exception(jc, + "couldn't stop tracing: %s", + dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp, + dtrace_errno(jc->dtjj_consumer->dtjc_dtp))); + /* safe to call with pending exception */ + (*jenv)->MonitorExit(jenv, g_caller_jc); + } else { + jc->dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP; + } + +rethrow: + if (e) { + if ((*jenv)->ExceptionCheck(jenv)) { + /* + * Favor earlier pending exception over + * exception thrown in this function. + */ + (*jenv)->ExceptionClear(jenv); + } + (*jenv)->Throw(jenv, e); + (*jenv)->DeleteLocalRef(jenv, e); + } +} + +/* + * Return Aggregate instance, or null if java exception pending. + */ +jobject +dtj_get_aggregate(dtj_java_consumer_t *jc) +{ + JNIEnv *jenv = jc->dtjj_jenv; + hrtime_t snaptime; + int rc; + + jobject aggregate = NULL; + + /* + * Aggregations must be snapped, walked, and cleared atomically, + * otherwise clearing loses data accumulated since the most recent snap. + * This per-consumer lock prevents dtrace_work() from snapping or + * clearing aggregations while we're in the middle of this atomic + * operation, so we continue to hold it until done clearing. + */ + (*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (NULL); + } + + dtj_aggwalk_init(jc); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + /* release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (NULL); + } + + /* + * Snap aggregations + * + * We need to record the snaptime here for the caller. Leaving it to + * the caller to record the snaptime before calling getAggregate() may + * be inaccurate because of the indeterminate delay waiting on the + * consumer lock before calling dtrace_aggregate_snap(). + */ + snaptime = gethrtime(); + if (dtrace_aggregate_snap(jc->dtjj_consumer->dtjc_dtp) != 0) { + dtj_error_t e; + if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) { + /* Do not wrap DTraceException */ + dtj_throw_dtrace_exception(jc, e.dtje_message); + } + /* release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (NULL); + } + + /* Create the Java representation of the aggregate snapshot. */ + aggregate = (*jenv)->NewObject(jenv, g_agg_jc, g_agginit_jm, + snaptime); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + /* release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (NULL); + } + jc->dtjj_aggregate = aggregate; + + /* + * Walk the aggregate, converting the data into Java Objects. Traverse + * in order by aggregation ID first and tuple second by using + * dtrace_aggregate_walk_keysorted (uses varkeycmp). We cannot do the + * same for aggregations generated by the printa() action, since + * dtrace_work() traverses aggregation data in the order determined by + * the various "aggsort" options. Functions used by both the consumer + * loop and the competing getAggregate() thread must not depend on the + * ordering of records by tuple key. + * + * It is impractical to hold the global lock around + * dtrace_aggregate_print(), since it may take a long time (e.g. an + * entire second) if it performs expensive conversions such as that + * needed for user stack traces. Most libdtrace functions are not + * guaranteed to be MT-safe, even when each thread has its own dtrace + * handle; or even if they are safe, there is no guarantee that future + * changes may not make them unsafe. Fortunately in this case, however, + * only a per-consumer lock is necessary to avoid conflict with + * dtrace_work() running in another thread (the consumer loop). + */ + rc = dtrace_aggregate_print(jc->dtjj_consumer->dtjc_dtp, NULL, + dtrace_aggregate_walk_keysorted); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + /* release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (NULL); + } + if (rc != 0) { + dtj_error_t e; + if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) { + /* release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (NULL); + } + + if (e.dtje_number != EINTR) { + /* Do not wrap DTraceException */ + dtj_throw_dtrace_exception(jc, e.dtje_message); + /* release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (NULL); + } + } + + dtj_aggwalk_init(jc); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + /* release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (NULL); + } + + /* + * dtrace_aggregate_clear() clears all aggregations, and we need to + * clear aggregations selectively. It also fails to preserve the + * lquantize() range and step size; using aggregate_walk() to clear + * aggregations does not have this problem. + */ + rc = dtrace_aggregate_walk(jc->dtjj_consumer->dtjc_dtp, dtj_clear, jc); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + /* release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (NULL); + } + if (rc != 0) { + dtj_error_t e; + if (dtj_get_dtrace_error(jc, &e) == DTJ_OK) { + /* Do not wrap DTraceException */ + dtj_throw_dtrace_exception(jc, e.dtje_message); + } + /* release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (NULL); + } + + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (NULL); + } + + aggregate = jc->dtjj_aggregate; + jc->dtjj_aggregate = NULL; + + return (aggregate); +} + +/* + * Process any requests, such as the setting of runtime options, enqueued during + * dtrace_sleep(). A Java exception is pending if this function returns + * DTJ_ERR. + */ +static dtj_status_t +dtj_process_requests(dtj_java_consumer_t *jc) +{ + dtj_request_t *r; + uu_list_t *list = jc->dtjj_consumer->dtjc_request_list; + pthread_mutex_t *list_lock = &jc->dtjj_consumer-> + dtjc_request_list_lock; + const char *opt; + const char *val; + + (void) pthread_mutex_lock(list_lock); + while (!dtj_list_empty(list)) { + r = uu_list_first(list); + uu_list_remove(list, r); + + switch (r->dtjr_type) { + case DTJ_REQUEST_OPTION: + opt = dtj_string_list_first(r->dtjr_args); + val = dtj_string_list_last(r->dtjr_args); + if (dtrace_setopt(jc->dtjj_consumer->dtjc_dtp, opt, + val) == -1) { + /* Do not wrap DTraceException */ + dtj_throw_dtrace_exception(jc, + "failed to set %s: %s", opt, + dtrace_errmsg(jc->dtjj_consumer->dtjc_dtp, + dtrace_errno(jc->dtjj_consumer->dtjc_dtp))); + dtj_request_destroy(r, NULL); + (void) pthread_mutex_unlock(list_lock); + return (DTJ_ERR); + } + break; + } + dtj_request_destroy(r, NULL); + } + (void) pthread_mutex_unlock(list_lock); + return (DTJ_OK); +} + +/* + * Return DTJ_OK if the consumer loop is stopped normally by either the exit() + * action or the Consumer stop() method. Otherwise return DTJ_ERR if the + * consumer loop terminates abnormally with an exception pending. + */ +dtj_status_t +dtj_consume(dtj_java_consumer_t *jc) +{ + JNIEnv *jenv = jc->dtjj_jenv; + dtrace_hdl_t *dtp = jc->dtjj_consumer->dtjc_dtp; + boolean_t done = B_FALSE; + dtj_error_t e; + + do { + if (!jc->dtjj_consumer->dtjc_interrupt) { + dtrace_sleep(dtp); + } + + if (jc->dtjj_consumer->dtjc_interrupt) { + done = B_TRUE; + dtj_stop(jc); + if ((*jenv)->ExceptionCheck(jenv)) { + /* + * Exception left pending by Consumer + * getAggregate() method. + */ + return (DTJ_ERR); + } + } else if (jc->dtjj_consumer->dtjc_process_list != NULL) { + int nprocs = uu_list_numnodes(jc->dtjj_consumer-> + dtjc_process_list); + if (jc->dtjj_consumer->dtjc_procs_ended == nprocs) { + done = B_TRUE; + dtj_stop(jc); + } + } + + /* + * Functions like dtrace_setopt() are not safe to call during + * dtrace_sleep(). Check the request list every time we wake up + * from dtrace_sleep(). + */ + if (!done) { + if (dtj_process_requests(jc) != DTJ_OK) { + /* Do not wrap DTraceException */ + return (DTJ_ERR); + } + } + + /* + * Use the per-consumer lock to avoid conflict with + * get_aggregate() called from another thread. + */ + (*jenv)->MonitorEnter(jenv, jc->dtjj_consumer_lock); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTJ_ERR); + } + (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, + g_interval_began_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (DTJ_ERR); + } + jc->dtjj_consumer->dtjc_printa_snaptime = gethrtime(); + switch (dtrace_work(dtp, NULL, dtj_chew, dtj_chewrec, jc)) { + case DTRACE_WORKSTATUS_DONE: + done = B_TRUE; + break; + case DTRACE_WORKSTATUS_OKAY: + break; + default: + /* + * Check for a pending exception that got us to this + * error workstatus case. + */ + if ((*jenv)->ExceptionCheck(jenv)) { + /* + * Ensure valid initial state before releasing + * the consumer lock + */ + jc->dtjj_consumer->dtjc_printa_snaptime = 0; + /* Do not wrap DTraceException */ + /* Release per-consumer lock */ + (*jenv)->MonitorExit(jenv, + jc->dtjj_consumer_lock); + return (DTJ_ERR); + } + + if (dtj_get_dtrace_error(jc, &e) != DTJ_OK) { + /* java exception pending */ + jc->dtjj_consumer->dtjc_printa_snaptime = 0; + /* Release per-consumer lock */ + (*jenv)->MonitorExit(jenv, + jc->dtjj_consumer_lock); + return (DTJ_ERR); + } + + if (e.dtje_number != EINTR) { + /* Do not wrap DTraceException */ + dtj_throw_dtrace_exception(jc, e.dtje_message); + jc->dtjj_consumer->dtjc_printa_snaptime = 0; + /* Release per-consumer lock */ + (*jenv)->MonitorExit(jenv, + jc->dtjj_consumer_lock); + return (DTJ_ERR); + } + } + /* + * Check for ConsumerException before doing anything else with + * the JNIEnv. + */ + if ((*jenv)->ExceptionCheck(jenv)) { + /* + * Do not wrap exception thrown from ConsumerListener. + */ + jc->dtjj_consumer->dtjc_printa_snaptime = 0; + /* Release per-consumer lock */ + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + return (DTJ_ERR); + } + jc->dtjj_consumer->dtjc_printa_snaptime = 0; + /* + * Notify ConsumerListeners the the dtrace_work() interval ended + * before releasing the lock. + */ + (*jenv)->CallVoidMethod(jenv, jc->dtjj_caller, + g_interval_ended_jm); + (*jenv)->MonitorExit(jenv, jc->dtjj_consumer_lock); + if ((*jenv)->ExceptionCheck(jenv)) { + WRAP_EXCEPTION(jenv); + return (DTJ_ERR); + } + + /* + * Check for a temporarily cleared exception set by a handler + * that could not safely leave the exception pending because it + * could not return an abort signal. Rethrow it now that it's + * safe to do so (when it's possible to ensure that no JNI calls + * will be made that are unsafe while an exception is pending). + */ + if (jc->dtjj_exception) { + (*jenv)->Throw(jenv, jc->dtjj_exception); + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_exception); + jc->dtjj_exception = NULL; + return (DTJ_ERR); + } + } while (!done); + + return (DTJ_OK); +} diff --git a/usr/src/lib/libdtrace_jni/common/dtj_error.c b/usr/src/lib/libdtrace_jni/common/dtj_error.c new file mode 100644 index 0000000000..7806fe90d4 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtj_error.c @@ -0,0 +1,79 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <dtrace.h> + +/* + * Not currently providing name equivalents of dtrace_errno, only the error + * message provided by libdtrace (which cannot be localized). The reason is + * that the EDT_ enumeration is still private. The DTRACEFLT_ values are + * public, however, so the API provides string equivalents for runtime faults + * encountered in the error handler (see dtrace_handle_err()). The API provides + * the error as a string rather than an integer so that user applications do not + * break if the integer values change. + */ + +const char * +dtj_get_fault_name(int fault) +{ + const char *name = NULL; + + switch (fault) { + case DTRACEFLT_BADADDR: + name = "DTRACEFLT_BADADDR"; + break; + case DTRACEFLT_BADALIGN: + name = "DTRACEFLT_BADALIGN"; + break; + case DTRACEFLT_ILLOP: + name = "DTRACEFLT_ILLOP"; + break; + case DTRACEFLT_DIVZERO: + name = "DTRACEFLT_DIVZERO"; + break; + case DTRACEFLT_NOSCRATCH: + name = "DTRACEFLT_NOSCRATCH"; + break; + case DTRACEFLT_KPRIV: + name = "DTRACEFLT_KPRIV"; + break; + case DTRACEFLT_UPRIV: + name = "DTRACEFLT_UPRIV"; + break; + case DTRACEFLT_TUPOFLOW: + name = "DTRACEFLT_TUPOFLOW"; + break; + case DTRACEFLT_LIBRARY: + name = "DTRACEFLT_LIBRARY"; + break; + default: + name = NULL; + } + + return (name); +} diff --git a/usr/src/lib/libdtrace_jni/common/dtj_jnitab.c b/usr/src/lib/libdtrace_jni/common/dtj_jnitab.c new file mode 100644 index 0000000000..f0f4cc6c8e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtj_jnitab.c @@ -0,0 +1,853 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stddef.h> +#include <limits.h> +#include <strings.h> +#include <pthread.h> +#include <dtrace_jni.h> + +/* + * dtj_jnitab.c defines the JNI table of classes, methods, and fields belonging + * to the Java DTrace API. Another JNI table defining classes from the JDK is + * defined in dtj_util.c. Utility functions specific to the Java DTrace API are + * also defined here, while general utilities are defined in dtj_util.c. + */ + +static uu_list_pool_t *g_request_pool = NULL; +static uu_list_pool_t *g_program_pool = NULL; +static uu_list_pool_t *g_aggval_pool = NULL; + +static boolean_t dtj_check_request_pool(void); +static boolean_t dtj_check_program_pool(void); +static boolean_t dtj_check_aggval_pool(void); + +/* LocalConsumer */ +jclass g_caller_jc = 0; +jmethodID g_gethandle_jm = 0; +jmethodID g_sethandle_jm = 0; +jmethodID g_pdatanext_jm = 0; +jmethodID g_drop_jm = 0; +jmethodID g_error_jm = 0; +jmethodID g_proc_jm = 0; +jmethodID g_interval_began_jm = 0; +jmethodID g_interval_ended_jm = 0; +jfieldID g_consumer_lock_jf = 0; + +/* DTraceException */ +jclass g_dtx_jc = 0; +jmethodID g_dtxinit_jm = 0; + +/* InterfaceAttributes */ +jclass g_attr_jc = 0; +jmethodID g_attrinit_jm = 0; +jmethodID g_attrset_name_jm = 0; +jmethodID g_attrset_data_jm = 0; +jmethodID g_attrset_class_jm = 0; + +/* ProbeDescription */ +jclass g_probedesc_jc = 0; +jmethodID g_probedescinit_jm = 0; +jfieldID g_probedesc_id_jf = 0; + +/* ProbeInfo */ +jclass g_probeinfo_jc = 0; +jmethodID g_probeinfoinit_jm = 0; + +/* Probe */ +jclass g_probe_jc = 0; +jmethodID g_probeinit_jm = 0; + +/* Program */ +jclass g_program_jc = 0; +jmethodID g_proginit_jm = 0; +jfieldID g_progid_jf = 0; +jfieldID g_proginfo_jf = 0; + +/* Program.File */ +jclass g_programfile_jc = 0; +jmethodID g_fproginit_jm = 0; + +/* ProgramInfo */ +jclass g_proginfo_jc = 0; +jmethodID g_proginfoinit_jm = 0; + +/* Flow */ +jclass g_flow_jc = 0; +jmethodID g_flowinit_jm = 0; + +/* ProbeData */ +jclass g_pdata_jc = 0; +jmethodID g_pdatainit_jm = 0; +jmethodID g_pdataadd_jm = 0; +jmethodID g_pdataadd_rec_jm = 0; +jmethodID g_pdataadd_trace_jm = 0; +jmethodID g_pdataadd_stack_jm = 0; +jmethodID g_pdataadd_printf_jm = 0; +jmethodID g_pdataadd_printa_jm = 0; +jmethodID g_pdatainvalidate_printa_jm = 0; +jmethodID g_pdataadd_aggrec_jm = 0; +jmethodID g_pdataadd_printa_str_jm = 0; +jmethodID g_pdataadd_exit_jm = 0; +jmethodID g_pdataattach_jm = 0; +jmethodID g_pdataset_formatted_jm = 0; +jmethodID g_pdataclear_jm = 0; + +/* Drop */ +jclass g_drop_jc = 0; +jmethodID g_dropinit_jm = 0; + +/* Error */ +jclass g_error_jc = 0; +jmethodID g_errinit_jm = 0; + +/* ProcessState */ +jclass g_process_jc = 0; +jmethodID g_procinit_jm = 0; +jmethodID g_procexit_jm = 0; + +/* Aggregate */ +jclass g_agg_jc = 0; +jmethodID g_agginit_jm = 0; +jmethodID g_aggaddrec_jm = 0; + +/* AggregateSpec */ +jclass g_aggspec_jc = 0; +jmethodID g_aggspec_included_jm = 0; +jmethodID g_aggspec_cleared_jm = 0; + +/* Tuple */ +jclass g_tuple_jc = 0; +jmethodID g_tupleinit_jm = 0; +jmethodID g_tupleadd_jm = 0; +jmethodID g_tuplesize_jm = 0; +jfieldID g_tuple_EMPTY_jsf = 0; + +/* AggregationRecord */ +jclass g_aggrec_jc = 0; +jmethodID g_aggrecinit_jm = 0; +jmethodID g_aggrecget_tuple_jm = 0; + +/* SumValue */ +jclass g_aggsum_jc = 0; +jmethodID g_aggsuminit_jm = 0; + +/* CountValue */ +jclass g_aggcount_jc = 0; +jmethodID g_aggcountinit_jm = 0; + +/* AvgValue */ +jclass g_aggavg_jc = 0; +jmethodID g_aggavginit_jm = 0; + +/* MinValue */ +jclass g_aggmin_jc = 0; +jmethodID g_aggmininit_jm = 0; + +/* MaxValue */ +jclass g_aggmax_jc = 0; +jmethodID g_aggmaxinit_jm = 0; + +/* KernelStackRecord */ +jclass g_stack_jc = 0; +jmethodID g_parsestack_jsm = 0; +jmethodID g_stackinit_jm = 0; +jmethodID g_stackset_frames_jm = 0; + +/* UserStackRecord */ +jclass g_ustack_jc = 0; +jmethodID g_ustackinit_jm = 0; +jmethodID g_ustackset_frames_jm = 0; + +/* Distribution */ +jclass g_adist_jc = 0; +jmethodID g_dist_normal_jm = 0; + +/* LogDistribution */ +jclass g_dist_jc = 0; +jmethodID g_distinit_jm = 0; + +/* LinearDistribution */ +jclass g_ldist_jc = 0; +jmethodID g_ldistinit_jm = 0; + + +static dtj_status_t +dtj_table_load(JNIEnv *jenv) +{ + static const dtj_table_entry_t table[] = { + /* LocalConsumer */ + { JCLASS, &g_caller_jc, + "org/opensolaris/os/dtrace/LocalConsumer" }, + { JMETHOD, &g_gethandle_jm, "getHandle", "()I" }, + { JMETHOD, &g_sethandle_jm, "setHandle", "(I)V" }, + { JMETHOD, &g_pdatanext_jm, "nextProbeData", + "(Lorg/opensolaris/os/dtrace/ProbeData;)V" }, + { JMETHOD, &g_drop_jm, "dataDropped", + "(Lorg/opensolaris/os/dtrace/Drop;)V" }, + { JMETHOD, &g_error_jm, "errorEncountered", + "(Lorg/opensolaris/os/dtrace/Error;)V" }, + { JMETHOD, &g_proc_jm, "processStateChanged", + "(Lorg/opensolaris/os/dtrace/ProcessState;)V" }, + { JMETHOD, &g_interval_began_jm, "intervalBegan", "()V" }, + { JMETHOD, &g_interval_ended_jm, "intervalEnded", "()V" }, + { JFIELD, &g_consumer_lock_jf, "consumerLock", + "Ljava/lang/Object;" }, + + /* DTraceException */ + { JCLASS, &g_dtx_jc, + "org/opensolaris/os/dtrace/DTraceException" }, + { JMETHOD, &g_dtxinit_jm, CONSTRUCTOR, + "(Ljava/lang/String;)V" }, + + /* InterfaceAttributes */ + { JCLASS, &g_attr_jc, + "org/opensolaris/os/dtrace/InterfaceAttributes" }, + { JMETHOD, &g_attrinit_jm, CONSTRUCTOR, "()V" }, + { JMETHOD, &g_attrset_name_jm, "setNameStability", + "(Ljava/lang/String;)V" }, + { JMETHOD, &g_attrset_data_jm, "setDataStability", + "(Ljava/lang/String;)V" }, + { JMETHOD, &g_attrset_class_jm, "setDependencyClass", + "(Ljava/lang/String;)V" }, + + /* ProbeDescription */ + { JCLASS, &g_probedesc_jc, + "org/opensolaris/os/dtrace/ProbeDescription" }, + { JMETHOD, &g_probedescinit_jm, CONSTRUCTOR, + "(Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Ljava/lang/String;)V" }, + { JFIELD, &g_probedesc_id_jf, "id", "I" }, + + /* ProbeInfo */ + { JCLASS, &g_probeinfo_jc, + "org/opensolaris/os/dtrace/ProbeInfo" }, + { JMETHOD, &g_probeinfoinit_jm, CONSTRUCTOR, + "(Lorg/opensolaris/os/dtrace/InterfaceAttributes;" + "Lorg/opensolaris/os/dtrace/InterfaceAttributes;" + ")V" }, + + /* Probe */ + { JCLASS, &g_probe_jc, "org/opensolaris/os/dtrace/Probe" }, + { JMETHOD, &g_probeinit_jm, CONSTRUCTOR, + "(Lorg/opensolaris/os/dtrace/ProbeDescription;" + "Lorg/opensolaris/os/dtrace/ProbeInfo;)V" }, + + /* Program */ + { JCLASS, &g_program_jc, + "org/opensolaris/os/dtrace/Program" }, + { JMETHOD, &g_proginit_jm, CONSTRUCTOR, "()V" }, + { JFIELD, &g_progid_jf, "id", "I" }, + { JFIELD, &g_proginfo_jf, "info", + "Lorg/opensolaris/os/dtrace/ProgramInfo;" }, + + /* Program.File */ + { JCLASS, &g_programfile_jc, + "org/opensolaris/os/dtrace/Program$File" }, + { JMETHOD, &g_fproginit_jm, CONSTRUCTOR, "()V" }, + + /* ProgramInfo */ + { JCLASS, &g_proginfo_jc, + "org/opensolaris/os/dtrace/ProgramInfo" }, + { JMETHOD, &g_proginfoinit_jm, CONSTRUCTOR, + "(Lorg/opensolaris/os/dtrace/InterfaceAttributes;" + "Lorg/opensolaris/os/dtrace/InterfaceAttributes;" + "I)V" }, + + /* Flow */ + { JCLASS, &g_flow_jc, "org/opensolaris/os/dtrace/Flow" }, + { JMETHOD, &g_flowinit_jm, CONSTRUCTOR, + "(Ljava/lang/String;I)V" }, + + /* ProbeData */ + { JCLASS, &g_pdata_jc, + "org/opensolaris/os/dtrace/ProbeData" }, + { JMETHOD, &g_pdatainit_jm, CONSTRUCTOR, + "(IILorg/opensolaris/os/dtrace/ProbeDescription;" + "Lorg/opensolaris/os/dtrace/Flow;I)V" }, + { JMETHOD, &g_pdataadd_jm, "addDataElement", + "(Ljava/lang/Object;)V" }, + { JMETHOD, &g_pdataadd_rec_jm, "addRecord", + "(Lorg/opensolaris/os/dtrace/Record;)V" }, + { JMETHOD, &g_pdataadd_trace_jm, "addTraceRecord", "(I)V" }, + { JMETHOD, &g_pdataadd_stack_jm, "addStackRecord", + "(ILjava/lang/String;)V" }, + { JMETHOD, &g_pdataadd_printf_jm, "addPrintfRecord", "()V" }, + { JMETHOD, &g_pdataadd_printa_jm, "addPrintaRecord", "(JZ)V" }, + { JMETHOD, &g_pdatainvalidate_printa_jm, + "invalidatePrintaRecord", "()V" }, + { JMETHOD, &g_pdataadd_aggrec_jm, "addAggregationRecord", + "(Ljava/lang/String;J" + "Lorg/opensolaris/os/dtrace/AggregationRecord;)V" }, + { JMETHOD, &g_pdataadd_printa_str_jm, + "addPrintaFormattedString", + "(Lorg/opensolaris/os/dtrace/Tuple;" + "Ljava/lang/String;)V" }, + { JMETHOD, &g_pdataadd_exit_jm, "addExitRecord", "(I)V" }, + { JMETHOD, &g_pdataattach_jm, "attachRecordElements", + "(II)V" }, + { JMETHOD, &g_pdataset_formatted_jm, "setFormattedString", + "(Ljava/lang/String;)V" }, + { JMETHOD, &g_pdataclear_jm, "clearNativeElements", "()V" }, + + /* Drop */ + { JCLASS, &g_drop_jc, "org/opensolaris/os/dtrace/Drop" }, + { JMETHOD, &g_dropinit_jm, CONSTRUCTOR, + "(ILjava/lang/String;JJLjava/lang/String;)V" }, + + /* Error */ + { JCLASS, &g_error_jc, "org/opensolaris/os/dtrace/Error" }, + { JMETHOD, &g_errinit_jm, CONSTRUCTOR, + "(Lorg/opensolaris/os/dtrace/ProbeDescription;IIII" + "Ljava/lang/String;JLjava/lang/String;)V" }, + + /* ProcessState */ + { JCLASS, &g_process_jc, + "org/opensolaris/os/dtrace/ProcessState" }, + { JMETHOD, &g_procinit_jm, CONSTRUCTOR, + "(ILjava/lang/String;ILjava/lang/String;" + "Ljava/lang/Integer;Ljava/lang/String;)V" }, + { JMETHOD, &g_procexit_jm, "setExitStatus", "(I)V" }, + + /* Aggregate */ + { JCLASS, &g_agg_jc, "org/opensolaris/os/dtrace/Aggregate" }, + { JMETHOD, &g_agginit_jm, CONSTRUCTOR, "(J)V" }, + { JMETHOD, &g_aggaddrec_jm, "addRecord", + "(Ljava/lang/String;J" + "Lorg/opensolaris/os/dtrace/AggregationRecord;)V" }, + + /* AggregateSpec */ + { JCLASS, &g_aggspec_jc, + "org/opensolaris/os/dtrace/AggregateSpec" }, + { JMETHOD, &g_aggspec_included_jm, "isIncluded", + "(Ljava/lang/String;)Z" }, + { JMETHOD, &g_aggspec_cleared_jm, "isCleared", + "(Ljava/lang/String;)Z" }, + + /* Tuple */ + { JCLASS, &g_tuple_jc, "org/opensolaris/os/dtrace/Tuple" }, + { JMETHOD, &g_tupleinit_jm, CONSTRUCTOR, "()V" }, + { JMETHOD, &g_tupleadd_jm, "addElement", + "(Ljava/lang/Object;)V" }, + { JMETHOD, &g_tuplesize_jm, "size", "()I" }, + { JFIELD_STATIC, &g_tuple_EMPTY_jsf, "EMPTY", + "Lorg/opensolaris/os/dtrace/Tuple;" }, + + /* AggregationRecord */ + { JCLASS, &g_aggrec_jc, + "org/opensolaris/os/dtrace/AggregationRecord" }, + { JMETHOD, &g_aggrecinit_jm, CONSTRUCTOR, + "(Lorg/opensolaris/os/dtrace/Tuple;" + "Lorg/opensolaris/os/dtrace/AggregationValue;)V" }, + { JMETHOD, &g_aggrecget_tuple_jm, "getTuple", + "()Lorg/opensolaris/os/dtrace/Tuple;" }, + + /* SumValue */ + { JCLASS, &g_aggsum_jc, + "org/opensolaris/os/dtrace/SumValue" }, + { JMETHOD, &g_aggsuminit_jm, CONSTRUCTOR, "(J)V" }, + + /* CountValue */ + { JCLASS, &g_aggcount_jc, + "org/opensolaris/os/dtrace/CountValue" }, + { JMETHOD, &g_aggcountinit_jm, CONSTRUCTOR, "(J)V" }, + + /* AvgValue */ + { JCLASS, &g_aggavg_jc, + "org/opensolaris/os/dtrace/AvgValue" }, + { JMETHOD, &g_aggavginit_jm, CONSTRUCTOR, "(JJJ)V" }, + + /* MinValue */ + { JCLASS, &g_aggmin_jc, + "org/opensolaris/os/dtrace/MinValue" }, + { JMETHOD, &g_aggmininit_jm, CONSTRUCTOR, "(J)V" }, + + /* MaxValue */ + { JCLASS, &g_aggmax_jc, + "org/opensolaris/os/dtrace/MaxValue" }, + { JMETHOD, &g_aggmaxinit_jm, CONSTRUCTOR, "(J)V" }, + + /* KernelStackRecord */ + { JCLASS, &g_stack_jc, + "org/opensolaris/os/dtrace/KernelStackRecord" }, + { JMETHOD_STATIC, &g_parsestack_jsm, "parse", + "(Ljava/lang/String;)" + "[Lorg/opensolaris/os/dtrace/StackFrame;" }, + { JMETHOD, &g_stackinit_jm, CONSTRUCTOR, "([B)V" }, + { JMETHOD, &g_stackset_frames_jm, "setStackFrames", + "([Lorg/opensolaris/os/dtrace/StackFrame;)V" }, + + /* UserStackRecord */ + { JCLASS, &g_ustack_jc, + "org/opensolaris/os/dtrace/UserStackRecord" }, + { JMETHOD, &g_ustackinit_jm, CONSTRUCTOR, "(I[B)V" }, + { JMETHOD, &g_ustackset_frames_jm, "setStackFrames", + "([Lorg/opensolaris/os/dtrace/StackFrame;)V" }, + + /* Distribution */ + { JCLASS, &g_adist_jc, + "org/opensolaris/os/dtrace/Distribution" }, + { JMETHOD, &g_dist_normal_jm, "normalizeBuckets", "(J)V" }, + + /* LogDistribution */ + { JCLASS, &g_dist_jc, + "org/opensolaris/os/dtrace/LogDistribution" }, + { JMETHOD, &g_distinit_jm, CONSTRUCTOR, "([J)V" }, + + /* LinearDistribution */ + { JCLASS, &g_ldist_jc, + "org/opensolaris/os/dtrace/LinearDistribution" }, + { JMETHOD, &g_ldistinit_jm, CONSTRUCTOR, "(JJ[J)V" }, + + { DTJ_TYPE_END } + }; + + return (dtj_cache_jni_classes(jenv, table)); +} + +dtj_status_t +dtj_load(JNIEnv *jenv) +{ + if (dtj_load_common(jenv) != DTJ_OK) { + /* Java Error pending */ + return (DTJ_ERR); + } + + return (dtj_table_load(jenv)); +} + +static boolean_t +dtj_check_request_pool(void) +{ + if (!g_request_pool) { + g_request_pool = uu_list_pool_create("g_request_pool", + sizeof (dtj_request_t), + offsetof(dtj_request_t, dtjr_node), + dtj_pointer_list_entry_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (!g_request_pool) { + return (B_FALSE); + } + } + return (B_TRUE); +} + +dtj_request_t * +dtj_request_create(JNIEnv *jenv, dtj_request_type_t type, ...) +{ + dtj_request_t *r; + + if (!dtj_check_request_pool()) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate request pool"); + return (NULL); + } + + r = uu_zalloc(sizeof (dtj_request_t)); + if (r) { + uu_list_node_init(r, &r->dtjr_node, g_request_pool); + r->dtjr_type = type; + r->dtjr_args = dtj_string_list_create(); + if (r->dtjr_args) { + va_list ap; + const char *arg; + int i, len; + + va_start(ap, type); + switch (type) { + case DTJ_REQUEST_OPTION: + len = 2; + break; + default: + len = 0; + } + + for (i = 0; i < len; ++i) { + arg = va_arg(ap, char *); + if (!dtj_string_list_add(r->dtjr_args, arg)) { + dtj_throw_out_of_memory(jenv, + "Failed to add request arg"); + uu_list_node_fini(r, &r->dtjr_node, + g_request_pool); + dtj_request_destroy(r, NULL); + r = NULL; + } + } + va_end(ap); + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate request arglist"); + uu_list_node_fini(r, &r->dtjr_node, g_request_pool); + dtj_request_destroy(r, NULL); + r = NULL; + } + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate request"); + } + + return (r); +} + +static boolean_t +dtj_check_program_pool(void) +{ + if (!g_program_pool) { + g_program_pool = uu_list_pool_create("g_program_pool", + sizeof (dtj_program_t), + offsetof(dtj_program_t, dtjp_node), + dtj_pointer_list_entry_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (!g_program_pool) { + return (B_FALSE); + } + } + return (B_TRUE); +} + +dtj_program_t * +dtj_program_create(JNIEnv *jenv, dtj_program_type_t type, const char *name) +{ + dtj_program_t *p; + + if (!dtj_check_program_pool()) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate program pool"); + return (NULL); + } + + p = uu_zalloc(sizeof (dtj_program_t)); + if (p) { + char *program_name; + + uu_list_node_init(p, &p->dtjp_node, g_program_pool); + p->dtjp_type = type; + program_name = malloc((size_t) + (sizeof (char)) * (strlen(name) + 1)); + if (program_name) { + (void) strcpy(program_name, name); + p->dtjp_name = program_name; + p->dtjp_enabled = B_FALSE; + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate program name"); + uu_list_node_fini(p, &p->dtjp_node, g_program_pool); + dtj_program_destroy(p, NULL); + p = NULL; + } + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate program"); + } + + return (p); +} + +static boolean_t +dtj_check_aggval_pool(void) +{ + if (!g_aggval_pool) { + g_aggval_pool = uu_list_pool_create("g_aggval_pool", + sizeof (dtj_aggval_t), + offsetof(dtj_aggval_t, dtja_node), + dtj_pointer_list_entry_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (!g_aggval_pool) { + return (B_FALSE); + } + } + return (B_TRUE); +} + +dtj_aggval_t * +dtj_aggval_create(JNIEnv *jenv, jobject aggval, const char *aggname, + int64_t aggid) +{ + dtj_aggval_t *e; + + if (!dtj_check_aggval_pool()) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate aggval entry pool"); + return (NULL); + } + + e = uu_zalloc(sizeof (dtj_aggval_t)); + if (e) { + char *a_name; + + uu_list_node_init(e, &e->dtja_node, g_aggval_pool); + e->dtja_value = aggval; + a_name = malloc((size_t) + (sizeof (char)) * (strlen(aggname) + 1)); + if (a_name) { + (void) strcpy(a_name, aggname); + e->dtja_aggname = a_name; + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate aggregation name"); + uu_list_node_fini(e, &e->dtja_node, g_aggval_pool); + /* caller responsible for input java reference */ + e->dtja_value = NULL; + dtj_aggval_destroy(e, jenv); + e = NULL; + } + e->dtja_aggid = aggid; + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate aggval entry"); + } + + return (e); +} + +dtj_status_t +dtj_java_consumer_init(JNIEnv *jenv, dtj_java_consumer_t *jc) +{ + if (!dtj_check_aggval_pool()) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate aggval pool"); + return (DTJ_ERR); + } + + jc->dtjj_aggval_list = uu_list_create(g_aggval_pool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + if (!jc->dtjj_aggval_list) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate aggval list"); + return (DTJ_ERR); + } + + /* Does not throw exceptions */ + jc->dtjj_consumer_lock = (*jenv)->GetObjectField(jenv, jc->dtjj_caller, + g_consumer_lock_jf); + + return (DTJ_OK); +} + +void +dtj_java_consumer_fini(JNIEnv *jenv, dtj_java_consumer_t *jc) +{ + if (jc) { + if (jc->dtjj_probedata) { + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_probedata); + jc->dtjj_probedata = NULL; + } + if (jc->dtjj_printa_buffer) { + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_printa_buffer); + jc->dtjj_printa_buffer = NULL; + } + if (jc->dtjj_aggregate) { + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_aggregate); + jc->dtjj_aggregate = NULL; + } + if (jc->dtjj_tuple) { + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_tuple); + jc->dtjj_tuple = NULL; + } + if (jc->dtjj_aggval_list) { + dtj_list_destroy(jc->dtjj_aggval_list, + dtj_aggval_destroy, jenv); + jc->dtjj_aggval_list = NULL; + } + + /* + * aggregate_spec records an input argument to a native JNI + * function (a reference we did not create), so we are not + * responsible for it. + */ + jc->dtjj_aggregate_spec = NULL; + + /* + * probelist records an in-out argument to a native JNI function + * (a reference we did not create), so we are not responsible + * for it. + */ + jc->dtjj_probelist = NULL; + + if (jc->dtjj_exception) { + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_exception); + jc->dtjj_exception = NULL; + } + (*jenv)->DeleteLocalRef(jenv, jc->dtjj_consumer_lock); + jc->dtjj_consumer_lock = NULL; + } +} + +dtj_consumer_t * +dtj_consumer_create(JNIEnv *jenv) +{ + dtj_consumer_t *c; + + if (!dtj_check_request_pool()) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate request pool"); + return (NULL); + } + + if (!dtj_check_program_pool()) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate program pool"); + return (NULL); + } + + c = uu_zalloc(sizeof (dtj_consumer_t)); + if (c) { + c->dtjc_request_list = uu_list_create(g_request_pool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + if (!c->dtjc_request_list) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate consumer request list"); + dtj_consumer_destroy(c); + return (NULL); + } + (void) pthread_mutex_init(&c->dtjc_request_list_lock, NULL); + + c->dtjc_program_list = uu_list_create(g_program_pool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + if (!c->dtjc_program_list) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate consumer program list"); + dtj_consumer_destroy(c); + return (NULL); + } + + c->dtjc_probedata_rec_i = 0; + c->dtjc_probedata_act = DTRACEACT_NONE; + c->dtjc_aggid = -1; + c->dtjc_expected = -1; + c->dtjc_state = DTJ_CONSUMER_INIT; + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate consumer"); + } + + return (c); +} + +void +/* ARGSUSED */ +dtj_request_destroy(void *v, void *arg) +{ + if (v) { + dtj_request_t *r = v; + dtj_string_list_destroy(r->dtjr_args); + uu_list_node_fini(r, &r->dtjr_node, g_request_pool); + bzero(v, sizeof (dtj_request_t)); + uu_free(v); + } +} + +void +/* ARGSUSED */ +dtj_program_destroy(void *v, void *arg) +{ + if (v) { + dtj_program_t *p = v; + if (p->dtjp_name) { + free((void *)p->dtjp_name); + } + uu_list_node_fini(p, &p->dtjp_node, g_program_pool); + bzero(v, sizeof (dtj_program_t)); + uu_free(v); + } +} + +void +dtj_aggval_destroy(void *v, void *arg) +{ + if (v) { + dtj_aggval_t *a = v; + if (a->dtja_value && arg) { + JNIEnv *jenv = arg; + (*jenv)->DeleteLocalRef(jenv, a->dtja_value); + } + if (a->dtja_aggname) { + free((void *)a->dtja_aggname); + } + uu_list_node_fini(a, &a->dtja_node, g_aggval_pool); + bzero(v, sizeof (dtj_aggval_t)); + uu_free(v); + } +} + +/* + * Frees per-consumer state. Assumes that the DTrace handle has been closed + * already. + */ +void +dtj_consumer_destroy(dtj_consumer_t *c) +{ + if (c) { + dtj_list_destroy(c->dtjc_request_list, dtj_request_destroy, + NULL); + (void) pthread_mutex_destroy(&c->dtjc_request_list_lock); + dtj_list_destroy(c->dtjc_program_list, dtj_program_destroy, + NULL); + /* + * Cannot dtrace_proc_release the c->process_list proc + * elements here, because we need the dtrace handle for that. + * By the time this destructor is called, the dtrace handle is + * already closed. The proc elements are released in + * dtrace_jni.c _close(). + */ + if (c->dtjc_process_list) { + dtj_list_destroy(c->dtjc_process_list, NULL, NULL); + } + bzero(c, sizeof (dtj_consumer_t)); + uu_free(c); + } +} + +void +dtj_throw_dtrace_exception(dtj_java_consumer_t *jc, const char *fmt, ...) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + va_list ap; + char msg[DTJ_MSG_SIZE]; + + jobject message = NULL; + jobject exception = NULL; + + va_start(ap, fmt); + (void) vsnprintf(msg, sizeof (msg), fmt, ap); + va_end(ap); + + message = dtj_NewStringNative(jenv, msg); + if (!message) { + return; /* java exception pending */ + } + + exception = (*jenv)->NewObject(jenv, g_dtx_jc, g_dtxinit_jm, message); + (*jenv)->DeleteLocalRef(jenv, message); + if (exception) { + (*jenv)->Throw(jenv, exception); + (*jenv)->DeleteLocalRef(jenv, exception); + } +} diff --git a/usr/src/lib/libdtrace_jni/common/dtj_probe.c b/usr/src/lib/libdtrace_jni/common/dtj_probe.c new file mode 100644 index 0000000000..9f64882653 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtj_probe.c @@ -0,0 +1,108 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> +#include <dtrace_jni.h> + +/* + * This file creates instances of the following Java classes: + * - org.opensolaris.os.dtrace.ProbeDescription + * - org.opensolaris.os.dtrace.ProbeInfo + */ + +jobject +dtj_new_probedesc(dtj_java_consumer_t *jc, const dtrace_probedesc_t *probedesc) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + jstring jprov = NULL; + jstring jmod = NULL; + jstring jfunc = NULL; + jstring jname = NULL; + jobject jprobedesc = NULL; + + jprov = (*jenv)->NewStringUTF(jenv, probedesc->dtpd_provider); + if ((*jenv)->ExceptionCheck(jenv)) { + goto cleanup; + } + jmod = (*jenv)->NewStringUTF(jenv, probedesc->dtpd_mod); + if ((*jenv)->ExceptionCheck(jenv)) { + goto cleanup; + } + jfunc = (*jenv)->NewStringUTF(jenv, probedesc->dtpd_func); + if ((*jenv)->ExceptionCheck(jenv)) { + goto cleanup; + } + jname = (*jenv)->NewStringUTF(jenv, probedesc->dtpd_name); + if ((*jenv)->ExceptionCheck(jenv)) { + goto cleanup; + } + jprobedesc = (*jenv)->NewObject(jenv, g_probedesc_jc, + g_probedescinit_jm, jprov, jmod, jfunc, jname); + if ((*jenv)->ExceptionCheck(jenv)) { + goto cleanup; + } + /* Does not throw exceptions */ + (*jenv)->SetIntField(jenv, jprobedesc, g_probedesc_id_jf, + probedesc->dtpd_id); + +cleanup: + + (*jenv)->DeleteLocalRef(jenv, jprov); + (*jenv)->DeleteLocalRef(jenv, jmod); + (*jenv)->DeleteLocalRef(jenv, jfunc); + (*jenv)->DeleteLocalRef(jenv, jname); + return (jprobedesc); +} + +jobject +dtj_new_probeinfo(dtj_java_consumer_t *jc, const dtrace_probeinfo_t *probeinfo) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + jobject jprobeattr = NULL; + jobject jargattr = NULL; + jobject jprobeinfo = NULL; /* return value */ + + jprobeattr = dtj_new_attribute(jc, &probeinfo->dtp_attr); + if ((*jenv)->ExceptionCheck(jenv)) { + return (NULL); + } + jargattr = dtj_new_attribute(jc, &probeinfo->dtp_arga); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, jprobeattr); + return (NULL); + } + + jprobeinfo = (*jenv)->NewObject(jenv, g_probeinfo_jc, + g_probeinfoinit_jm, jprobeattr, jargattr); + + (*jenv)->DeleteLocalRef(jenv, jprobeattr); + (*jenv)->DeleteLocalRef(jenv, jargattr); + return (jprobeinfo); +} diff --git a/usr/src/lib/libdtrace_jni/common/dtj_util.c b/usr/src/lib/libdtrace_jni/common/dtj_util.c new file mode 100644 index 0000000000..17463e0f0e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtj_util.c @@ -0,0 +1,1506 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <stddef.h> +#include <sys/types.h> +#include <pthread.h> +#include <string.h> +#include <dtj_util.h> + +/* + * dtj_util.c separates functionality that is generally useful from + * that which is specific to the Java DTrace API. If moved to a separate + * library, this functionality could be shared by other JNI wrappers. + */ + +boolean_t g_dtj_util_debug = B_FALSE; +static boolean_t g_dtj_load_common = B_FALSE; + +/* NativeException */ +jclass g_nx_jc = 0; +jmethodID g_nxinit_jm = 0; + +/* java.io.Serializable */ +jclass g_serial_jc = 0; + +/* java.lang.Number */ +jclass g_number_jc = 0; +jmethodID g_shortval_jm = 0; +jmethodID g_intval_jm = 0; +jmethodID g_longval_jm = 0; + +/* java.lang.Byte */ +jclass g_byte_jc = 0; +jmethodID g_byteinit_jm = 0; + +/* java.lang.Character */ +jclass g_char_jc = 0; +jmethodID g_charinit_jm = 0; +jmethodID g_charval_jm = 0; + +/* java.lang.Short */ +jclass g_short_jc = 0; +jmethodID g_shortinit_jm = 0; + +/* java.lang.Integer */ +jclass g_int_jc = 0; +jmethodID g_intinit_jm = 0; + +/* java.lang.Long */ +jclass g_long_jc = 0; +jmethodID g_longinit_jm = 0; + +/* java.lang.String */ +jclass g_string_jc = 0; +jmethodID g_strinit_bytes_jm = 0; +jmethodID g_strbytes_jm = 0; +jmethodID g_trim_jm = 0; + +/* java.lang.StringBuffer */ +jclass g_buf_jc = 0; +jmethodID g_bufinit_jm = 0; +jmethodID g_buf_append_char_jm = 0; +jmethodID g_buf_append_int_jm = 0; +jmethodID g_buf_append_long_jm = 0; +jmethodID g_buf_append_str_jm = 0; +jmethodID g_buf_append_obj_jm = 0; +jmethodID g_buflen_jm = 0; +jmethodID g_bufsetlen_jm = 0; + +/* java.lang.Object */ +jclass g_object_jc = 0; +jmethodID g_tostring_jm = 0; +jmethodID g_equals_jm = 0; + +/* java.lang.Enum */ +jclass g_enum_jc = 0; +jmethodID g_enumname_jm = 0; + +/* List */ +jclass g_list_jc = 0; +jmethodID g_listclear_jm = 0; +jmethodID g_listadd_jm = 0; +jmethodID g_listget_jm = 0; +jmethodID g_listsize_jm = 0; + +/* Global list pools */ +static uu_list_pool_t *g_pointer_pool = NULL; +static uu_list_pool_t *g_string_pool = NULL; + +static dtj_status_t dtj_get_jni_classes(JNIEnv *, uu_list_t *, uu_list_pool_t *, + uu_list_pool_t *, uu_list_pool_t *, const dtj_table_entry_t *); +static dtj_status_t dtj_cache_jni_methods(JNIEnv *, dtj_java_class_t *); +static dtj_status_t dtj_cache_jni_fields(JNIEnv *, dtj_java_class_t *); + +/* Constructors */ +static dtj_java_class_t *dtj_java_class_create(JNIEnv *, jclass *, char *, + uu_list_pool_t *, uu_list_pool_t *, uu_list_pool_t *); +static dtj_java_method_t *dtj_java_method_create(JNIEnv *, jmethodID *, char *, + char *, uu_list_pool_t *); +static dtj_java_method_t *dtj_java_static_method_create(JNIEnv *, jmethodID *, + char *, char *, uu_list_pool_t *); +static dtj_java_field_t *dtj_java_field_create(JNIEnv *, jfieldID *, char *, + char *, uu_list_pool_t *); +static dtj_java_field_t *dtj_java_static_field_create(JNIEnv *, jfieldID *, + char *, char *, uu_list_pool_t *); + +/* Destructors */ +static void dtj_java_class_destroy(void *, void *); +static void dtj_java_method_destroy(void *, void *); +static void dtj_java_field_destroy(void *, void *); + +/* Comparison functions, uu_compare_fn_t signature */ +static int dtj_java_class_cmp(const void *, const void *, void *); +static int dtj_java_method_cmp(const void *, const void *, void *); +static int dtj_java_field_cmp(const void *, const void *, void *); + +/* Java Throwable */ +static void dtj_throw(JNIEnv *, jclass, const char *, va_list *); + +/* Support for uu_list_t wrappers */ +static boolean_t dtj_check_pointer_pool(void); +static boolean_t dtj_check_string_pool(void); + +dtj_status_t +dtj_load_common(JNIEnv *jenv) +{ + dtj_status_t status; + + static const dtj_table_entry_t table[] = { + /* NativeException */ + { JCLASS, &g_nx_jc, + "org/opensolaris/os/dtrace/NativeException" }, + { JMETHOD, &g_nxinit_jm, CONSTRUCTOR, + "(Ljava/lang/String;ILjava/lang/Throwable;)V" }, + + /* java.io.Serializable */ + { JCLASS, &g_serial_jc, "java/io/Serializable" }, + + /* java.lang.Number */ + { JCLASS, &g_number_jc, "java/lang/Number" }, + { JMETHOD, &g_shortval_jm, "shortValue", "()S" }, + { JMETHOD, &g_intval_jm, "intValue", "()I" }, + { JMETHOD, &g_longval_jm, "longValue", "()J" }, + + /* java.lang.Byte */ + { JCLASS, &g_byte_jc, "java/lang/Byte" }, + { JMETHOD, &g_byteinit_jm, CONSTRUCTOR, "(B)V" }, + + /* java.lang.Character */ + { JCLASS, &g_char_jc, "java/lang/Character" }, + { JMETHOD, &g_charinit_jm, CONSTRUCTOR, "(C)V" }, + { JMETHOD, &g_charval_jm, "charValue", "()C" }, + + /* java.lang.Short */ + { JCLASS, &g_short_jc, "java/lang/Short" }, + { JMETHOD, &g_shortinit_jm, CONSTRUCTOR, "(S)V" }, + + /* java.lang.Integer */ + { JCLASS, &g_int_jc, "java/lang/Integer" }, + { JMETHOD, &g_intinit_jm, CONSTRUCTOR, "(I)V" }, + + /* java.lang.Long */ + { JCLASS, &g_long_jc, "java/lang/Long" }, + { JMETHOD, &g_longinit_jm, CONSTRUCTOR, "(J)V" }, + + /* java.lang.String */ + { JCLASS, &g_string_jc, "java/lang/String" }, + { JMETHOD, &g_strinit_bytes_jm, CONSTRUCTOR, "([B)V" }, + { JMETHOD, &g_strbytes_jm, "getBytes", "()[B" }, + { JMETHOD, &g_trim_jm, "trim", "()Ljava/lang/String;" }, + + /* java.lang.StringBuffer */ + { JCLASS, &g_buf_jc, "java/lang/StringBuffer" }, + { JMETHOD, &g_bufinit_jm, CONSTRUCTOR, "()V" }, + { JMETHOD, &g_buf_append_char_jm, "append", + "(C)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buf_append_int_jm, "append", + "(I)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buf_append_long_jm, "append", + "(J)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buf_append_str_jm, "append", + "(Ljava/lang/String;)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buf_append_obj_jm, "append", + "(Ljava/lang/Object;)Ljava/lang/StringBuffer;" }, + { JMETHOD, &g_buflen_jm, "length", "()I" }, + { JMETHOD, &g_bufsetlen_jm, "setLength", "(I)V" }, + + /* java.lang.Object */ + { JCLASS, &g_object_jc, "java/lang/Object" }, + { JMETHOD, &g_tostring_jm, "toString", + "()Ljava/lang/String;" }, + { JMETHOD, &g_equals_jm, "equals", + "(Ljava/lang/Object;)Z" }, + + /* java.lang.Enum */ + { JCLASS, &g_enum_jc, "java/lang/Enum" }, + { JMETHOD, &g_enumname_jm, "name", + "()Ljava/lang/String;" }, + + /* List */ + { JCLASS, &g_list_jc, "java/util/List" }, + { JMETHOD, &g_listclear_jm, "clear", "()V" }, + { JMETHOD, &g_listadd_jm, "add", "(Ljava/lang/Object;)Z" }, + { JMETHOD, &g_listget_jm, "get", "(I)Ljava/lang/Object;" }, + { JMETHOD, &g_listsize_jm, "size", "()I" }, + + { DTJ_TYPE_END } + }; + + status = dtj_cache_jni_classes(jenv, table); + if (status == DTJ_OK) { + g_dtj_load_common = B_TRUE; + } + return (status); +} + +static int +/* ARGSUSED */ +dtj_java_class_cmp(const void * v1, const void * v2, void *arg) +{ + const dtj_java_class_t *c1 = v1; + const dtj_java_class_t *c2 = v2; + return (strcmp(c1->djc_name, c2->djc_name)); +} + +static int +/* ARGSUSED */ +dtj_java_method_cmp(const void *v1, const void *v2, void *arg) +{ + int cmp; + const dtj_java_method_t *m1 = v1; + const dtj_java_method_t *m2 = v2; + cmp = strcmp(m1->djm_name, m2->djm_name); + if (cmp == 0) { + cmp = strcmp(m1->djm_signature, m2->djm_signature); + } + return (cmp); +} + +static int +/* ARGSUSED */ +dtj_java_field_cmp(const void *v1, const void *v2, void *arg) +{ + const dtj_java_field_t *f1 = v1; + const dtj_java_field_t *f2 = v2; + return (strcmp(f1->djf_name, f2->djf_name)); +} + +static dtj_java_class_t * +dtj_java_class_create(JNIEnv *jenv, jclass *jc, char *name, + uu_list_pool_t *classpool, uu_list_pool_t *methodpool, + uu_list_pool_t *fieldpool) +{ + dtj_java_class_t *c = uu_zalloc(sizeof (dtj_java_class_t)); + if (c) { + uu_list_node_init(c, &c->djc_node, classpool); + c->djc_ptr = jc; + c->djc_name = name; + c->djc_methods = uu_list_create(methodpool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + if (!c->djc_methods) { + dtj_throw_out_of_memory(jenv, + "Failed method list creation"); + uu_list_node_fini(c, &c->djc_node, classpool); + free(c); + c = NULL; + } + c->djc_fields = uu_list_create(fieldpool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + if (!c->djc_fields) { + dtj_throw_out_of_memory(jenv, + "Failed field list creation"); + uu_list_destroy(c->djc_methods); + c->djc_methods = NULL; + uu_list_node_fini(c, &c->djc_node, classpool); + free(c); + c = NULL; + } + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate class description"); + } + return (c); +} + +static dtj_java_method_t * +dtj_java_method_create(JNIEnv *jenv, jmethodID *jm, char *name, char *signature, + uu_list_pool_t *methodpool) +{ + dtj_java_method_t *m = uu_zalloc(sizeof (dtj_java_method_t)); + if (m) { + uu_list_node_init(m, &m->djm_node, methodpool); + m->djm_ptr = jm; + m->djm_name = name; + m->djm_signature = signature; + m->djm_static = B_FALSE; + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate method description"); + } + return (m); +} + +static dtj_java_method_t * +dtj_java_static_method_create(JNIEnv *jenv, jmethodID *jm, char *name, + char *signature, uu_list_pool_t *methodpool) +{ + dtj_java_method_t *m = dtj_java_method_create(jenv, jm, name, signature, + methodpool); + if (m) { + m->djm_static = B_TRUE; + } + return (m); +} + +static dtj_java_field_t * +dtj_java_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type, + uu_list_pool_t *fieldpool) +{ + dtj_java_field_t *f = uu_zalloc(sizeof (dtj_java_field_t)); + if (f) { + uu_list_node_init(f, &f->djf_node, fieldpool); + f->djf_ptr = jf; + f->djf_name = name; + f->djf_type = type; + f->djf_static = B_FALSE; + } else { + dtj_throw_out_of_memory(jenv, + "Failed to allocate field description"); + } + return (f); +} + +static dtj_java_field_t * +dtj_java_static_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type, + uu_list_pool_t *fieldpool) +{ + dtj_java_field_t *f = dtj_java_field_create(jenv, jf, name, type, + fieldpool); + if (f) { + f->djf_static = B_TRUE; + } + return (f); +} + +static void +/* ARGSUSED */ +dtj_java_class_destroy(void *v, void *arg) +{ + if (v) { + dtj_java_class_t *c = v; + c->djc_ptr = NULL; /* do not free user-defined storage */ + c->djc_name = NULL; /* string literal */ + dtj_list_destroy(c->djc_methods, dtj_java_method_destroy, NULL); + dtj_list_destroy(c->djc_fields, dtj_java_field_destroy, NULL); + c->djc_methods = NULL; + c->djc_fields = NULL; + uu_free(v); + } +} + +static void +/* ARGSUSED */ +dtj_java_method_destroy(void *v, void *arg) +{ + if (v) { + dtj_java_method_t *m = v; + m->djm_ptr = NULL; /* do not free user-defined space */ + m->djm_name = NULL; /* string literal */ + m->djm_signature = NULL; /* string literal */ + uu_free(v); + } +} + +static void +/* ARGSUSED */ +dtj_java_field_destroy(void *v, void *arg) +{ + if (v) { + dtj_java_field_t *f = v; + f->djf_ptr = NULL; /* do not free user-defined space */ + f->djf_name = NULL; /* string literal */ + f->djf_type = NULL; /* string literal */ + uu_free(f); + } +} + +dtj_status_t +dtj_cache_jni_classes(JNIEnv *jenv, const dtj_table_entry_t *table) +{ + dtj_java_class_t *class; + uu_list_pool_t *classpool; + uu_list_pool_t *methodpool; + uu_list_pool_t *fieldpool; + uu_list_t *classes; + uu_list_walk_t *itr; + jclass jc; + jclass gjc; + dtj_status_t status; + + classpool = uu_list_pool_create("classpool", + sizeof (dtj_java_class_t), + offsetof(dtj_java_class_t, djc_node), dtj_java_class_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (!classpool) { + dtj_throw_out_of_memory(jenv, "failed class pool creation"); + return (DTJ_ERR); + } + methodpool = uu_list_pool_create("methodpool", + sizeof (dtj_java_method_t), + offsetof(dtj_java_method_t, djm_node), dtj_java_method_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (!methodpool) { + dtj_throw_out_of_memory(jenv, "failed method pool creation"); + return (DTJ_ERR); + } + fieldpool = uu_list_pool_create("fieldpool", + sizeof (dtj_java_field_t), + offsetof(dtj_java_field_t, djf_node), dtj_java_field_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (!fieldpool) { + dtj_throw_out_of_memory(jenv, "failed field pool creation"); + return (DTJ_ERR); + } + + classes = uu_list_create(classpool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + if (!classes) { + dtj_throw_out_of_memory(jenv, "failed class list creation"); + return (DTJ_ERR); + } + + status = dtj_get_jni_classes(jenv, classes, classpool, methodpool, + fieldpool, table); + if (status != DTJ_OK) { + /* java error pending */ + return (status); + } + + itr = uu_list_walk_start(classes, 0); + while ((class = uu_list_walk_next(itr)) != NULL) { + jc = (*jenv)->FindClass(jenv, class->djc_name); + if (!jc) { + /* NoClassDefFoundError pending */ + return (DTJ_ERR); + } + gjc = (*jenv)->NewGlobalRef(jenv, jc); + (*jenv)->DeleteLocalRef(jenv, jc); + if (!gjc) { + dtj_throw_out_of_memory(jenv, + "Failed to create global class reference"); + return (DTJ_ERR); + } + *(class->djc_ptr) = gjc; + status = dtj_cache_jni_methods(jenv, class); + if (status != DTJ_OK) { + /* java error pending */ + return (status); + } + status = dtj_cache_jni_fields(jenv, class); + if (status != DTJ_OK) { + /* java error pending */ + return (status); + } + } + uu_list_walk_end(itr); + dtj_list_destroy(classes, dtj_java_class_destroy, NULL); + uu_list_pool_destroy(classpool); + uu_list_pool_destroy(methodpool); + uu_list_pool_destroy(fieldpool); + return (DTJ_OK); +} + +/* + * Converts JNI table entry desriptions into java_class_t descriptors. + */ +static dtj_status_t +dtj_get_jni_classes(JNIEnv *jenv, uu_list_t *classes, + uu_list_pool_t *classpool, uu_list_pool_t *methodpool, + uu_list_pool_t *fieldpool, const dtj_table_entry_t *table) +{ + int i; + dtj_java_class_t *c = NULL; + dtj_java_method_t *m; + dtj_java_field_t *f; + + for (i = 0; table[i].djte_type != DTJ_TYPE_END; ++i) { + /* + * Class not added until all of its method and field information + * is attached, so we defer adding a class until the next + * element with type JCLASS. + */ + switch (table[i].djte_type) { + case JCLASS: + if (c) { + /* previous class */ + if (!dtj_list_add(classes, c)) { + dtj_throw_out_of_memory(jenv, + "Failed to add class description"); + /* + * In response to an error return value, + * the caller will delete the class + * descriptions list with any + * descriptions created so far. + */ + return (DTJ_ERR); + } + } + c = dtj_java_class_create(jenv, + (jclass *)table[i].djte_addr, table[i].djte_name, + classpool, methodpool, fieldpool); + if (!c) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + break; + case JMETHOD: + if (!c) { + dtj_throw_illegal_state(jenv, + "method description not preceded " + "by class description"); + return (DTJ_ERR); + } + m = dtj_java_method_create(jenv, + (jmethodID *)table[i].djte_addr, + table[i].djte_name, table[i].djte_desc, + methodpool); + if (!m) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + if (!dtj_list_add(c->djc_methods, m)) { + dtj_throw_out_of_memory(jenv, + "Failed to add method description"); + return (DTJ_ERR); + } + break; + case JMETHOD_STATIC: + if (!c) { + dtj_throw_illegal_state(jenv, + "static method description not preceded " + "by class description"); + return (DTJ_ERR); + } + m = dtj_java_static_method_create(jenv, + (jmethodID *)table[i].djte_addr, + table[i].djte_name, table[i].djte_desc, + methodpool); + if (!m) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + if (!dtj_list_add(c->djc_methods, m)) { + dtj_throw_out_of_memory(jenv, + "Failed to add static method description"); + return (DTJ_ERR); + } + break; + case JFIELD: + if (!c) { + dtj_throw_illegal_state(jenv, + "field description not preceded " + "by class description"); + return (DTJ_ERR); + } + f = dtj_java_field_create(jenv, + (jfieldID *)table[i].djte_addr, + table[i].djte_name, table[i].djte_desc, + fieldpool); + if (!f) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + if (!dtj_list_add(c->djc_fields, f)) { + dtj_throw_out_of_memory(jenv, + "Failed to add field description"); + return (DTJ_ERR); + } + break; + case JFIELD_STATIC: + if (!c) { + dtj_throw_illegal_state(jenv, + "static field description not preceded " + "by class description"); + return (DTJ_ERR); + } + f = dtj_java_static_field_create(jenv, + (jfieldID *)table[i].djte_addr, + table[i].djte_name, table[i].djte_desc, + fieldpool); + if (!f) { + /* OutOfMemoryError pending */ + return (DTJ_ERR); + } + if (!dtj_list_add(c->djc_fields, f)) { + dtj_throw_out_of_memory(jenv, + "Failed to add static field description"); + return (DTJ_ERR); + } + break; + default: + dtj_throw_illegal_state(jenv, + "Unexpected jni_type_e: %d", table[i].djte_type); + return (DTJ_ERR); + } + } + if (c) { + /* last class */ + if (!dtj_list_add(classes, c)) { + dtj_throw_out_of_memory(jenv, + "Failed to add class description"); + return (DTJ_ERR); + } + } + + return (DTJ_OK); +} + +static dtj_status_t +dtj_cache_jni_methods(JNIEnv *jenv, dtj_java_class_t *c) +{ + dtj_java_method_t *method; + jmethodID jm; + uu_list_walk_t *itr; + itr = uu_list_walk_start(c->djc_methods, 0); + while ((method = uu_list_walk_next(itr)) != NULL) { + if (method->djm_static) { + jm = (*jenv)->GetStaticMethodID(jenv, *(c->djc_ptr), + method->djm_name, method->djm_signature); + } else { + jm = (*jenv)->GetMethodID(jenv, *(c->djc_ptr), + method->djm_name, method->djm_signature); + } + if (jm == 0) { + /* + * The pending NoSuchMethodError gives only the + * method name, which is not so helpful for + * overloaded methods and methods such as <init> + * that have the same name in multiple classes. + * Clear the pending error and throw one that + * includes the class name and the method + * signature. + */ + jclass jc; + char msg[DTJ_MSG_SIZE]; + (*jenv)->ExceptionClear(jenv); + (void) snprintf(msg, sizeof (msg), "%s %s %s", + c->djc_name, method->djm_name, + method->djm_signature); + + jc = (*jenv)->FindClass(jenv, + "java/lang/NoSuchMethodError"); + (*jenv)->ThrowNew(jenv, jc, msg); + (*jenv)->DeleteLocalRef(jenv, jc); + return (DTJ_ERR); + } + *(method->djm_ptr) = jm; + } + uu_list_walk_end(itr); + return (DTJ_OK); +} + +static dtj_status_t +dtj_cache_jni_fields(JNIEnv *jenv, dtj_java_class_t *c) +{ + dtj_java_field_t *field; + jfieldID jf; + uu_list_walk_t *itr; + itr = uu_list_walk_start(c->djc_fields, 0); + while ((field = uu_list_walk_next(itr)) != NULL) { + if (field->djf_static) { + jf = (*jenv)->GetStaticFieldID(jenv, *(c->djc_ptr), + field->djf_name, field->djf_type); + } else { + jf = (*jenv)->GetFieldID(jenv, *(c->djc_ptr), + field->djf_name, field->djf_type); + } + if (jf == 0) { + jclass jc; + char msg[DTJ_MSG_SIZE]; + (*jenv)->ExceptionClear(jenv); + (void) snprintf(msg, sizeof (msg), + "%s.%s signature: %s", c->djc_name, + field->djf_name, field->djf_type); + + jc = (*jenv)->FindClass(jenv, + "java/lang/NoSuchFieldError"); + (*jenv)->ThrowNew(jenv, jc, msg); + (*jenv)->DeleteLocalRef(jenv, jc); + return (DTJ_ERR); + } + *(field->djf_ptr) = jf; + } + uu_list_walk_end(itr); + return (DTJ_OK); +} + + +/* Common utilities */ + +static void +dtj_throw(JNIEnv *jenv, jclass jc, const char *fmt, va_list *ap) +{ + char msg[DTJ_MSG_SIZE]; + (void) vsnprintf(msg, sizeof (msg), fmt, *ap); + (*jenv)->ThrowNew(jenv, jc, msg); +} + +void +dtj_throw_out_of_memory(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc; + /* + * JNI documentation unclear whether NewGlobalRef() can throw + * OutOfMemoryError, so we'll make this function safe in case + * OutOfMemoryError has already been thrown + */ + if ((*jenv)->ExceptionCheck(jenv)) { + return; + } + jc = (*jenv)->FindClass(jenv, + "java/lang/OutOfMemoryError"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_null_pointer(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/NullPointerException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_illegal_state(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/IllegalStateException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_illegal_argument(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/IllegalArgumentException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_no_such_element(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/util/NoSuchElementException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_class_cast(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/ClassCastException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_assertion(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "java/lang/AssertionError"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_throw_resource_limit(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + jclass jc = (*jenv)->FindClass(jenv, + "org/opensolaris/os/dtrace/ResourceLimitException"); + va_start(ap, fmt); + dtj_throw(jenv, jc, fmt, &ap); + (*jenv)->DeleteLocalRef(jenv, jc); + va_end(ap); +} + +void +dtj_wrap_exception(JNIEnv *jenv, const char *file, int line) +{ + jthrowable e = NULL; + jthrowable nx = NULL; + jstring jfile = NULL; + + e = (*jenv)->ExceptionOccurred(jenv); + if (!e) { + return; + } + + if (!g_dtj_load_common) { + return; + } + + (*jenv)->ExceptionClear(jenv); + + /* Unsafe to test while exception pending */ + if ((*jenv)->IsInstanceOf(jenv, e, g_nx_jc)) { + /* Already wrapped */ + (*jenv)->Throw(jenv, e); + (*jenv)->DeleteLocalRef(jenv, e); + return; + } + + jfile = dtj_NewStringNative(jenv, file); + if ((*jenv)->ExceptionCheck(jenv)) { + /* + * Only wrap the exception if possible, otherwise just throw the + * original exception. + */ + (*jenv)->ExceptionClear(jenv); + (*jenv)->Throw(jenv, e); + (*jenv)->DeleteLocalRef(jenv, e); + return; + } + + nx = (jthrowable)(*jenv)->NewObject(jenv, g_nx_jc, g_nxinit_jm, + jfile, line, e); + (*jenv)->DeleteLocalRef(jenv, jfile); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->ExceptionClear(jenv); + (*jenv)->Throw(jenv, e); + (*jenv)->DeleteLocalRef(jenv, e); + return; + } + + (*jenv)->DeleteLocalRef(jenv, e); + (*jenv)->Throw(jenv, nx); + (*jenv)->DeleteLocalRef(jenv, nx); +} + +/* + * Calls the given java object's toString() method and prints the value to + * stdout. Useful for debugging. Guaranteed that no exception is pending when + * this function returns. + */ +void +dtj_print_object(JNIEnv *jenv, jobject jobj) +{ + jstring jstr; + const char *cstr; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + (*jenv)->ExceptionDescribe(jenv); /* clears the exception */ + return; + } + + if (!jobj) { + (void) printf("null\n"); + return; + } + + jstr = (*jenv)->CallObjectMethod(jenv, jobj, g_tostring_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->ExceptionDescribe(jenv); /* clears the exception */ + return; + } + cstr = (*jenv)->GetStringUTFChars(jenv, jstr, 0); + if (cstr) { + (void) printf("%s\n", cstr); + } else { + (*jenv)->ExceptionDescribe(jenv); /* clears the exception */ + (*jenv)->DeleteLocalRef(jenv, jstr); + return; + } + (*jenv)->ReleaseStringUTFChars(jenv, jstr, cstr); + (*jenv)->DeleteLocalRef(jenv, jstr); +} + +jstring +dtj_format_string(JNIEnv *jenv, const char *fmt, ...) +{ + va_list ap; + char str[DTJ_MSG_SIZE]; + + jstring jstr = NULL; + + va_start(ap, fmt); + (void) vsnprintf(str, sizeof (str), fmt, ap); + va_end(ap); + + jstr = dtj_NewStringNative(jenv, str); + /* return NULL if OutOfMemoryError pending */ + return (jstr); +} + +jstring +dtj_NewStringNative(JNIEnv *jenv, const char *str) +{ + jstring result; + jbyteArray bytes = 0; + int len; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + return (NULL); + } + + len = strlen(str); + + bytes = (*jenv)->NewByteArray(jenv, len); + if (!bytes) { + return (NULL); /* OutOfMemoryError pending */ + } + (*jenv)->SetByteArrayRegion(jenv, bytes, 0, len, + (jbyte *)str); + if ((*jenv)->ExceptionCheck(jenv)) { + (*jenv)->DeleteLocalRef(jenv, bytes); + return (NULL); /* ArrayIndexOutOfBoundsException pending */ + } + result = (*jenv)->NewObject(jenv, g_string_jc, g_strinit_bytes_jm, + bytes); + (*jenv)->DeleteLocalRef(jenv, bytes); + /* return NULL result if exception pending */ + return (result); +} + +char * +dtj_GetStringNativeChars(JNIEnv *jenv, jstring jstr) +{ + jbyteArray bytes = NULL; + + jint len; + char *result = NULL; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + return (NULL); + } + + bytes = (*jenv)->CallObjectMethod(jenv, jstr, g_strbytes_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + return (NULL); /* OutOfMemoryError pending */ + } + /* Does not throw exceptions */ + len = (*jenv)->GetArrayLength(jenv, bytes); + result = malloc(len + 1); + if (!result) { + (*jenv)->DeleteLocalRef(jenv, bytes); + dtj_throw_out_of_memory(jenv, + "could not allocate native chars"); + return (NULL); + } + + /* Skip check for ArrayIndexOutOfBoundsException */ + (*jenv)->GetByteArrayRegion(jenv, bytes, 0, len, + (jbyte *)result); + (*jenv)->DeleteLocalRef(jenv, bytes); + result[len] = '\0'; /* NUL-terminate */ + + return (result); +} + +void +/* ARGSUSED */ +dtj_ReleaseStringNativeChars(JNIEnv *jenv, jstring jstr, const char *str) +{ + free((void *)str); +} + +char ** +dtj_get_argv(JNIEnv *jenv, jobjectArray args, int *argc) +{ + char **argv = NULL; /* return value */ + const char *str; + int i; + + jstring jstr = NULL; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + return (NULL); + } + + *argc = (*jenv)->GetArrayLength(jenv, args); + /* + * Initialize all string pointers to NULL so that in case of an error + * filling in the array, free_argv() will not attempt to free the + * unallocated elements. Also NULL-terminate the string array for + * functions that expect terminating NULL rather than rely on argc. + */ + argv = uu_zalloc((sizeof (char *)) * (*argc + 1)); + if (!argv) { + dtj_throw_out_of_memory(jenv, "Failed to allocate args array"); + return (NULL); + } + + for (i = 0; i < *argc; ++i) { + jstr = (*jenv)->GetObjectArrayElement(jenv, args, i); + if ((*jenv)->ExceptionCheck(jenv)) { + dtj_free_argv(argv); + return (NULL); + } + str = dtj_GetStringNativeChars(jenv, jstr); + if ((*jenv)->ExceptionCheck(jenv)) { + dtj_free_argv(argv); + (*jenv)->DeleteLocalRef(jenv, jstr); + return (NULL); + } + argv[i] = malloc(strlen(str) + 1); + if (!argv[i]) { + dtj_throw_out_of_memory(jenv, "Failed to allocate arg"); + dtj_free_argv(argv); + dtj_ReleaseStringNativeChars(jenv, jstr, str); + (*jenv)->DeleteLocalRef(jenv, jstr); + return (NULL); + } + (void) strcpy(argv[i], str); + dtj_ReleaseStringNativeChars(jenv, jstr, str); + (*jenv)->DeleteLocalRef(jenv, jstr); + jstr = NULL; + } + + return (argv); +} + +char ** +dtj_make_argv(JNIEnv *jenv, jstring command, int *argc) +{ + const char *ws = "\f\n\r\t\v "; + char **argv = NULL; /* return value */ + const char *cmd; /* native command string */ + char *s; /* writable command */ + char *tok; /* token */ + int len; + + if (!g_dtj_load_common) { + dtj_throw_illegal_state(jenv, + "dtj_load_common() has not been called"); + return (NULL); + } + + if (!command) { + dtj_throw_null_pointer(jenv, "command is null"); + return (NULL); + } else if ((*jenv)->GetStringLength(jenv, command) == 0) { + dtj_throw_illegal_argument(jenv, "command is empty"); + return (NULL); + } + + cmd = dtj_GetStringNativeChars(jenv, command); + if ((*jenv)->ExceptionCheck(jenv)) { + return (NULL); + } + len = strlen(cmd); + s = malloc(len + 1); + if (!s) { + dtj_throw_out_of_memory(jenv, + "failed to allocate command string"); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (NULL); + } + (void) strcpy(s, cmd); + /* + * Initialize all string pointers to NULL so that in case of an error + * filling in the array, free_argv() will not attempt to free the + * unallocated elements. Also NULL-terminate the string array for + * functions that expect terminating NULL rather than rely on argc. + * Allow for maximum length resulting from single-character tokens + * separated by single spaces. + */ + argv = uu_zalloc(sizeof (char *) * (len / 2 + 1)); + if (!argv) { + dtj_throw_out_of_memory(jenv, "failed to allocate args array"); + free(s); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (NULL); + } + + *argc = 0; + for (tok = strtok(s, ws); tok != NULL; tok = strtok(NULL, ws)) { + argv[*argc] = malloc(strlen(tok) + 1); + if (!argv[*argc]) { + dtj_throw_out_of_memory(jenv, "Failed to allocate arg"); + dtj_free_argv(argv); + free(s); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (NULL); + } + (void) strcpy(argv[(*argc)++], tok); + } + + if (*argc == 0) { + dtj_throw_illegal_argument(jenv, "command is blank"); + dtj_free_argv(argv); + free(s); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (NULL); + } + + free(s); + dtj_ReleaseStringNativeChars(jenv, command, cmd); + return (argv); +} + +void +dtj_free_argv(char **argv) +{ + if (argv) { + char **s = argv; + while (*s) { + free((void *)*s); + *s++ = NULL; + } + free((void *)argv); + } +} + + +/* Wrappers for uu_list_t */ + +int +/* ARGSUSED */ +dtj_pointer_list_entry_cmp(const void *v1, const void *v2, void *arg) +{ + const dtj_pointer_list_entry_t *p1 = v1; + const dtj_pointer_list_entry_t *p2 = v2; + + /* + * It is not valid to compare pointers using the relational operators + * unless they point to elements in the same array. + */ + uint64_t x = (uint64_t)p1->dple_ptr; + uint64_t y = (uint64_t)p2->dple_ptr; + int rc; + rc = ((x > y) ? 1 : ((x < y) ? -1 : 0)); + return (rc); +} + +int +/* ARGSUSED */ +dtj_string_list_entry_cmp(const void *v1, const void *v2, void *arg) +{ + const dtj_string_list_entry_t *p1 = v1; + const dtj_string_list_entry_t *p2 = v2; + const char *s1 = p1->dsle_value; + const char *s2 = p2->dsle_value; + if (s1 == NULL) { + return (s2 == NULL ? 0 : -1); + } + if (s2 == NULL) { + return (1); + } + return (strcmp(s1, s2)); +} + +static boolean_t +dtj_check_pointer_pool(void) +{ + if (g_pointer_pool == NULL) { + g_pointer_pool = uu_list_pool_create("g_pointer_pool", + sizeof (dtj_pointer_list_entry_t), + offsetof(dtj_pointer_list_entry_t, dple_node), + dtj_pointer_list_entry_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (g_pointer_pool == NULL) { + return (B_FALSE); + } + } + return (B_TRUE); +} + +uu_list_t * +dtj_pointer_list_create(void) +{ + uu_list_t *list; + + if (!dtj_check_pointer_pool()) { + return (NULL); + } + + list = uu_list_create(g_pointer_pool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + return (list); +} + +dtj_pointer_list_entry_t * +dtj_pointer_list_entry_create(void *p) +{ + dtj_pointer_list_entry_t *e; + + if (!dtj_check_pointer_pool()) { + return (NULL); + } + + e = uu_zalloc(sizeof (dtj_pointer_list_entry_t)); + if (e) { + uu_list_node_init(e, &e->dple_node, g_pointer_pool); + e->dple_ptr = p; + } + return (e); +} + +static boolean_t +dtj_check_string_pool(void) +{ + if (g_string_pool == NULL) { + g_string_pool = uu_list_pool_create("g_string_pool", + sizeof (dtj_string_list_entry_t), + offsetof(dtj_string_list_entry_t, dsle_node), + dtj_string_list_entry_cmp, + (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0)); + if (g_string_pool == NULL) { + return (B_FALSE); + } + } + return (B_TRUE); +} + +uu_list_t * +dtj_string_list_create(void) +{ + uu_list_t *list; + + if (!dtj_check_string_pool()) { + return (NULL); + } + + list = uu_list_create(g_string_pool, NULL, + (g_dtj_util_debug ? UU_LIST_DEBUG : 0)); + return (list); +} + +dtj_string_list_entry_t * +dtj_string_list_entry_create(const char *s) +{ + dtj_string_list_entry_t *e; + + if (!dtj_check_string_pool()) { + return (NULL); + } + + e = uu_zalloc(sizeof (dtj_string_list_entry_t)); + if (e) { + uu_list_node_init(e, &e->dsle_node, g_string_pool); + if (s) { + e->dsle_value = malloc(strlen(s) + 1); + if (e->dsle_value) { + (void) strcpy(e->dsle_value, s); + } else { + uu_list_node_fini(e, &e->dsle_node, + g_string_pool); + uu_free(e); + e = NULL; + } + } + } + return (e); +} + +void +dtj_pointer_list_entry_destroy(void *v, + dtj_value_destroy_f *value_destroy, void *arg) +{ + if (v) { + dtj_pointer_list_entry_t *e = v; + if (value_destroy) { + value_destroy(e->dple_ptr, arg); + } + uu_list_node_fini(e, &e->dple_node, g_pointer_pool); + e->dple_ptr = NULL; + uu_free(v); + } +} + +void +/* ARGSUSED */ +dtj_string_list_entry_destroy(void *v, void *arg) +{ + if (v) { + dtj_string_list_entry_t *e = v; + free(e->dsle_value); + uu_list_node_fini(e, &e->dsle_node, g_string_pool); + e->dsle_value = NULL; + uu_free(v); + } +} + +void +dtj_list_clear(uu_list_t *list, dtj_value_destroy_f *value_destroy, + void *arg) +{ + void *cookie; /* needed for uu_list_teardown */ + void *value; + + if (!list) { + return; + } + + cookie = NULL; + if (value_destroy) { + while ((value = uu_list_teardown(list, &cookie)) != NULL) { + value_destroy(value, arg); + } + } else { + while ((value = uu_list_teardown(list, &cookie)) != NULL) { + } + } +} + +void +dtj_list_destroy(uu_list_t *list, + dtj_value_destroy_f *value_destroy, void *arg) +{ + dtj_list_clear(list, value_destroy, arg); + uu_list_destroy(list); +} + +void +dtj_pointer_list_clear(uu_list_t *list, + dtj_value_destroy_f *value_destroy, void *arg) +{ + void *cookie; /* needed for uu_list_teardown */ + dtj_pointer_list_entry_t *e; + + if (!list) { + return; + } + + cookie = NULL; + while ((e = uu_list_teardown(list, &cookie)) != NULL) { + dtj_pointer_list_entry_destroy(e, value_destroy, arg); + } +} + +void +dtj_pointer_list_destroy(uu_list_t *list, + dtj_value_destroy_f *value_destroy, void *arg) +{ + dtj_pointer_list_clear(list, value_destroy, arg); + uu_list_destroy(list); +} + +void +dtj_string_list_clear(uu_list_t *list) +{ + dtj_list_clear(list, dtj_string_list_entry_destroy, NULL); +} + +void +dtj_string_list_destroy(uu_list_t *list) +{ + dtj_list_destroy(list, dtj_string_list_entry_destroy, NULL); +} + +boolean_t +dtj_list_empty(uu_list_t *list) +{ + return (uu_list_numnodes(list) == 0); +} + +boolean_t +dtj_list_add(uu_list_t *list, void *value) +{ + return (uu_list_insert_before(list, NULL, value) == 0); +} + +boolean_t +dtj_pointer_list_add(uu_list_t *list, void *p) +{ + dtj_pointer_list_entry_t *e = dtj_pointer_list_entry_create(p); + if (!e) { + return (B_FALSE); + } + return (dtj_list_add(list, e)); +} + +void * +dtj_pointer_list_walk_next(uu_list_walk_t *itr) +{ + dtj_pointer_list_entry_t *e = uu_list_walk_next(itr); + if (!e) { + return (DTJ_INVALID_PTR); + } + return (e->dple_ptr); +} + +void * +dtj_pointer_list_first(uu_list_t *list) +{ + dtj_pointer_list_entry_t *e = uu_list_first(list); + if (!e) { + /* NULL is a valid value; use -1 for invalid */ + return (DTJ_INVALID_PTR); + } + return (e->dple_ptr); +} + +void * +dtj_pointer_list_last(uu_list_t *list) +{ + dtj_pointer_list_entry_t *e = uu_list_last(list); + if (!e) { + /* NULL is a valid value; use -1 for invalid */ + return (DTJ_INVALID_PTR); + } + return (e->dple_ptr); +} + +boolean_t +dtj_string_list_add(uu_list_t *list, const char *s) +{ + dtj_string_list_entry_t *e = dtj_string_list_entry_create(s); + if (!e) { + return (B_FALSE); + } + return (dtj_list_add(list, e)); +} + +const char * +dtj_string_list_walk_next(uu_list_walk_t *itr) +{ + dtj_string_list_entry_t *e = uu_list_walk_next(itr); + if (!e) { + return (DTJ_INVALID_STR); + } + return (e->dsle_value); +} + +const char * +dtj_string_list_first(uu_list_t *list) +{ + dtj_string_list_entry_t *e = uu_list_first(list); + if (!e) { + /* NULL is a valid string value; use -1 for invalid */ + return (DTJ_INVALID_STR); + } + return (e->dsle_value); +} + +const char * +dtj_string_list_last(uu_list_t *list) +{ + dtj_string_list_entry_t *e = uu_list_last(list); + if (!e) { + /* NULL is a valid string value; use -1 for invalid */ + return (DTJ_INVALID_STR); + } + return (e->dsle_value); +} diff --git a/usr/src/lib/libdtrace_jni/common/dtj_util.h b/usr/src/lib/libdtrace_jni/common/dtj_util.h new file mode 100644 index 0000000000..70d4c31455 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtj_util.h @@ -0,0 +1,376 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DTJ_UTIL_H +#define _DTJ_UTIL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <jni.h> +#include <libuutil.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * dtj_util.h separates functionality that is generally useful from + * that which is specific to the Java DTrace API. If moved to a separate + * library, this functionality could be shared by other JNI wrappers. + */ + +#ifdef JNI_VERSION_1_4 +#define JNI_VERSION JNI_VERSION_1_4 +#else +#define JNI_VERSION JNI_VERSION_1_2 +#endif + +#define CONSTRUCTOR "<init>" +#define DTJ_MSG_SIZE 1024 +#define DTJ_INVALID_PTR ((void *)-1) +#define DTJ_INVALID_STR ((const char *)-1) + +#define WRAP_EXCEPTION(JENV) dtj_wrap_exception((JENV), __FILE__, __LINE__) + +extern boolean_t g_dtj_util_debug; + +typedef enum dtj_status { + DTJ_OK = JNI_OK, + DTJ_ERR = JNI_ERR +} dtj_status_t; + +typedef enum dtj_type { + JCLASS, + JMETHOD, + JMETHOD_STATIC, + JFIELD, + JFIELD_STATIC, + DTJ_TYPE_END = -1 +} dtj_type_t; + +/* + * Convenient description format for java classes, methods, and fields. The + * java_class_t, java_method_t, and java_field_t structures derived from these + * descriptions are used to create a table of usable JNI jclass, jmethodID, and + * jfieldID instances. + */ +typedef struct dtj_table_entry { + dtj_type_t djte_type; /* JNI type */ + void *djte_addr; /* jclass, jmethodID, or jfieldID address */ + char *djte_name; /* symbol name declared in Java */ + char *djte_desc; /* JNI descriptor (string format) */ +} dtj_table_entry_t; + +typedef struct dtj_java_class { + jclass *djc_ptr; /* address in user-defined structure */ + char *djc_name; /* fully qualified '/' delimited class name */ + uu_list_t *djc_methods; /* element type (java_method_t *) */ + uu_list_t *djc_fields; /* element type (java_field_t *) */ + uu_list_node_t djc_node; +} dtj_java_class_t; + +typedef struct dtj_java_method { + jmethodID *djm_ptr; /* address in user-defined structure */ + char *djm_name; /* method name in java source file */ + char *djm_signature; /* javap -s method signature string */ + boolean_t djm_static; /* flag indicating static qualifier */ + uu_list_node_t djm_node; +} dtj_java_method_t; + +typedef struct dtj_java_field { + jfieldID *djf_ptr; /* address in user-defined structure */ + char *djf_name; /* field name in java source file */ + char *djf_type; /* javap -s field type string */ + boolean_t djf_static; /* flag indicating static qualifier */ + uu_list_node_t djf_node; +} dtj_java_field_t; + +/* + * Table of cached jclass, jmethodID, and jfieldID values usable across multiple + * native method calls and multiple threads. + * + * Suffix conventions: + * jc java class + * jm java method + * jsm java static method + * jf java field + * jsf java static field + */ + +/* NativeException */ +extern jclass g_nx_jc; +extern jmethodID g_nxinit_jm; + +/* java.io.Serializable */ +extern jclass g_serial_jc; + +/* java.lang.Number */ +extern jclass g_number_jc; +extern jmethodID g_shortval_jm; +extern jmethodID g_intval_jm; +extern jmethodID g_longval_jm; + +/* java.lang.Byte */ +extern jclass g_byte_jc; +extern jmethodID g_byteinit_jm; + +/* java.lang.Character */ +extern jclass g_char_jc; +extern jmethodID g_charinit_jm; +extern jmethodID g_charval_jm; + +/* java.lang.Short */ +extern jclass g_short_jc; +extern jmethodID g_shortinit_jm; + +/* java.lang.Integer */ +extern jclass g_int_jc; +extern jmethodID g_intinit_jm; + +/* java.lang.Long */ +extern jclass g_long_jc; +extern jmethodID g_longinit_jm; + +/* java.lang.String */ +extern jclass g_string_jc; +extern jmethodID g_strinit_bytes_jm; +extern jmethodID g_strbytes_jm; +extern jmethodID g_trim_jm; + +/* java.lang.StringBuffer */ +extern jclass g_buf_jc; +extern jmethodID g_bufinit_jm; +extern jmethodID g_buf_append_char_jm; +extern jmethodID g_buf_append_int_jm; +extern jmethodID g_buf_append_long_jm; +extern jmethodID g_buf_append_str_jm; +extern jmethodID g_buf_append_obj_jm; +extern jmethodID g_buflen_jm; +extern jmethodID g_bufsetlen_jm; + +/* java.lang.Object */ +extern jclass g_object_jc; +extern jmethodID g_tostring_jm; +extern jmethodID g_equals_jm; + +/* java.lang.Enum */ +extern jclass g_enum_jc; +extern jmethodID g_enumname_jm; + +/* List */ +extern jclass g_list_jc; +extern jmethodID g_listclear_jm; +extern jmethodID g_listadd_jm; +extern jmethodID g_listget_jm; +extern jmethodID g_listsize_jm; + +/* + * Populates the common java class references and associated method and field + * IDs declared in this file (above) using the dtj_cache_jni_classes() method. + */ +extern dtj_status_t dtj_load_common(JNIEnv *); + +/* + * Populates the user-declared java class references and associated method and + * field IDs described in the given table. Because the class references are + * created as global JNI references, the method and field IDs remain valid + * across multiple native method calls and across multiple threads. + * + * This function assumes that the given table of java class, method, and field + * descriptions is terminated by an entry with DTJ_TYPE_END, and that the + * method and field descriptions immediately follow the description of their + * containing class. + * + * Throws NoClassDefFoundError, NoSuchMethodError, or NoSuchFieldError if any + * dtj_table_entry_t in common_jni_table.c is incorrect. + */ +extern dtj_status_t dtj_cache_jni_classes(JNIEnv *, const dtj_table_entry_t *); + +/* Common utilities */ + +/* + * The following functions each create a pending Java Error or Exception: + * + * OutOfMemoryError + * NullPointerException + * IllegalArgumentException + * IllegalStateException + * NoSuchElementException + * ClassCastException + * AssertionError + * org.opensolaris.os.support.ResourceLimitException + * + * Control should be returned to Java immediately afterwards. + */ +extern void dtj_throw_out_of_memory(JNIEnv *, const char *, ...); +extern void dtj_throw_null_pointer(JNIEnv *, const char *, ...); +extern void dtj_throw_illegal_argument(JNIEnv *, const char *, ...); +extern void dtj_throw_illegal_state(JNIEnv *, const char *, ...); +extern void dtj_throw_no_such_element(JNIEnv *, const char *, ...); +extern void dtj_throw_class_cast(JNIEnv *, const char *, ...); +extern void dtj_throw_assertion(JNIEnv *, const char *, ...); +extern void dtj_throw_resource_limit(JNIEnv *, const char *, ...); + +/* + * Attaches native filename and line number to the currently pending java + * exception, since that information is not present in the exception stack + * trace. + */ +extern void dtj_wrap_exception(JNIEnv *, const char *, int); + +/* + * Calls the toString() method of the given object and prints the value to + * stdout (useful for debugging). If an exception is thrown in this function, + * it is described on stdout and cleared. It's guaranteed that no exception is + * pending when this function returns. + */ +extern void dtj_print_object(JNIEnv *jenv, jobject obj); + +/* + * Gets a formatted String (local reference) from a format and a variable + * argument list of placeholder values. Returns NULL if OutOfMemoryError is + * thrown. + */ +extern jstring dtj_format_string(JNIEnv *jenv, const char *fmt, ...); + +/* + * Internationalization support. These functions taken (not verbatim) from + * Section 8.2 of The Java Native Interface by Sheng Liang, The Java Series. + * Use these functions for locale-specific strings such as file names. + */ +extern jstring dtj_NewStringNative(JNIEnv *jenv, const char *str); +extern char *dtj_GetStringNativeChars(JNIEnv *jenv, jstring jstr); +extern void dtj_ReleaseStringNativeChars(JNIEnv *jenv, jstring jstr, + const char *str); + +/* + * Converts the args array of main(String[] args) in Java into a native + * dynamically allocated array of strings. The returned array must be + * deallocated by calling free_argv(). A java exception is pending if this + * function returns NULL (in that case, any allocations made up to the point of + * failure in get_argv() are automatically freed). + * + * Returns a NULL-terminated array that works with functions that expect a + * terminating NULL rather than relying on an element count. The argc parameter + * is also overwritten with the number of returned array elements (not including + * the terminating NULL). + */ +extern char **dtj_get_argv(JNIEnv *jenv, jobjectArray args, int *argc); +/* + * Tokenizes a command string to create a native dynamically allocated array of + * strings. The first element of the returned array is assumed to be the name + * of the command, and subsequent elements are arguments to that command. + * Otherwise behaves exactly like get_argv() above, including requiring a + * subsequent call to free_argv() on the returned array. + * Throws NullPointerException if cmd is NULL. + * Throws IllegalArgumentException if cmd is empty. + */ +extern char **dtj_make_argv(JNIEnv *jenv, jstring cmd, int *argc); +extern void dtj_free_argv(char **argv); + + +/* Wrappers for uu_list_t */ + +/* + * List element destructor. + * params: node pointer, user arg (may be NULL) + */ +typedef void dtj_value_destroy_f(void *, void *); + +/* + * uu_list_t generic entry type for pointers compared by pointer value, similar + * to Java's default Object.equals() implementation (referenced objects are + * equal only if they have the same address in memory). Used with + * pointer_list_entry_cmp. + */ +typedef struct dtj_pointer_list_entry { + void *dple_ptr; + uu_list_node_t dple_node; +} dtj_pointer_list_entry_t; + +typedef struct dtj_string_list_entry { + char *dsle_value; + uu_list_node_t dsle_node; +} dtj_string_list_entry_t; + +/* Comparison functions, uu_compare_fn_t signature */ +extern int dtj_pointer_list_entry_cmp(const void *, const void *, void *); +extern int dtj_string_list_entry_cmp(const void *, const void *, void *); + +/* Constructors */ +extern uu_list_t *dtj_pointer_list_create(void); +extern dtj_pointer_list_entry_t *dtj_pointer_list_entry_create(void *); +extern uu_list_t *dtj_string_list_create(void); +extern dtj_string_list_entry_t *dtj_string_list_entry_create(const char *); + +/* Destructors */ +extern void dtj_pointer_list_entry_destroy(void *, dtj_value_destroy_f *, + void *); +extern void dtj_string_list_entry_destroy(void *, void *); +/* + * Convenience function destroys a uu_list_t and its values. + * + * param list: list to be destroyed, call is a no-op if list is NULL + * param value_destroy: optional destructor; if non-NULL, it is called on each + * list value + * param arg: user argument to the optional destructor + */ +extern void dtj_list_destroy(uu_list_t *, dtj_value_destroy_f *, void *); +extern void dtj_pointer_list_destroy(uu_list_t *, dtj_value_destroy_f *, + void *); +extern void dtj_string_list_destroy(uu_list_t *); + +/* + * Convenience functions clear a uu_list_t without destroying it. Destroys all + * list elements and leaves the list empty. The *_list_destroy() functions + * implicitly clear the list before destroying it. + */ +extern void dtj_list_clear(uu_list_t *, dtj_value_destroy_f *, void *); +extern void dtj_pointer_list_clear(uu_list_t *, dtj_value_destroy_f *, + void *); +extern void dtj_string_list_clear(uu_list_t *); + +extern boolean_t dtj_list_empty(uu_list_t *); +/* Return B_TRUE if successful, B_FALSE otherwise */ +extern boolean_t dtj_list_add(uu_list_t *, void *); +extern boolean_t dtj_pointer_list_add(uu_list_t *, void *); +extern boolean_t dtj_string_list_add(uu_list_t *, const char *); +/* Return INVALID_PTR if list is empty (NULL is a valid list element) */ +extern void * dtj_pointer_list_first(uu_list_t *); +extern void * dtj_pointer_list_last(uu_list_t *); +/* Return INVALID_STR if list is empty (NULL is a valid list element) */ +extern const char *dtj_string_list_first(uu_list_t *); +extern const char *dtj_string_list_last(uu_list_t *); +/* Return INVALID_PTR at end of list (NULL is a valid list element) */ +extern void *dtj_pointer_list_walk_next(uu_list_walk_t *); +/* Return INVALID_STR at end of list (NULL is a valid list element) */ +extern const char *dtj_string_list_walk_next(uu_list_walk_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DTJ_UTIL_H */ diff --git a/usr/src/lib/libdtrace_jni/common/dtrace_jni.c b/usr/src/lib/libdtrace_jni/common/dtrace_jni.c new file mode 100644 index 0000000000..089d13ac74 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtrace_jni.c @@ -0,0 +1,1893 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <libgen.h> +#include <assert.h> +#include <strings.h> +#include <libproc.h> +#include <pthread.h> +#include <dtrace_jni.h> +/* generated by javah */ +#include <LocalConsumer.h> + +/* + * dtrace_jni.c defines all the native methods of the Java DTrace API. Every + * native method is declared in a single class, LocalConsumer.java. + * + * Notes: + * + * The data generating loop must explicitly release every object reference it + * obtains in order to avoid a memory leak. A local JNI object reference is not + * automatically released until control returns to java, which never happens as + * long as the data generating loop runs. This applies to any JNI function that + * obtains an object reference (such as CallObjectMethod() or NewObject()). A + * local reference is released by calling DeleteLocalRef(), which is safe to + * call with an exception pending. + * + * It is important to check for an exception after calling java code from native + * C, such as after notifying the java consumer of new data. Failure to do this + * makes it possible for users of the interface to crash the JVM by throwing an + * exception in java code. + * + * Some JNI functions, like GetIntField() or ReleaseStringUTFChars(), do not + * need to be checked for exceptions. + * + * GetStringUTFChars() returns NULL if and only if an exception was thrown. + * + * It is important to stop a DTrace consumer and remove it if an exception + * occurs. This API guarantees that a consumer is stopped automatically if it + * throws an exception. An application running multiple DTrace consumers + * simultaneously should be able to continue running the others normally if any + * fail. + * + * Calls to libdtrace are not guaranteed to be MT-safe. Even if they are + * currently MT-safe, they are not guaranteed to remain that way. To address + * this, a global lock (the LocalConsumer.class reference) is used around calls + * to libdtrace. In many cases, the locking is done in java, which should be + * indicated in this file by a comment above the function that assumes prior + * locking. To access the same global lock from native C code, the JNI function + * MonitorEnter() is used. Each MonitorEnter() must have a matching + * MonitorExit() or the application will hang (all consumer threads). The + * consumer loop and the getAggregate() method require a per-consumer lock + * rather than a global lock; in that case the argument to MonitorEnter() and + * MonitorExit() is the consumerLock member of the LocalConsumer, not the + * LocalConsumer itself. + */ + +#define DTRACE_JNI_VERSION 1 + +#define FIRST_HANDLE 0 /* sequence-generated consumer ID */ +#define NO_HANDLE -1 +#define INITIAL_CAPACITY 8 /* initial size of consumer array */ +#define MAX_CAPACITY_INCREMENT 1024 + +static boolean_t g_dtj_load = B_FALSE; +static int g_handle_seq = NO_HANDLE; +/* + * key: caller's consumer handle (int) + * value: per-consumer data includes dtrace handle (consumer_t *) + */ +static dtj_consumer_t **g_consumer_table = NULL; +static size_t g_consumer_capacity = 0; +static size_t g_consumer_count = 0; +static size_t g_max_capacity_increment = MAX_CAPACITY_INCREMENT; +static size_t g_max_consumers = 0; /* no maximum */ +static boolean_t g_init = B_FALSE; +static pthread_mutex_t g_table_lock; +static pthread_mutexattr_t g_table_lock_attr; +pthread_key_t g_dtj_consumer_key; + +static int dtj_get_handle(JNIEnv *, jobject); +static dtj_status_t dtj_get_java_consumer(JNIEnv *, jobject, + dtj_java_consumer_t *); +static const char *dtj_getexecname(void); +static jobject dtj_get_program_info(dtj_java_consumer_t *, dtrace_proginfo_t *); +static jobject dtj_add_program(dtj_java_consumer_t *, dtj_program_t *); +static void dtj_flag(uint_t *, uint_t, boolean_t *, boolean_t *); +static boolean_t dtj_cflag(dtj_java_consumer_t *, const char *, boolean_t *, + boolean_t *); +static void dtj_list_probes(JNIEnv *, jobject, jobject, jobject, + dtrace_probe_f *); +static void dtj_list_compiled_probes(JNIEnv *, jobject, jobject, jobject, + dtrace_probe_f *); +static int dtj_list_probe(dtrace_hdl_t *, const dtrace_probedesc_t *, void *); +static int dtj_list_probe_detail(dtrace_hdl_t *, const dtrace_probedesc_t *, + void *); +static int dtj_list_stmt(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *, + void *); +static boolean_t dtj_add_consumer(JNIEnv *, dtj_consumer_t *, int *); +static dtj_consumer_t *dtj_remove_consumer(JNIEnv *, jobject); +static dtj_consumer_t *dtj_remove_consumer_at(int); + +/* + * Gets a sequence-generated consumer ID, or NO_HANDLE if exception pending + */ +static int +dtj_get_handle(JNIEnv *jenv, jobject caller) +{ + int handle; + + if (!g_dtj_load) { + dtj_throw_illegal_state(jenv, "JNI table not loaded"); + return (NO_HANDLE); + } + handle = (*jenv)->CallIntMethod(jenv, caller, g_gethandle_jm); + if ((*jenv)->ExceptionCheck(jenv)) { + return (NO_HANDLE); + } + if (handle == NO_HANDLE) { + dtj_throw_illegal_state(jenv, "no consumer handle"); + } + return (handle); +} + +/* + * Populates the given java consumer created for use in the current native + * method call. If the return value is DTJ_ERR, a java exception is pending. + * Throws IllegalStateException if the caller does not have a valid handle. + * Throws NoSuchElementException if the caller's handle is not in the global + * caller table. + */ +static dtj_status_t +dtj_get_java_consumer(JNIEnv *jenv, jobject caller, dtj_java_consumer_t *jc) +{ + dtj_consumer_t *consumer; + int handle = dtj_get_handle(jenv, caller); + if (handle == NO_HANDLE) { + return (DTJ_ERR); /* java exception pending */ + } + (void) pthread_mutex_lock(&g_table_lock); + if (g_consumer_table) { + if ((handle >= 0) && (handle < g_consumer_capacity)) { + consumer = g_consumer_table[handle]; + } else { + consumer = NULL; + } + } else { + consumer = NULL; + } + (void) pthread_mutex_unlock(&g_table_lock); + if (consumer == NULL) { + dtj_throw_no_such_element(jenv, "consumer handle %d", handle); + return (DTJ_ERR); + } + + /* Initialize java consumer */ + bzero(jc, sizeof (dtj_java_consumer_t)); + + /* Attach per-consumer data */ + jc->dtjj_consumer = consumer; + + /* Attach per-JNI-invocation data */ + jc->dtjj_caller = caller; + jc->dtjj_jenv = jenv; + + return (DTJ_OK); +} + +/* + * Adds a consumer to the global consumer table. + * Returns B_TRUE if successful; a java exception is pending otherwise. + * Postcondition: if successful, g_handle_seq is the handle of the consumer just + * added. + */ +static boolean_t +dtj_add_consumer(JNIEnv *jenv, dtj_consumer_t *c, int *seq) +{ + int start; + + if (!g_init) { + (void) pthread_key_create(&g_dtj_consumer_key, NULL); + (void) pthread_mutexattr_init(&g_table_lock_attr); + (void) pthread_mutexattr_settype(&g_table_lock_attr, + PTHREAD_MUTEX_RECURSIVE); + (void) pthread_mutex_init(&g_table_lock, + &g_table_lock_attr); + g_init = B_TRUE; + } + + *seq = NO_HANDLE; + (void) pthread_mutex_lock(&g_table_lock); + if (g_consumer_table == NULL) { + g_consumer_table = malloc(INITIAL_CAPACITY * + sizeof (dtj_consumer_t *)); + if (!g_consumer_table) { + g_handle_seq = NO_HANDLE; + dtj_throw_out_of_memory(jenv, + "could not allocate consumer table"); + (void) pthread_mutex_unlock(&g_table_lock); + return (B_FALSE); + } + bzero(g_consumer_table, (INITIAL_CAPACITY * + sizeof (dtj_consumer_t *))); + g_consumer_capacity = INITIAL_CAPACITY; + } else if (g_consumer_count >= g_consumer_capacity) { + dtj_consumer_t **t; + size_t new_capacity; + + if ((g_max_consumers > 0) && (g_consumer_count >= + g_max_consumers)) { + dtj_throw_resource_limit(jenv, "Too many consumers"); + (void) pthread_mutex_unlock(&g_table_lock); + return (B_FALSE); + } + + if (g_consumer_capacity <= g_max_capacity_increment) { + new_capacity = (g_consumer_capacity * 2); + } else { + new_capacity = (g_consumer_capacity + + g_max_capacity_increment); + } + + if ((g_max_consumers > 0) && (new_capacity > g_max_consumers)) { + new_capacity = g_max_consumers; + } + + t = realloc(g_consumer_table, + new_capacity * sizeof (dtj_consumer_t *)); + if (!t) { + dtj_throw_out_of_memory(jenv, + "could not reallocate consumer table"); + (void) pthread_mutex_unlock(&g_table_lock); + return (B_FALSE); + } + + g_consumer_table = t; + bzero(g_consumer_table + g_consumer_capacity, ((new_capacity - + g_consumer_capacity) * sizeof (dtj_consumer_t *))); + g_consumer_capacity = new_capacity; + } + + /* Look for an empty slot in the table */ + g_handle_seq = (g_handle_seq == NO_HANDLE + ? FIRST_HANDLE : g_handle_seq + 1); + if (g_handle_seq >= g_consumer_capacity) { + g_handle_seq = FIRST_HANDLE; + } + start = g_handle_seq; /* guard against infinite loop */ + while (g_consumer_table[g_handle_seq] != NULL) { + ++g_handle_seq; + if (g_handle_seq == start) { + dtj_throw_illegal_state(jenv, "consumer table full," + " but count %d < capacity %d", + g_consumer_count, g_consumer_capacity); + (void) pthread_mutex_unlock(&g_table_lock); + return (B_FALSE); + } else if (g_handle_seq >= g_consumer_capacity) { + g_handle_seq = FIRST_HANDLE; + } + } + g_consumer_table[g_handle_seq] = c; + *seq = g_handle_seq; + ++g_consumer_count; + (void) pthread_mutex_unlock(&g_table_lock); + return (B_TRUE); +} + +/* + * Removes a consumer from the global consumer table. The call may be initiated + * from Java code or from native code (because an exception has occurred). + * Precondition: no exception pending (any pending exception must be temporarily + * cleared) + * Returns NULL if the caller is not in the table or if this function throws an + * exception; either case leaves the global consumer table unchanged. + * Throws IllegalStateException if the caller does not have a valid handle. + */ +static dtj_consumer_t * +dtj_remove_consumer(JNIEnv *jenv, jobject caller) +{ + dtj_consumer_t *consumer; + int handle = dtj_get_handle(jenv, caller); + if (handle == NO_HANDLE) { + return (NULL); /* java exception pending */ + } + consumer = dtj_remove_consumer_at(handle); + return (consumer); +} + +/* + * Returns NULL if there is no consumer with the given handle. Does not throw + * exceptions. + */ +static dtj_consumer_t * +dtj_remove_consumer_at(int handle) +{ + dtj_consumer_t *consumer; + (void) pthread_mutex_lock(&g_table_lock); + if (g_consumer_table) { + if ((handle >= 0) && (handle < g_consumer_capacity)) { + consumer = g_consumer_table[handle]; + if (consumer != NULL) { + g_consumer_table[handle] = NULL; + --g_consumer_count; + if (g_consumer_count == 0) { + free(g_consumer_table); + g_consumer_table = NULL; + g_consumer_capacity = 0; + g_handle_seq = NO_HANDLE; + } + } + } else { + consumer = NULL; + } + } else { + consumer = NULL; + } + (void) pthread_mutex_unlock(&g_table_lock); + return (consumer); +} + +/* + * Gets the name of the executable in case it is an application with an embedded + * JVM and not "java". Implementation is copied from lib/mpss/common/mpss.c. + * The use of static auxv_t makes the MT-level unsafe. The caller is expected + * to use the global lock (LocalConsumer.class). + */ +static const char * +dtj_getexecname(void) +{ + const char *execname = NULL; + static auxv_t auxb; + + /* + * The first time through, read the initial aux vector that was + * passed to the process at exec(2). Only do this once. + */ + int fd = open("/proc/self/auxv", O_RDONLY); + + if (fd >= 0) { + while (read(fd, &auxb, sizeof (auxv_t)) == sizeof (auxv_t)) { + if (auxb.a_type == AT_SUN_EXECNAME) { + execname = auxb.a_un.a_ptr; + break; + } + } + (void) close(fd); + } + return (execname); +} + +/* + * Add the compiled program to a list of programs the API expects to enable. + * Returns the Program instance identifying the listed program, or NULL if the + * Program constructor fails (exception pending in that case). + */ +static jobject +dtj_add_program(dtj_java_consumer_t *jc, dtj_program_t *p) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + jobject jprogram = NULL; + + switch (p->dtjp_type) { + case DTJ_PROGRAM_STRING: + jprogram = (*jenv)->NewObject(jenv, g_program_jc, + g_proginit_jm); + break; + case DTJ_PROGRAM_FILE: + jprogram = (*jenv)->NewObject(jenv, g_programfile_jc, + g_fproginit_jm); + break; + default: + dtj_throw_illegal_argument(jenv, "unexpected program type %d\n", + p->dtjp_type); + } + if ((*jenv)->ExceptionCheck(jenv)) { + return (NULL); + } + + /* Does not throw exceptions */ + (*jenv)->SetIntField(jenv, jprogram, g_progid_jf, + uu_list_numnodes(jc->dtjj_consumer->dtjc_program_list)); + + if (!dtj_list_add(jc->dtjj_consumer->dtjc_program_list, p)) { + (*jenv)->DeleteLocalRef(jenv, jprogram); + dtj_throw_out_of_memory(jenv, + "could not add program"); + return (NULL); + } + + return (jprogram); +} + +/* + * Returns a new ProgramInfo, or NULL if the constructor fails (java exception + * pending in that case). + */ +static jobject +dtj_get_program_info(dtj_java_consumer_t *jc, dtrace_proginfo_t *pinfo) +{ + JNIEnv *jenv = jc->dtjj_jenv; + + jobject minProbeAttributes = NULL; + jobject minStatementAttributes = NULL; + jobject programInfo = NULL; /* return value */ + + minProbeAttributes = dtj_new_attribute(jc, &pinfo->dpi_descattr); + if (!minProbeAttributes) { + return (NULL); /* java exception pending */ + } + minStatementAttributes = dtj_new_attribute(jc, &pinfo->dpi_stmtattr); + if (!minStatementAttributes) { + (*jenv)->DeleteLocalRef(jenv, minProbeAttributes); + return (NULL); /* java exception pending */ + } + + programInfo = (*jenv)->NewObject(jenv, g_proginfo_jc, + g_proginfoinit_jm, minProbeAttributes, minStatementAttributes, + pinfo->dpi_matches); + (*jenv)->DeleteLocalRef(jenv, minProbeAttributes); + (*jenv)->DeleteLocalRef(jenv, minStatementAttributes); + + return (programInfo); +} + +/* + * Called by LocalConsumer static initializer. + */ +JNIEXPORT void JNICALL +/* ARGSUSED */ +Java_org_opensolaris_os_dtrace_LocalConsumer__1checkVersion(JNIEnv *env, + jclass class, jint version) +{ + if (version != DTRACE_JNI_VERSION) { + dtj_throw_illegal_state(env, + "LocalConsumer version %d incompatible with native " + "implementation version %d", version, DTRACE_JNI_VERSION); + } +} + +/* + * Called by LocalConsumer static initializer. + */ +JNIEXPORT void JNICALL +/* ARGSUSED */ +Java_org_opensolaris_os_dtrace_LocalConsumer__1loadJniTable(JNIEnv *env, + jclass class) +{ + if (g_dtj_load) { + /* + * JNI table includes a global reference to the LocalConsumer + * class, preventing the class from being unloaded. The + * LocalConsumer static initializer should never execute more + * than once. + */ + dtj_throw_illegal_state(env, "JNI table already loaded"); + return; + } + + /* If this fails, a Java Error (e.g. NoSuchMethodError) is pending */ + if (dtj_load(env) == DTJ_OK) { + g_dtj_load = B_TRUE; + } +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1open(JNIEnv *env, jobject obj, + jobjectArray flags) +{ + dtrace_hdl_t *dtp; + dtj_consumer_t *c; + const char *f; /* flag name */ + int oflags = 0; + int i, len; + int id; + int err; + + jobject flag = NULL; + jstring flagname = NULL; + + if (!g_dtj_load) { + dtj_throw_illegal_state(env, "JNI table not loaded"); + return; + } + + c = dtj_consumer_create(env); + if (!c) { + return; /* java exception pending */ + } + + /* Get open flags */ + len = (flags ? (*env)->GetArrayLength(env, flags) : 0); + for (i = 0; i < len; ++i) { + flag = (*env)->GetObjectArrayElement(env, flags, i); + if ((*env)->ExceptionCheck(env)) { + dtj_consumer_destroy(c); + return; + } + + flagname = (*env)->CallObjectMethod(env, flag, g_enumname_jm); + (*env)->DeleteLocalRef(env, flag); + if ((*env)->ExceptionCheck(env)) { + dtj_consumer_destroy(c); + return; + } + f = (*env)->GetStringUTFChars(env, flagname, NULL); + if ((*env)->ExceptionCheck(env)) { + (*env)->DeleteLocalRef(env, flagname); + dtj_consumer_destroy(c); + return; + } + if (strcmp(f, "ILP32") == 0) { + oflags |= DTRACE_O_ILP32; + } else if (strcmp(f, "LP64") == 0) { + oflags |= DTRACE_O_LP64; + } + (*env)->ReleaseStringUTFChars(env, flagname, f); + (*env)->DeleteLocalRef(env, flagname); + } + + /* Check for mutually exclusive flags */ + if ((oflags & DTRACE_O_ILP32) && (oflags & DTRACE_O_LP64)) { + dtj_throw_illegal_argument(env, + "Cannot set both ILP32 and LP64"); + dtj_consumer_destroy(c); + return; + } + + /* + * Make sure we can add the consumer before calling dtrace_open(). + * Repeated calls to open() when the consumer table is maxed out should + * avoid calling dtrace_open(). (Normally there is no limit to the size + * of the consumer table, but the undocumented JAVA_DTRACE_MAX_CONSUMERS + * system property lets you set a limit after which + * ResourceLimitException is thrown.) + */ + if (!dtj_add_consumer(env, c, &id)) { + dtj_consumer_destroy(c); + return; /* java exception pending */ + } + + (*env)->CallVoidMethod(env, obj, g_sethandle_jm, id); + if ((*env)->ExceptionCheck(env)) { + (void) dtj_remove_consumer_at(id); + dtj_consumer_destroy(c); + return; + } + + if ((dtp = dtrace_open(DTRACE_VERSION, oflags, &err)) == NULL) { + dtj_java_consumer_t jc; + jc.dtjj_jenv = env; + dtj_throw_dtrace_exception(&jc, dtrace_errmsg(NULL, err)); + (void) dtj_remove_consumer_at(id); + dtj_consumer_destroy(c); + return; + } + c->dtjc_dtp = dtp; /* set consumer handle to native DTrace library */ +} + +static void +dtj_flag(uint_t *flags, uint_t flag, boolean_t *get, boolean_t *set) +{ + assert((get && !set) || (set && !get)); + + if (get) { + *get = (*flags & flag); + } else { + if (*set) { + *flags |= flag; + } else { + *flags &= ~flag; + } + } +} + +/* + * Returns B_TRUE if opt is a recognized compile flag, B_FALSE otherwise. + */ +static boolean_t +dtj_cflag(dtj_java_consumer_t *jc, const char *opt, boolean_t *get, + boolean_t *set) +{ + boolean_t is_cflag = B_TRUE; + uint_t *flags = &jc->dtjj_consumer->dtjc_cflags; + + /* see lib/libdtrace/common/dt_option.c */ + if (strcmp(opt, "argref") == 0) { + dtj_flag(flags, DTRACE_C_ARGREF, get, set); + } else if (strcmp(opt, "cpp") == 0) { + dtj_flag(flags, DTRACE_C_CPP, get, set); + } else if (strcmp(opt, "defaultargs") == 0) { + dtj_flag(flags, DTRACE_C_DEFARG, get, set); + } else if (strcmp(opt, "empty") == 0) { + dtj_flag(flags, DTRACE_C_EMPTY, get, set); + } else if (strcmp(opt, "errtags") == 0) { + dtj_flag(flags, DTRACE_C_ETAGS, get, set); + } else if (strcmp(opt, "knodefs") == 0) { + dtj_flag(flags, DTRACE_C_KNODEF, get, set); + } else if (strcmp(opt, "nolibs") == 0) { + dtj_flag(flags, DTRACE_C_NOLIBS, get, set); + } else if (strcmp(opt, "pspec") == 0) { + dtj_flag(flags, DTRACE_C_PSPEC, get, set); + } else if (strcmp(opt, "unodefs") == 0) { + dtj_flag(flags, DTRACE_C_UNODEF, get, set); + } else if (strcmp(opt, "verbose") == 0) { + dtj_flag(flags, DTRACE_C_DIFV, get, set); + } else if (strcmp(opt, "zdefs") == 0) { + dtj_flag(flags, DTRACE_C_ZDEFS, get, set); + } else { + is_cflag = B_FALSE; + } + + if (is_cflag && set && + (jc->dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT)) { + dtj_throw_illegal_state(jc->dtjj_jenv, + "cannot set compile time option \"%s\" after calling go()", + opt); + return (is_cflag); + } + + return (is_cflag); +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT jobject JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1compileString(JNIEnv *env, + jobject obj, jstring program, jobjectArray args) +{ + const char *prog; + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + dtj_program_t *p; + int argc = 0; + char **argv = NULL; + + jstring jprogram = NULL; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return (NULL); /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + prog = (*env)->GetStringUTFChars(env, program, 0); + if ((*env)->ExceptionCheck(env)) { + return (NULL); + } + + p = dtj_program_create(env, DTJ_PROGRAM_STRING, prog); + if (!p) { + (*env)->ReleaseStringUTFChars(env, program, prog); + return (NULL); /* java exception pending */ + } + + if (args) { + argv = dtj_get_argv(env, args, &argc); + if ((*env)->ExceptionCheck(env)) { + (*env)->ReleaseStringUTFChars(env, program, prog); + dtj_program_destroy(p, NULL); + return (NULL); + } + } + + if ((p->dtjp_program = dtrace_program_strcompile(dtp, + prog, DTRACE_PROBESPEC_NAME, jc.dtjj_consumer->dtjc_cflags, + argc, argv)) == NULL) { + dtj_throw_dtrace_exception(&jc, + "invalid probe specifier %s: %s", + prog, dtrace_errmsg(dtp, dtrace_errno(dtp))); + (*env)->ReleaseStringUTFChars(env, program, prog); + dtj_program_destroy(p, NULL); + dtj_free_argv(argv); + return (NULL); + } + (*env)->ReleaseStringUTFChars(env, program, prog); + dtj_free_argv(argv); + + jprogram = dtj_add_program(&jc, p); + return (jprogram); /* NULL if exception pending */ +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT jobject JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1compileFile(JNIEnv *env, + jobject obj, jstring filename, jobjectArray args) +{ + FILE *fp; + const char *file; + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + dtj_program_t *p; + int argc = 0; + char **argv = NULL; + + jstring jprogram = NULL; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return (NULL); /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + file = dtj_GetStringNativeChars(env, filename); + if ((fp = fopen(file, "r")) == NULL) { + dtj_throw_dtrace_exception(&jc, "failed to open %s", file); + dtj_ReleaseStringNativeChars(env, filename, file); + return (NULL); + } + + p = dtj_program_create(env, DTJ_PROGRAM_FILE, file); + if (!p) { + (void) fclose(fp); + dtj_ReleaseStringNativeChars(env, filename, file); + return (NULL); /* java exception pending */ + } + + if (args) { + argv = dtj_get_argv(env, args, &argc); + if ((*env)->ExceptionCheck(env)) { + (void) fclose(fp); + dtj_ReleaseStringNativeChars(env, filename, file); + dtj_program_destroy(p, NULL); + return (NULL); + } + } + + if ((p->dtjp_program = dtrace_program_fcompile(dtp, + fp, jc.dtjj_consumer->dtjc_cflags, argc, argv)) == NULL) { + dtj_throw_dtrace_exception(&jc, + "failed to compile script %s: %s", file, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + (void) fclose(fp); + dtj_ReleaseStringNativeChars(env, filename, file); + dtj_program_destroy(p, NULL); + dtj_free_argv(argv); + return (NULL); + } + (void) fclose(fp); + dtj_ReleaseStringNativeChars(env, filename, file); + dtj_free_argv(argv); + + jprogram = dtj_add_program(&jc, p); + return (jprogram); /* NULL if exception pending */ +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1exec(JNIEnv *env, jobject obj, + jobject program) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + int progid = -1; + uu_list_walk_t *itr; + dtj_program_t *p; + dtrace_proginfo_t *pinfo = NULL; + int i; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + if (program) { + progid = (*env)->GetIntField(env, program, g_progid_jf); + } + + if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { + dtj_throw_illegal_state(env, "no program compiled"); + return; + } + + itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); + for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) { + /* enable all probes or those of given program only */ + if ((progid != -1) && (progid != i)) { + continue; + } + + if (p->dtjp_enabled) { + dtj_throw_illegal_state(env, "program already enabled"); + uu_list_walk_end(itr); + return; + } + + pinfo = &p->dtjp_info; + if (dtrace_program_exec(dtp, p->dtjp_program, pinfo) == -1) { + dtj_throw_dtrace_exception(&jc, + "failed to enable %s: %s", p->dtjp_name, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + uu_list_walk_end(itr); + return; + } + p->dtjp_enabled = B_TRUE; + } + uu_list_walk_end(itr); + + if (program) { + jobject programInfo = NULL; + + if (!pinfo) { + /* + * Consumer.enable() has already checked that the + * program was compiled by this consumer. This is an + * implementation error, not a user error. + */ + dtj_throw_illegal_state(env, "program not found"); + return; + } + + programInfo = dtj_get_program_info(&jc, pinfo); + if (!programInfo) { + return; /* java exception pending */ + } + + (*env)->SetObjectField(env, program, g_proginfo_jf, + programInfo); + (*env)->DeleteLocalRef(env, programInfo); + } +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1getProgramInfo(JNIEnv *env, + jobject obj, jobject program) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + int progid; + uu_list_walk_t *itr; + dtj_program_t *p; + dtrace_proginfo_t *pinfo = NULL; + int i; + + jobject programInfo = NULL; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + progid = (*env)->GetIntField(env, program, g_progid_jf); + + if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { + dtj_throw_illegal_state(env, "no program compiled"); + return; + } + + itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); + for (i = 0; ((p = uu_list_walk_next(itr)) != NULL) && !pinfo; ++i) { + if (progid != i) { + /* get info of given program only */ + continue; + } + + pinfo = &p->dtjp_info; + dtrace_program_info(dtp, p->dtjp_program, pinfo); + } + uu_list_walk_end(itr); + + programInfo = dtj_get_program_info(&jc, pinfo); + if (!programInfo) { + return; /* java exception pending */ + } + + (*env)->SetObjectField(env, program, g_proginfo_jf, + programInfo); + (*env)->DeleteLocalRef(env, programInfo); +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1setOption(JNIEnv *env, + jobject obj, jstring option, jstring value) +{ + dtj_java_consumer_t jc; + const char *opt; + const char *val; + boolean_t on; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + + opt = (*env)->GetStringUTFChars(env, option, 0); + if (!opt) { + dtj_throw_out_of_memory(env, + "could not allocate option string"); + return; + } + + if (value) { + val = (*env)->GetStringUTFChars(env, value, 0); + if (!val) { + dtj_throw_out_of_memory(env, + "could not allocate option value string"); + (*env)->ReleaseStringUTFChars(env, option, opt); + return; + } + } else { + /* + * dtrace_setopt() sets option to 0 if value is NULL. That's + * not the same thing as unsetting a boolean option, since + * libdtrace uses -2 to mean unset. We'll leave it to + * LocalConsumer.java to disallow null or not. + */ + val = NULL; + } + + /* + * Check for boolean compile-time options not handled by + * dtrace_setopt(). + */ + on = (!val || (strcmp(val, "unset") != 0)); + if (dtj_cflag(&jc, opt, NULL, &on)) { + (*env)->ReleaseStringUTFChars(env, option, opt); + if (value) { + (*env)->ReleaseStringUTFChars(env, value, val); + } + return; + } + + /* + * The transition from INIT to GO is protected by synchronization + * (a per-consumer lock) in LocalConsumer.java, ensuring that go() and + * setOption() execute sequentially. + */ + if (jc.dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT) { + /* + * If the consumer is already running, defer setting the option + * until we wake up from dtrace_sleep. + */ + dtj_request_t *request; + + + (void) pthread_mutex_lock( + &jc.dtjj_consumer->dtjc_request_list_lock); + request = dtj_request_create(env, DTJ_REQUEST_OPTION, opt, val); + if (request) { + if (!dtj_list_add(jc.dtjj_consumer->dtjc_request_list, + request)) { + dtj_throw_out_of_memory(env, + "Failed to add setOption request"); + } + } /* else java exception pending */ + (void) pthread_mutex_unlock( + &jc.dtjj_consumer->dtjc_request_list_lock); + } else { + dtrace_hdl_t *dtp = jc.dtjj_consumer->dtjc_dtp; + if (dtrace_setopt(dtp, opt, val) == -1) { + dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp, + dtrace_errno(dtp))); + } + } + + (*env)->ReleaseStringUTFChars(env, option, opt); + if (value) { + (*env)->ReleaseStringUTFChars(env, value, val); + } +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT jlong JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1getOption(JNIEnv *env, + jobject obj, jstring option) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + const char *opt; + dtrace_optval_t optval; + boolean_t cflag; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return (0); /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + opt = (*env)->GetStringUTFChars(env, option, 0); + if (!opt) { + dtj_throw_out_of_memory(env, + "could not allocate option string"); + return (0); + } + + /* + * Check for boolean compile-time options not handled by + * dtrace_setopt(). + */ + if (dtj_cflag(&jc, opt, &cflag, NULL)) { + (*env)->ReleaseStringUTFChars(env, option, opt); + return (cflag ? 1 : DTRACEOPT_UNSET); + } + + if (dtrace_getopt(dtp, opt, &optval) == -1) { + dtj_throw_dtrace_exception(&jc, + "couldn't get option %s: %s", opt, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + (*env)->ReleaseStringUTFChars(env, option, opt); + return (0); + } + (*env)->ReleaseStringUTFChars(env, option, opt); + return (optval); +} + +/* + * Throws IllegalStateException if not all compiled programs are enabled. + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1checkProgramEnabling(JNIEnv *env, + jobject obj) +{ + dtj_java_consumer_t jc; + dtj_program_t *p; + uu_list_walk_t *itr; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + + if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { + dtj_throw_illegal_state(env, "no program compiled"); + return; + } + + itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); + while ((p = uu_list_walk_next(itr)) != NULL) { + if (!p->dtjp_enabled) { + const char *type; + switch (p->dtjp_type) { + case DTJ_PROGRAM_STRING: + type = "description"; + break; + case DTJ_PROGRAM_FILE: + type = "script"; + break; + default: + assert(p->dtjp_type == DTJ_PROGRAM_STRING || + p->dtjp_type == DTJ_PROGRAM_FILE); + } + dtj_throw_illegal_state(env, + "Not all compiled probes are enabled. " + "Compiled %s %s not enabled.", + type, p->dtjp_name); + uu_list_walk_end(itr); + return; + } + } + uu_list_walk_end(itr); +} + +JNIEXPORT jboolean JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1isEnabled(JNIEnv *env, + jobject obj) +{ + dtj_java_consumer_t jc; + dtj_program_t *p; + uu_list_walk_t *itr; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return (JNI_FALSE); /* java exception pending */ + } + + if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { + return (JNI_FALSE); /* no program compiled */ + } + + itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); + while ((p = uu_list_walk_next(itr)) != NULL) { + if (!p->dtjp_enabled) { + uu_list_walk_end(itr); + return (JNI_FALSE); + } + } + uu_list_walk_end(itr); + + return (JNI_TRUE); +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1go(JNIEnv *env, jobject obj) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + if (dtj_set_callback_handlers(&jc) != DTJ_OK) { + return; /* java exception pending */ + } + + if (dtrace_go(dtp) != 0) { + dtj_throw_dtrace_exception(&jc, + "could not enable tracing: %s", + dtrace_errmsg(dtp, dtrace_errno(dtp))); + return; + } + + jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_GO; +} + +/* + * Protected by global lock (LocalConsumer.class). Called when aborting the + * consumer loop before it starts. + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1stop(JNIEnv *env, + jobject obj) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + if (dtrace_stop(dtp) == -1) { + dtj_throw_dtrace_exception(&jc, + "couldn't stop tracing: %s", + dtrace_errmsg(jc.dtjj_consumer->dtjc_dtp, + dtrace_errno(jc.dtjj_consumer->dtjc_dtp))); + } else { + jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP; + } +} + +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1consume(JNIEnv *env, + jobject obj) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_START; + + if (dtj_java_consumer_init(env, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + + /* + * Must set the thread-specific java consumer before starting the + * dtrace_work() loop because the bufhandler can also be invoked by + * getAggregate() from another thread. The bufhandler needs access to + * the correct JNI state specific to either the consumer loop or the + * getAggregate() call. + */ + (void) pthread_setspecific(g_dtj_consumer_key, &jc); + + if (jc.dtjj_consumer->dtjc_process_list != NULL) { + struct ps_prochandle *P; + uu_list_walk_t *itr; + + (*env)->MonitorEnter(env, g_caller_jc); + if ((*env)->ExceptionCheck(env)) { + dtj_java_consumer_fini(env, &jc); + return; /* java exception pending */ + } + + itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list, + 0); + while ((P = dtj_pointer_list_walk_next(itr)) != + DTJ_INVALID_PTR) { + dtrace_proc_continue(dtp, P); + } + uu_list_walk_end(itr); + + (*env)->MonitorExit(env, g_caller_jc); + if ((*env)->ExceptionCheck(env)) { + dtj_java_consumer_fini(env, &jc); + return; /* java exception pending */ + } + } + + /* + * Blocking call starts consumer loop. + */ + (void) dtj_consume(&jc); + + dtj_java_consumer_fini(env, &jc); + /* + * Stop the consumer after the consumer loop terminates, whether + * normally because of the exit() action or LocalConsumer.stop(), or + * abnormally because of an exception. The call is ignored if the + * consumer is already stopped. It is safe to call dtj_stop() with a + * pending exception. + */ + dtj_stop(&jc); +} + +/* + * Interrupts a running consumer. May be called from any thread. + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1interrupt(JNIEnv *env, + jobject obj) +{ + dtj_java_consumer_t jc; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + + jc.dtjj_consumer->dtjc_interrupt = B_TRUE; +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1close(JNIEnv *env, jobject obj) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + /* + * Need to release any created procs here, since the consumer_t + * destructor (called by _destroy) will not have access to the dtrace + * handle needed to release them (this function closes the dtrace + * handle). + */ + if (jc.dtjj_consumer->dtjc_process_list != NULL) { + struct ps_prochandle *P; + uu_list_walk_t *itr; + itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list, + 0); + while ((P = dtj_pointer_list_walk_next(itr)) != + DTJ_INVALID_PTR) { + dtrace_proc_release(dtp, P); + } + uu_list_walk_end(itr); + } + + dtrace_close(dtp); +} + +/* + * Static Consumer.java method + */ +JNIEXPORT jint JNICALL +/* ARGSUSED */ +Java_org_opensolaris_os_dtrace_LocalConsumer__1openCount(JNIEnv *env, jclass c) +{ + int open; + (void) pthread_mutex_lock(&g_table_lock); + if (g_consumer_table == NULL) { + open = 0; + } else { + open = g_consumer_count; + } + (void) pthread_mutex_unlock(&g_table_lock); + return (open); +} + +/* + * Static Consumer.java method + */ +JNIEXPORT jlong JNICALL +/* ARGSUSED */ +Java_org_opensolaris_os_dtrace_LocalConsumer__1quantizeBucket(JNIEnv *env, + jclass c, jint bucket) +{ + return (DTRACE_QUANTIZE_BUCKETVAL(bucket)); +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT jstring JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupKernelFunction( + JNIEnv *jenv, jobject caller, jobject address) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + + jstring jfunc; /* return value */ + + GElf_Addr addr; + char dummy; + char *s; + int rc; + + if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { + return (NULL); /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + /* Does not throw exceptions */ + if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) { + /* intValue() of class Number does not throw exceptions */ + addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv, + address, g_intval_jm); + } else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) { + /* longValue() of class Number does not throw exceptions */ + addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv, + address, g_longval_jm); + } else { + dtj_throw_class_cast(jenv, "Expected Number address"); + return (NULL); + } + + rc = dtrace_addr2str(dtp, addr, &dummy, 1); + s = malloc(rc); + if (!s) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate kernel function name"); + return (NULL); + } + (void) dtrace_addr2str(dtp, addr, s, rc); + + jfunc = (*jenv)->NewStringUTF(jenv, s); + free(s); + return (jfunc); +} + +/* + * Protected by global lock in Consumer.java + */ +JNIEXPORT jstring JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupUserFunction(JNIEnv *jenv, + jobject caller, jint pid, jobject address) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + + jstring jfunc; /* return value */ + + GElf_Addr addr; + char dummy; + char *s; + int rc; + + if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { + return (NULL); /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + /* Does not throw exceptions */ + if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) { + /* intValue() of class Number does not throw exceptions */ + addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv, + address, g_intval_jm); + } else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) { + /* longValue() of class Number does not throw exceptions */ + addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv, + address, g_longval_jm); + } else { + dtj_throw_class_cast(jenv, "Expected Number address"); + return (NULL); + } + + rc = dtrace_uaddr2str(dtp, pid, addr, &dummy, 1); + s = malloc(rc); + if (!s) { + dtj_throw_out_of_memory(jenv, + "Failed to allocate user function name"); + return (NULL); + } + (void) dtrace_uaddr2str(dtp, pid, addr, s, rc); + + jfunc = (*jenv)->NewStringUTF(jenv, s); + free(s); + return (jfunc); +} + +JNIEXPORT jobject JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1getAggregate(JNIEnv *env, + jobject obj, jobject spec) +{ + dtj_java_consumer_t jc; + + jobject aggregate = NULL; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return (NULL); /* java exception pending */ + } + + if (dtj_java_consumer_init(env, &jc) != DTJ_OK) { + return (NULL); /* java exception pending */ + } + jc.dtjj_aggregate_spec = spec; + + /* + * Must set the thread-specific java consumer before calling any + * function that triggers callbacks to the bufhandler set by + * dtrace_handle_buffered(). The bufhandler needs access to the correct + * JNI state specific to either the consumer loop or the + * getAggregate() call. + */ + (void) pthread_setspecific(g_dtj_consumer_key, &jc); + aggregate = dtj_get_aggregate(&jc); + if (!aggregate) { + /* java exception pending */ + jc.dtjj_consumer->dtjc_interrupt = B_TRUE; + } + /* + * Cleans up only references created by this JNI invocation. Leaves + * cached per-consumer state untouched. + */ + dtj_java_consumer_fini(env, &jc); + return (aggregate); +} + +/* + * Returns the pid of the created process, or -1 in case of an error (java + * exception pending). + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT int JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1createProcess(JNIEnv *jenv, + jobject caller, jstring command) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + struct ps_prochandle *P; + char **argv; + int argc; + + if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { + return (-1); /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + if (jc.dtjj_consumer->dtjc_process_list == NULL) { + jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create(); + if (!jc.dtjj_consumer->dtjc_process_list) { + dtj_throw_out_of_memory(jenv, + "Could not allocate process list"); + return (-1); + } + } + + argv = dtj_make_argv(jenv, command, &argc); + if ((*jenv)->ExceptionCheck(jenv)) { + return (-1); + } + + P = dtrace_proc_create(dtp, argv[0], argv); + dtj_free_argv(argv); + + if (!P) { + dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp, + dtrace_errno(dtp))); + return (-1); + } + + if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) { + dtj_throw_out_of_memory(jenv, + "Failed to add process to process list"); + dtrace_proc_release(dtp, P); + return (-1); + } + return (Pstatus(P)->pr_pid); +} + +/* + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1grabProcess(JNIEnv *jenv, + jobject caller, jint pid) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + struct ps_prochandle *P; + + if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + if (jc.dtjj_consumer->dtjc_process_list == NULL) { + jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create(); + if (jc.dtjj_consumer->dtjc_process_list == NULL) { + dtj_throw_out_of_memory(jenv, + "Could not allocate process list"); + return; + } + } + + P = dtrace_proc_grab(dtp, (pid_t)pid, 0); + if (!P) { + dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp, + dtrace_errno(dtp))); + return; + } + + if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) { + dtj_throw_out_of_memory(jenv, + "Failed to add process to process list"); + dtrace_proc_release(dtp, P); + return; + } +} + +/* + * Lists all probes, or lists matching probes (using the matching rules from + * Table 4-1 of the DTrace manual). + * + * In the future it may be desirable to support an array of probe filters rather + * than a single filter. It could be that if a probe matched any of the given + * filters, it would be included (implied logical OR). + * + * Protected by global lock (LocalConsumer.class) + * param list: an empty list to populate (this function empties the list if it + * is not empty already) + * param filter: a ProbeDescription instance; the list will include only probes + * that match the filter (match all probes if filter is null) + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbes(JNIEnv *env, + jobject obj, jobject list, jobject filter) +{ + dtj_list_probes(env, obj, list, filter, dtj_list_probe); +} + +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbeDetail(JNIEnv *env, + jobject obj, jobject list, jobject filter) +{ + dtj_list_probes(env, obj, list, filter, dtj_list_probe_detail); +} + +static void +dtj_list_probes(JNIEnv *env, jobject obj, jobject list, jobject filter, + dtrace_probe_f *func) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + dtrace_probedesc_t probe; + dtrace_probedesc_t *pdp = NULL; + const char *probestr; + int rc; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + + jc.dtjj_probelist = list; + + /* clear in-out list parameter */ + (*env)->CallVoidMethod(env, list, g_listclear_jm); + if ((*env)->ExceptionCheck(env)) { + return; + } + + if (filter) { + jstring jprobestr = NULL; + + jprobestr = (*env)->CallObjectMethod(env, filter, + g_tostring_jm); + if ((*env)->ExceptionCheck(env)) { + return; + } + probestr = (*env)->GetStringUTFChars(env, jprobestr, NULL); + if (!probestr) { + (*env)->DeleteLocalRef(env, jprobestr); + return; /* java exception pending */ + } + + bzero(&probe, sizeof (probe)); + rc = dtrace_str2desc(dtp, DTRACE_PROBESPEC_NAME, probestr, + &probe); + (*env)->ReleaseStringUTFChars(env, jprobestr, probestr); + (*env)->DeleteLocalRef(env, jprobestr); + if (rc == -1) { + dtj_throw_dtrace_exception(&jc, + "%s is not a valid probe description: %s", + probestr, dtrace_errmsg(dtp, + dtrace_errno(dtp))); + return; + } + + pdp = &probe; + } + + (void) dtrace_probe_iter(dtp, pdp, func, &jc); +} + +/* + * Returns 0 to indicate success, or -1 to cause dtrace_probe_iter() to return a + * negative value prematurely (indicating no match or failure). + */ +static int +/* ARGSUSED */ +dtj_list_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg) +{ + dtj_java_consumer_t *jc = arg; + JNIEnv *jenv = jc->dtjj_jenv; + + jobject jprobedesc = NULL; + + jprobedesc = dtj_new_probedesc(jc, pdp); + if (!jprobedesc) { + return (-1); /* java exception pending */ + } + + /* add probe to list */ + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm, + jprobedesc); + (*jenv)->DeleteLocalRef(jenv, jprobedesc); + if ((*jenv)->ExceptionCheck(jenv)) { + return (-1); + } + + return (0); +} + +/*ARGSUSED*/ +static int +dtj_list_probe_detail(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, + void *arg) +{ + dtj_java_consumer_t *jc = arg; + JNIEnv *jenv = jc->dtjj_jenv; + dtrace_probeinfo_t p; + + jobject jprobedesc = NULL; + jobject jprobeinfo = NULL; + jobject jprobe = NULL; + + jprobedesc = dtj_new_probedesc(jc, pdp); + if (!jprobedesc) { + return (-1); /* java exception pending */ + } + + /* + * If dtrace_probe_info() returns a non-zero value, dtrace_errno is set + * for us. In that case, ignore the dtrace error and simply omit probe + * info. That error is implicitly cleared the next time a call is made + * using the same dtrace handle. + */ + if (dtrace_probe_info(dtp, pdp, &p) == 0) { + /* create probe info instance */ + jprobeinfo = dtj_new_probeinfo(jc, &p); + if (!jprobeinfo) { + (*jenv)->DeleteLocalRef(jenv, jprobedesc); + return (-1); /* java exception pending */ + } + } + + /* create listed probe instance */ + jprobe = (*jenv)->NewObject(jenv, g_probe_jc, g_probeinit_jm, + jprobedesc, jprobeinfo); + (*jenv)->DeleteLocalRef(jenv, jprobedesc); + (*jenv)->DeleteLocalRef(jenv, jprobeinfo); + if (!jprobe) { + return (-1); /* java exception pending */ + } + + /* add probe to list */ + (*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm, + jprobe); + (*jenv)->DeleteLocalRef(jenv, jprobe); + if ((*jenv)->ExceptionCheck(jenv)) { + return (-1); + } + + return (0); +} + +/*ARGSUSED*/ +static int +dtj_list_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, + dtrace_stmtdesc_t *stp, void *arg) +{ + dtj_java_consumer_t *jc = arg; + dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc; + + if (edp == jc->dtjj_consumer->dtjc_last_probe) { + return (0); + } + + if (dtrace_probe_iter(dtp, &edp->dted_probe, + jc->dtjj_consumer->dtjc_plistfunc, arg) != 0) { + dtj_throw_dtrace_exception(jc, + "failed to match %s:%s:%s:%s: %s", + edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod, + edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + return (1); + } + + jc->dtjj_consumer->dtjc_last_probe = edp; + return (0); +} + +/* + * Protected by global lock in Consumer.java + */ +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbes(JNIEnv *env, + jobject obj, jobject list, jobject program) +{ + dtj_list_compiled_probes(env, obj, list, program, dtj_list_probe); +} + +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbeDetail( + JNIEnv *env, jobject obj, jobject list, jobject program) +{ + dtj_list_compiled_probes(env, obj, list, program, + dtj_list_probe_detail); +} + +static void +dtj_list_compiled_probes(JNIEnv *env, jobject obj, jobject list, + jobject program, dtrace_probe_f *func) +{ + dtj_java_consumer_t jc; + dtrace_hdl_t *dtp; + uu_list_walk_t *itr; + dtj_program_t *p; + boolean_t found; + int progid = -1; + int i; + + if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) { + return; /* java exception pending */ + } + dtp = jc.dtjj_consumer->dtjc_dtp; + jc.dtjj_probelist = list; + + (*env)->CallVoidMethod(env, list, g_listclear_jm); + if ((*env)->ExceptionCheck(env)) { + return; + } + + if (program) { + if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) { + dtj_throw_no_such_element(env, "no compiled program"); + return; + } + progid = (*env)->GetIntField(env, program, g_progid_jf); + if (progid == -1) { + dtj_throw_illegal_argument(env, "invalid program"); + return; + } + } + + jc.dtjj_consumer->dtjc_plistfunc = func; + found = B_FALSE; + itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0); + for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) { + if ((progid != -1) && (progid != i)) { + continue; + } + + found = B_TRUE; + (void) dtrace_stmt_iter(dtp, p->dtjp_program, + (dtrace_stmt_f *)dtj_list_stmt, &jc); + } + uu_list_walk_end(itr); + + if (program && !found) { + dtj_throw_no_such_element(env, "program not found"); + } +} + +/* + * Static LocalConsumer.java method + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT jstring JNICALL +/* ARGSUSED */ +Java_org_opensolaris_os_dtrace_LocalConsumer__1getVersion(JNIEnv *env, + jclass class) +{ + /* + * Handles the case of locale-specific encoding of the user-visible + * version string containing non-ASCII characters. + */ + return (dtj_NewStringNative(env, _dtrace_version)); +} + +/* + * Static LocalConsumer.java method + * Protected by global lock (LocalConsumer.class) + */ +JNIEXPORT jstring JNICALL +/* ARGSUSED */ +Java_org_opensolaris_os_dtrace_LocalConsumer__1getExecutableName(JNIEnv *env, + jclass class) +{ + jstring jname = NULL; + const char *name = NULL; + char *s; + int len; + + name = dtj_getexecname(); + len = strlen(name); + s = malloc(len + 1); + if (!s) { + dtj_throw_out_of_memory(env, "Failed to allocate execname"); + return (NULL); + } + (void) strcpy(s, name); + name = basename(s); + free(s); + jname = (*env)->NewStringUTF(env, name); + return (jname); +} + +/* + * Static LocalConsumer.java method + */ +JNIEXPORT void JNICALL +/* ARGSUSED */ +Java_org_opensolaris_os_dtrace_LocalConsumer__1setMaximumConsumers(JNIEnv *env, + jclass class, jint max) +{ + g_max_consumers = max; +} + +/* + * Static LocalConsumer.java method + */ +JNIEXPORT void JNICALL +/* ARGSUSED */ +Java_org_opensolaris_os_dtrace_LocalConsumer__1setDebug(JNIEnv *env, + jclass class, jboolean debug) +{ + g_dtj_util_debug = debug; +} + +JNIEXPORT void JNICALL +Java_org_opensolaris_os_dtrace_LocalConsumer__1destroy(JNIEnv *env, jobject obj) +{ + dtj_consumer_t *c; + + c = dtj_remove_consumer(env, obj); + if (c == NULL) { + return; /* java exception pending */ + } + dtj_consumer_destroy(c); +} diff --git a/usr/src/lib/libdtrace_jni/common/dtrace_jni.h b/usr/src/lib/libdtrace_jni/common/dtrace_jni.h new file mode 100644 index 0000000000..7df12ce930 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/common/dtrace_jni.h @@ -0,0 +1,491 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DTRACE_JNI_H +#define _DTRACE_JNI_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libuutil.h> +#include <jni.h> +#include <dtrace.h> +#include <dtj_util.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Java DTrace API native library */ + + +/* + * Thread-specific data key used to obtain JNI state specific to either the + * consumer loop (calls dtrace_work()) or the getAggregate() method (calls + * dtrace_aggregate_print()). + */ +extern pthread_key_t g_dtj_consumer_key; + +typedef enum dtj_consumer_state { + DTJ_CONSUMER_INIT, + DTJ_CONSUMER_GO, + DTJ_CONSUMER_START, + DTJ_CONSUMER_STOP +} dtj_consumer_state_t; + +typedef struct dtj_error { + int dtje_number; /* dtrace_errno() */ + const char *dtje_message; /* dtrace_errmsg() */ +} dtj_error_t; + +/* + * Identifies which function should handle a request dequeued after + * dtrace_sleep(). + */ +typedef enum dtj_request_type { + DTJ_REQUEST_OPTION /* set DTrace runtime option */ +} dtj_request_type_t; + +/* + * A request made from Java (by native method call) that is unsafe to process + * until just after the consumer loop wakes up from dtrace_sleep(). + */ +typedef struct dtj_request { + dtj_request_type_t dtjr_type; /* request handler ID */ + uu_list_t *dtjr_args; /* string args to request handler */ + uu_list_node_t dtjr_node; /* points to next and prev requests */ +} dtj_request_t; + +typedef enum dtj_program_type { + DTJ_PROGRAM_NONE, + DTJ_PROGRAM_STRING, /* dtrace_program_strcompile() */ + DTJ_PROGRAM_FILE /* dtrace_program_fcompile() */ +} dtj_program_type_t; + +/* Identifier and description of a compiled DTrace program */ +typedef struct dtj_program { + dtj_program_type_t dtjp_type; /* string or file */ + const char *dtjp_name; /* string or filename for err msg */ + dtrace_prog_t *dtjp_program; /* libdtrace program handle */ + dtrace_proginfo_t dtjp_info; /* program attributes */ + boolean_t dtjp_enabled; /* dtrace_program_exec() flag */ + uu_list_node_t dtjp_node; /* points to next and prev programs */ +} dtj_program_t; + +/* + * An entry used to maintain the association between the value of an aggregating + * action (such as count()) and the aggregation to which the value belongs until + * all the data associated with a single tuple is available to the callback + * handler. + */ +typedef struct dtj_aggval { + jobject dtja_value; /* value of aggregating action */ + const char *dtja_aggname; /* aggregation name */ + int64_t dtja_aggid; /* libdtrace aggregation ID */ + uu_list_node_t dtja_node; /* points to next and prev aggvals */ +} dtj_aggval_t; + +/* + * Per-consumer state, including the libdtrace consumer handle, is valid across + * multiple threads. One consumer entry is added to a global table per + * dtrace_open(). + */ +typedef struct dtj_consumer { + /* Consumer state */ + + dtrace_hdl_t *dtjc_dtp; /* libdtrace consumer handle */ + uu_list_t *dtjc_program_list; /* program_t list */ + uu_list_t *dtjc_process_list; /* proc handle list */ + + /* + * Count of processes that have ended. The consumer is stopped when + * this count equals the number of outstanding target processes and + * grabbed processes (see the Java Consumer createProcess() and + * grabProcess() methods). + */ + int dtjc_procs_ended; + + /* + * Bit-field passed to dtrace_program_strcompile() and + * dtrace_program_fcompile() containing compile flags. The flags are + * set from Java by the setOption() Consumer method (just like the + * runtime options handled by dtrace_setopt(), except that they must be + * set before program compilation to have any effect). + */ + uint_t dtjc_cflags; + + boolean_t dtjc_flow; /* current value of the flowindent option */ + dtj_consumer_state_t dtjc_state; /* execution state */ + boolean_t dtjc_interrupt; /* flag that stops consumer */ + + /* Pending requests */ + uu_list_t *dtjc_request_list; /* request_t queue */ + pthread_mutex_t dtjc_request_list_lock; + + + /* Cached for optimization and for use across functions */ + + /* + * Nanosecond timestamp cached in the consumer loop just before + * dtrace_work(). The timestamp is applied to each Java PrintaRecord + * generated in that iteration of the consumer loop. A value of zero + * indicates that we are not in the consumer loop, but that the + * callback was triggered instead by the Consumer getAggregate() method + * (from dtrace_aggregate_print()). + */ + hrtime_t dtjc_printa_snaptime; + + /* + * The aggregation ID is used to optimize aggregation inclusion by + * testing for inclusion only when the aggregation has changed. + */ + int64_t dtjc_aggid; + boolean_t dtjc_included; + + /* + * The expected tuple member count is used to determine whether or not + * the aggregation tuple values are completely specified in the printa() + * format string. + */ + int dtjc_expected; + + int dtjc_probedata_rec_i; /* probe data record index */ + + /* + * The current DTrace action may apply across multiple libdtrace probe + * data records. + */ + dtrace_actkind_t dtjc_probedata_act; + + /* Placeholder used when listing probes */ + dtrace_ecbdesc_t *dtjc_last_probe; + + /* Function used by statement iterator when listing probes */ + dtrace_probe_f *dtjc_plistfunc; +} dtj_consumer_t; + +/* + * A view of a dtj_consumer_t that lasts only as long as a single native method + * call. This view attaches state needed for interaction with Java and specific + * to the JNI. + */ +typedef struct dtj_java_consumer { + /* Per-consumer state in global consumer table */ + dtj_consumer_t *dtjj_consumer; + + JNIEnv *dtjj_jenv; /* Java environment pointer */ + jobject dtjj_caller; /* Java Consumer to call back with probe data */ + + /* + * Java Object references used across function boundaries, valid only + * within the current native method call. + */ + + jobject dtjj_probedata; /* instance of class ProbeData */ + + /* + * StringBuffer used to concatenate buffered printa() output associated + * with the current tuple. + */ + jobject dtjj_printa_buffer; + + jobject dtjj_aggregate; /* instance of class Aggregate */ + jobject dtjj_tuple; /* instance of class Tuple */ + + /* + * AggregationValue instances cached until we receive the + * DTRACE_BUFDATA_AGGLAST flag indicating the last callback associated + * with the current tuple. + */ + uu_list_t *dtjj_aggval_list; + + /* AggregateSpec used by get_aggregate() */ + jobject dtjj_aggregate_spec; + + jobject dtjj_probelist; /* java.util.List returned by listProbes() */ + + /* + * Exception temporarily cleared by callback handlers who cannot return + * a signal to abort the consumer. At a safe point when the consumer + * loop gets control back from libdtrace, the exception is rethrown. + */ + jthrowable dtjj_exception; + + jobject dtjj_consumer_lock; /* per-consumer lock */ + +} dtj_java_consumer_t; + +/* + * Cache of jclass, jmethodID, and jfieldID values, usable across multiple + * native method calls and multiple threads. Caching all of them up front + * rather than as-needed guarantees early detection of incorrect class, method, + * or field definitions, and eliminates the need for test cases to cover + * seldom-used definitions. + * + * Suffix conventions: + * jc java class + * jm java method + * jsm java static method + * jf java field + * jsf java static field + */ + +/* LocalConsumer */ +extern jclass g_caller_jc; +extern jmethodID g_gethandle_jm; +extern jmethodID g_sethandle_jm; +extern jmethodID g_pdatanext_jm; +extern jmethodID g_drop_jm; +extern jmethodID g_error_jm; +extern jmethodID g_proc_jm; +extern jmethodID g_interval_began_jm; +extern jmethodID g_interval_ended_jm; +extern jfieldID g_consumer_lock_jf; + +/* DTraceException */ +extern jclass g_dtx_jc; +extern jmethodID g_dtxinit_jm; + +/* InterfaceAttributes */ +extern jclass g_attr_jc; +extern jmethodID g_attrinit_jm; +extern jmethodID g_attrset_name_jm; +extern jmethodID g_attrset_data_jm; +extern jmethodID g_attrset_class_jm; + +/* ProbeDescription */ +extern jclass g_probedesc_jc; +extern jmethodID g_probedescinit_jm; +extern jfieldID g_probedesc_id_jf; + +/* ProbeInfo */ +extern jclass g_probeinfo_jc; +extern jmethodID g_probeinfoinit_jm; + +/* Probe */ +extern jclass g_probe_jc; +extern jmethodID g_probeinit_jm; + +/* Program */ +extern jclass g_program_jc; +extern jmethodID g_proginit_jm; +extern jfieldID g_progid_jf; +extern jfieldID g_proginfo_jf; + +/* Program.File */ +extern jclass g_programfile_jc; +extern jmethodID g_fproginit_jm; + +/* ProgramInfo */ +extern jclass g_proginfo_jc; +extern jmethodID g_proginfoinit_jm; + +/* Flow */ +extern jclass g_flow_jc; +extern jmethodID g_flowinit_jm; + +/* ProbeData */ +extern jclass g_pdata_jc; +extern jmethodID g_pdatainit_jm; +extern jmethodID g_pdataadd_jm; +extern jmethodID g_pdataadd_rec_jm; +extern jmethodID g_pdataadd_trace_jm; +extern jmethodID g_pdataadd_stack_jm; +extern jmethodID g_pdataadd_printf_jm; +extern jmethodID g_pdataadd_printa_jm; +extern jmethodID g_pdatainvalidate_printa_jm; +extern jmethodID g_pdataadd_aggrec_jm; +extern jmethodID g_pdataadd_printa_str_jm; +extern jmethodID g_pdataadd_exit_jm; +extern jmethodID g_pdataattach_jm; +extern jmethodID g_pdataset_formatted_jm; +extern jmethodID g_pdataclear_jm; + +/* Drop */ +extern jclass g_drop_jc; +extern jmethodID g_dropinit_jm; + +/* Error */ +extern jclass g_error_jc; +extern jmethodID g_errinit_jm; + +/* ProcessState */ +extern jclass g_process_jc; +extern jmethodID g_procinit_jm; +extern jmethodID g_procexit_jm; + +/* Aggregate */ +extern jclass g_agg_jc; +extern jmethodID g_agginit_jm; +extern jmethodID g_aggaddrec_jm; + +/* AggregateSpec */ +extern jclass g_aggspec_jc; +extern jmethodID g_aggspec_included_jm; +extern jmethodID g_aggspec_cleared_jm; + +/* Tuple */ +extern jclass g_tuple_jc; +extern jmethodID g_tupleinit_jm; +extern jmethodID g_tupleadd_jm; +extern jmethodID g_tuplesize_jm; +extern jfieldID g_tuple_EMPTY_jsf; + +/* AggregationRecord */ +extern jclass g_aggrec_jc; +extern jmethodID g_aggrecinit_jm; +extern jmethodID g_aggrecget_tuple_jm; + +/* SumValue */ +extern jclass g_aggsum_jc; +extern jmethodID g_aggsuminit_jm; + +/* CountValue */ +extern jclass g_aggcount_jc; +extern jmethodID g_aggcountinit_jm; + +/* AvgValue */ +extern jclass g_aggavg_jc; +extern jmethodID g_aggavginit_jm; + +/* MinValue */ +extern jclass g_aggmin_jc; +extern jmethodID g_aggmininit_jm; + +/* MaxValue */ +extern jclass g_aggmax_jc; +extern jmethodID g_aggmaxinit_jm; + +/* KernelStackRecord */ +extern jclass g_stack_jc; +extern jmethodID g_parsestack_jsm; +extern jmethodID g_stackinit_jm; +extern jmethodID g_stackset_frames_jm; + +/* UserStackRecord */ +extern jclass g_ustack_jc; +extern jmethodID g_ustackinit_jm; +extern jmethodID g_ustackset_frames_jm; + +/* Distribution */ +extern jclass g_adist_jc; +extern jmethodID g_dist_normal_jm; + +/* LogDistribution */ +extern jclass g_dist_jc; +extern jmethodID g_distinit_jm; + +/* LinearDistribution */ +extern jclass g_ldist_jc; +extern jmethodID g_ldistinit_jm; + +/* + * Populates the java class references and associated method and field IDs + * declared in this file (above). + * + * Throws NoClassDefFoundError, NoSuchMethodError, or NoSuchFieldError if any + * dtj_table_entry_t in dtj_jnitab.c is incorrect. + */ +extern dtj_status_t dtj_load(JNIEnv *); + +/* + * Functions that create a structure return NULL if out of memory. A Java + * OutOfMemoryError is pending in that case. + */ +extern dtj_request_t *dtj_request_create(JNIEnv *, dtj_request_type_t, ...); +extern dtj_program_t *dtj_program_create(JNIEnv *, dtj_program_type_t, + const char *); +extern dtj_aggval_t *dtj_aggval_create(JNIEnv *, jobject, const char *, + int64_t); + +/* + * uu_list_t element destructors' signatures match uuwrap_value_destroy_f + */ +extern void dtj_request_destroy(void *, void *); /* expects NULL user arg */ +extern void dtj_program_destroy(void *, void *); /* expects NULL user arg */ +extern void dtj_aggval_destroy(void *, void *); /* expects JNIEnv * user arg */ + +/* Allocates and frees per-consumer state kept in the global consumer table */ +extern dtj_consumer_t *dtj_consumer_create(JNIEnv *); +extern void dtj_consumer_destroy(dtj_consumer_t *); + +/* Sets callback handlers before calling dtrace_go() */ +extern dtj_status_t dtj_set_callback_handlers(dtj_java_consumer_t *); + +/* + * Initializes Java Object references cached across multiple functions called + * within the consumer loop. Deletes the references after exiting the consumer + * loop. It is only necessary to initialize and finalize a dtj_java_consumer_t + * if the native method call will enter the consumer loop. + */ +extern dtj_status_t dtj_java_consumer_init(JNIEnv *, dtj_java_consumer_t *); +extern void dtj_java_consumer_fini(JNIEnv *, dtj_java_consumer_t *); + +/* + * Throws a DTraceException with a message constructed from the given format + * string and variable arg list. + */ +extern void dtj_throw_dtrace_exception(dtj_java_consumer_t *, + const char *, ...); + +/* Returns NULL if pending Java Exception or OutOfMemoryError */ +extern jobject dtj_new_probedesc(dtj_java_consumer_t *, + const dtrace_probedesc_t *); +extern jobject dtj_new_probeinfo(dtj_java_consumer_t *, + const dtrace_probeinfo_t *); +extern jobject dtj_new_attribute(dtj_java_consumer_t *, + const dtrace_attribute_t *); + +/* + * Returns NULL if the given fault is unrecognized, otherwise returns the name + * of the fault, guaranteed not to change across multiple versions of this API + * even if the integer value changes in libdtrace. + */ +extern const char *dtj_get_fault_name(int); + +/* Gets the libdtrace error number and message */ +extern dtj_status_t dtj_get_dtrace_error(dtj_java_consumer_t *, dtj_error_t *); + +/* Stops the DTrace consumer */ +extern void dtj_stop(dtj_java_consumer_t *); + +/* + * The Consumer getAggregate() method runs in the caller's current thread + * separate from the consumer loop. + */ +extern jobject dtj_get_aggregate(dtj_java_consumer_t *); + +/* + * A blocking call that runs the consumer loop. If this function returns an + * error status, it is necessary to call stop() in order to dtrace_stop() the + * consumer in libdtrace (it is safe to call stop() in either case). + */ +extern dtj_status_t dtj_consume(dtj_java_consumer_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DTRACE_JNI_H */ diff --git a/usr/src/lib/libdtrace_jni/i386/Makefile b/usr/src/lib/libdtrace_jni/i386/Makefile new file mode 100644 index 0000000000..5f95ecb5e8 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/i386/Makefile @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +MAPDIR = ../spec/i386 + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libdtrace_jni/java/Makefile b/usr/src/lib/libdtrace_jni/java/Makefile new file mode 100644 index 0000000000..b47b8ea6f9 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/Makefile @@ -0,0 +1,242 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include $(SRC)/Makefile.master + +# The Java DTrace API package + +PKGPATH=org/opensolaris/os/dtrace +PKGNAME=org.opensolaris.os.dtrace + +# The Java DTrace API directories + +APIDIR=$(SRC)/lib/libdtrace_jni/java +CLASSES=$(APIDIR)/classes +JNI=$(APIDIR)/native +JARS=$(APIDIR)/lib +DOCS=$(APIDIR)/docs +DOCAPI=$(DOCS)/api +DOCEXAMPLES=$(DOCS)/examples +DOCHTML=$(DOCS)/html +DOCIMAGES=$(DOCS)/images +MANIFEST=$(APIDIR)/manifest +APIJAVASRC=$(APIDIR)/src + +# Java DTrace API jar file, its ultimate destination, and packaging details +APIJAR=dtrace.jar +JARDESTDIR=$(ROOT)/usr/share/lib/java +INSJARS=$(JARDESTDIR)/$(APIJAR) +# javadoc +DOCDESTDIR=$(ROOT)/usr/share/lib/java/javadoc/dtrace +DOCAPIDESTDIR=$(DOCDESTDIR)/api +DOCEXAMPLESDESTDIR=$(DOCDESTDIR)/examples +DOCHTMLDESTDIR=$(DOCDESTDIR)/html +DOCIMAGESDESTDIR=$(DOCDESTDIR)/images + +$(JARDESTDIR)/%: $(JARS)/% + $(INS.file) + +$(DOCEXAMPLESDESTDIR)/%: $(DOCEXAMPLES)/% + $(INS.file) + +$(DOCHTMLDESTDIR)/%: $(DOCHTML)/% + $(INS.file) + +$(DOCIMAGESDESTDIR)/%: $(DOCIMAGES)/% + $(INS.file) + +# Manifest files +APIMANI= $(MANIFEST)/dtrace.mf +MANIFESTS= $(APIMANI) + +# Controlled CLASSPATH for making +APICLASSPATH=$(CLASSES):$(APIJAVASRC) + +# javac flags +JFLAGS= -g -d $(CLASSES) -sourcepath $(APICLASSPATH) -deprecation + +# The default make rule for Java files +COMPILE.java=$(JAVAC) $(JFLAGS) + +$(CLASSES)/$(PKGPATH)/%.class: $(APIJAVASRC)/$(PKGPATH)/%.java + $(COMPILE.java) $< + +# javah generated headers +APIHDR= LocalConsumer.h +JNIHDRS=$(JNI)/$(APIHDRS) + +# API classes with native methods +JNI_CLASSNAMES=\ + LocalConsumer + +JNI_CLASSES=${JNI_CLASSNAMES:%=%.class} +DTRACE_JNI_CLASSES=${JNI_CLASSES:%=$(CLASSES)/$(PKGPATH)/%} +JNI_FULL_CLASSNAMES=${JNI_CLASSNAMES:%=$(PKGNAME).%} + +# All API classes +API_CLASSNAMES=\ + AbstractAggregationValue \ + Aggregate \ + AggregateSpec \ + Aggregation \ + AggregationRecord \ + AggregationValue \ + AvgValue \ + Consumer \ + ConsumerAdapter \ + ConsumerEvent \ + ConsumerException \ + ConsumerListener \ + CountValue \ + DTraceException \ + DataEvent \ + Distribution \ + Drop \ + DropEvent \ + Error \ + ErrorEvent \ + ExceptionHandler \ + ExitRecord \ + Flow \ + InterfaceAttributes \ + KernelStackRecord \ + LinearDistribution \ + LocalConsumer \ + LogDistribution \ + MaxValue \ + MinValue \ + NativeException \ + Option \ + PrintaRecord \ + PrintfRecord \ + Probe \ + ProbeData \ + ProbeDescription \ + ProbeInfo \ + ProcessEvent \ + ProcessState \ + Program \ + ProgramInfo \ + Record \ + ResourceLimitException \ + ScalarRecord \ + StackFrame \ + StackValueRecord \ + SumValue \ + Tuple \ + UserStackRecord \ + Utility \ + ValueRecord + +API_CLASSES=${API_CLASSNAMES:%=%.class} +DTRACE_API_CLASSES=${API_CLASSES:%=$(CLASSES)/$(PKGPATH)/%} + + +all: $(CLASSES) $(DTRACE_API_CLASSES) $(JNI)/$(APIHDR) + +clean: + -$(RM) $(CLASSES)/$(PKGPATH)/*.class + -$(RM) $(JNI)/*.h + -$(RM) -r $(DOCAPI) + +clobber: clean + -$(RM) $(JARS)/*.jar + +# Make the class dir, if it doesn't exist +$(CLASSES): + -@mkdir -p $@ + +# Make the directory for javah-generated headers, if it doesn't exist +$(JNI): + -@mkdir -p $@ + +$(JNI)/$(APIHDR): $(JNI) $(DTRACE_JNI_CLASSES) + $(JAVAH) -o $@ -classpath $(CLASSES) $(JNI_FULL_CLASSNAMES) + -@touch $@ + +# Rule for installing API javadoc. +$(DOCAPIDESTDIR)/index.html: $(DTRACE_API_CLASSES) + -@mkdir -p $(DOCAPIDESTDIR) + -$(RM) -r $(DOCAPIDESTDIR)/* + cd $(APIJAVASRC); $(JAVADOC) -protected -use \ + -classpath $(APICLASSPATH) -d $(DOCAPIDESTDIR) \ + $(PKGNAME) + +$(CLASSES)/$(PKGPATH): + $(INS.dir) + +$(JARS)/$(APIJAR): $(DTRACE_API_CLASSES) $(APIMANI) + -@mkdir -p $(JARS) + $(JAR) cfm $@ $(APIMANI) -C $(CLASSES) . + +$(JARDESTDIR): + $(INS.dir) + +$(DOCDESTDIR): + $(INS.dir) + +$(DOCAPIDESTDIR): + $(INS.dir) + +$(DOCEXAMPLESDESTDIR): + $(INS.dir) + +$(DOCHTMLDESTDIR): + $(INS.dir) + +$(DOCIMAGESDESTDIR): + $(INS.dir) + +install: all $(JARDESTDIR) $(INSJARS) $(DOCDESTDIR) \ + $(DOCAPIDESTDIR) \ + $(DOCAPIDESTDIR)/index.html \ + $(DOCEXAMPLESDESTDIR) \ + $(DOCEXAMPLESDESTDIR)/TestAPI.java \ + $(DOCEXAMPLESDESTDIR)/TestAPI2.java \ + $(DOCEXAMPLESDESTDIR)/TestTarget.java \ + $(DOCEXAMPLESDESTDIR)/hello.d \ + $(DOCEXAMPLESDESTDIR)/intrstat.d \ + $(DOCEXAMPLESDESTDIR)/syscall.d \ + $(DOCEXAMPLESDESTDIR)/target.d \ + $(DOCHTMLDESTDIR) \ + $(DOCHTMLDESTDIR)/JavaDTraceAPI.html \ + $(DOCHTMLDESTDIR)/fast.html \ + $(DOCIMAGESDESTDIR) \ + $(DOCIMAGESDESTDIR)/JavaDTraceAPI.gif + +# empty targets for top-level building compatability + +install_h lint: + +# create API javadoc + +doc: + -@mkdir -p $(DOCAPI) + cd $(APIJAVASRC); $(JAVADOC) -protected -use \ + -classpath $(APICLASSPATH) -d $(DOCAPI) \ + $(PKGNAME) diff --git a/usr/src/lib/libdtrace_jni/java/docs/examples/TestAPI.java b/usr/src/lib/libdtrace_jni/java/docs/examples/TestAPI.java new file mode 100644 index 0000000000..bd7fad2aa8 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/examples/TestAPI.java @@ -0,0 +1,62 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ + +import org.opensolaris.os.dtrace.*; +import java.io.File; + +public class TestAPI { + public static void + main(String[] args) + { + if (args.length < 1) { + System.err.println("Usage: java TestAPI <script> [ macroargs... ]"); + System.exit(2); + } + + File file = new File(args[0]); + String[] macroArgs = new String[args.length - 1]; + System.arraycopy(args, 1, macroArgs, 0, (args.length - 1)); + + Consumer consumer = new LocalConsumer(); + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + System.out.println(e.getProbeData()); + } + }); + + try { + consumer.open(); + consumer.compile(file, macroArgs); + consumer.enable(); + consumer.go(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/docs/examples/TestAPI2.java b/usr/src/lib/libdtrace_jni/java/docs/examples/TestAPI2.java new file mode 100644 index 0000000000..2a064a401e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/examples/TestAPI2.java @@ -0,0 +1,80 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ + +import org.opensolaris.os.dtrace.*; +import java.io.File; + +public class TestAPI2 { + public static void + main(String[] args) + { + if (args.length < 1) { + System.err.println("Usage: java TestAPI2 <script> " + + "[ macroargs... ]"); + System.exit(2); + } + + File file = new File(args[0]); + String[] macroArgs = new String[args.length - 1]; + System.arraycopy(args, 1, macroArgs, 0, (args.length - 1)); + + Consumer consumer = new LocalConsumer(); + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + // System.out.println(e.getProbeData()); + ProbeData data = e.getProbeData(); + java.util.List < Record > records = data.getRecords(); + for (Record r : records) { + if (r instanceof ExitRecord) { + } else { + System.out.println(r); + } + } + } + }); + + try { + consumer.open(); + consumer.compile(file, macroArgs); + consumer.enable(); + consumer.go(); + + Aggregate a; + do { + Thread.sleep(1000); + a = consumer.getAggregate(); + if (!a.asMap().isEmpty()) { + System.out.println(a); + } + } while (consumer.isRunning()); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/docs/examples/TestTarget.java b/usr/src/lib/libdtrace_jni/java/docs/examples/TestTarget.java new file mode 100644 index 0000000000..647d07dacd --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/examples/TestTarget.java @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ + +import org.opensolaris.os.dtrace.*; +import java.io.File; + +public class TestTarget { + public static void + main(String[] args) + { + if (args.length != 2) { + System.err.println("Usage: java TestTarget <script> <command>"); + System.exit(2); + } + + File file = new File(args[0]); + String command = args[1]; + + final Consumer consumer = new LocalConsumer(); + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + System.out.println(e.getProbeData()); + } + public void consumerStopped(ConsumerEvent e) { + try { + Aggregate a = consumer.getAggregate(); + for (Aggregation agg : a.asMap().values()) { + for (AggregationRecord rec : agg.asMap().values()) { + System.out.println(rec.getTuple() + " " + + rec.getValue()); + } + } + } catch (Exception x) { + x.printStackTrace(); + System.exit(1); + } + consumer.close(); + } + public void processStateChanged(ProcessEvent e) { + System.out.println(e.getProcessState()); + } + }); + + try { + consumer.open(); + // pid replaces $target variable in D script + consumer.createProcess(command); + consumer.compile(file); + consumer.enable(); + consumer.go(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/docs/examples/hello.d b/usr/src/lib/libdtrace_jni/java/docs/examples/hello.d new file mode 100755 index 0000000000..67940f8e5b --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/examples/hello.d @@ -0,0 +1,33 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +dtrace:::BEGIN +{ + trace("hello, world"); + exit(0); +} diff --git a/usr/src/lib/libdtrace_jni/java/docs/examples/intrstat.d b/usr/src/lib/libdtrace_jni/java/docs/examples/intrstat.d new file mode 100644 index 0000000000..edb6e9d70d --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/examples/intrstat.d @@ -0,0 +1,43 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +sdt:::interrupt-start +{ + self->ts = vtimestamp; +} + +sdt:::interrupt-complete +/self->ts && arg0/ +{ + this->devi = (struct dev_info *)arg0; + @counts[stringof(`devnamesp[this->devi->devi_major].dn_name), + this->devi->devi_instance, cpu] = count(); + @times[stringof(`devnamesp[this->devi->devi_major].dn_name), + this->devi->devi_instance, cpu] = sum(vtimestamp - self->ts); + self->ts = 0; +} diff --git a/usr/src/lib/libdtrace_jni/java/docs/examples/syscall.d b/usr/src/lib/libdtrace_jni/java/docs/examples/syscall.d new file mode 100644 index 0000000000..b1b95519b1 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/examples/syscall.d @@ -0,0 +1,39 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +syscall:::entry +/ execname == $$1 / +{ + @[probefunc] = count(); +} + +profile:::tick-1sec +{ + printa(@); + clear(@); +} diff --git a/usr/src/lib/libdtrace_jni/java/docs/examples/target.d b/usr/src/lib/libdtrace_jni/java/docs/examples/target.d new file mode 100644 index 0000000000..afd4ce9684 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/examples/target.d @@ -0,0 +1,33 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +syscall:::entry +/pid == $target/ +{ + @[probefunc] = count(); +} diff --git a/usr/src/lib/libdtrace_jni/java/docs/html/JavaDTraceAPI.html b/usr/src/lib/libdtrace_jni/java/docs/html/JavaDTraceAPI.html new file mode 100644 index 0000000000..4787f49625 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/html/JavaDTraceAPI.html @@ -0,0 +1,207 @@ +<!-- + Copyright 2006 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + ident "%Z%%M% %I% %E% SMI" + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END +--> +<HTML> +<HEAD> + <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1"> + <TITLE></TITLE> +</HEAD> +<BODY LANG="en-US" DIR="LTR"> +<MAP NAME=api_map> + <!-- package level javadoc overview --> + <AREA HREF="../api/org/opensolaris/os/dtrace/package-summary.html" + ALT="org.opensolaris.os.dtrace package overview" + SHAPE=RECT + COORDS="556,0,725,28"> + <!-- double-nested areas overlay singly-nested areas and need to come first --> + <AREA HREF="../api/org/opensolaris/os/dtrace/ValueRecord.html" + ALT="ValueRecord javadoc API" + SHAPE=RECT + COORDS="457,667,563,697"> + <AREA HREF="../api/org/opensolaris/os/dtrace/AggregationRecord.html" + ALT="AggregationRecord javadoc API" + SHAPE=RECT + COORDS="207,648,359,678"> + <!-- nested areas overlay containing areas and need to come first --> + <AREA HREF="../api/org/opensolaris/os/dtrace/Record.html" + ALT="Record javadoc API" + SHAPE=RECT + COORDS="353,119,426,156"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ProbeData.html" + ALT="ProbeData javadoc API" + SHAPE=RECT + COORDS="193,96,291,127"> + <AREA HREF="../api/org/opensolaris/os/dtrace/Aggregation.html" + ALT="Aggregation javadoc API" + SHAPE=RECT + COORDS="193,596,388,704"> + <AREA HREF="../api/org/opensolaris/os/dtrace/Aggregation.html" + ALT="Aggregation javadoc API" + SHAPE=RECT + COORDS="755,280,866,321"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ValueRecord.html" + ALT="ValueRecord javadoc API" + SHAPE=RECT + COORDS="603,248,697,299"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ConsumerListener.html" + ALT="ConsumerListener javadoc API" + SHAPE=RECT + COORDS="10,52,97,97"> + <AREA HREF="../api/org/opensolaris/os/dtrace/Distribution.Bucket.html" + ALT="Distribution.Bucket javadoc API" + SHAPE=RECT + COORDS="822,537,977,615"> + <AREA HREF="../api/org/opensolaris/os/dtrace/Drop.html" + ALT="Drop javadoc API" + SHAPE=RECT + COORDS="193,191,291,223"> + <AREA HREF="../api/org/opensolaris/os/dtrace/Error.html" + ALT="Error javadoc API" + SHAPE=RECT + COORDS="193,382,291,414"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ProcessState.html" + ALT="ProcessState javadoc API" + SHAPE=RECT + COORDS="193,286,291,319"> + <AREA HREF="../api/org/opensolaris/os/dtrace/Tuple.html" + ALT="Tuple javadoc API" + SHAPE=RECT + COORDS="444,631,592,722"> + <AREA HREF="../api/org/opensolaris/os/dtrace/AggregationValue.html" + ALT="AggregationValue javadoc API" + SHAPE=RECT + COORDS="605,631,704,674"> + <!-- containing areas need to come after nested areas --> + <AREA HREF="../api/org/opensolaris/os/dtrace/Aggregate.html" + ALT="Aggregate javadoc API" + SHAPE=RECT + COORDS="179,543,413,732"> + <AREA HREF="../api/org/opensolaris/os/dtrace/AggregationRecord.html" + ALT="AggregationRecord javadoc API" + SHAPE=RECT + COORDS="429,593,715,731"> + <AREA HREF="../api/org/opensolaris/os/dtrace/AggregationValue.html" + ALT="AggregationValue javadoc API" + SHAPE=RECT + COORDS="672,388,818,443"> + <AREA HREF="../api/org/opensolaris/os/dtrace/AvgValue.html" + ALT="AvgValue javadoc API" + SHAPE=RECT + COORDS="442,485,507,564"> + <AREA HREF="../api/org/opensolaris/os/dtrace/Consumer.html" + ALT="Consumer javadoc API" + SHAPE=RECT + COORDS="0,0,151,645"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ConsumerEvent.html" + ALT="ConsumerEvent javadoc API" + SHAPE=RECT + COORDS="178,440,309,480"> + <AREA HREF="../api/org/opensolaris/os/dtrace/CountValue.html" + ALT="CountValue javadoc API" + SHAPE=RECT + COORDS="515,485,581,540"> + <AREA HREF="../api/org/opensolaris/os/dtrace/DataEvent.html" + ALT="DataEvent javadoc API" + SHAPE=RECT + COORDS="178,58,309,143"> + <AREA HREF="../api/org/opensolaris/os/dtrace/Distribution.html" + ALT="Distribution javadoc API" + SHAPE=RECT + COORDS="811,485,1005,639"> + <AREA HREF="../api/org/opensolaris/os/dtrace/DropEvent.html" + ALT="DropEvent javadoc API" + SHAPE=RECT + COORDS="179,153,309,239"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ErrorEvent.html" + ALT="ErrorEvent javadoc API" + SHAPE=RECT + COORDS="178,344,309,429"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ExitRecord.html" + ALT="ExitRecord javadoc API" + SHAPE=RECT + COORDS="909,196,1017,251"> + <AREA HREF="../api/org/opensolaris/os/dtrace/LinearDistribution.html" + ALT="LinearDistribution javadoc API" + SHAPE=RECT + COORDS="876,679,1024,766"> + <AREA HREF="../api/org/opensolaris/os/dtrace/LocalConsumer.html" + ALT="LocalConsumer javadoc API" + SHAPE=RECT + COORDS="0,668,152,737"> + <AREA HREF="../api/org/opensolaris/os/dtrace/LogDistribution.html" + ALT="LogDistribution javadoc API" + SHAPE=RECT + COORDS="733,680,863,734"> + <AREA HREF="../api/org/opensolaris/os/dtrace/MaxValue.html" + ALT="MaxValue javadoc API" + SHAPE=RECT + COORDS="662,485,728,540"> + <AREA HREF="../api/org/opensolaris/os/dtrace/MinValue.html" + ALT="MinValue javadoc API" + SHAPE=RECT + COORDS="589,485,654,540"> + <AREA HREF="../api/org/opensolaris/os/dtrace/PrintaRecord.html" + ALT="PrintaRecord javadoc API" + SHAPE=RECT + COORDS="741,196,892,349"> + <AREA HREF="../api/org/opensolaris/os/dtrace/PrintfRecord.html" + ALT="PrintfRecord javadoc API" + SHAPE=RECT + COORDS="591,196,724,325"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ProbeData.html" + ALT="ProbeData javadoc API" + SHAPE=RECT + COORDS="340,25,455,182"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ProcessEvent.html" + ALT="ProcessEvent javadoc API" + SHAPE=RECT + COORDS="178,248,309,335"> + <AREA HREF="../api/org/opensolaris/os/dtrace/Record.html" + ALT="Record javadoc API" + SHAPE=RECT + COORDS="690,93,800,147"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ScalarRecord.html" + ALT="ScalarRecord javadoc API" + SHAPE=RECT + COORDS="371,339,486,382"> + <AREA HREF="../api/org/opensolaris/os/dtrace/StackValueRecord.html" + ALT="StackValueRecord javadoc API" + SHAPE=RECT + COORDS="498,339,645,382"> + <AREA HREF="../api/org/opensolaris/os/dtrace/SumValue.html" + ALT="SumValue javadoc API" + SHAPE=RECT + COORDS="735,485,799,540"> + <AREA HREF="../api/org/opensolaris/os/dtrace/ValueRecord.html" + ALT="ValueRecord javadoc API" + SHAPE=RECT + COORDS="460,196,574,253"> +</MAP> +<P><IMG SRC="../images/JavaDTraceAPI.gif" NAME="Graphic1" ALIGN=LEFT +WIDTH=1029 HEIGHT=784 BORDER=0 +USEMAP="#api_map"><BR CLEAR=LEFT><BR><BR> +</P> +</BODY> +</HTML> diff --git a/usr/src/lib/libdtrace_jni/java/docs/html/fast.html b/usr/src/lib/libdtrace_jni/java/docs/html/fast.html new file mode 100644 index 0000000000..c820d77306 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/html/fast.html @@ -0,0 +1,542 @@ +<!-- + Copyright 2006 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + ident "%Z%%M% %I% %E% SMI" + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END +--> +<html> +<head> + <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> + <title>Quick Start Guide to the Java DTrace API</title> +</head> +<body> +<h1><a name="Quick_Start_Guide_to_the_Java_DTrace_API_"></a>Quick Start +Guide</h1> +<h1><small><small>to the</small> Java DTrace API</small></h1> +<hr style="width: 100%; height: 2px;"> +<h2>Contents</h2> +<ul> + <li><a href="#Hello_World">"hello, world" Example</a></li> + <ul> + <li><a href="#Writing_a_Simple_Consumer">Writing a Simple Consumer</a></li> + <li><a href="#Running_hello.d_Script">Running the <tt>hello.d</tt> + Script</a></li> + </ul> + <li><a href="#Aggregations">Aggregations</a></li> + <li><a href="#Target_Process">Target Process ID</a></li> + <li><a href="#Closing_Consumers">Closing Consumers</a></li> + <li><a href="#Learning_DTrace">Learning More</a><br> + </li> +</ul> +<h2><a name="Hello_World"></a>"hello, world" Example</h2> +To demonstrate how to use the Java DTrace API, let's write a simple Java +program that runs a D script, in this case <tt>hello.d</tt> (prints +"hello, world" and exits). You will need root permission to use the +Java DTrace API (just as you do to use the <tt>dtrace(1M)</tt> command). +You may want to eliminate this inconvenience by adding the following +line to <tt>/etc/user_attr</tt>: +<br> +<br> +<tt><i>user-name</i>::::defaultpriv=basic,dtrace_kernel,dtrace_proc</tt> +<br> +<br> +<i>(Substitute your user name.)</i> See the <a +href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidln0?a=view> +<b>Security</b></a> chapter of the <i>Solaris Dynamic Tracing Guide</i> +for more information. +<br> +<h4><a name="Writing_a_Simple_Consumer"></a>Writing a Simple Consumer</h4> +Creating a DTrace <a +href="../api/org/opensolaris/os/dtrace/Consumer.html">consumer</a> +is easy: +<pre><tt> + Consumer consumer = new LocalConsumer(); +</tt></pre> +<br> +Before you can do anything with the consumer, you must first open it. +Then you simply compile and enable one or more D programs and run it: +<pre><tt> + consumer.open(); + consumer.compile(program); + consumer.enable(); + consumer.go(); // runs in a background thread +</tt></pre> +<br> +To get the data generated by DTrace, you also need to add a <a +href="../api/org/opensolaris/os/dtrace/ConsumerListener.html">listener</a>: +<pre><tt> + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + System.out.println(e.getProbeData()); + } + }); +</tt></pre> +<br> +Here is a simple example that runs a given D script:<br> +<br> +<b>Java program (<a href="../examples/TestAPI.java">TestAPI.java</a>)</b> +<pre><tt><font color=#aaaaaa> + import org.opensolaris.os.dtrace.*; + import java.io.File; + + public class TestAPI { + public static void + main(String[] args) + { + if (args.length < 1) { + System.err.println("Usage: java TestAPI <script> [ macroargs... ]"); + System.exit(2); + } + + File file = new File(args[0]); + String[] macroArgs = new String[args.length - 1]; + System.arraycopy(args, 1, macroArgs, 0, (args.length - 1));</font> + + Consumer consumer = new LocalConsumer(); + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + System.out.println(e.getProbeData()); + } + }); +<font color=#aaaaaa> + try {</font> + consumer.open(); + consumer.compile(file, macroArgs); + consumer.enable(); + consumer.go();<font color=#aaaaaa> + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + }</font> +</tt></pre> +<br> +Compile the test program as follows: +<pre><tt> + java -cp dtrace.jar TestAPI.java +</tt></pre> +<br> +<h4><a name="Running_hello.d_Script"></a>Running the <tt>hello.d</tt> Script</h4> +Now we need a D script for the program to run. The following is a +simple example that prints "hello, world" and exits:<br> +<b>D script (<a href="../examples/hello.d">hello.d</a>)</b> +<pre><tt> + dtrace:::BEGIN + { + trace("hello, world"); + exit(0); + } +</tt></pre> +<br> +Run as follows:<br> +On i86:<br> +<pre><tt> + java -cp .:dtrace.jar TestAPI hello.d +</tt></pre> +<br> +On sparc you need to add the <tt>-d64</tt> option to java: +<pre><tt> + java -d64 -cp .:dtrace.jar TestAPI hello.d</span><br> +</tt></pre> +<br> +You may need to set <tt>LD_LIBRARY_PATH</tt> so that Java can find +<tt>libdtrace_jni.so</tt> on your system. The output should look like +this: +<pre><tt> + org.opensolaris.os.dtrace.ProbeData[epid = 1, cpu = 1, + enabledProbeDescription = dtrace:::BEGIN, flow = null, records = + ["hello, world", 0]] +</tt></pre> +<br> +There is one record in the <a +href="../api/org/opensolaris/os/dtrace/ProbeData.html"><tt>ProbeData</tt></a> +for each action in the D script. The first record is generated by the +<tt>trace()</tt> action. The second is generated by the <tt>exit()</tt> +action. For prettier output, you could change the <tt>ConsumerAdapter <a + href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#dataReceived%28org.opensolaris.os.dtrace.DataEvent%29">dataReceived()</a></tt> +implementation as follows: +<pre><tt><font color=#aaaaaa> + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + // System.out.println(e.getProbeData());</font> + ProbeData data = e.getProbeData(); + java.util.List < Record > records = data.getRecords(); + for (Record r : records) { + if (r instanceof ExitRecord) { + } else { + System.out.println(r); + } + }<font color=#aaaaaa> + } + });</font> +</tt></pre> +<br> +<h2><a name="Aggregations"></a>Aggregations</h2> +The example Java program can just as easily run a more complex script, +such as an aggregation:<br> +<b>D script (<a href="../examples/syscall.d">syscall.d</a>)</b> +<pre><tt> + syscall:::entry + / execname == $$1 / + { + @[probefunc] = count(); + } + + profile:::tick-1sec + { + printa(@); + clear(@); + } +</tt></pre> +<br> +The above script uses the <tt>$$1</tt> macro variable as a placeholder +for whatever executable you'd like to trace. See the <a +href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidliq?a=view><b> +Macro Arguments</b></a> section of the <b>Scripting</b> chapter of the +<i>Solaris Dynamic Tracing Guide</i>. Using two dollar signs (<tt>$$1</tt>) +instead of one (<tt>$1</tt>) forces expansion of the macro variable to +type string.<br> +<br> +To run the example Java program using the above D script, you need to +specify an argument to the <tt>execname</tt> placeholder. To be sure of +getting output, let's use "java":<br> +On i86: +<pre><tt> + java -cp .:dtrace.jar TestAPI syscall.d java +</tt></pre> +<br> +On sparc: +<pre><tt> + java -d64 -cp .:dtrace.jar TestAPI syscall.d java +</tt></pre> +<br> +A data record generated by the <tt>printa()</tt> action is printed to +the console once every second. It contains counts of system calls by +function name made by java. No record is generated by the +<tt>clear()</tt> action.<br> +<br> +If you omit the argument to the <tt>execname</tt> placeholder, the +program fails to compile and the API throws the following exception: +<pre><tt> + org.opensolaris.os.dtrace.DTraceException: failed to compile script + syscall.d: line 2: macro argument $$1 is not defined + at org.opensolaris.os.dtrace.LocalConsumer._compileFile(Native Method) + at org.opensolaris.os.dtrace.LocalConsumer.compile(LocalConsumer.java:342) + at TestAPI.main(TestAPI.java:26) +</tt></pre> +<br> +A DTrace script may have more than one aggregation. In that case, each +aggregation needs a distinct name:<br> +<b>D script (<a href="../examples/intrstat.d">intrstat.d</a>)</b> +<pre><tt> + sdt:::interrupt-start + { + self->ts = vtimestamp; + } + + sdt:::interrupt-complete + / self->ts && arg0 / + { + this->devi = (struct dev_info *)arg0; + @counts[stringof(`devnamesp[this->devi->devi_major].dn_name), + this->devi->devi_instance, cpu] = count(); + @times[stringof(`devnamesp[this->devi->devi_major].dn_name), + this->devi->devi_instance, cpu] = sum(vtimestamp - self->ts); + self->ts = 0; + } +</tt></pre> +<br> +The <tt>@counts</tt> and <tt>@times</tt> aggregations both accumulate +values for each unique combination of device name, device instance, and +CPU (a three-element tuple). In this example we drop the <tt>tick</tt> +probe to demonstrate a more convenient way to get aggregation data +without the use of the <tt>printa()</tt> action. The <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#getAggregate%28%29"> +<tt>getAggregate()</tt></a> method allows us to get all aggregations at +once on a programmatic interval.<br> +<b>Java program (<a href="../examples/TestAPI2.java">TestAPI2.java</a>)</b> +<pre><tt><font color=#aaaaaa> + ... + + try { + consumer.open(); + consumer.compile(file, macroArgs); + consumer.enable(); + consumer.go();</font> + + Aggregate a; + do { + Thread.sleep(1000); // 1 second + a = consumer.getAggregate(); + if (!a.asMap().isEmpty()) { + System.out.println(a); + } + } while (consumer.isRunning());<font color=#aaaaaa> + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + ...</font> +</tt></pre> +<br> +Compile and run: +<pre><tt> + javac -cp dtrace.jar TestAPI2.java +</tt></pre> +<br> +On i86: +<pre><tt> + java -cp .:dtrace.jar TestAPI2 intrstat.d +</tt></pre> +<br> +On sparc: +<pre><tt> + java -d64 -cp .:dtrace.jar TestAPI2 intrstat.d +</tt></pre> +<br> +Try removing the <tt>tick</tt> probe from the <tt>syscall.d</tt> example +and running it again with the above modification (<tt>TestAPI2</tt>).<br> +<br> +By default, the requested aggregate includes every aggregation and +accumulates running totals. To display values per time interval +(instead of running totals), clear the aggregations each time you call +<tt>getAggregate()</tt>. Clearing an aggregation resets all counts to +zero without removing any tuples. The following modification to the +example above clears all aggregations: +<pre><tt><font color=#aaaaaa> + // a = consumer.getAggregate();</font> + a = consumer.getAggregate(null, null); // included, cleared +</tt></pre> +<br> +Each <tt>Set</tt> of aggregation names, <tt>included</tt> and +<tt>cleared</tt>, specifies <i>all</i> aggregations if <tt>null</tt> and +no aggregations if empty. Any subset is possible. However, if an +aggregation has ever been used in the <tt>printa()</tt> action, it is no +longer available to the <tt>getAggregate()</tt> method.<br> +<br> +Be aware that you cannot call <tt>getAggregate()</tt> on an interval +faster that the <tt>aggrate</tt> setting. See the <a +href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlis?a=view> +<b>Options and Tunables</b></a> chapter of the <i>Solaris Dynamic +Tracing Guide</i>. See also the <a +href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhf?a=view> +<b>Minimizing Drops</b></a> section of the <b>Aggregations</b> chapter +for specific information about the <tt>aggrate</tt> option. The default +<tt>aggrate</tt> is once per second. Here's an example of how you might +double the <tt>aggrate</tt> to minimize drops: +<pre><tt> + consumer.setOption(Option.aggrate, Option.millis(500)); // every half second +</tt></pre> +<br> +Even a single drop terminates the consumer unless you override the <a +href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#dataDropped%28org.opensolaris.os.dtrace.DropEvent%29"> +<tt>dataDropped()</tt></a> method of <tt>ConsumerAdapter</tt> to handle +drops differently. To avoid drops, it is probably better to increase +the <tt>aggsize</tt> option, since increasing the <tt>aggrate</tt> makes +the consumer work harder. In most cases, the <tt>aggrate</tt> should +only be increased when you need to update a display of aggregation data +more frequently than once per second. Many runtime options, including +<tt>aggrate</tt>, can be changed dynamically while the consumer is +running.<br> +<br> +It's also worth mentioning that a D aggregation may omit square +brackets and aggregate only a single value: +<pre><tt> + @total = count(); +</tt></pre> +<br> +The resulting singleton <a +href="../api/org/opensolaris/os/dtrace/Aggregation.html"> +<tt>Aggregation</tt></a> instance has one record that may be obtained as +follows: +<pre><tt> + Aggregate a = consumer.getAggregate(); + Aggregation total = a.getAggregation("total"); + AggregationRecord totalRecord = total.getRecord(Tuple.EMPTY); +</tt></pre> +<br> +<h2><a name="Target_Process"></a>Target Process ID</h2> +In addition to supporting macro arguments (see the <tt>syscall.d</tt> +aggregation example above), the Java DTrace API also supports the +<tt>$target</tt> macro variable. (See the <a +href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlir?a=view> +<b>Target Process ID</b></a> section of the <b>Scripting</b> chapter of +the <i>Solaris Dynamic Tracing Guide</i>.) This allows you to trace a +process from the very beginning of its execution, rather than sometime +after you manually obtain its process ID. The API does this by creating +a process that is initially suspended and allowed to start only after <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#go%28%29"> +<tt>go()</tt></a> has initiated tracing. For example, we might want to +aggregate all the system calls from start to finish made by the +<tt>date</tt> command:<br> +<b>D script (<a href="../examples/target.d">target.d</a>)</b> +<pre><tt> + syscall:::entry + / pid == $target / + { + @[probefunc] = count(); + } +</tt></pre> +<br> +A modified version of the <tt>TestAPI.java</tt> program adds the <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#createProcess%28java.lang.String%29"> +<tt>createProcess()</tt></a> call to execute the given command but +prevent it from starting until the consumer is running:<br> +<b>Java program (<a href="../examples/TestTarget.java">TestTarget.java</a>)</b> +<pre><tt><font color=#aaaaaa> + ... + consumer.open();</font> + consumer.createProcess(command);<font color=#aaaaaa> + consumer.compile(file); + consumer.enable(); + consumer.go(); + ...</font> +</tt></pre> +<br> +It also overrides the <a +href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#processStateChanged%28org.opensolaris.os.dtrace.ProcessEvent%29"> +<tt>processStateChanged()</tt></a> method of the +<tt>ConsumerAdapter</tt> to print a notification when the process has +ended: +<pre><tt><font color=#aaaaaa> + ... + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + System.out.println(e.getProbeData()); + } + public void consumerStopped(ConsumerEvent e) { + try { + Aggregate a = consumer.getAggregate(); + for (Aggregation agg : a.asMap().values()) { + for (AggregationRecord rec : agg.asMap().values()) { + System.out.println(rec.getTuple() + " " + + rec.getValue()); + } + } + } catch (Exception x) { + x.printStackTrace(); + System.exit(1); + } + consumer.close(); + }</font> + public void processStateChanged(ProcessEvent e) { + System.out.println(e.getProcessState()); + }<font color=#aaaaaa> + }); + ...</font> +</tt></pre> +<br> +Compile and run: +<pre><tt> + javac -cp dtrace.jar TestTarget.java +</tt></pre> +<br> +On i86: +<pre><tt> + java -cp .:dtrace.jar TestTarget target.d date +</tt></pre> +<br> +On sparc: +<pre><tt> + java -d64 -cp .:dtrace.jar TestTarget target.d date +</tt></pre> +<br> +The consumer exits automatically when the target <tt>date</tt> process +completes.<br> +<h2><a name="Closing_Consumers"></a>Closing Consumers</h2> +An application using the Java DTrace API may run multiple consumers +simultaneously. When a consumer stops running, the programmer is +responsible for closing it in order to release the system resources it +holds. A consumer may stop running for any of the following reasons: +<ul> + <li>It was stopped explicitly by a call to its <a +href=../api/org/opensolaris/os/dtrace/Consumer.html#stop()> +<tt>stop()</tt></a> method</li> + <li>It encountered the <tt>exit()</tt> action</li> + <li>Its <tt>$target</tt> process or processes (if any) all completed</li> + <li>It encountered an exception</li> +</ul> +By default, an exception prints a stack trace to <tt>stderr</tt> before +notifying listeners that the consumer has stopped. You can define +different behavior by setting an <a +href=../api/org/opensolaris/os/dtrace/ExceptionHandler.html> +<tt>ExceptionHandler</tt></a>, but the consumer is still stopped.<br> +<br> +The same listener that receives probe data generated by DTrace is also +notified when the consumer stops. This is a good place to close the +consumer: +<pre><tt><font color=#aaaaaa> + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + System.out.println(e.getProbeData()); + }</font> + public void consumerStopped(ConsumerEvent e) { + Consumer consumer = (Consumer)e.getSource(); + consumer.close(); + } + }<font color=#aaaaaa> + });</font> +</tt></pre> +<br> +This releases the resources held by the consumer in all cases, i.e. +after it exits for <i>any</i> of the reasons listed above.<br> +<br> +You can request the last aggregate snapshot made by a stopped consumer, +as long as it has not yet been closed: +<pre><tt> + Aggregate a = consumer.getAggregate(); +</tt></pre> +<br> +Note however that any aggregation that has already appeared in a <a +href=../api/org/opensolaris/os/dtrace/PrintaRecord.html> +<tt>PrintaRecord</tt></a> as a result of the <tt>printa()</tt> action +action will not be included in the requested aggregate. +<h2><a name="Learning_DTrace"></a>Learning More</h2> +<br> +The <a href="http://www.opensolaris.org/os/community/dtrace/"> +OpenSolaris DTrace page</a> has links to resources to help you learn +DTrace. In particular, you should read the <a +href="http://docs.sun.com/db/doc/817-6223"><i>Solaris Dynamic Tracing + Guide</i></a>.<br> +<br> +Try the example Java programs on this page with other D scripts. You +need not remove <tt>#!/usr/sbin/dtrace -s</tt> from the top of an +executable script. You may want to remove <tt>profile:::tick*</tt> +clauses if you plan to use the <tt>Consumer</tt> <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#getAggregate%28%29"> +<tt>getAggregate()</tt></a> method and control the aggregating interval +programmatically. If the script uses the pre-compiler, you will need to +call the <tt>Consumer</tt> <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#setOption%28java.lang.String%29"> +<tt>setOption()</tt></a> method with the <a +href="../api/org/opensolaris/os/dtrace/Option.html#cpp"> +<tt>Option.cpp</tt></a> argument.<br> +<br> +To quickly familiarize yourself with the Java DTrace API, take a look at +the overview <a href="JavaDTraceAPI.html">diagram</a>.<br> +<br> +<a href="#Quick_Start_Guide_to_the_Java_DTrace_API_">Back to top</a><br> +<br> +</body> +</html> diff --git a/usr/src/lib/libdtrace_jni/java/docs/images/JavaDTraceAPI.gif b/usr/src/lib/libdtrace_jni/java/docs/images/JavaDTraceAPI.gif Binary files differnew file mode 100644 index 0000000000..a3a7aa871f --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/images/JavaDTraceAPI.gif diff --git a/usr/src/lib/libdtrace_jni/java/manifest/dtrace.mf b/usr/src/lib/libdtrace_jni/java/manifest/dtrace.mf new file mode 100644 index 0000000000..46fc8c8207 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/manifest/dtrace.mf @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Specification-Title: Java DTrace API +Specification-Version: 1.0 +Implementation-Title: org.opensolaris.os.dtrace +Implementation-Vendor: Sun Microsystems, Inc. + +Name: org/opensolaris/os/dtrace/ +Sealed: true + +Name: org/opensolaris/os/support/ +Sealed: true diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AbstractAggregationValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AbstractAggregationValue.java new file mode 100644 index 0000000000..a136c287a5 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AbstractAggregationValue.java @@ -0,0 +1,108 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.Serializable; +import java.beans.*; + +/** + * A numeric value generated by a D aggregating action such as {@code + * count()} or {@code sum()}. + * <p> + * Immutable. + * + * @author Tom Erickson + */ +abstract class AbstractAggregationValue + implements AggregationValue, Serializable +{ + static final long serialVersionUID = 2340811719178724026L; + + /** @serial */ + private final Number value; + + public + AbstractAggregationValue(long v) + { + value = new Long(v); + } + + public + AbstractAggregationValue(double v) + { + value = new Double(v); + } + + public Number + getValue() + { + return value; + } + + /** + * Compares the specified object with this aggregation value for + * equality. Defines equality as having the same type and the same + * numeric value. + * + * @return {@code true} if and only if the specified object is an + * aggregation value of the same {@code Class} as this value, and + * both values return equal numbers from {@link #getValue()}. + */ + public boolean + equals(Object o) + { + if (o instanceof AbstractAggregationValue) { + AbstractAggregationValue v = (AbstractAggregationValue)o; + return (value.equals(v.value) && + (getClass() == v.getClass())); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return value.hashCode(); + } + + /** + * Gets the string representation of {@link #getValue()}. + * + * @return the string representation of {@link #getValue()} returned + * by {@link Object#toString()} + */ + public String + toString() + { + return value.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregate.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregate.java new file mode 100644 index 0000000000..23aa3af99b --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregate.java @@ -0,0 +1,278 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.beans.*; +import java.io.*; + +/** + * A consistent snapshot of all aggregations requested by a single + * {@link Consumer}. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#getAggregate() + * + * @author Tom Erickson + */ +public final class Aggregate implements Serializable +{ + static final long serialVersionUID = 3180340417154076628L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Aggregate.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"snaptime", "aggregations"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final long snaptime; + + // Map must not have same name as named PersistenceDelegate property + // ("aggregations"), otherwise it gets confused for a bean property + // and XMLDecoder calls the constructor with a Map instead of the + // value of the getAggregations() method. + + private transient Map <String, Aggregation> map; + + /** + * Called by native code. + */ + private + Aggregate(long snaptimeNanos) + { + snaptime = snaptimeNanos; + map = new HashMap <String, Aggregation> (); + } + + /** + * Creates an aggregate with the given snaptime and aggregations. + * Supports XML persistence. + * + * @param snaptimeNanos nanosecond timestamp when this aggregate was + * snapped + * @param aggregations unordered collection of aggregations + * belonging to this aggregate + * @throws NullPointerException if the given collection of + * aggregations is {@code null} + */ + public + Aggregate(long snaptimeNanos, Collection <Aggregation> aggregations) + { + snaptime = snaptimeNanos; + mapAggregations(aggregations); + } + + // assumes map is not yet created + private void + mapAggregations(Collection <Aggregation> aggregations) + { + int capacity = (int)(((float)aggregations.size() * 3.0f) / 2.0f); + // avoid rehashing and optimize lookup; will never be modified + map = new HashMap <String, Aggregation> (capacity, 1.0f); + for (Aggregation a : aggregations) { + map.put(a.getName(), a); + } + } + + /** + * Gets the nanosecond timestamp of this aggregate snapshot. + * + * @return nanosecond timestamp of this aggregate snapshot + */ + public long + getSnaptime() + { + return snaptime; + } + + /** + * Gets an unordered list of all aggregations in this aggregate + * snapshot. The list is easily sortable using {@link + * java.util.Collections#sort(List list, Comparator c)} provided any + * user-defined ordering. Modifying the returned list has no effect + * on this aggregate. Supports XML persistence. + * + * @return modifiable unordered list of all aggregations in this + * aggregate snapshot; list is non-null and possibly empty + */ + public List <Aggregation> + getAggregations() + { + // Must return an instance of a public, mutable class in order + // to support XML persistence. + List <Aggregation> list = new ArrayList <Aggregation> (map.size()); + list.addAll(map.values()); + return list; + } + + /** + * Gets the aggregation with the given name if it exists in this + * aggregate snapshot. + * + * @param name the name of the desired aggregation, or empty string + * to request the unnamed aggregation. In D, the unnamed + * aggregation is used anytime a name does not follow the + * aggregation symbol '{@code @}', for example: + * <pre> {@code @ = count();}</pre> as opposed to + * <pre> {@code @counts = count()}</pre> resulting in an + * {@code Aggregation} with the name "counts". + * + * @return {@code null} if no aggregation by the given name exists + * in this aggregate + * @see Aggregation#getName() + */ + public Aggregation + getAggregation(String name) + { + // This was decided March 18, 2005 in a meeting with the DTrace + // team that calling getAggregation() with underbar should + // return the unnamed aggregation (same as calling with empty + // string). Underbar is used to identify the unnamed + // aggregation in libdtrace; in the jave API it is identifed by + // the empty string. The API never presents underbar but + // accepts it as input (just converts underbar to empty string + // everywhere it sees it). + name = Aggregate.filterUnnamedAggregationName(name); + return map.get(name); + } + + /** + * In the native DTrace library, the unnamed aggregation {@code @} + * is given the name {@code _} (underbar). The Java DTrace API does + * not expose this implementation detail but instead identifies the + * unnamed aggregation with the empty string. Here we convert the + * name of the unnamed aggregation at the earliest opportunity. + * <p> + * Package level access. Called by this class and PrintaRecord when + * adding the Aggregation abstraction on top of native aggregation + * records. + */ + static String + filterUnnamedAggregationName(String name) + { + if ((name != null) && name.equals("_")) { + return ""; + } + return name; + } + + /** + * Gets a read-only {@code Map} view of this aggregate. + * + * @return a read-only {@code Map} view of this aggregate keyed by + * aggregation name + */ + public Map <String, Aggregation> + asMap() + { + return Collections. <String, Aggregation> unmodifiableMap(map); + } + + /** + * Called by native code. + * + * @throws IllegalStateException if the aggregation with the given + * name already has a record with the same tuple key as the given + * record. + */ + private void + addRecord(String aggregationName, long aggid, AggregationRecord rec) + { + aggregationName = Aggregate.filterUnnamedAggregationName( + aggregationName); + Aggregation aggregation = getAggregation(aggregationName); + if (aggregation == null) { + aggregation = new Aggregation(aggregationName, aggid); + map.put(aggregationName, aggregation); + } + aggregation.addRecord(rec); + } + + /** + * Serialize this {@code Aggregate} instance. + * + * @serialData Serialized fields are emitted, followed by a {@link + * java.util.List} of {@link Aggregation} instances. + */ + private void + writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeObject(getAggregations()); + } + + @SuppressWarnings("unchecked") + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // cannot cast to parametric type without compiler warning + List <Aggregation> aggregations = (List)s.readObject(); + // load serialized form into private map as a defensive copy + mapAggregations(aggregations); + // check class invariants after defensive copy + } + + /** + * Gets a string representation of this aggregate snapshot useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Aggregate.class.getName()); + buf.append("[snaptime = "); + buf.append(snaptime); + buf.append(", aggregations = "); + List <Aggregation> a = getAggregations(); + Collections.sort(a, new Comparator <Aggregation> () { + public int compare(Aggregation a1, Aggregation a2) { + return a1.getName().compareTo(a2.getName()); + } + }); + buf.append(Arrays.toString(a.toArray())); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregateSpec.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregateSpec.java new file mode 100644 index 0000000000..656c6f6b15 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregateSpec.java @@ -0,0 +1,163 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; + +/** + * Implementation detail used by {@link Consumer#getAggregate()}. + * Package level access. + * + * @author Tom Erickson + */ +class AggregateSpec { + private Set <String> includedAggregationNames; + private Set <String> clearedAggregationNames; + + AggregateSpec() + { + includedAggregationNames = new HashSet <String> (); + clearedAggregationNames = new HashSet <String> (); + } + + public boolean + isIncludeByDefault() + { + return (includedAggregationNames == null); + } + + public boolean + isClearByDefault() + { + return (clearedAggregationNames == null); + } + + public void + setIncludeByDefault(boolean include) + { + if (include) { + includedAggregationNames = null; + } else if (includedAggregationNames == null) { + includedAggregationNames = new HashSet <String> (); + } + } + + public void + setClearByDefault(boolean clear) + { + if (clear) { + clearedAggregationNames = null; + } else if (clearedAggregationNames == null) { + clearedAggregationNames = new HashSet <String> (); + } + } + + /** + * Specifies which aggregations to include in an aggregate snapshot. + * If none are specified, all aggregations are included. A snapshot + * is read-consistent across all included aggregations. + * + * @see Consumer#getAggregate(AggregateSpec spec) + */ + public void + addIncludedAggregationName(String name) + { + if (includedAggregationNames == null) { + includedAggregationNames = new HashSet <String> (); + } + includedAggregationNames.add(name); + } + + /** + * Specifies which aggregations to clear after snapping the + * aggregate. If none are specified, no aggregations are cleared. + * <p> + * Aggregations are cleared immediately after they are snapped + * before any more data can be accumulated in order to prevent loss + * of data between snapshots. + * + * @see Consumer#getAggregate(AggregateSpec spec) + */ + public void + addClearedAggregationName(String name) + { + if (clearedAggregationNames == null) { + clearedAggregationNames = new HashSet <String> (); + } + clearedAggregationNames.add(name); + } + + public Set <String> + getIncludedAggregationNames() + { + if (includedAggregationNames == null) { + return Collections. <String> emptySet(); + } + return Collections.unmodifiableSet(includedAggregationNames); + } + + public Set <String> + getClearedAggregationNames() + { + if (clearedAggregationNames == null) { + return Collections. <String> emptySet(); + } + return Collections.unmodifiableSet(clearedAggregationNames); + } + + public boolean + isIncluded(String aggregationName) + { + return ((includedAggregationNames == null) || + includedAggregationNames.contains(aggregationName)); + } + + public boolean + isCleared(String aggregationName) + { + return ((clearedAggregationNames == null) || + clearedAggregationNames.contains(aggregationName)); + } + + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(AggregateSpec.class.getName()); + buf.append("[includedAggregationNames = "); + buf.append(Arrays.toString(getIncludedAggregationNames().toArray())); + buf.append(", clearedAggregationNames = "); + buf.append(Arrays.toString(getClearedAggregationNames().toArray())); + buf.append(", includeByDefault = "); + buf.append(isIncludeByDefault()); + buf.append(", clearByDefault = "); + buf.append(isClearByDefault()); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregation.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregation.java new file mode 100644 index 0000000000..f88532819a --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregation.java @@ -0,0 +1,368 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.beans.*; +import java.io.*; + +/** + * A snapshot of a DTrace aggregation. The name of an {@code + * Aggregation} instance matches the source declaration, for example + * <pre> {@code @a[execname] = count();}</pre> + * results in an {@code Aggregation} named "a" (the name does not + * include the preceding {@code @}). For convenience, a single + * aggregation can remain unnamed (multiple aggregations in the same D + * program need distinct names). The unnamed aggregation results in an + * {@code Aggregation} instance whose name is the empty string, for + * example + * <pre> {@code @[execname] = count();}</pre> + * An aggregation can list more than one variable in square brackets in + * order to accumulate a value for each unique combination, or {@link + * Tuple}. Each tuple instance is associated with its accumulated + * {@link AggregationValue} in an {@link AggregationRecord}. For + * example + * <pre> {@code @counts[execname, probefunc, cpu] = count();}</pre> + * results in an {@code Aggregation} named "counts" containing records + * each pairing a {@link CountValue} to a three-element {@code Tuple}. + * It is also possible to omit the square brackets, for example + * <pre> {@code @a = count();}</pre> + * results in an {@code Aggregation} named "a" with only a single record + * keyed to the empty tuple ({@link Tuple#EMPTY}). + * <p> + * For more information, see the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlh7?a=view> + * <b>Aggregations</b></a> chapter of the <i>Solaris Dynamic Tracing + * Guide</i>. Also, the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlfv?a=view> + * <b>Built-in Variables</b></a> section of the <b>Variables</b> chapter + * describes variables like {@code execname}, {@code probefunc}, and + * {@code cpu} useful for aggregating. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregate + * @see PrintaRecord + * + * @author Tom Erickson + */ +public final class Aggregation implements Serializable { + static final long serialVersionUID = 2340811719178724026L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Aggregation.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"name", "id", "records"}) + { + @Override + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return ((newInstance != null) && (oldInstance != null) && + (oldInstance.getClass() == newInstance.getClass())); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** @serial */ + private String name; + /** @serial */ + private final long id; + private transient Map <Tuple, AggregationRecord> map; + + /** + * Package-level access, called by Aggregate + */ + Aggregation(String aggregationName, long aggregationID) + { + name = Aggregate.filterUnnamedAggregationName(aggregationName); + id = aggregationID; + map = new HashMap <Tuple, AggregationRecord> (); + } + + /** + * Creates an aggregation with the given name, ID, and records. + * Supports XML persistence. + * + * @param aggregationName the name of this aggregation, empty string + * if this aggregation is unnamed + * @param aggregationID ID generated from a sequence by the native + * DTrace library + * @param aggregationRecords unordered collection of records + * belonging to this aggregation + * @throws NullPointerException if the specified name or list of + * records is {@code null} + * @throws IllegalArgumentException if any record has an empty + * tuple, unless it is the only record in the given collection (only + * a singleton generated by an aggregation without square brackets + * uses {@link Tuple#EMPTY} as a key) + * @see #getRecord(Tuple key) + */ + public + Aggregation(String aggregationName, long aggregationID, + Collection <AggregationRecord> aggregationRecords) + { + name = Aggregate.filterUnnamedAggregationName(aggregationName); + id = aggregationID; + mapRecords(aggregationRecords); + validate(); + } + + // assumes map is not yet created + private void + mapRecords(Collection <AggregationRecord> records) + { + int capacity = (int)(((float)records.size() * 3.0f) / 2.0f); + // avoid rehashing and optimize lookup; will never be modified + map = new HashMap <Tuple, AggregationRecord> (capacity, 1.0f); + for (AggregationRecord record : records) { + map.put(record.getTuple(), record); + } + } + + private void + validate() + { + if (name == null) { + throw new NullPointerException("name is null"); + } + for (AggregationRecord r : map.values()) { + if ((r.getTuple().size() == 0) && (map.size() > 1)) { + throw new IllegalArgumentException("empty tuple " + + "allowed only in singleton aggregation"); + } + } + } + + /** + * Gets the name of this aggregation. + * + * @return the name of this aggregation exactly as it appears in the + * D program minus the preceding {@code @}, or an empty string if + * the aggregation is unnamed, for example: + * <pre> {@code @[execname] = count();}</pre> + */ + public String + getName() + { + return name; + } + + /** + * Gets the D compiler-generated ID of this aggregation. + * + * @return the D compiler-generated ID + */ + public long + getID() + { + return id; + } + + /** + * Gets an unordered list of this aggregation's records. The list + * is easily sortable using {@link java.util.Collections#sort(List + * list, Comparator c)} provided any user-defined ordering. + * Modifying the returned list has no effect on this aggregation. + * Supports XML persistence. + * + * @return a newly created list that copies this aggregation's + * records by reference in no particular order + */ + public List <AggregationRecord> + getRecords() + { + List <AggregationRecord> list = + new ArrayList <AggregationRecord> (map.values()); + return list; + } + + /** + * Package level access, called by Aggregate and PrintaRecord. + * + * @throws IllegalArgumentException if this aggregation already + * contains a record with the same tuple key as the given record + */ + void + addRecord(AggregationRecord record) + { + Tuple key = record.getTuple(); + if (map.put(key, record) != null) { + throw new IllegalArgumentException("already contains a record " + + "with tuple " + key); + } + } + + /** + * Gets a read-only {@code Map} view of this aggregation. + * + * @return a read-only {@code Map} view of this aggregation + */ + public Map <Tuple, AggregationRecord> + asMap() + { + return Collections.unmodifiableMap(map); + } + + /** + * Compares the specified object with this aggregation for equality. + * Defines equality as having equal names and equal records. + * + * @return {@code true} if and only if the specified object is an + * {@code Aggregation} with the same name as this aggregation and + * the {@code Map} views of both aggregations returned by {@link + * #asMap()} are equal as defined by {@link + * AbstractMap#equals(Object o) AbstractMap.equals()} + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof Aggregation) { + Aggregation a = (Aggregation)o; + return (name.equals(a.name) && + (map.equals(a.map))); // same mappings + } + return false; + } + + /** + * Overridden to ensure that equal aggregations have equal hash + * codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + name.hashCode(); + hash = (37 * hash) + map.hashCode(); + return hash; + } + + /** + * Gets the record associated with the given key, or the singleton + * record of an aggregation declared without square brackets if + * {@code key} is {@code null} or empty. + * + * @param key The record key, or an empty tuple (see {@link + * Tuple#EMPTY}) to obtain the value from a <i>singleton</i> (a + * non-keyed instance with only a single value) generated from a + * DTrace aggregation declarated without square brackets, for + * example: + * <pre> {@code @a = count();}</pre> + * @return the record associated with the given key, or {@code null} + * if no record in this aggregation is associated with the given key + */ + public AggregationRecord + getRecord(Tuple key) + { + if (key == null) { + key = Tuple.EMPTY; + } + return map.get(key); + } + + /** + * Serialize this {@code Aggregation} instance. + * + * @serialData Serialized fields are emitted, followed by a {@link + * java.util.List} of {@link AggregationRecord} instances. + */ + private void + writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeObject(getRecords()); + } + + @SuppressWarnings("unchecked") + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // cannot cast to parametric type without compiler warning + List <AggregationRecord> records = (List)s.readObject(); + // load serialized form into private map as a defensive copy + mapRecords(records); + // Check class invariants (only after defensive copy) + name = Aggregate.filterUnnamedAggregationName(name); + validate(); + } + + /** + * Gets a string representation of this aggregation useful for + * logging and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + @Override + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Aggregation.class.getName()); + buf.append("[name = "); + buf.append(name); + buf.append(", id = "); + buf.append(id); + buf.append(", records = "); + List <AggregationRecord> recordList = getRecords(); + // Sort by tuple so that equal aggregations have equal strings + Collections.sort(recordList, new Comparator <AggregationRecord> () { + public int compare(AggregationRecord r1, AggregationRecord r2) { + Tuple t1 = r1.getTuple(); + Tuple t2 = r2.getTuple(); + return t1.compareTo(t2); + } + }); + buf.append('['); + boolean first = true; + for (AggregationRecord record : recordList) { + if (first) { + first = false; + } else { + buf.append(", "); + } + buf.append(record); + } + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationRecord.java new file mode 100644 index 0000000000..4ab439c4c5 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationRecord.java @@ -0,0 +1,208 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A single key-value pair in a DTrace aggregation. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class AggregationRecord implements Record, Serializable { + static final long serialVersionUID = -8439277589555814411L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(AggregationRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"tuple", "value"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private Tuple tuple; + /** @serial */ + private AggregationValue value; + + /** + * Creates an aggregation record with the given key and value. + * Supports XML persistence. + * + * @param tupleKey aggregation tuple, may be empty (see {@link + * Tuple#EMPTY}) to indicate that this record's value belongs to an + * unkeyed aggregation declared without square brackets, for + * example: <pre> {@code @a = count();}</pre> + * @param recordValue aggregated value associated with the given + * tuple + * @throws NullPointerException if the given key or value is + * {@code null} + */ + public + AggregationRecord(Tuple tupleKey, AggregationValue recordValue) + { + tuple = tupleKey; + value = recordValue; + validate(); + } + + private void + validate() + { + if (tuple == null) { + throw new NullPointerException("key is null"); + } + if (value == null) { + throw new NullPointerException("value is null"); + } + } + + /** + * Gets the multi-element key associated with {@link + * #getValue()}. + * + * @return non-null, possibly empty tuple + * @see Aggregation#getRecord(Tuple key) + */ + public Tuple + getTuple() + { + return tuple; + } + + /** + * Gets the value associated with {@link #getTuple()}. Values + * generated by the DTrace actions {@code count()}, {@code sum()}, + * {@code avg()}, {@code min()}, and {@code max()} are of type + * {@link Long}. Values generated by the DTrace actions {@code + * quantize(}) and {@code lquantize()} are of type {@link + * Distribution}. + * + * @return non-null value keyed to {@link #getTuple()} + */ + public AggregationValue + getValue() + { + return value; + } + + /** + * Compares the specified object with this aggregation record for + * equality. Defines equality as having the same tuple and value. + * + * @return {@code true} if and only if the specified object is an + * {@code AggregationRecord} and both records have equal tuples as + * defined by {@link Tuple#equals(Object o)} and equal values as + * defined by the implementation of {@link AggregationValue} + */ + public boolean + equals(Object o) + { + if (o instanceof AggregationRecord) { + AggregationRecord r = (AggregationRecord)o; + return (tuple.equals(r.tuple) && + value.equals(r.value)); + } + return false; + } + + /** + * Overridden to ensure that equal records have equal hash codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + tuple.hashCode(); + hash = (37 * hash) + value.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this aggregation record useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + @Override + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(AggregationRecord.class.getName()); + buf.append("[tuple = "); + buf.append(tuple); + buf.append(", value = "); + buf.append(value); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationValue.java new file mode 100644 index 0000000000..1bc2b176c1 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationValue.java @@ -0,0 +1,52 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * A value accumulated by an aggregating DTrace action such as {@code + * count()} or {@code sum()}. Each {@code AggregationValue} is + * associated with a {@link Tuple} in an {@link AggregationRecord}. In + * other words it is a value in a key-value pair (each pair representing + * an entry in a DTrace aggregation). + * <p> + * This value may be a single number or consist of multiple numbers, + * such as a value distribution. In the latter case, it still has a + * single, composite value useful for display and/or comparison. + * + * @see AggregationRecord + * + * @author Tom Erickson + */ +public interface AggregationValue { + /** + * Gets the numeric value of this instance. + * + * @return non-null numeric value + */ + public Number getValue(); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AvgValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AvgValue.java new file mode 100644 index 0000000000..bd1257a009 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AvgValue.java @@ -0,0 +1,154 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code avg()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class AvgValue extends AbstractAggregationValue { + static final long serialVersionUID = 1633169020110237906L; + + /** @serial */ + private final long total; + /** @serial */ + private final long count; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(AvgValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value", "total", "count"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code avg()} action. + * Supports XML persistence. + * + * @param v average + * @param averagedTotal sum total of all values averaged + * @param averagedValueCount number of values averaged + * @throws IllegalArgumentException if the given count is negative + * or if the given average is not the value expected for the given + * total and count + */ + public + AvgValue(long v, long averagedTotal, long averagedValueCount) + { + super(v); + total = averagedTotal; + count = averagedValueCount; + validate(); + } + + private void + validate() + { + if (count < 0) { + throw new IllegalArgumentException("count is negative"); + } + long average = super.getValue().longValue(); + if (count == 0) { + if (average != 0) { + throw new IllegalArgumentException( + "count of values is zero, average is non-zero (" + + average + ")"); + } + } else { + if (average != (total / count)) { + throw new IllegalArgumentException( + getValue().toString() + " is not the expected " + + "average of total " + total + " and count " + + count); + } + } + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the average of the aggregated values. + * + * @return average of the aggregated values, equal to <code>({@link + * #getTotal()} / {@link #getCount()})</code> rounded down + */ + public Long + getValue() + { + return (Long)super.getValue(); + } + + /** + * Gets the sum total of the aggregated values. + * + * @return the sum total of the aggregated values + */ + public long + getTotal() + { + return total; + } + + /** + * Gets the number of aggregated values included in the average. + * + * @return the number of aggregated values included in the average + */ + public long + getCount() + { + return count; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Consumer.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Consumer.java new file mode 100644 index 0000000000..6b6b47681d --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Consumer.java @@ -0,0 +1,751 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.*; + +/** + * Interface to the native DTrace library, each instance is a single + * DTrace consumer. To consume the output of DTrace program actions, + * {@link #addConsumerListener(ConsumerListener l) register a probe data + * listener}. To get a snapshot of all aggregations in a D program on + * your own programmatic interval without relying on DTrace actions to + * generate that output, use the {@link #getAggregate()} method. + * + * @see ProbeData + * @see Aggregate + * + * @author Tom Erickson + */ +public interface Consumer { + + /** + * Optional flags passed to {@link #open(Consumer.OpenFlag[] flags) + * open()}. + */ + public enum OpenFlag { + /** + * Generate 32-bit D programs. {@code ILP32} and {@link + * Consumer.OpenFlag#LP64 LP64} are mutually exclusive. + */ + ILP32, + /** + * Generate 64-bit D programs. {@code LP64} and {@link + * Consumer.OpenFlag#ILP32 ILP32} are mutually exclusive. + */ + LP64, + }; + + /** + * Opens this DTrace consumer. Optional flags indicate behaviors + * that can only be set at the time of opening. Most optional + * behaviors are set using {@link #setOption(String option, String + * value) setOption()} after opening the consumer. In the great + * majority of cases, the consumer is opened without specifying any + * flags: + * <pre> {@code consumer.open();}</pre> + * Subsequent calls to set options, compile DTrace programs, enable + * probes, and run this consumer may be made from any thread. + * + * @throws NullPointerException if any of the given open flags is + * {@code null} + * @throws IllegalArgumentException if any of the given flags are + * mutually exlusive + * @throws IllegalStateException if this consumer is closed or has + * already been opened + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #compile(File program, String[] macroArgs) + * @see #compile(String program, String[] macroArgs) + * @see #enable() + * @see #go() + */ + public void open(OpenFlag ... flags) throws DTraceException; + + /** + * Compiles the given D program string. Optional macro arguments + * replace corresponding numbered macro variables in the D program + * starting at {@code $1}. + * + * @param program program string + * @param macroArgs macro substitutions for <i>$n</i> placeholders + * embedded in the given D program: {@code macroArgs[0]} replaces + * all occurrences of {@code $1}, {@code macroArgs[1]} replaces all + * occurrences of {@code $2}, and so on. {@code $0} is + * automatically replaced by the executable name and should not be + * included in the {@code macroArgs} parameter. See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidliq?a=view> + * <b>Macro Arguments</b></a> section of the <b>Scripting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * @return a non-null {@code Program} identifier that may be passed + * to {@link #enable(Program program) enable()} + * @throws NullPointerException if the given program string or any + * of the given macro arguments is {@code null} + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #go()}, or if the + * consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #compile(File program, String[] macroArgs) + */ + public Program compile(String program, String ... macroArgs) + throws DTraceException; + + /** + * Compiles the given D program file. Optional macro arguments + * replace corresponding numbered macro variables in the D program + * starting at {@code $1}. + * + * @param program program file + * @param macroArgs macro substitutions for <i>$n</i> placeholders + * embedded in the given D program: {@code macroArgs[0]} replaces + * all occurrences of {@code $1}, {@code macroArgs[1]} replaces all + * occurrences of {@code $2}, and so on. {@code $0} is + * automatically set to the name of the given file and should not be + * included in the {@code macroArgs} parameter. See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidliq?a=view> + * <b>Macro Arguments</b></a> section of the <b>Scripting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * @return a non-null {@code Program} identifier that may be passed + * to {@link #enable(Program program) enable()} + * @throws NullPointerException if the given program file or any of + * the given macro arguments is {@code null} + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #go()}, or if the + * consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @throws FileNotFoundException if the given program file cannot be + * opened + * @throws IOException if an I/O error occurs while reading the + * contents of the given program file + * @throws SecurityException if a security manager exists and its + * {@code checkRead()} method denies read access to the file + * @see #compile(String program, String[] macroArgs) + */ + public Program compile(File program, String ... macroArgs) + throws DTraceException, IOException, SecurityException; + + /** + * Enables all DTrace probes compiled by this consumer. Call {@code + * enable()} with no argument to enable everything this consumer has + * compiled so far (most commonly a single program, the only one to + * be compiled). Call with one {@link Program} at a time if you + * need information about enabled probes specific to each program. + * + * @throws IllegalStateException if called before compiling at least + * one program, or if any compiled program is already enabled, or if + * {@link #go()} was already called, or if this consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #enable(Program program) + */ + public void enable() throws DTraceException; + + /** + * Enables DTrace probes matching the given program and attaches + * information about those probes to the given program. A probe + * matched multiple times (within the same D program or in multiple + * D programs) triggers the actions associated with each matching + * occurrence every time that probe fires. + * + * @param program A {@code Program} identifier returned by {@link + * #compile(String program, String[] macroArgs) compile(String + * program, ...)} or {@link #compile(File program, String[] + * macroArgs) compile(File program, ...)}: If the given program is + * {@code null}, the call has the same behavior as {@link #enable()} + * with no argument; if the given program is non-null, the call + * enables only those probes matching that program. In the latter + * case, the {@code Program} parameter is modified as a way of + * passing back information about the given program and its matching + * probes, including program stability. + * @throws IllegalArgumentException if the given program is non-null + * and not compiled by this {@code Consumer} + * @throws IllegalStateException if the given program is already + * enabled (or if the given program is {@code null} and <i>any</i> + * program is already enabled), or if {@link #go()} was already + * called, or if this consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #compile(String program, String[] macroArgs) + * @see #compile(File program, String[] macroArgs) + * @see #enable() + * @see #getProgramInfo(Program program) + */ + public void enable(Program program) throws DTraceException; + + /** + * Attaches information about matching DTrace probes to the given + * program. Attaches the same information to the given program as + * that attached by {@link #enable(Program program)} but without + * enabling the probes. + * + * @throws NullPointerException if the given program is {@code null} + * @throws IllegalArgumentException if the given program was not + * compiled by this {@code Consumer} + * @throws IllegalStateException if called after {@link #close()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #compile(String program, String[] macroArgs) + * @see #compile(File program, String[] macroArgs) + * @see #enable(Program program) + */ + public void getProgramInfo(Program program) throws DTraceException; + + /** + * Sets a boolean option. + * + * @throws NullPointerException if the given option is {@code null} + * @throws DTraceException if a value is expected for the given + * option, or if the option is otherwise invalid + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #close()}, or if + * the given option is a boolean compile-time option and {@link + * #go()} has already been called (see {@link Option} for a + * breakdown of runtime and compile-time options) + * @see #setOption(String option, String value) + * @see #unsetOption(String option) + */ + public void setOption(String option) throws DTraceException; + + /** + * Unsets a boolean option. + * + * @throws NullPointerException if the given option is {@code null} + * @throws DTraceException if the given option is not a boolean + * option, or if the option is otherwise invalid + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #close()}, or if + * the given option is a boolean compile-time option and {@link + * #go()} has already been called (see {@link Option} for a + * breakdown of runtime and compile-time options) + * @see #setOption(String option) + */ + public void unsetOption(String option) throws DTraceException; + + /** + * Sets the value of a DTrace option. If the given option affects + * compile-time behavior, it must be set before calling {@link + * #compile(String program, String[] macroArgs) compile(String + * program, ...)} or {@link #compile(File program, String[] + * macroArgs) compile(File program, ...)} in order to have an effect + * on compilation. Some runtime options including {@link + * Option#switchrate switchrate} and {@link Option#aggrate aggrate} + * are settable while a consumer is running; others must be set + * before calling {@link #go()}. See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlis?a=view> + * <b>Options and Tunables</b></a> chapter of the <i>Solaris Dynamic + * Guide</i> for information about specific options. + * + * @throws NullPointerException if the given option or value is + * {@code null} + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #close()}, or if + * the given option is a boolean compile-time option and {@code + * go()} has already been called (see {@link Option} for a breakdown + * of runtime and compile-time options) + * @throws DTraceException for any of the following: + * <ul><li>The option is invalid</li> + * <li>The value is invalid for the given option</li> + * <li>{@code go()} has been called to start this consumer, and the + * option is not settable on a running consumer (some runtime + * options, including {@link Option#switchrate switchrate} and + * {@link Option#aggrate aggrate} are settable while the consumer is + * running)</li></ul> + * + * @see #open(OpenFlag[] flags) + * @see #getOption(String option) + * @see Option + */ + public void setOption(String option, String value) throws DTraceException; + + /** + * Gets the value of a DTrace option. + * + * @throws NullPointerException if the given option is {@code null} + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #close()} + * @throws DTraceException if the given option is invalid + * @return the value of the given DTrace option: If the given option + * is a boolean option and is currently unset, the returned value is + * {@link Option#UNSET}. If the given option is a <i>size</i> + * option, the returned value is in bytes. If the given option is a + * <i>time</i> option, the returned value is in nanoseconds. If the + * given option is {@link Option#bufpolicy bufpolicy}, the returned + * value is one of {@link Option#BUFPOLICY_RING BUFPOLICY_RING}, + * {@link Option#BUFPOLICY_FILL BUFPOLICY_FILL}, or {@link + * Option#BUFPOLICY_SWITCH BUFPOLICY_SWITCH}. If the given option + * is {@link Option#bufresize bufresize}, the returned value is one + * of {@link Option#BUFRESIZE_AUTO BUFRESIZE_AUTO} or {@link + * Option#BUFRESIZE_MANUAL BUFRESIZE_MANUAL}. + * + * @see #setOption(String option) + * @see #unsetOption(String option) + * @see #setOption(String option, String value) + * @see Option + */ + public long getOption(String option) throws DTraceException; + + /** + * Reports whether or not this consumer is open. + * + * @return {@code true} if and only if {@link #open(OpenFlag[] + * flags) open()} has been called on this consumer and {@link + * #close()} has not + */ + public boolean isOpen(); + + /** + * Reports whether or not it is valid to call {@link #go()}. + * + * @return {@code true} if and only if at least one program has been + * compiled, all compiled programs have been enabled, {@code go()} + * has not already been called, and {@link #close()} has not been + * called + */ + public boolean isEnabled(); + + /** + * Reports whether or not this consumer is running. There may be a + * delay after calling {@link #go()} before this consumer actually + * starts running (listeners are notified by the {@link + * ConsumerListener#consumerStarted(ConsumerEvent e) + * consumerStarted()} method). + * + * @return {@code true} if this consumer is running, {@code false} + * otherwise + */ + public boolean isRunning(); + + /** + * Reports whether or not this consumer is closed. A closed + * consumer cannot be reopened. + * <p> + * Note that a closed consumer is different from a consumer that has + * not yet been opened. + * + * @return {@code true} if {@link #close()} has been called on this + * consumer, {@code false} otherwise + */ + public boolean isClosed(); + + /** + * Begin tracing and start a background thread to consume generated + * probe data. + * + * @throws IllegalStateException if not {@link #isEnabled()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #go(ExceptionHandler h) + * @see #open(OpenFlag[] flags) + * @see #compile(String program, String[] macroArgs) + * @see #compile(File program, String[] macroArgs) + * @see #enable() + * @see #stop() + * @see #close() + */ + public void go() throws DTraceException; + + /** + * Begin tracing and start a background thread to consume generated + * probe data. Handle any exception thrown in the consumer thread + * with the given handler. + * + * @throws IllegalStateException if not {@link #isEnabled()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #go() + */ + public void go(ExceptionHandler h) throws DTraceException; + + /** + * Stops all tracing, as well as the background thread started by + * {@link #go()} to consume generated probe data. A stopped + * consumer cannot be restarted. It is necessary to {@code close()} + * a stopped consumer to release the system resources it holds. + * <p> + * A consumer may stop on its own in response to the {@code exit()} + * action (see <b>{@code exit()}</b> in the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhm?a=view> + * <b>Special Actions</b></a> section of the <b>Actions and + * Subroutines</b> chapter of the <i>Solaris Dynamic Tracing + * Guide</i>). Similarly, a consumer stops automatically if it has + * at least one target process and all its target processes have + * completed (see {@link #createProcess(String command) + * createProcess()} and {@link #grabProcess(int pid) + * grabProcess()}). A consumer also stops automatically if it + * encounters an exception while consuming probe data. In these + * cases it is not necessary to call {@code stop()}. If a consumer + * stops for any reason (an explicit call to {@code stop()} or any + * of the reasons just given), listeners are notified through the + * {@link ConsumerListener#consumerStopped(ConsumerEvent e) + * consumerStopped()} method. + * <p> + * Note that a call to {@code stop()} blocks until the background + * thread started by {@code go()} actually stops. After {@code + * stop()} returns, a call to {@link #isRunning()} returns {@code + * false}. If a {@code DTraceException} is thrown while stopping + * this consumer, it is handled by the handler passed to {@link + * #go(ExceptionHandler h)} (or a default handler if none is + * specified). + * + * @throws IllegalStateException if called before {@link #go()} or + * if {@code stop()} was already called + * @see #go() + * @see #close() + */ + public void stop(); + + /** + * Closes an open consumer and releases the system resources it was + * holding. If the consumer is running, {@code close()} will {@link + * #stop()} it automatically. A closed consumer cannot be + * reopened. Closing a consumer that has not yet been opened makes + * it illegal to open that consumer afterwards. It is a no-op to + * call {@code close()} on a consumer that is already closed. + * + * @see #open(OpenFlag[] flags) + */ + public void close(); + + /** + * Adds a listener for probe data generated by this consumer. + */ + public void addConsumerListener(ConsumerListener l); + + /** + * Removes a listener for probe data generated by this consumer. + */ + public void removeConsumerListener(ConsumerListener l); + + /** + * Gets a snapshot of all aggregations except those that have + * already been captured in a {@link PrintaRecord}. Does not clear + * any aggregation. + * <p> + * Provides a programmatic alternative to the {@code printa(}) + * action (see <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhv?a=view> + * <b>{@code printa()}</b></a> in the <b>Output Formatting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>). + * + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #getAggregate(Set includedAggregationNames, Set + * clearedAggregationNames) + */ + public Aggregate getAggregate() throws DTraceException; + + /** + * Gets a snapshot of all the specified aggregations except those + * that have already been captured in a {@link PrintaRecord}. Does + * not clear any aggregation. + * <p> + * Provides a programmatic alternative to the {@code printa(}) + * action (see <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhv?a=view> + * <b>{@code printa()}</b></a> in the <b>Output Formatting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>). + * + * @param includedAggregationNames if {@code null}, all available + * aggregations are included; if non-null, only those aggregations + * specifically named by the given set are included + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #getAggregate(Set includedAggregationNames, Set + * clearedAggregationNames) + */ + public Aggregate getAggregate(Set <String> includedAggregationNames) + throws DTraceException; + + /** + * Gets a snapshot of all the specified aggregations except those + * that have already been captured in a {@link PrintaRecord}, with + * the side effect of atomically clearing any subset of those + * aggregations. Clearing an aggregation resets all of its values + * to zero without removing any of its keys. Leave aggregations + * uncleared to get running totals, otherwise specify that an + * aggregation be cleared to get values per time interval. Note + * that once an aggregation is captured in a {@code PrintaRecord} + * (as a result of the {@code printa()} action), it is no longer + * available to the {@code getAggregate()} method. + * <p> + * Provides a programmatic alternative to the {@code printa(}) (see + * <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhv?a=view> + * <b>{@code printa()}</b></a> in the <b>Output Formatting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>) and {@code + * clear()} actions. + * + * @param includedAggregationNames if {@code null}, all available + * aggregations are included; if non-null, only those aggregations + * specifically named by the given set are included + * @param clearedAggregationNames if {@code null}, all available + * aggregations are cleared; if non-null, only those aggregations + * specifically named by the given set are cleared + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + */ + public Aggregate getAggregate(Set <String> includedAggregationNames, + Set <String> clearedAggregationNames) throws DTraceException; + + /** + * Creates a process by executing the given command on the system + * and returns the created process ID. The created process is + * suspended until calling {@link #go()} so that the process waits + * to do anything until this consumer has started tracing (allowing + * a process to be traced from the very beginning of its execution). + * The macro variable {@code $target} in a D program will be + * replaced by the process ID of the created process. When the + * created process exits, this consumer notifies listeners through + * the {@link ConsumerListener#processStateChanged(ProcessEvent e) + * processStateChanged()} method. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlir?a=view> + * <b>Target Process ID</b></a> section of the <b>Scripting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * + * @param command a string whose first token is assumed to be the + * name of the command and whose subsequent tokens are the arguments + * to that command. + * @return ID of the created process (pid) + * @throws NullPointerException if the given command is {@code nul}l + * @throws IllegalArgumentException if the given command is empty or + * contains only whitespace + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if the process cannot be created + * @see #grabProcess(int pid) + */ + public int createProcess(String command) throws DTraceException; + + /** + * Grabs the specified process and caches its symbol tables. The + * macro variable {@code $target} in a D program will be replaced by + * the process ID of the grabbed process. When the specified + * process exits, this consumer notifies listeners through the + * {@link ConsumerListener#processStateChanged(ProcessEvent e) + * processStateChanged()} method. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlir?a=view> + * <b>Target Process ID</b></a> section of the <b>Scripting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * + * @param pid process ID of the process to be grabbed + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if the process cannot be grabbed + * @see #createProcess(String command) + */ + public void grabProcess(int pid) throws DTraceException; + + /** + * Lists probes that match the given probe description. See {@link + * ProbeDescription} for information about pattern syntax and + * wildcarding. + * + * @param filter use {@link ProbeDescription#EMPTY} to get all + * probes, otherwise get only those probes that match the given + * filter + * @return a non-null list of probe descriptions + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #open(OpenFlag[] flags) + * @see #close() + * @see #listProbeDetail(ProbeDescription filter) + * @see #listProgramProbes(Program program) + */ + public List <ProbeDescription> listProbes(ProbeDescription filter) + throws DTraceException; + + /** + * Lists probes that match the given probe description and includes + * detail such as stability information about each listed probe. + * + * @param filter use {@link ProbeDescription#EMPTY} to get all + * probes, otherwise get only those probes that match the given + * filter + * @return a non-null list of probe detail + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #listProbes(ProbeDescription filter) + * @see #listProgramProbeDetail(Program program) + */ + public List <Probe> listProbeDetail(ProbeDescription filter) + throws DTraceException; + + /** + * Lists probes that match the given compiled program. A probe + * matches a D program if that program contains any matching probe + * description. + * + * @param program a {@code Program} identifier returned by {@link + * #compile(String program, String[] macroArgs) compile(String + * program, ...)} or {@link #compile(File program, String[] + * macroArgs) compile(File program, ...)} + * @return a non-null list of probe descriptions + * @throws NullPointerException if the given program identifier is + * {@code null} + * @throws IllegalArgumentException if the specified program was not + * compiled by this consumer + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #listProbes(ProbeDescription filter) + */ + public List <ProbeDescription> listProgramProbes(Program program) + throws DTraceException; + + /** + * Lists probes that match the given compiled program and includes + * detail such as stability information about each listed probe. + * + * @param program a {@code Program} identifier returned by {@link + * #compile(String program, String[] macroArgs) compile(String + * program, ...)} or {@link #compile(File program, String[] + * macroArgs) compile(File program, ...)} + * @return a non-null list of probe detail + * @throws NullPointerException if the given program identifier is + * {@code null} + * @throws IllegalArgumentException if the specified program was not + * compiled by this consumer + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #listProgramProbes(Program program) + * @see #listProbeDetail(ProbeDescription filter) + */ + public List <Probe> listProgramProbeDetail(Program program) + throws DTraceException; + + /** + * Gets the kernel function name for the given 32-bit kernel + * address. + * + * @param address 32-bit kernel function address, such as the value + * of a {@link Tuple} member in an {@link AggregationRecord} to be + * converted for display + * @return the result of kernel function lookup as one of the + * following:<ul><li>{@code module`function}</li> + * <li>{@code module`function+offset}</li> + * <li>{@code module`address}</li> + * <li>{@code address}</li></ul> where {@code module} and {@code + * function} are names, and {@code offset} and {@code address} are + * integers in hexadecimal format preceded by "{@code 0x}". {@code + * offset} is the number of bytes from the beginning of the + * function, included when non-zero. {@code address} is simply the + * hex form of the input paramater, returned when function lookup + * fails. The exact details of this format are subject to change. + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @see #lookupKernelFunction(long address) + */ + public String lookupKernelFunction(int address); + + /** + * Gets the kernel function name for the given 64-bit kernel + * address. + * + * @param address 64-bit kernel function address + * @return kernel function name + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @see #lookupKernelFunction(int address) + */ + public String lookupKernelFunction(long address); + + /** + * Gets the user function name for the given 32-bit user address and + * process ID. + * + * @param pid ID of the user process containing the addressed + * function + * @param address 32-bit user function address, such as the value + * of a {@link Tuple} member in an {@link AggregationRecord} to be + * converted for display. + * @return result of user function lookup as one of the + * following:<ul> <li>{@code module`function}</li> + * <li>{@code module`function+offset}</li> + * <li>{@code module`address}</li> + * <li>{@code address}</li></ul> where {@code module} and {@code + * function} are names, and {@code offset} and {@code address} are + * integers in hexadecimal format preceded by "{@code 0x}". {@code + * offset} is the number of bytes from the beginning of the + * function, included when non-zero. {@code address} is simply the + * hex form of the input paramater, returned when function lookup + * fails. The exact details of this format are subject to change. + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @see #lookupUserFunction(int pid, long address) + */ + public String lookupUserFunction(int pid, int address); + + /** + * Gets the user function name for the given 64-bit user address and + * process ID. + * + * @param pid ID of the user process containing the addressed + * function + * @param address 64-bit user function address + * @return user function name + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @see #lookupUserFunction(int pid, int address) + */ + public String lookupUserFunction(int pid, long address); + + /** + * Gets the version of the native DTrace library. + * + * @return version string generated by the native DTrace library + * (same as the output of {@code dtrace(1M)} with the {@code -V} + * option) + */ + public String getVersion(); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerAdapter.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerAdapter.java new file mode 100644 index 0000000000..f12cba5541 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerAdapter.java @@ -0,0 +1,79 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * An abstract adapter class for getting events from a {@link Consumer}. + * The methods in this class are empty except for a few that implement + * the default behavior of terminating a consumer by throwing an + * exception. This class exists as a convenience for implementing + * consumer listeners. + * + * @see Consumer#addConsumerListener(ConsumerListener l) + * + * @author Tom Erickson + */ +public abstract class ConsumerAdapter implements ConsumerListener { + /** Empty method */ + public void dataReceived(DataEvent e) throws ConsumerException {} + + /** + * Terminates a running {@link Consumer} by throwing an exception. + * + * @throws ConsumerException + */ + public void + dataDropped(DropEvent e) throws ConsumerException + { + Drop drop = e.getDrop(); + throw new ConsumerException(drop.getDefaultMessage(), drop); + } + + /** + * Terminates a running {@link Consumer} by throwing an exception. + * + * @throws ConsumerException + */ + public void + errorEncountered(ErrorEvent e) throws ConsumerException + { + Error error = e.getError(); + throw new ConsumerException(error.getDefaultMessage(), error); + } + + /** Empty method */ + public void processStateChanged(ProcessEvent e) throws ConsumerException {} + /** Empty method */ + public void consumerStarted(ConsumerEvent e) {} + /** Empty method */ + public void consumerStopped(ConsumerEvent e) {} + /** Empty method */ + public void intervalBegan(ConsumerEvent e) {} + /** Empty method */ + public void intervalEnded(ConsumerEvent e) {} +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerEvent.java new file mode 100644 index 0000000000..555e30caeb --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerEvent.java @@ -0,0 +1,71 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.EventObject; + +/** + * An event indicating a state change in a DTrace {@link Consumer}. + * + * @see ConsumerListener + * + * @author Tom Erickson + */ +public class ConsumerEvent extends EventObject { + static final long serialVersionUID = 1659441401142401810L; + + /** @serial */ + private long timestamp; + + /** + * Creates a consumer event with the given source {@link Consumer} + * and nanosecond timestamp. + * + * @param source the {@link Consumer} that is the source of the + * event + * @param timeNanos nanosecond timestamp of this event + */ + public + ConsumerEvent(Object source, long timeNanos) + { + super(source); + timestamp = timeNanos; + } + + /** + * Gets the nanosecond timestamp of this event. + * + * @return nanosecond timestamp of the event on the system where the + * consumer opened a native DTrace library handle + */ + public long + getTimestamp() + { + return timestamp; + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerException.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerException.java new file mode 100644 index 0000000000..cd6437c169 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerException.java @@ -0,0 +1,107 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * Exception thrown by a {@link ConsumerListener} to terminate a running + * {@link Consumer}. + * + * @author Tom Erickson + */ +public class ConsumerException extends Exception { + static final long serialVersionUID = -2125855097525822644L; + + /** @serial */ + private Object notificationObject; + + /** + * Creates a consumer exception with the given message. + * + * @see #ConsumerException(String message, Object + * dtraceNotificationObject) + */ + public + ConsumerException(String message) + { + super(message); + } + + /** + * Creates an exception thrown by a {@link ConsumerListener} + * implementation to terminate a running {@link Consumer}, usually + * in response to a drop or an error reported by the native DTrace + * library. Optionally includes the object reported by the native + * DTrace library so it can be used by an {@link ExceptionHandler} + * to display details about why the consumer terminated. + * + * @param message default display message explaining why the + * consumer was terminated. + * @param notification usually the object passed to a {@link + * ConsumerListener} from DTrace that prompted this exception. The + * notification could be any of the following: <ul> <li>a {@link + * Drop} passed to {@link ConsumerListener#dataDropped(DropEvent e) + * dataDropped()}</li> <li>an {@link Error} passed to {@link + * ConsumerListener#errorEncountered(ErrorEvent e) + * errorEncountered()}</li> <li>a {@link ProcessState} passed to + * {@link ConsumerListener#processStateChanged(ProcessEvent e) + * processStateChanged()}</li> </ul> or it could be a user-defined + * object that describes anything unexpected in {@link + * ConsumerListener#dataReceived(DataEvent e) dataReceived()} or + * that defines an arbitrary error threshold. An {@link + * ExceptionHandler} should be defined to handle any type of + * notification object set by user code. May be {@code null}. + * @see Consumer#go(ExceptionHandler h) + */ + public + ConsumerException(String message, Object notification) + { + super(message); + notificationObject = notification; + } + + /** + * Gets the optional object from the {@link ConsumerListener} that + * communicates to the {@link ExceptionHandler} why the listener + * threw this exception. Usually this is the object from DTrace + * (such as an {@link org.opensolaris.os.dtrace.Error Error}) that + * prompted the exception, simply forwarded to the exception + * handler. + * + * @return an object that communicates to the {@link + * ExceptionHandler} why the {@link ConsumerListener} threw this + * exception, may be {@code null} + * @see Consumer#go(ExceptionHandler h) + * @see #ConsumerException(String message, + * Object dtraceNotificationObject) + */ + public Object + getNotificationObject() + { + return notificationObject; + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerListener.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerListener.java new file mode 100644 index 0000000000..d207705678 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerListener.java @@ -0,0 +1,127 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.EventListener; + +/** + * Listener for data generated by a single DTrace {@link Consumer}. + * + * @author Tom Erickson + */ +public interface ConsumerListener extends EventListener { + /** + * Called whenever a DTrace probe fires (that is, once for each + * instance of {@link ProbeData} generated by DTrace). Identifies + * the probe and provides data generated by the probe's actions. To + * terminate the consumer in the event of unexpected data, throw a + * {@link ConsumerException} from this method. + * + * @throws ConsumerException if the implementation should terminate + * the running consumer + */ + public void dataReceived(DataEvent e) throws ConsumerException; + + /** + * Called when traced data is dropped because of inadequate buffer + * space. To terminate the consumer in the event of a drop, throw + * a {@link ConsumerException} from this method. + * + * @throws ConsumerException if the implementation should terminate + * the running consumer + */ + public void dataDropped(DropEvent e) throws ConsumerException; + + /** + * Called when an error is encountered in the native DTrace library + * while tracing probe data. To terminate the consumer, throw a + * {@link ConsumerException} from this method. + * + * @throws ConsumerException if the implementation should terminate + * the running consumer + */ + public void errorEncountered(ErrorEvent e) throws ConsumerException; + + /** + * Called when the state of a target process changes. To terminate + * the consumer in the event of unexpected process state, throw a + * {@link ConsumerException} from this method. + * + * @throws ConsumerException if the implementation should terminate + * the running consumer + * @see Consumer#createProcess(String command) + * @see Consumer#grabProcess(int pid) + */ + public void processStateChanged(ProcessEvent e) throws ConsumerException; + + /** + * Called once when the source {@link Consumer} is successfully + * started in response to {@link Consumer#go()}. + * + * @see #consumerStopped(ConsumerEvent e) + */ + public void consumerStarted(ConsumerEvent e); + + /** + * Called once when the source {@link Consumer} is stopped, + * indicating that this listener should expect no further events. + * Called only if there was a prior call to {@link + * #consumerStarted(ConsumerEvent e) consumerStarted()}, that is, + * only if the consumer was successfully started by a call to {@link + * Consumer#go()}. Guaranteed to be called whether the consumer was + * stopped by request (via {@link Consumer#stop()}), terminated + * normally as a result of the DTrace {@code exit()} action or the + * completion of all target processes, or terminated abnormally + * because of an exception. It is necessary to call {@link + * Consumer#close()} to release any system resources still held by + * the stopped consumer. + * + * @see #consumerStarted(ConsumerEvent e) + */ + public void consumerStopped(ConsumerEvent e); + + /** + * Called when the source {@link Consumer} wakes up to process its + * buffer of traced probe data. + * + * @see #intervalEnded(ConsumerEvent e) + */ + public void intervalBegan(ConsumerEvent e); + + /** + * Called when the source {@link Consumer} finishes processing its + * buffer of traced probe data and is about to sleep until the next + * interval. The rate of consumption may be controlled with the + * {@link Option#switchrate switchrate} and {@link Option#aggrate + * aggrate} options (see {@link Consumer#setOption(String option, + * String value)}). + * + * @see #intervalBegan(ConsumerEvent e) + */ + public void intervalEnded(ConsumerEvent e); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/CountValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/CountValue.java new file mode 100644 index 0000000000..d7c6c39924 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/CountValue.java @@ -0,0 +1,106 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code count()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class CountValue extends AbstractAggregationValue { + static final long serialVersionUID = 5948954123445410783L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(CountValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code count()} action. + * Supports XML persistence. + * + * @param v aggregated value count + * @throws IllegalArgumentException if the given count is negative + */ + public + CountValue(long v) + { + super(v); + validate(); + } + + private void + validate() + { + long count = super.getValue().longValue(); + if (count < 0) { + throw new IllegalArgumentException("count is negative"); + } + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the number of aggregated values. + * + * @return the number of aggregated values + */ + public Long + getValue() + { + return (Long)super.getValue(); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DTraceException.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DTraceException.java new file mode 100644 index 0000000000..9ed5366fd7 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DTraceException.java @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * Exception in the native DTrace library. + * + * @author Tom Erickson + */ +public class DTraceException extends Exception { + static final long serialVersionUID = -4048327080469337476L; + + /** + * Creates a DTrace exception with the given message. + */ + public + DTraceException(String message) + { + super(message); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DataEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DataEvent.java new file mode 100644 index 0000000000..e863d127c1 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DataEvent.java @@ -0,0 +1,117 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.EventObject; + +/** + * An event used to pass probe data generated by a DTrace {@link + * Consumer} to interested listeners. + * + * @see ConsumerListener#dataReceived(DataEvent e) + * + * @author Tom Erickson + */ +public class DataEvent extends EventObject { + static final long serialVersionUID = 3068774547474769821L; + + /** @serial */ + private ProbeData probeData; + + /** + * Creates a {@link ConsumerListener#dataReceived(DataEvent e) + * dataReceived()} event that conveys data generated by DTrace from + * a single probe firing. + * + * @throws NullPointerException if the given probe data is {@code + * null} + */ + public + DataEvent(Object source, ProbeData generatedData) + { + super(source); + probeData = generatedData; + validate(); + } + + private void + validate() + { + if (probeData == null) { + throw new NullPointerException("probe data is null"); + } + } + + /** + * Gets the data generated by DTrace from a single probe firing. + * + * @return non-null probe data generated by DTrace from a single + * probe firing + */ + public ProbeData + getProbeData() + { + return probeData; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this event useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(DataEvent.class.getName()); + buf.append("[source = "); + buf.append(getSource()); + buf.append(", probeData = "); + buf.append(probeData); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Distribution.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Distribution.java new file mode 100644 index 0000000000..7557047aa5 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Distribution.java @@ -0,0 +1,620 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A frequency distribution aggregated by the DTrace {@code quantize()} + * or {@code lquantize()} action. Each aggregated value falls into a + * range known as a bucket and counts toward the frequency of that + * bucket. Bucket ranges are consecutive, with the maximum of one + * bucket's range always one less than the minimum of the next bucket's + * range. By convention each bucket is identified by the minimum of its + * range. + * + * @author Tom Erickson + */ +public abstract class Distribution implements AggregationValue, + Iterable <Distribution.Bucket>, Serializable +{ + static final long serialVersionUID = 1186243118882654932L; + + /** @serial */ + private List <Bucket> buckets; + private transient double total; + private transient boolean initialized; + + /** + * Package level access, called by subclasses LinearDistribution and + * LogDistribution, but not available outside the API. + * + * @param base the lower bound of this distribution, or zero if not + * applicable + * @param constant the constant term of the distribution function + * used to calculate the lower bound of any bucket given the lower + * bound of the previous bucket, for example the step in a linear + * distribution or the log base in a logarithmic distribution. + * @param frequencies for each bucket, the number of aggregated + * values falling into that bucket's range; each element must be a + * positive integer + * @throws NullPointerException if frequencies is null + * @throws IllegalArgumentException if any element of frequencies + * does not have the expected range as defined by checkBucketRange() + */ + Distribution(long base, long constant, long[] frequencies) + { + total = 0; + long frequency; + for (int i = 0, len = frequencies.length; i < len; ++i) { + frequency = frequencies[i]; + total += frequency; + } + + buckets = createBuckets(base, constant, frequencies); + initialized = true; + } + + /** + * Supports XML persistence of subclasses. Sub-class implementation + * must call initialize() after setting any state specific to that + * subclass for determining bucket ranges. + * + * @throws NullPointerException if frequencies is null + * @throws IllegalArgumentException if any element of frequencies + * does not have the expected range as defined by checkBucketRange() + */ + Distribution(List <Bucket> frequencies) + { + // defensively copy frequencies list + int len = frequencies.size(); + // don't need gratuitous capacity % added by constructor that + // takes a Collection argument; list will not be modified + buckets = new ArrayList <Bucket> (len); + buckets.addAll(frequencies); + } + + final void + initialize() + { + // Called by constructor and readObject() (deserialization). + // 1. Check class invariants, throw exception if deserialized + // state inconsistent with a Distribution that can result + // from the public constructor. + // 2. Compute total (transient property derived from buckets) + total = 0; + long frequency; + Bucket bucket; + int len = buckets.size(); + for (int i = 0; i < len; ++i) { + bucket = buckets.get(i); + frequency = bucket.getFrequency(); + // relies on package-private getBucketRange() + // implementation + checkBucketRange(i, len, bucket); + total += frequency; + } + initialized = true; + } + + // Must be called by public instance methods (since the AbstractList + // methods all depend on get() and size(), it is sufficient to call + // checkInit() only in those inherited methods). + private void + checkInit() + { + if (!initialized) { + throw new IllegalStateException("Uninitialized"); + } + } + + /** + * Gets a two element array: the first elelemt is the range minimum + * (inclusive), the second element is the range maximum (inclusive). + * Implemented by subclasses LinearDistribution and LogDistribution + * to define bucket ranges for this distribution and not available + * outside the API. Used by the private general purpose constructor + * called from native code. Implementation must not use + * subclass-specific state, since subclass state has not yet been + * allocated. + * + * @see #Distribution(long base, long constant, long[] frequencies) + */ + abstract long[] + getBucketRange(int i, int len, long base, long constant); + + /** + * Used by public constructors and deserialization only after + * state specific to the subclass is available to the method. + */ + abstract long[] + getBucketRange(int i, int len); + + private List <Distribution.Bucket> + createBuckets(long base, long constant, long[] frequencies) + { + int len = frequencies.length; + Bucket bucket; + List <Bucket> buckets = new ArrayList <Bucket> (len); + long min; // current bucket + long max; // next bucket minus one + long[] range; // two element array: { min, max } + + for (int i = 0; i < len; ++i) { + range = getBucketRange(i, len, base, constant); + min = range[0]; + max = range[1]; + bucket = new Distribution.Bucket(min, max, frequencies[i]); + buckets.add(bucket); + } + + return buckets; + } + + /** + * Validates that bucket has the expected range for the given bucket + * index. Uses {@code base} and {@code constant} constructor args + * to check invariants specific to each subclass, since state + * derived from these args in a subclass is not yet available in the + * superclass constructor. + * + * @throws IllegalArgumentException if bucket does not have the + * expected range for the given bucket index {@code i} + */ + private void + checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket, + long base, long constant) + { + long[] range = getBucketRange(i, bucketCount, base, constant); + checkBucketRange(i, bucket, range); + } + + private void + checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket) + { + long[] range = getBucketRange(i, bucketCount); + checkBucketRange(i, bucket, range); + } + + private void + checkBucketRange(int i, Distribution.Bucket bucket, long[] range) + { + long min = range[0]; + long max = range[1]; + + if (bucket.getMin() != min) { + throw new IllegalArgumentException("bucket min " + + bucket.getMin() + " at index " + i + ", expected " + min); + } + if (bucket.getMax() != max) { + throw new IllegalArgumentException("bucket max " + + bucket.getMax() + " at index " + i + ", expected " + max); + } + } + + /** + * Gets a modifiable list of this distribution's buckets ordered by + * bucket range. Modifying the returned list has no effect on this + * distribution. Supports XML persistence. + * + * @return a modifiable list of this distribution's buckets ordered + * by bucket range + */ + public List <Bucket> + getBuckets() + { + checkInit(); + return new ArrayList <Bucket> (buckets); + } + + /** + * Gets a read-only {@code List} view of this ditribution. + * + * @return a read-only {@code List} view of this ditribution + */ + public List <Bucket> + asList() + { + checkInit(); + return Collections.unmodifiableList(buckets); + } + + /** + * Gets the number of buckets in this distribution. + * + * @return non-negative bucket count + */ + public int + size() + { + checkInit(); + return buckets.size(); + } + + /** + * Gets the bucket at the given distribution index (starting at + * zero). + * + * @return non-null distribution bucket at the given zero-based + * index + */ + public Bucket + get(int index) + { + checkInit(); + return buckets.get(index); + } + + /** + * Gets an iterator over the buckets of this distribution. + * + * @return an iterator over the buckets of this distribution + */ + public Iterator<Bucket> + iterator() + { + checkInit(); + return buckets.iterator(); + } + + /** + * Compares the specified object with this {@code Distribution} + * instance for equality. Defines equality as having the same + * buckets with the same values. + * + * @return {@code true} if and only if the specified object is of + * type {@code Distribution} and both instances have the same size + * and equal buckets at corresponding distribution indexes + */ + public boolean + equals(Object o) + { + checkInit(); + if (o instanceof Distribution) { + Distribution d = (Distribution)o; + return buckets.equals(d.buckets); + } + return false; + } + + /** + * Overridden to ensure that equals instances have equal hash codes. + */ + public int + hashCode() + { + checkInit(); + return buckets.hashCode(); + } + + /** + * Gets the total frequency across all buckets. + * + * @return sum of the frequency of all buckets in this distribution + */ + public double + getTotal() + { + checkInit(); + return total; + } + + /** + * Gets the numeric value of this distribution used to compare + * distributions by overall magnitude, defined as the sum total of + * each bucket's frequency times the minimum of its range. + */ + public abstract Number getValue(); + + /** + * Called by native code + */ + private void + normalizeBuckets(long normal) + { + for (Bucket b : buckets) { + b.frequency /= normal; + } + } + + /** + * A range inclusive at both endpoints and a count of aggregated + * values that fall in that range. Buckets in a {@link + * Distribution} are consecutive, such that the max of one bucket is + * always one less than the min of the next bucket (or {@link + * Long#MAX_VALUE} if it is the last bucket in the {@code + * Distribution}). + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + */ + public static final class Bucket implements Serializable { + static final long serialVersionUID = 4863264115375406295L; + + /** @serial */ + private final long min; + /** @serial */ + private final long max; + /** @serial */ + private long frequency; // non-final so native code can normalize + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Bucket.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"min", "max", "frequency"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + (oldInstance.getClass() == + newInstance.getClass())); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a distribution bucket with the given range and + * frequency. + * + * @param rangeMinimumInclusive sets the lower bound (inclusive) + * returned by {@link #getMin()} + * @param rangeMaximumInclusive sets the upper bound (inclusive) + * returned by {@link #getMax()} + * @param valuesInRange sets the value frequency in this + * bucket's range returned by {@link #getFrequency()} + * @throws IllegalArgumentException if {@code + * rangeMaximumInclusive} is less than {@code + * rangeMinimumInclusive} + */ + public + Bucket(long rangeMinimumInclusive, long rangeMaximumInclusive, + long valuesInRange) + { + if (rangeMaximumInclusive < rangeMinimumInclusive) { + throw new IllegalArgumentException("upper bound " + + rangeMaximumInclusive + " is less than lower bound " + + rangeMinimumInclusive); + } + + min = rangeMinimumInclusive; + max = rangeMaximumInclusive; + frequency = valuesInRange; + } + + /** + * Gets the lower bound of this bucket's range (inclusive). + */ + public long + getMin() + { + return min; + } + + /** + * Gets the upper bound of this bucket's range (inclusive). + */ + public long + getMax() + { + return max; + } + + /** + * Gets the number of values in a {@link Distribution} that fall + * into the range defined by this bucket. + */ + public long + getFrequency() + { + return frequency; + } + + /** + * Compares the specified object with this distribution bucket + * for equality. Defines equality of two distribution buckets + * as having the same range and the same frequency. + * + * @return false if the specified object is not a {@code + * Distribution.Bucket} + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof Bucket) { + Bucket b = (Bucket)o; + return ((min == b.min) && + (max == b.max) && + (frequency == b.frequency)); + } + return false; + } + + /** + * Overridden to ensure that equal buckets have equal hashcodes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + ((int)(min ^ (min >>> 32))); + hash = (37 * hash) + ((int)(max ^ (max >>> 32))); + hash = (37 * hash) + ((int)(frequency ^ (frequency >>> 32))); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants (as constructor does) + if (max < min) { + throw new InvalidObjectException("upper bound " + + max + " is less than lower bound " + min); + } + } + + /** + * Gets a string representation of this distribution bucket + * useful for logging and not intended for display. The exact + * details of the representation are unspecified and subject to + * change, but the following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Bucket.class.getName()); + buf.append("[min = "); + buf.append(min); + buf.append(", max = "); + buf.append(max); + buf.append(", frequency = "); + buf.append(frequency); + buf.append(']'); + return buf.toString(); + } + } + + /** + * Gets a list of buckets of interest by excluding empty buckets at + * both ends of the distribution. Leaves one empty bucket on each + * end if possible to convey the distribution context more + * effectively in a display. + * + * @return an unmodifiable sublist that includes the range starting + * from the first bucket with a non-zero frequency and ending with + * the last bucket with a non-zero frequency, plus one empty bucket + * before and after that range if possible + */ + public List <Bucket> + getDisplayRange() + { + checkInit(); + int min = -1; + int max = -1; + int len = size(); + Bucket b; + // Get first non-empty bucket + for (int i = 0; i < len; ++i) { + b = buckets.get(i); + if (b.getFrequency() > 0L) { + min = i; + break; + } + } + if (min < 0) { + return Collections. <Bucket> emptyList(); + } + // Get last non-empty bucket + for (int i = (len - 1); i >= 0; --i) { + b = buckets.get(i); + if (b.getFrequency() > 0L) { + max = i; + break; + } + } + // If possible, pad non-empty range with one empty bucket at + // each end. + if (min > 0) { + --min; + } + if (max < (len - 1)) { + ++max; + } + + // subList inclusive at low index, exclusive at high index + return Collections. <Bucket> + unmodifiableList(buckets).subList(min, max + 1); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + + // Defensively copy buckets _before_ validating. Subclass + // validates by calling initialize() after reading any state + // specific to that subclass needed for validation. + int len = buckets.size(); + ArrayList <Bucket> copy = new ArrayList <Bucket> (len); + copy.addAll(buckets); + buckets = copy; + } + + /** + * Gets a string representation of this {@code Distribution} useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + checkInit(); + StringBuffer buf = new StringBuffer(); + buf.append(Distribution.class.getName()); + buf.append("[buckets = "); + List <Bucket> list = getDisplayRange(); + if (list.isEmpty()) { + buf.append("<empty>"); + } else { + buf.append(Arrays.toString(getDisplayRange().toArray())); + } + buf.append(", total = "); + buf.append(getTotal()); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Drop.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Drop.java new file mode 100644 index 0000000000..a0ade2a93e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Drop.java @@ -0,0 +1,274 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.Serializable; +import java.io.*; +import java.beans.*; + +/** + * Detail about one or more records dropped by DTrace (not reported to + * {@link ConsumerListener#dataReceived(DataEvent e) + * ConsumerListener.dataReceived()}) due to inadequte buffer space. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see ConsumerListener#dataDropped(DropEvent e) + * + * @author Tom Erickson + */ +public final class Drop implements Serializable { + static final long serialVersionUID = 26653827678657381L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Drop.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"CPU", "kind", "count", "total", + "defaultMessage"}) + { + protected Expression + instantiate(Object oldInstance, Encoder out) + { + Drop drop = (Drop)oldInstance; + return new Expression(oldInstance, oldInstance.getClass(), + "new", new Object[] { drop.getCPU(), + drop.getKind().name(), drop.getCount(), + drop.getTotal(), drop.getDefaultMessage() }); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** + * Indicates what kind of buffer space experienced the data drop + * (such as principal buffer or aggregation buffer) and possibly a + * reason. + */ + public enum Kind { + /** Drop to principal buffer */ + PRINCIPAL("Principal buffer"), + /** Drop to aggregation buffer */ + AGGREGATION("Aggregation"), + /** Dynamic drop */ + DYNAMIC("Dynamic"), + /** Dynamic drop due to rinsing */ + DYNRINSE("Dynamic (rinse)"), + /** Dynamic drop due to dirtiness */ + DYNDIRTY("Dynamic (dirty)"), + /** Speculative drop */ + SPEC("Speculation"), + /** Speculative drop due to business */ + SPECBUSY("Speculation (busy)"), + /** Speculative drop due to unavailability */ + SPECUNAVAIL("Speculation (unavailable)"), + /** Stack string table overflow */ + STKSTROVERFLOW("Stack string table overflow"), + /** Error in ERROR probe */ + DBLERROR("error in ERROR probe"), + /** Unrecognized value from native DTrace library */ + UNKNOWN("Unknown"); + + private String s; + + private + Kind(String displayString) + { + s = displayString; + } + + /** + * Overridden to get the default display value. To + * internationalize the display value, use {@link Enum#name()} + * instead as an I18N lookup key. + */ + public String + toString() + { + return s; + } + } + + /** @serial */ + private final int cpu; + /** @serial */ + private final Kind kind; + /** @serial */ + private final long count; + /** @serial */ + private final long total; + /** @serial */ + private final String defaultMessage; + + /** + * Creates a {@code Drop} instance with the given CPU, drop kind, + * drop counts, and default message. Supports XML persistence. + * + * @param dropCPU cpu where drops occurred + * @param dropKindName name of enumeration value indicating the kind + * of buffer space where the drop occurred and possibly a reason + * @param dropCount number of drops + * @param totalDrops total number of drops since the source {@link + * Consumer} started running + * @param defaultDropMessage drop message provided by DTrace + * @throws IllegalArgumentException if there is no {@code Drop.Kind} + * value with the given name or if {@code dropCount} or {@code + * totalDrops} is negative + * @throws NullPointerException if the given {@code Drop.Kind} name + * or default message is {@code null} + */ + public + Drop(int dropCPU, String dropKindName, long dropCount, long totalDrops, + String defaultDropMessage) + { + cpu = dropCPU; + kind = Enum.valueOf(Kind.class, dropKindName); + count = dropCount; + total = totalDrops; + defaultMessage = defaultDropMessage; + validate(); + } + + private void + validate() + { + if (count < 0) { + throw new IllegalArgumentException("count is negative"); + } + if (total < 0) { + throw new IllegalArgumentException("total is negative"); + } + if (defaultMessage == null) { + throw new NullPointerException("default message is null"); + } + } + + /** + * Gets the CPU where the drops occurred. + * + * @return non-negative CPU ID, or a negative number if the CPU is + * unknown + */ + public int + getCPU() + { + return cpu; + } + + /** + * Gets the kind of drop for all drops included in {@link + * #getCount()}. + * + * @return non-null drop kind + */ + public Kind + getKind() + { + return kind; + } + + /** + * Gets the number of drops reported by this {@code Drop} instance. + * + * @return non-negative drop count + */ + public long + getCount() + { + return count; + } + + /** + * Gets the total number of drops since the source {@link Consumer} + * started running. + * + * @return non-negative drop total since tracing started + */ + public long + getTotal() + { + return total; + } + + /** + * Gets the message provided by DTrace. + * + * @return non-null message provided by DTrace + */ + public String + getDefaultMessage() + { + return defaultMessage; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this drop instance, not intended + * for display. The exact details of the representation are + * unspecified and subject to change, but the following format may + * be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Drop.class.getName()); + buf.append("[cpu = "); + buf.append(cpu); + buf.append(", kind = "); + buf.append(kind); + buf.append(", count = "); + buf.append(count); + buf.append(", total = "); + buf.append(total); + buf.append(", defaultMessage = "); + buf.append(defaultMessage); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DropEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DropEvent.java new file mode 100644 index 0000000000..3a621d26ef --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DropEvent.java @@ -0,0 +1,114 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.EventObject; + +/** + * Notification that DTrace has dropped data due to inadequate buffer + * space. + * + * @see ConsumerListener#dataDropped(DropEvent e) + * + * @author Tom Erickson + */ +public class DropEvent extends EventObject { + static final long serialVersionUID = 5454623535426339134L; + + /** @serial */ + private Drop drop; + + /** + * Creates a {@link ConsumerListener#dataDropped(DropEvent e) + * dataDropped()} event that reports dropped data. + * + * @throws NullPointerException if the given drop is {@code null} + */ + public + DropEvent(Object source, Drop dataDrop) + { + super(source); + drop = dataDrop; + validate(); + } + + private void + validate() + { + if (drop == null) { + throw new NullPointerException("drop is null"); + } + } + + /** + * Gets the drop information generated by DTrace. + * + * @return non-null drop information generated by DTrace + */ + public Drop + getDrop() + { + return drop; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this event useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(DropEvent.class.getName()); + buf.append("[source = "); + buf.append(getSource()); + buf.append(", drop = "); + buf.append(drop); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Error.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Error.java new file mode 100644 index 0000000000..b08bdb6692 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Error.java @@ -0,0 +1,324 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * An error encountered in the native DTrace library while tracing probe + * data. Each of the fault name constants beginning with {@code + * DTRACEFLT_} identifies a specific fault with a name that is + * guaranteed not to change across API versions. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see ConsumerListener#errorEncountered(ErrorEvent e) + * + * @author Tom Erickson + */ +public final class Error implements Serializable { + static final long serialVersionUID = 5069931629562700614L; + + /** + * Invalid address. + */ + public static final String DTRACEFLT_BADADDR = "DTRACEFLT_BADADDR"; + /** + * Invalid alignment. + */ + public static final String DTRACEFLT_BADALIGN = "DTRACEFLT_BADALIGN"; + /** + * Illegal operation. + */ + public static final String DTRACEFLT_ILLOP = "DTRACEFLT_ILLOP"; + /** + * Divide-by-zero. + */ + public static final String DTRACEFLT_DIVZERO = "DTRACEFLT_DIVZERO"; + /** + * Out of scratch space. + */ + public static final String DTRACEFLT_NOSCRATCH = "DTRACEFLT_NOSCRATCH"; + /** + * Invalid kernel access. + */ + public static final String DTRACEFLT_KPRIV = "DTRACEFLT_KPRIV"; + /** + * Invalid user access. + */ + public static final String DTRACEFLT_UPRIV = "DTRACEFLT_UPRIV"; + /** + * Tuple stack overflow. + */ + public static final String DTRACEFLT_TUPOFLOW = "DTRACEFLT_TUPOFLOW"; + /** + * Library-level fault. + */ + public static final String DTRACEFLT_LIBRARY = "DTRACEFLT_LIBRARY"; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Error.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"probeDescription", + "enabledProbeID", "CPU", "action", "offset", + "fault", "address", "defaultMessage"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** @serial */ + private final ProbeDescription probeDescription; + /** @serial */ + private final int epid; + /** @serial */ + private final int cpu; + /** @serial */ + private final int action; + /** @serial */ + private final int offset; + /** @serial */ + private final String fault; + /** @serial */ + private final long address; + /** @serial */ + private final String defaultMessage; + + /** + * Creates a DTrace error with the given properties. Supports XML + * persistence. + * + * @param pdesc probe description that identifies the error-inducing + * probe among all the probes on the system + * @param enabledProbeID identifies the error-inducing probe among + * all probes enabled by the same {@link Consumer} + * @param errorCPU non-negative ID of the CPU where the error was + * encountered, or a negative number if the CPU is unknown + * @param errorAction integer that identifies the error-inducing + * action as the nth action (starting at one) in the error-inducing + * probe, or zero if the error is in the predicate rather than in an + * action + * @param errorOffset error offset in compiled DTrace Intermediate + * Format (DIF), or a negative number if the offset is not available + * @param faultName name of the specific fault, or {@code null} + * if the fault is unknown to the Java DTrace API + * @param faultAddress address of fault, or -1 if address is not + * applicable to the specific fault + * @param errorMessage default message from the native DTrace + * library preconstructed from the properties of this error + * @throws NullPointerException if the given probe description or + * default message is {@code null} + */ + public + Error(ProbeDescription pdesc, int enabledProbeID, int errorCPU, + int errorAction, int errorOffset, String faultName, + long faultAddress, String errorMessage) + { + probeDescription = pdesc; + epid = enabledProbeID; + cpu = errorCPU; + action = errorAction; + offset = errorOffset; + fault = faultName; + address = faultAddress; + defaultMessage = errorMessage; + validate(); + } + + private void + validate() + { + if (probeDescription == null) { + throw new NullPointerException( + "enabled probe description is null"); + } + if (defaultMessage == null) { + throw new NullPointerException("default message is null"); + } + } + + /** + * Gets the probe description that identifies the error-inducing + * probe among all the probes on the system. + * + * @return non-null probe description + */ + public ProbeDescription + getProbeDescription() + { + return probeDescription; + } + + /** + * Gets the enabled probe ID. The "epid" is different from {@link + * ProbeDescription#getID()} because it identifies a probe among all + * the probes enabled by a {@link Consumer}, rather than among all + * the probes on the system. + * + * @return the enabled probe ID + */ + public int + getEnabledProbeID() + { + return epid; + } + + /** + * Gets the CPU that encountered the error. + * + * @return non-negative CPU ID, or a negative number if the CPU is + * unknown + */ + public int + getCPU() + { + return cpu; + } + + /** + * Gets the error-inducing action as the <i>nth</i> action (starting + * at one) in the error-inducing probe, or zero if the error is in + * the predicate rather than in an action. Note that some actions + * in a D program consist of multiple actions internally within the + * DTrace library. + * + * @return zero if the error is in the probe predicate, otherwise + * the <i>nth</i> action (<i>n</i> starting at one) from the start + * of the probe that induced the error + */ + public int + getAction() + { + return action; + } + + /** + * Gets the error offset in compiled DTrace Intermediate Format + * (DIF), or a negative number if the offset is not available. + * + * @return the error offset in compiled DTrace Intermediate Format + * (DIF), or a negative number if the offset is not available + */ + public int + getOffset() + { + return offset; + } + + /** + * Gets the name identifying the specific fault. The names are + * guaranteed not to change across API versions as long as the fault + * cases they identify still exist. + * + * @return name of the specific fault or {@code null} if the + * fault is unknown to the Java DTrace API + */ + public String + getFault() + { + return fault; + } + + /** + * Gets the address of the fault, if any. + * + * @return address of fault, or -1 if address is not applicable to + * the specific fault (the fault is not one of {@link + * #DTRACEFLT_BADADDR} or {@link #DTRACEFLT_BADALIGN}) + */ + public long + getAddress() + { + return address; + } + + /** + * Gets the default message from the native DTrace library + * preconstructed from the properties of this error. + * + * @return non-null preconstructed message + */ + public String + getDefaultMessage() + { + return defaultMessage; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this error useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Error.class.getName()); + buf.append("[probeDescription = "); + buf.append(probeDescription); + buf.append(", epid = "); + buf.append(epid); + buf.append(", cpu = "); + buf.append(cpu); + buf.append(", action = "); + buf.append(action); + buf.append(", offset = "); + buf.append(offset); + buf.append(", fault = "); + buf.append(fault); + buf.append(", address = "); + buf.append(address); + buf.append(", defaultMessage = "); + buf.append(defaultMessage); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ErrorEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ErrorEvent.java new file mode 100644 index 0000000000..d956705ac5 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ErrorEvent.java @@ -0,0 +1,116 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.EventObject; + +/** + * Notification that DTrace has encountered an error. + * + * @see ConsumerListener#errorEncountered(ErrorEvent e) + * + * @author Tom Erickson + */ +public class ErrorEvent extends EventObject { + static final long serialVersionUID = 2236600660422267215L; + + /** @serial */ + private Error error; + + /** + * Creates an {@link ConsumerListener#errorEncountered(ErrorEvent e) + * errorEncountered()} event that reports an error encountered in + * the native DTrace library during tracing. + * + * @param source the {@link Consumer} that is the source of this event + * @param dtraceError the error encountered by DTrace + * @throws NullPointerException if the given error is {@code null} + */ + public + ErrorEvent(Object source, Error dtraceError) + { + super(source); + error = dtraceError; + validate(); + } + + private void + validate() + { + if (error == null) { + throw new NullPointerException("error is null"); + } + } + + /** + * Gets the error reported by DTrace. + * + * @return non-null error reported by DTrace + */ + public Error + getError() + { + return error; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this event useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ErrorEvent.class.getName()); + buf.append("[source = "); + buf.append(getSource()); + buf.append(", error = "); + buf.append(error); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExceptionHandler.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExceptionHandler.java new file mode 100644 index 0000000000..e73c443ed3 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExceptionHandler.java @@ -0,0 +1,54 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * User-defined application behavior after an exception terminates + * a running DTrace consumer. The {@link Consumer} that threw the + * exception is stopped automatically whether or not an {@code + * ExceptionHandler} is set, but a handler must be set to do anything + * other than print a stack trace to {@code stderr}. + * + * @see Consumer#go(ExceptionHandler handler) + * + * @author Tom Erickson + */ +public interface ExceptionHandler { + /** + * Defines what to do after an exception terminates a running {@link + * Consumer}. For example, a handler might be implemented to + * display details about what went wrong. + * + * @param e a {@link DTraceException} if encountered in the native + * DTrace library, a {@link ConsumerException} if thrown from a + * {@link ConsumerListener} method to terminate the consumer, or a + * {@link RuntimeException} to indicate an unexpected error in the + * Java DTrace API. + */ + public void handleException(Throwable e); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExitRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExitRecord.java new file mode 100644 index 0000000000..7a046e555e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExitRecord.java @@ -0,0 +1,94 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.Serializable; +import java.beans.*; + +/** + * A record indicating that the DTrace {@code exit()} action is about to + * stop the source {@link Consumer}. The exit status is whatever value + * was passed to the {@code exit()} action in the D program. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class ExitRecord implements Record, Serializable { + static final long serialVersionUID = -2062716683135961493L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ExitRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"status"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final int status; + + /** + * Creates an exit record with the given status. + * + * @param exitStatus value passed to the D {@code exit()} action + */ + public + ExitRecord(int exitStatus) + { + status = exitStatus; + } + + /** + * Gets the exit status of a DTrace {@link Consumer}. + * + * @return the value passed to the D {@code exit()} action + */ + public int + getStatus() + { + return status; + } + + /** + * Gets a string representation of the exit status. + * + * @return the string form of {@link #getStatus()} returned by + * {@link Integer#toString(int i)} + */ + public String + toString() + { + return Integer.toString(status); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Flow.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Flow.java new file mode 100644 index 0000000000..8c74c8d02d --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Flow.java @@ -0,0 +1,234 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * Description of control flow across function boundaries including + * direction (entry or return) and depth in the call stack. This + * information is added to {@link ProbeData} instances only when the + * {@link Option#flowindent flowindent} option is used: + * <pre><code> + * Consumer consumer = new LocalConsumer(); + * consumer.open(); + * consumer.setOption(Option.flowindent); + * ... + * </code></pre> + * See the <a + * href="http://docs.sun.com/app/docs/doc/817-6223/6mlkidlk1?a=view"> + * <b>Examples</b></a> section of the <b>{@code fbt} + * Provider</b> chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#setOption(String option) + * @see Option#flowindent + * + * @author Tom Erickson + */ +public final class Flow implements Serializable { + static final long serialVersionUID = -9178272444872063901L; + + /** + * Indicates direction of flow across a boundary, such as entering + * or returing from a function. + */ + public enum Kind { + /** Entry into a function. */ + ENTRY, + /** Return from a function. */ + RETURN, + /** No function boundary crossed. */ + NONE + } + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Flow.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"kind", "depth"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + + protected Expression + instantiate(Object oldInstance, Encoder out) + { + Flow flow = (Flow)oldInstance; + return new Expression(oldInstance, oldInstance.getClass(), + "new", new Object[] { flow.getKind().name(), + flow.getDepth() }); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** @serial */ + private final Kind kind; + /** @serial */ + private final int depth; + + /** + * Creates a {@code Flow} instance with the given flow kind and + * depth. Supports XML persistence. + * + * @param flowKindName name of enumeration value indicating the + * direction of flow + * @param flowDepth current depth in the call stack + * @throws IllegalArgumentException if there is no {@code Flow.Kind} + * value with the given name or if the given {@code flowDepth} is + * negative + * @throws NullPointerException if the given {@code Flow.Kind} name + * is {@code null} + */ + public + Flow(String flowKindName, int flowDepth) + { + kind = Enum.valueOf(Kind.class, flowKindName); + depth = flowDepth; + if (depth < 0) { + throw new IllegalArgumentException("depth is negative"); + } + } + + /** + * Gets the direction of the flow of control (entry or return) + * across a function boundary. + * + * @return non-null flow kind indicating direction of flow (entry or + * return) across a function boundary + */ + public Kind + getKind() + { + return kind; + } + + /** + * Gets the current depth in the call stack. + * + * @return A non-negative sum of the function entries minus the + * function returns up until the moment described by this control + * flow instance. For example, if the traced flow of control + * entered two functions but only returned from one, the depth is + * one (2 entries minus 1 return). + */ + public int + getDepth() + { + return depth; + } + + /** + * Compares the specified object with this {@code Flow} instance for + * equality. Defines equality as having the same flow kind and + * depth. + * + * @return {@code true} if and only if the specified object is of + * type {@code Flow} and both instances have equal flow kind and + * depth. + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof Flow) { + Flow f = (Flow)o; + return ((kind == f.kind) && (depth == f.depth)); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + kind.hashCode(); + hash = (37 * hash) + depth; + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + if (kind == null) { + throw new InvalidObjectException("kind is null"); + } + if (depth < 0) { + throw new InvalidObjectException("depth is negative"); + } + } + + /** + * Gets a string representation of this {@code Flow} instance useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Flow.class.getName()); + buf.append("[kind = "); + buf.append(kind); + buf.append(", depth = "); + buf.append(depth); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/InterfaceAttributes.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/InterfaceAttributes.java new file mode 100644 index 0000000000..aa7ab73a81 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/InterfaceAttributes.java @@ -0,0 +1,529 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * Triplet of attributes consisting of two stability levels and a + * dependency class. Attributes may vary independently. They use + * labels described in the {@code attributes(5)} man page to help set + * expectations for what kinds of changes might occur in different kinds + * of future releases. The D compiler includes features to dynamically + * compute the stability levels of D programs you create. For more + * information, refer to the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlnp?a=view> + * <b>Stability</b></a> chapter of the <i>Solaris Dynamic Tracing + * Guide</i>. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#getProgramInfo(Program program) + * @see Consumer#enable(Program program) + * @see Consumer#listProbes(ProbeDescription filter) + * @see Consumer#listProgramProbes(Program program) + * + * @author Tom Erickson + */ +public final class InterfaceAttributes implements Serializable { + static final long serialVersionUID = -2814012588381562694L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(InterfaceAttributes.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"nameStability", "dataStability", + "dependencyClass" }) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + + protected Expression + instantiate(Object oldInstance, Encoder out) + { + InterfaceAttributes attr = (InterfaceAttributes) + oldInstance; + return new Expression(oldInstance, oldInstance.getClass(), + "new", new Object[] { + attr.getNameStability().name(), + attr.getDataStability().name(), + attr.getDependencyClass().name() }); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** + * Interface stability level. Assists developers in making risk + * assessments when developing scripts and tools based on DTrace by + * indicating how likely an interface or DTrace entity is to change + * in a future release or patch. + */ + public enum Stability { + /** + * The interface is private to DTrace itself and represents an + * implementation detail of DTrace. Internal interfaces might + * change in minor or micro releases. + */ + INTERNAL("Internal"), + /** + * The interface is private to Sun and represents an interface + * developed for use by other Sun products that is not yet + * publicly documented for use by customers and ISVs. Private + * interfaces might change in minor or micro releases. + */ + PRIVATE("Private"), + /** + * The interface is supported in the current release but is + * scheduled to be removed, most likely in a future minor + * release. When support of an interface is to be discontinued, + * Sun will attempt to provide notification before discontinuing + * the interface. The D compiler might produce warning messages + * if you attempt to use an Obsolete interface. + */ + OBSOLETE("Obsolete"), + /** + * The interface is controlled by an entity other than Sun. At + * Sun's discretion, Sun can deliver updated and possibly + * incompatible versions as part of any release, subject to + * their availability from the controlling entity. Sun makes no + * claims regarding either the source or binary compatibility + * for External interfaces between two releases. Applications + * based on these interfaces might not work in future releases, + * including patches that contain External interfaces. + */ + EXTERNAL("External"), + /** + * The interface is provided to give developers early access to + * new or rapidly changing technology or to an implementation + * artifact that is essential for observing or debugging system + * behavior for which a more stable solution is anticipated in + * the future. Sun makes no claims about either source of + * binary compatibility for Unstable interfaces from one minor + * release to another. + */ + UNSTABLE("Unstable"), + /** + * The interface might eventually become Standard or Stable but + * is still in transition. Sun will make reasonable efforts to + * ensure compatibility with previous releases as it eveolves. + * When non-upward compatible changes become necessary, they + * will occur in minor and major releases. These changes will + * be avoided in micro releases whenever possible. If such a + * change is necessary, it will be documented in the release + * notes for the affected release, and when feasible, Sun will + * provide migration aids for binary compatibility and continued + * D program development. + */ + EVOLVING("Evolving"), + /** + * The interface is a mature interface under Sun's control. Sun + * will try to avoid non-upward-compatible changes to these + * interfaces, especially in minor or micro releases. If + * support of a Stable interface must be discontinued, Sun will + * attempt to provide notification and the stability level + * changes to Obsolete. + */ + STABLE("Stable"), + /** + * The interface complies with an industry standard. The + * corresponding documentation for the interface will describe + * the standard to which the interface conforms. Standards are + * typically controlled by a standards development organization, + * and changes can be made to the interface in accordance with + * approved changes to the standard. This stability level can + * also apply to interfaces that have been adopted (without a + * formal standard) by an industry convention. Support is + * provided for only the specified versions of a standard; + * support for later versions is not guaranteed. If the + * standards development organization approves a + * non-upward-compatible change to a Standard interface that Sun + * decides to support, Sun will announce a compatibility and + * migration strategy. + */ + STANDARD("Standard"); + + private String s; + + private + Stability(String displayName) + { + s = displayName; + } + + /** + * Overridden to get the default display value. To + * internationalize the display value, use {@link + * java.lang.Enum#name()} instead as a lookup key. + */ + @Override + public String + toString() + { + return s; + } + } + + /** + * Architectural dependency class. Tells whether an interface is + * common to all Solaris platforms and processors, or whether the + * interface is associated with a particular architecture such as + * SPARC processors only. + */ + public enum DependencyClass { + // Note that the compareTo() method depends on the order in + // which the instances are instantiated + + /** + * The interface has an unknown set of architectural dependencies. + * DTrace does not necessarily know the architectural dependencies of + * all entities, such as data types defined in the operating system + * implementation. The Unknown label is typically applied to interfaces + * of very low stability for which dependencies cannot be computed. The + * interface might not be available when using DTrace on <i>any</i> + * architecture other than the one you are currently using. + */ + UNKNOWN("Unknown"), + /** + * The interface is specific to the CPU model of the current + * system. You can use the {@code psrinfo(1M)} utility's {@code + * -v} option to display the current CPU model and + * implementation names. Interfaces with CPU model dependencies + * might not be available on other CPU implementations, even if + * those CPUs export the same instruction set architecture + * (ISA). For example, a CPU-dependent interface on an + * UltraSPARC-III+ microprocessor might not be available on an + * UltraSPARC-II microprocessor, even though both processors + * support the SPARC instruction set. + */ + CPU("CPU"), + /** + * The interface is specific to the hardware platform of the current + * system. A platform typically associates a set of system components + * and architectural characteristics such as a set of supported CPU + * models with a system name such as <code>SUNW, + * Ultra-Enterprise-10000</code>. You can display the current + * platform name using the {@code uname(1)} {@code -i} option. + * The interface might not be available on other hardware + * platforms. + */ + PLATFORM("Platform"), + /** + * The interface is specific to the hardware platform group of the + * current system. A platform group typically associates a set of + * platforms with related characteristics together under a single name, + * such as {@code sun4u}. You can display the current platform + * group name using the {@code uname(1)} {@code -m} option. The + * interface is available on other platforms in the platform + * group, but might not be available on hardware platforms that + * are not members of the group. + */ + GROUP("Group"), + /** + * The interface is specific to the instruction set architecture (ISA) + * supported by the microprocessor on this system. The ISA describes a + * specification for software that can be executed on the + * microprocessor, including details such as assembly language + * instructions and registers. You can display the native + * instruction sets supported by the system using the {@code + * isainfo(1)} utility. The interface might not be supported on + * systems that do not export any of of the same instruction + * sets. For example, an ISA-dependent interface on a Solaris + * SPARC system might not be supported on a Solaris x86 system. + */ + ISA("ISA"), + /** + * The interface is common to all Solaris systems regardless of the + * underlying hardware. DTrace programs and layered applications that + * depend only on Common interfaces can be executed and deployed on + * other Solaris systems with the same Solaris and DTrace revisions. + * The majority of DTrace interfaces are Common, so you can use them + * wherever you use Solaris. + */ + COMMON("Common"); + + private String s; + + private + DependencyClass(String displayString) + { + s = displayString; + } + + /** + * Overridden to get the default display value. To + * internationalize the display value, use {@link + * java.lang.Enum#name()} instead as a lookup key. + */ + @Override + public String + toString() + { + return s; + } + } + + /** @serial */ + private Stability nameStability; + /** @serial */ + private Stability dataStability; + /** @serial */ + private DependencyClass dependencyClass; + + /** + * Called by native code. + */ + private + InterfaceAttributes() + { + } + + /** + * Creates an interface attribute triplet from the given attributes. + * + * @param nameStabilityAttribute the stability level of the + * interface associated with its name in a D program + * @param dataStabilityAttribute stability of the data format used + * by the interface and any associated data semantics + * @param dependencyClassAttribute describes whether the interface + * is specific to the current operating platform or microprocessor + * @throws NullPointerException if any parameter is {@code null} + */ + public + InterfaceAttributes(Stability nameStabilityAttribute, + Stability dataStabilityAttribute, + DependencyClass dependencyClassAttribute) + { + nameStability = nameStabilityAttribute; + dataStability = dataStabilityAttribute; + dependencyClass = dependencyClassAttribute; + validate(); + } + + /** + * Creates an interface attribute triplet from the given attribute + * names. Supports XML persistence. + * + * @throws NullPointerException if any parameter is {@code null} + * @throws IllegalArgumentException if any parameter fails to match + * an enumerated stability value + */ + public + InterfaceAttributes(String nameStabilityAttributeName, + String dataStabilityAttributeName, + String dependencyClassAttributeName) + { + this(Enum.valueOf(Stability.class, nameStabilityAttributeName), + Enum.valueOf(Stability.class, dataStabilityAttributeName), + Enum.valueOf(DependencyClass.class, + dependencyClassAttributeName)); + // validate() unnecessary because Enum.valueOf() has already + // thrown the exception + } + + private void + validate() + { + if (nameStability == null) { + throw new NullPointerException("nameStability is null"); + } + if (dataStability == null) { + throw new NullPointerException("dataStability is null"); + } + if (dependencyClass == null) { + throw new NullPointerException("dependencyClass is null"); + } + } + + /** + * Gets the stabiltiy level of an interface associated with its name + * as it appears in a D program. For example, the {@code execname} + * D variable is a {@link Stability#STABLE STABLE} name: Sun + * guarantees this identifier will continue to be supported in D + * programs according to the rules described for Stable interfaces. + * + * @return the stability level of an interface associated with its + * name as it appears in a D program + */ + public Stability + getNameStability() + { + return nameStability; + } + + /** + * Called by native code. + */ + private void + setNameStability(String s) + { + nameStability = Enum.valueOf(Stability.class, s); + } + + /** + * Gets the stability level of the data format used by an interface + * and any associated data semantics. For example, the {@code pid} + * D variable is a {@link Stability#STABLE STABLE} interface: + * process IDs are a stable concept in Solaris, and Sun guarantees + * that the {@code pid} variable will be of type {@code pid_t} with + * the semantic that it is set to the process ID corresponding to + * the thread that fired a given probe in accordance with the rules + * described for Stable interfaces. + * + * @return the stability level of the data format used by an + * interface and any associated data semantics. + */ + public Stability + getDataStability() + { + return dataStability; + } + + /** + * Called by native code. + */ + private void + setDataStability(String s) + { + dataStability = Enum.valueOf(Stability.class, s); + } + + /** + * Gets the interface dependency class. + * + * @return the dependency class describing whether the interface is + * specific to the current operating platform or microprocessor + */ + public DependencyClass + getDependencyClass() + { + return dependencyClass; + } + + /** + * Called by native code. + */ + private void + setDependencyClass(String s) + { + dependencyClass = Enum.valueOf(DependencyClass.class, s); + } + + /** + * Compares the specified object with this attribute triplet for + * equality. Defines equality as having the same attributes. + * + * @return {@code true} if and only if the specified object is also + * an {@code InterfaceAttributes} instance and has all the same + * attributes as this instance. + */ + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof InterfaceAttributes) { + InterfaceAttributes a = (InterfaceAttributes)o; + return ((nameStability == a.nameStability) && + (dataStability == a.dataStability) && + (dependencyClass == a.dependencyClass)); + } + return false; + } + + /** + * Overridden to ensure that equal {@code InterfaceAttributes} + * instances have equal hashcodes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + nameStability.hashCode(); + hash = (37 * hash) + dataStability.hashCode(); + hash = (37 * hash) + dependencyClass.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Check constructor invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the string representation of this triplet of interface + * attributes. The format follows the convention described in the + * <a href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlnt?a=view> + * <b>Interface Attributes</b></a> section of the <b>Stability</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. The + * attributes appear in the following order, separated by slashes: + * <pre><code> + * <i>name-stability / data-stability / dependency-class</i> + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(nameStability); + buf.append(" / "); + buf.append(dataStability); + buf.append(" / "); + buf.append(dependencyClass); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelStackRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelStackRecord.java new file mode 100644 index 0000000000..95b5fd2bd0 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelStackRecord.java @@ -0,0 +1,382 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.util.regex.Pattern; +import java.beans.*; + +/** + * A value generated by the DTrace {@code stack()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class KernelStackRecord implements StackValueRecord, + Serializable, Comparable <KernelStackRecord> +{ + static final long serialVersionUID = 8616454544771346573L; + static final int STACK_INDENT = 14; + static final StackFrame[] EMPTY_FRAMES = new StackFrame[] {}; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(KernelStackRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"stackFrames", "rawStackData"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Splits formatted call stack generated by DTrace stack() and + * ustack() actions into tokens delimited by whitespace. Matches + * any number of whitespace characters on either side of a newline. + * Can't assume that a line has no whitespace characters. A java + * stack might have the line "StubRoutines (1)", which must not get + * split into two tokens. + */ + static final Pattern STACK_TOKENIZER = Pattern.compile("\\s*\n\\s*"); + + /** + * Called by JNI layer to convert a stack formatted by the native + * DTrace library into an unformatted array of stack frames. + * + * @param s string representation of stack data generated by the D + * {@code stack()}, {@code ustack()}, or {@code jstack()} action + * @return array of human-readable stack frames + */ + static StackFrame[] + parse(String s) + { + // + // First trim the leading whitespace to avoid an initial empty + // element in the returned array. + // + s = s.trim(); + StackFrame[] frames; + if (s.length() == 0) { + frames = EMPTY_FRAMES; + } else { + String[] f = STACK_TOKENIZER.split(s); + int n = f.length; + frames = new StackFrame[n]; + for (int i = 0; i < n; ++i) { + frames[i] = new StackFrame(f[i]); + } + } + return frames; + } + + /** @serial */ + private StackFrame[] stackFrames; + /** @serial */ + private byte[] rawStackData; + + /** + * Called by native code and by UserStackRecord (in its constructor + * called by native code). + * + * @throws NullPointerException if rawBytes is {@code null} + */ + KernelStackRecord(byte[] rawBytes) + { + // No need for defensive copy; native code will not modify input + // raw bytes. + rawStackData = rawBytes; + if (rawStackData == null) { + throw new NullPointerException("raw stack data is null"); + } + } + + /** + * Creates a {@code KernelStackRecord} with the given stack frames + * and raw stack data. Supports XML persistence. + * + * @param frames array of human-readable stack frames, copied so + * that later modifying the given frames array will not affect this + * {@code KernelStackRecord}; may be {@code null} or empty to + * indicate that the raw stack data was not converted to + * human-readable stack frames (see {@link + * StackValueRecord#getStackFrames()}) + * @param rawBytes array of raw bytes used to represent this stack + * value in the native DTrace library, needed to distinguish stacks + * that have the same display value but are considered distinct by + * DTrace; copied so that later modifying the given array will not + * affect this {@code KernelStackRecord} + * @throws NullPointerException if the given array of raw bytes is + * {@code null} or if any element of the {@code frames} array is + * {@code null} + */ + public + KernelStackRecord(StackFrame[] frames, byte[] rawBytes) + { + if (frames != null) { + stackFrames = (StackFrame[])frames.clone(); + } + if (rawBytes != null) { + rawStackData = (byte[])rawBytes.clone(); + } + validate(); + } + + private void + validate() + { + if (rawStackData == null) { + throw new NullPointerException("raw stack data is null"); + } + // stackFrames may be null; if non-null, cannot contain null + // elements + if (stackFrames != null) { + for (StackFrame f : stackFrames) { + if (f == null) { + throw new NullPointerException("stack frame is null"); + } + } + } + } + + public StackFrame[] + getStackFrames() + { + if (stackFrames == null) { + return EMPTY_FRAMES; + } + return (StackFrame[])stackFrames.clone(); + } + + /** + * Called by native code and by UserStackRecord in its + * setStackFrames() method. + */ + void + setStackFrames(StackFrame[] frames) + { + // No need for defensive copy; native code will not modify input + // frames. + stackFrames = frames; + validate(); + } + + /** + * Gets the native DTrace representation of this record's stack as + * an array of raw bytes. The raw bytes are used in {@link + * #equals(Object o) equals()} and {@link + * #compareTo(KernelStackRecord r) compareTo()} to test equality and + * to determine the natural ordering of kernel stack records. + * + * @return the native DTrace library's internal representation of + * this record's stack as a non-null array of bytes + */ + public byte[] + getRawStackData() + { + return (byte[])rawStackData.clone(); + } + + /** + * Gets the raw bytes used to represent this record's stack value in + * the native DTrace library. To get a human-readable + * representation, call {@link #toString()}. + * + * @return {@link #getRawStackData()} + */ + public Object + getValue() + { + return (byte[])rawStackData.clone(); + } + + public List <StackFrame> + asList() + { + if (stackFrames == null) { + return Collections. <StackFrame> emptyList(); + } + return Collections.unmodifiableList(Arrays.asList(stackFrames)); + } + + /** + * Compares the specified object with this {@code KernelStackRecord} + * for equality. Returns {@code true} if and only if the specified + * object is also a {@code KernelStackRecord} and both records have + * the same raw stack data. + * <p> + * This implementation first checks if the specified object is this + * {@code KernelStackRecord}. If so, it returns {@code true}. + * + * @return {@code true} if and only if the specified object is also + * a {@code KernelStackRecord} and both records have the same raw + * stack data + */ + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof KernelStackRecord) { + KernelStackRecord r = (KernelStackRecord)o; + return Arrays.equals(rawStackData, r.rawStackData); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return Arrays.hashCode(rawStackData); + } + + /** + * Compares this record with the given stack record. Compares the + * first unequal pair of bytes at the same index in each record's + * raw stack data, or if all corresponding bytes are equal, compares + * the length of each record's array of raw stack data. The {@code + * compareTo()} method is compatible with {@link #equals(Object o) + * equals()}. + * <p> + * This implementation first checks if the specified record is this + * {@code KernelStackRecord}. If so, it returns {@code 0}. + * + * @return -1, 0, or 1 as this record's raw stack data is less than, + * equal to, or greater than the given record's raw stack data. + */ + public int + compareTo(KernelStackRecord r) + { + if (r == this) { + return 0; + } + + int len1 = rawStackData.length; + int len2 = r.rawStackData.length; + int cmp = 0; + for (int i = 0; (cmp == 0) && (i < len1) && (i < len2); ++i) { + cmp = ((rawStackData[i] < r.rawStackData[i]) ? -1 : + ((rawStackData[i] > r.rawStackData[i]) ? 1 : 0)); + } + if (cmp == 0) { + cmp = ((len1 < len2) ? -1 : ((len1 > len2) ? 1 : 0)); + } + return cmp; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Make a defensive copy of stack frames and raw bytes + if (stackFrames != null) { + stackFrames = (StackFrame[])stackFrames.clone(); + } + if (rawStackData != null) { + rawStackData = (byte[])rawStackData.clone(); + } + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a multi-line string representation of a stack with one frame + * per line. Each line is of the format {@code + * module`function+offset}, where {@code module} and {@code + * function} are symbol names and offset is a hex integer preceded + * by {@code 0x}. For example: {@code genunix`open+0x19}. The + * offset (and the preceding '+' sign) are omitted if offset is + * zero. If function name lookup fails, the raw pointer value is + * used instead. In that case, the module name (and the ` + * delimiter) may or may not be present, depending on whether or not + * module lookup also fails, and a raw pointer value appears in + * place of {@code function+offset} as a hex value preceded by + * {@code 0x}. The format just described, not including surrounding + * whitespce, is defined in the native DTrace library and is as + * stable as that library definition. Each line is indented by an + * equal (unspecified) number of spaces. + * <p> + * If human-readable stack frames are not available (see {@link + * #getStackFrames()}), a table represenation of {@link + * #getRawStackData()} is returned instead. The table displays 16 + * bytes per row in unsigned hex followed by the ASCII character + * representations of those bytes. Each unprintable character is + * represented by a period (.). + */ + @Override + public String + toString() + { + StackFrame[] frames = getStackFrames(); + if (frames.length == 0) { + return ScalarRecord.rawBytesString(rawStackData); + } + + StringBuffer buf = new StringBuffer(); + buf.append('\n'); + for (StackFrame f : frames) { + for (int i = 0; i < STACK_INDENT; ++i) { + buf.append(' '); + } + buf.append(f); + buf.append('\n'); + } + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LinearDistribution.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LinearDistribution.java new file mode 100644 index 0000000000..3b52960864 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LinearDistribution.java @@ -0,0 +1,291 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A linear frequency distribution aggregated by the DTrace {@code + * lquantize()} action. Aggregated values fall into consecutive ranges + * bounded by the step parameter of the {@code lquantize()} action. + * Each range, known as a bucket, begins at the {@code lquantize()} + * lower bound, or base, plus a multiple of the {@code lquantize()} + * step, unless it is the first bucket, which is the frequency of all + * aggregated values less than the base. The last bucket counts all + * aggregated values greater than or equal to the {@code lquantize()} + * upper bound. For example + * <pre> {@code @ = lquantize(n, 0, 100, 10);}</pre> + * results in a distribution with a base of 0, an upper bound of 100, + * and a step of 10. It has twelve buckets starting with {@code n < 0} + * and ending with {@code n >= 100}. The buckets in between are {@code + * 0 .. 9}, {@code 10 .. 19}, etc. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see LogDistribution + * @see Aggregation + * + * @author Tom Erickson + */ +public final class LinearDistribution extends Distribution + implements Serializable, Comparable <LinearDistribution> +{ + static final long serialVersionUID = 7100080045858770132L; + + /** @serial */ + private long base; + /** @serial */ + private long step; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(LinearDistribution.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"base", "step", "buckets" }); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Called by native C code + */ + private + LinearDistribution(long lowerBound, long frequencyStep, + long[] frequencies) + { + // initializes using lowerBound and frequencyStep + super(lowerBound, frequencyStep, frequencies); + base = lowerBound; + step = frequencyStep; + } + + /** + * Creates a linear distribution with the given base, step, and + * frequencies. Supports XML persistence. + * + * @param lowerBound also known as the <i>base</i>; the minimum of + * the second bucket in this distribution (the first bucket contains + * the frequency of everything lower than the base) + * @param bucketStep the distance between the lower bound of one + * bucket and the lower bound of the next consecutive bucket + * (excluding the first bucket) + * @param frequencies list of frequencies in each bucket range + * @throws NullPointerException if {@code frequencies} is {@code + * null} + * @throws IllegalArgumentException if the given step is less than + * one, or if any bucket does not have the expected range + * (consecutive steps) + */ + public + LinearDistribution(long lowerBound, long bucketStep, + List <Bucket> frequencies) + { + super(frequencies); // makes defensive copy + base = lowerBound; + step = bucketStep; + initialize(); // checks class invariants, calculates total + if (step < 1) { + throw new IllegalArgumentException("step is less than one"); + } + } + + /** + * Gets a two element array: the first elelemt is the range minimum + * (inclusive), the second element is the range maximum (inclusive). + */ + @Override + long[] + getBucketRange(int i, int len, long base, long step) + { + long min; + long max; + if (i == 0) { + // first bucket is everything less than the base + min = Long.MIN_VALUE; + } else { + min = (base + ((i - 1) * step)); + } + + if (i == (len - 1)) { + // last bucket is everything greater than or equal to + // the upper bound + max = Long.MAX_VALUE; + } else { + max = ((base + (i * step)) - 1); + } + + long[] range = new long[] {min, max}; + return range; + } + + @Override + long[] + getBucketRange(int i, int len) + { + return getBucketRange(i, len, base, step); + } + + /** + * Gets the lower bound of this distribution. In a linear + * distribution, the first bucket holds the frequency of all values + * less than the base, so the base is the minimum of the second + * bucket's range. + * + * @return the lower bound of this distribution + */ + public long + getBase() + { + return base; + } + + /** + * Gets the difference between the lower bounds of consecutive + * buckets after the first. + * + * @return the step between the lower bounds of consecutive buckets + * after the first + */ + public long + getStep() + { + return step; + } + + public Number + getValue() + { + double total = 0; + List <Distribution.Bucket> buckets = getBuckets(); + int len = buckets.size(); + Distribution.Bucket bucket; + + if (len > 0) { + bucket = buckets.get(0); + total = (double)bucket.getFrequency() * (double)(getBase() - 1); + } + for (int i = 1; i < (len - 1); ++i) { + bucket = buckets.get(i); + total += (double)bucket.getFrequency() * (double)bucket.getMin(); + } + if (len > 1) { + bucket = buckets.get(len - 1); + // There doesn't seem to be any reason to add one to the + // minimum of the last bucket range, but that's how it's + // implemented in libdtrace dt_aggregate.c. + total += (double)bucket.getFrequency() * + (double)(bucket.getMin() + 1); + } + return (new Double(total)); + } + + private long + getZeroBucketValue() + { + for (Distribution.Bucket b : this) { + if (b.getMin() == 0) { + return b.getFrequency(); + } + } + return 0; + } + + /** + * Compares the {@code double} values of {@link #getValue()} for + * overall magnitude, and if those are equal, compares the + * frequencies at zero if the distributions include a bucket whose + * range has a minimum of zero. + */ + public int + compareTo(LinearDistribution d) + { + Number v1 = getValue(); + Number v2 = d.getValue(); + double d1 = v1.doubleValue(); + double d2 = v2.doubleValue(); + int cmp = (d1 < d2 ? -1 : (d1 > d2 ? 1 : 0)); + if (cmp == 0) { + long z1 = getZeroBucketValue(); + long z2 = d.getZeroBucketValue(); + cmp = (z1 < z2 ? -1 : (z1 > z2 ? 1 : 0)); + } + return (cmp); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + try { + initialize(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + if (step < 1) { + throw new InvalidObjectException("step is less than one"); + } + } + + /** + * Gets a string representation of this linear distribution useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(LinearDistribution.class.toString()); + buf.append("[base = "); + buf.append(getBase()); + buf.append(", step = "); + buf.append(getStep()); + buf.append(", buckets = "); + List <Bucket> list = getDisplayRange(); + if (list.isEmpty()) { + buf.append("<empty>"); + } else { + buf.append(Arrays.toString(getDisplayRange().toArray())); + } + buf.append(", total = "); + buf.append(getTotal()); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LocalConsumer.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LocalConsumer.java new file mode 100644 index 0000000000..455944a000 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LocalConsumer.java @@ -0,0 +1,1332 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.*; +import java.net.InetAddress; +import java.net.UnknownHostException; +import javax.swing.event.EventListenerList; +import java.util.logging.*; + +/** + * Interface to the native DTrace library, each instance is a single + * DTrace consumer. + * + * @author Tom Erickson + */ +public class LocalConsumer implements Consumer { + // + // Implementation notes: + // + // libdtrace is *not* thread-safe. You cannot make multiple calls + // into it simultaneously from different threads, even if those + // threads are operating on different dtrace_hdl_t's. Calls to + // libdtrace are synchronized on a global lock, LocalConsumer.class. + + static Logger logger = Logger.getLogger(LocalConsumer.class.getName()); + + private static final int DTRACE_JNI_VERSION = 1; + + private static final Option[] DEFAULT_OPTIONS = new Option[] { + new Option(Option.bufsize, Option.kb(256)), + new Option(Option.aggsize, Option.kb(256)), + }; + + private static native void _loadJniTable(); + + private static boolean debug; + private static int maxConsumers; + + static { + // configure logging + logger.addHandler(new ConsoleHandler()); + logger.setLevel(Level.OFF); + + // Undocumented configuration options using java -Doption=value + String property; + property = System.getProperty("JAVA_DTRACE_MAX_CONSUMERS"); + if (property != null && property.length() != 0) { + try { + maxConsumers = Integer.parseInt(property); + } catch (NumberFormatException e) { + e.printStackTrace(); + System.exit(1); + } + } + property = System.getProperty("JAVA_DTRACE_API_DEBUG"); + if (property != null && property.length() != 0) { + try { + int i = Integer.parseInt(property); + debug = (i != 0); + } catch (NumberFormatException e) { + System.err.println("Warning: property ignored: " + + "JAVA_DTRACE_API_DEBUG=" + property); + } + } + + Utility.loadLibrary("libdtrace_jni.so.1", debug); + + _checkVersion(DTRACE_JNI_VERSION); + if (maxConsumers > 0) { + _setMaximumConsumers(maxConsumers); + } + _setDebug(debug); + + // + // Last of all in case configuration options affect the loading + // of the JNI table. + // + _loadJniTable(); + } + + // Native JNI interface (see lib/libdtrace_jni/dtrace_jni.c) + private static native void _checkVersion(int version); + private native void _open(OpenFlag[] flags) throws DTraceException; + private native Program _compileString(String program, String[] args) + throws DTraceException; + private native Program.File _compileFile(String path, String[] args) + throws DTraceException; + private native void _exec(Program program) throws DTraceException; + private native void _getProgramInfo(Program program) + throws DTraceException; + private native void _setOption(String option, String value) + throws DTraceException; + private native long _getOption(String option) throws DTraceException; + private native boolean _isEnabled(); + private native void _checkProgramEnabling(); + private native void _go() throws DTraceException; + private native void _stop() throws DTraceException; + private native void _consume() throws DTraceException; + private native void _interrupt(); + private native void _close(); + private native Aggregate _getAggregate(AggregateSpec spec) + throws DTraceException; + private native int _createProcess(String cmd) throws DTraceException; + private native void _grabProcess(int pid) throws DTraceException; + private native void _listProbes(List <ProbeDescription> probeList, + ProbeDescription filter); + private native void _listProbeDetail(List <Probe> probeList, + ProbeDescription filter); + private native void _listCompiledProbes( + List <ProbeDescription> probeList, Program program); + private native void _listCompiledProbeDetail( + List <Probe> probeList, Program program); + private static native String _getVersion(); + private static native int _openCount(); + // + // Releases memory held in the JNI layer after dtrace_close() has + // released critical system resources like file descriptors, and + // calls to libdtrace are no longer needed (or possible). + // + private native void _destroy(); + // Called by LogDistribution + static native long _quantizeBucket(int i); + // + // Cannot be static because the necessary dtrace handle is specific + // to this Consumer. + // + private native String _lookupKernelFunction(Number address); + private native String _lookupUserFunction(int pid, Number address); + private static native String _getExecutableName(); + + // Undocumented configuration options + private static native void _setMaximumConsumers(int max); + private static native void _setDebug(boolean debug); + + protected EventListenerList listenerList; + protected ExceptionHandler exceptionHandler; + + private int _handle = -1; // native C identifier (do not modify) + private final Identifier id; // java identifier + + private enum State { + INIT, + OPEN, + COMPILED, + GO, + STARTED, + STOPPED, + CLOSED + } + + private State state = State.INIT; + private boolean stopCalled; + + // + // Per-consumer lock used in native code to prevent conflict between + // the native consumer loop and the getAggregate() thread without + // locking this LocalConsumer. A distinct per-consumer lock allows + // the stop() method to be synchronized without causing deadlock + // when the consumer loop grabs the per-consumer lock before + // dtrace_work(). + // + private Object consumerLock; + // + // Synchronization lock used to ensure that stop() does not return + // until this consumer is actually stopped. + // + private Object stopLock; + private boolean workEnded; + + private static int sequence = 0; + + /** + * Creates a consumer that interacts with the native DTrace library + * on the local system. + */ + public + LocalConsumer() + { + id = new LocalConsumer.Identifier(this); + consumerLock = new Object(); + stopLock = new Object(); + listenerList = new EventListenerList(); + } + + /** + * Called by native C code only + */ + private int + getHandle() + { + return _handle; + } + + /** + * Called by native C code only + */ + private void + setHandle(int n) + { + _handle = n; + } + + public synchronized void + open(OpenFlag ... flags) throws DTraceException + { + if (state == State.CLOSED) { + throw new IllegalStateException("cannot reopen a closed consumer"); + } + if (state != State.INIT) { + throw new IllegalStateException("consumer already open"); + } + + for (OpenFlag flag : flags) { + if (flag == null) { + throw new NullPointerException("open flag is null"); + } + } + + synchronized (LocalConsumer.class) { + _open(flags); + } + + state = State.OPEN; + setOptions(DEFAULT_OPTIONS); + + if (logger.isLoggable(Level.INFO)) { + logger.info("consumer table count: " + _openCount()); + } + } + + private synchronized void + checkCompile() + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: // caller may compile more than one program + break; + case GO: + case STARTED: + throw new IllegalStateException("go() already called"); + case STOPPED: + throw new IllegalStateException("consumer stopped"); + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + } + + public synchronized Program + compile(String program, String ... macroArgs) throws DTraceException + { + if (program == null) { + throw new NullPointerException("program string is null"); + } + checkCompile(); + Program p = null; + + String[] argv = null; + if (macroArgs != null) { + for (String macroArg : macroArgs) { + if (macroArg == null) { + throw new NullPointerException("macro argument is null"); + } + } + argv = new String[macroArgs.length + 1]; + synchronized (LocalConsumer.class) { + // + // Could be an application with an embedded JVM, not + // necessarily "java". + // + argv[0] = _getExecutableName(); + } + System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length); + } else { + synchronized (LocalConsumer.class) { + argv = new String[] { _getExecutableName() }; + } + } + synchronized (LocalConsumer.class) { + p = _compileString(program, argv); + } + p.consumerID = id; + p.contents = program; + p.validate(); + state = State.COMPILED; + + return p; + } + + public synchronized Program + compile(File program, String ... macroArgs) throws DTraceException, + IOException, SecurityException + { + if (program == null) { + throw new NullPointerException("program file is null"); + } + if (!program.canRead()) { + throw new FileNotFoundException("failed to open " + + program.getName()); + } + checkCompile(); + Program.File p = null; + + String[] argv = null; + if (macroArgs != null) { + for (String macroArg : macroArgs) { + if (macroArg == null) { + throw new NullPointerException("macro argument is null"); + } + } + argv = new String[macroArgs.length + 1]; + argv[0] = program.getPath(); + System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length); + } else { + macroArgs = new String[] { program.getPath() }; + } + synchronized (LocalConsumer.class) { + p = _compileFile(program.getPath(), argv); + } + p.consumerID = id; + p.contents = Program.getProgramString(program); + p.file = program; + p.validate(); + state = State.COMPILED; + + return p; + } + + private synchronized void + checkProgram(Program program) + { + if (program == null) { + throw new NullPointerException("program is null"); + } + if (!id.equals(program.consumerID)) { + throw new IllegalArgumentException("program not compiled " + + "by this consumer"); + } + } + + public void + enable() throws DTraceException + { + enable(null); + } + + public synchronized void + enable(Program program) throws DTraceException + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + throw new IllegalStateException("no compiled program"); + case COMPILED: + break; + case GO: + case STARTED: + throw new IllegalStateException("go() already called"); + case STOPPED: + throw new IllegalStateException("consumer stopped"); + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + + // Compile all programs if null + if (program != null) { + checkProgram(program); + } + + // + // Left to native code to throw IllegalArgumentException if the + // program is already enabled, since only the native code knows + // the enabled state. + // + synchronized (LocalConsumer.class) { + _exec(program); + } + } + + public synchronized void + getProgramInfo(Program program) throws DTraceException + { + checkProgram(program); + if (state == State.CLOSED) { + throw new IllegalStateException("consumer closed"); + } + + // + // The given program was compiled by this consumer, so we can + // assert the following: + // + assert ((state != State.INIT) && (state != State.OPEN)); + + synchronized (LocalConsumer.class) { + _getProgramInfo(program); + } + } + + private void + setOptions(Option[] options) throws DTraceException + { + for (Option o : options) { + setOption(o.getOption(), o.getValue()); + } + } + + public void + setOption(String option) throws DTraceException + { + setOption(option, Option.VALUE_SET); + } + + public void + unsetOption(String option) throws DTraceException + { + setOption(option, Option.VALUE_UNSET); + } + + public synchronized void + setOption(String option, String value) throws DTraceException + { + if (option == null) { + throw new NullPointerException("option is null"); + } + if (value == null) { + throw new NullPointerException("option value is null"); + } + + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + case GO: + case STARTED: // Some options can be set on a running consumer + case STOPPED: // Allowed (may affect getAggregate()) + break; + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + + synchronized (LocalConsumer.class) { + _setOption(option, value); + } + } + + public synchronized long + getOption(String option) throws DTraceException + { + if (option == null) { + throw new NullPointerException("option is null"); + } + + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + case GO: + case STARTED: + case STOPPED: + break; + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + + long value; + synchronized (LocalConsumer.class) { + value = _getOption(option); + } + return value; + } + + public final synchronized boolean + isOpen() + { + return ((state != State.INIT) && (state != State.CLOSED)); + } + + public final synchronized boolean + isEnabled() + { + if (state != State.COMPILED) { + return false; + } + + return _isEnabled(); + } + + public final synchronized boolean + isRunning() + { + return (state == State.STARTED); + } + + public final synchronized boolean + isClosed() + { + return (state == State.CLOSED); + } + + /** + * Called in the runnable target of the thread returned by {@link + * #createThread()} to run this DTrace consumer. + * + * @see #createThread() + */ + protected final void + work() + { + try { + synchronized (this) { + if (state != State.GO) { + // + // stop() was called after go() but before the + // consumer started + // + return; // executes finally block before returning + } + + state = State.STARTED; + fireConsumerStarted(new ConsumerEvent(this, + System.nanoTime())); + } + + // + // We should not prevent other consumers from running + // concurrently while this consumer blocks on the native + // consumer loop. Instead, native code will acquire the + // LocalConsumer.class monitor as needed before calling + // libdtrace functions. + // + _consume(); + + } catch (Throwable e) { + if (exceptionHandler != null) { + exceptionHandler.handleException(e); + } else { + e.printStackTrace(); + } + } finally { + synchronized (stopLock) { + // Notify the stop() method to stop waiting + workEnded = true; + stopLock.notifyAll(); + } + + // Waits for stop() to finish + synchronized (this) { + // + // The stop() method may have updated the state and + // notified listeners already. Note that whoever + // updates the state and notifies listeners must be + // holding the lock on this LocalConsumer. If we update + // the state and notify listeners here while stop() is + // waiting, counting on stop() to hold the lock for us + // in the meantime, then a ConsumerListener could + // deadlock the application by calling a synchronized + // LocalConsumer method. + // + if (state == State.STARTED) { + state = State.STOPPED; + fireConsumerStopped(new ConsumerEvent(this, + System.nanoTime())); + } + } + } + } + + /** + * Creates the background thread started by {@link #go()} to run + * this consumer. Override this method if you need to set + * non-default {@code Thread} options or create the thread in a + * {@code ThreadGroup}. If you don't need to create the thread + * yourself, set the desired options on {@code super.createThread()} + * before returning it. Otherwise, the {@code Runnable} target of + * the created thread must call {@link #work()} in order to run this + * DTrace consumer. For example, to modify the default background + * consumer thread: + * <pre><code> + * protected Thread + * createThread() + * { + * Thread t = super.createThread(); + * t.setPriority(Thread.MIN_PRIORITY); + * return t; + * } + * </code></pre> + * Or if you need to create your own thread: + * <pre></code> + * protected Thread + * createThread() + * { + * Runnable target = new Runnable() { + * public void run() { + * work(); + * } + * }; + * String name = "Consumer " + UserApplication.sequence++; + * Thread t = new Thread(UserApplication.threadGroup, + * target, name); + * return t; + * } + * </code></pre> + * Do not start the returned thread, otherwise {@code go()} will + * throw an {@link IllegalThreadStateException} when it tries to + * start the returned thread a second time. + */ + protected Thread + createThread() + { + Thread t = new Thread(new Runnable() { + public void run() { + work(); + } + }, "DTrace consumer " + id); + return t; + } + + /** + * @inheritDoc + * @throws IllegalThreadStateException if a subclass calls {@link + * Thread#start()} on the value of {@link #createThread()} + * @see #createThread() + */ + public void + go() throws DTraceException + { + go(null); + } + + /** + * @inheritDoc + * @throws IllegalThreadStateException if a subclass calls {@link + * Thread#start()} on the value of {@link #createThread()} + * @see #createThread() + */ + public synchronized void + go(ExceptionHandler h) throws DTraceException + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + throw new IllegalStateException("no compiled program"); + case COMPILED: + // + // Throws IllegalStateException if not all compiled programs are + // also enabled. Does not make any calls to libdtrace. + // + _checkProgramEnabling(); + break; + case GO: + case STARTED: + throw new IllegalStateException("go() already called"); + case STOPPED: + throw new IllegalStateException("consumer stopped"); + case CLOSED: + throw new IllegalStateException("consumer closed"); + default: + throw new IllegalArgumentException("unknown state: " + state); + } + + synchronized (LocalConsumer.class) { + _go(); + } + + state = State.GO; + exceptionHandler = h; + Thread t = createThread(); + t.start(); + } + + public synchronized void + stop() + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + throw new IllegalStateException("go() not called"); + case GO: + try { + synchronized (LocalConsumer.class) { + _stop(); + } + state = State.STOPPED; + } catch (DTraceException e) { + if (exceptionHandler != null) { + exceptionHandler.handleException(e); + } else { + e.printStackTrace(); + } + } + break; + case STARTED: + // + // Calls no libdtrace methods, so no synchronization is + // needed. Sets a native flag that causes the consumer + // thread to exit the consumer loop and call native + // dtrace_stop() at the end of the current interval + // (after grabbing the global Consumer.class lock + // required for any libdtrace call). + // + _interrupt(); + + synchronized (stopLock) { + // + // If the work() thread got the stopLock first, then + // we have nothing to wait for. (Calling wait() on + // the stopLock can only deadlock this LocalConsumer + // in that case.) However, we still need to notify + // listeners here, since we're holding the lock on + // this LocalConsumer. + // + while (!workEnded) { + try { + stopLock.wait(); + } catch (InterruptedException e) { + logger.warning(e.toString()); + // do nothing but re-check the condition for waiting + } + } + } + + state = State.STOPPED; + fireConsumerStopped(new ConsumerEvent(this, + System.nanoTime())); + break; + case STOPPED: + // + // The work() thread that runs the native consumer loop + // may have terminated because of the exit() action in a + // DTrace program. In that case, a RuntimeException is + // inappropriate because there is no misuse of the API + // Creating a new checked exception type to handle this + // case seems to offer no benefit for the trouble to the + // caller. Instead, the situation calls for stop() to + // be quietly tolerant. + // + if (stopCalled) { + throw new IllegalStateException("consumer already stopped"); + } + logger.info("consumer already stopped"); + break; + case CLOSED: + throw new IllegalStateException("consumer closed"); + default: + throw new IllegalArgumentException("unknown state: " + state); + } + + stopCalled = true; + } + + public synchronized void + close() + { + if ((state == State.INIT) || (state == State.CLOSED)) { + state = State.CLOSED; + return; + } + + if ((state == State.STARTED) || (state == State.GO)) { + stop(); + } + + synchronized (LocalConsumer.class) { + _close(); + } + _destroy(); + state = State.CLOSED; + + if (logger.isLoggable(Level.INFO)) { + logger.info("consumer table count: " + _openCount()); + } + } + + public void + addConsumerListener(ConsumerListener l) + { + listenerList.add(ConsumerListener.class, l); + } + + public void + removeConsumerListener(ConsumerListener l) + { + listenerList.remove(ConsumerListener.class, l); + } + + public Aggregate + getAggregate() throws DTraceException + { + // include all, clear none + return getAggregate(null, Collections. <String> emptySet()); + } + + public Aggregate + getAggregate(Set <String> includedAggregationNames) + throws DTraceException + { + return getAggregate(includedAggregationNames, + Collections. <String> emptySet()); + } + + public Aggregate + getAggregate(Set <String> includedAggregationNames, + Set <String> clearedAggregationNames) + throws DTraceException + { + AggregateSpec spec = new AggregateSpec(); + + if (includedAggregationNames == null) { + spec.setIncludeByDefault(true); + } else { + spec.setIncludeByDefault(false); + for (String included : includedAggregationNames) { + spec.addIncludedAggregationName(included); + } + } + + if (clearedAggregationNames == null) { + spec.setClearByDefault(true); + } else { + spec.setClearByDefault(false); + for (String cleared : clearedAggregationNames) { + spec.addClearedAggregationName(cleared); + } + } + + return getAggregate(spec); + } + + private synchronized Aggregate + getAggregate(AggregateSpec spec) throws DTraceException + { + // + // It should be possible to request aggregation data after a + // consumer has stopped but not after it has been closed. + // + checkGoCalled(); + + // + // Getting the aggregate is a time-consuming request that should not + // prevent other consumers from running concurrently. Instead, + // native code will acquire the LocalConsumer.class monitor as + // needed before calling libdtrace functions. + // + Aggregate aggregate = _getAggregate(spec); + return aggregate; + } + + private synchronized void + checkGoCalled() + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + throw new IllegalStateException("go() not called"); + case GO: + case STARTED: + case STOPPED: + break; + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + } + + private synchronized void + checkGoNotCalled() + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + break; + case GO: + case STARTED: + throw new IllegalStateException("go() already called"); + case STOPPED: + throw new IllegalStateException("consumer stopped"); + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + } + + public synchronized int + createProcess(String command) throws DTraceException + { + if (command == null) { + throw new NullPointerException("command is null"); + } + + checkGoNotCalled(); + + int pid; + synchronized (LocalConsumer.class) { + pid = _createProcess(command); + } + return pid; + } + + public synchronized void + grabProcess(int pid) throws DTraceException + { + checkGoNotCalled(); + + synchronized (LocalConsumer.class) { + _grabProcess(pid); + } + } + + public synchronized List <ProbeDescription> + listProbes(ProbeDescription filter) throws DTraceException + { + checkGoNotCalled(); + List <ProbeDescription> probeList = + new LinkedList <ProbeDescription> (); + if (filter == ProbeDescription.EMPTY) { + filter = null; + } + synchronized (LocalConsumer.class) { + _listProbes(probeList, filter); + } + return probeList; + } + + public synchronized List <Probe> + listProbeDetail(ProbeDescription filter) throws DTraceException + { + checkGoNotCalled(); + List <Probe> probeList = new LinkedList <Probe> (); + if (filter == ProbeDescription.EMPTY) { + filter = null; + } + synchronized (LocalConsumer.class) { + _listProbeDetail(probeList, filter); + } + return probeList; + } + + public synchronized List <ProbeDescription> + listProgramProbes(Program program) throws DTraceException + { + checkProgram(program); + checkGoNotCalled(); + List <ProbeDescription> probeList = + new LinkedList <ProbeDescription> (); + synchronized (LocalConsumer.class) { + _listCompiledProbes(probeList, program); + } + return probeList; + } + + public synchronized List <Probe> + listProgramProbeDetail(Program program) throws DTraceException + { + checkProgram(program); + checkGoNotCalled(); + List <Probe> probeList = new LinkedList <Probe> (); + synchronized (LocalConsumer.class) { + _listCompiledProbeDetail(probeList, program); + } + return probeList; + } + + public synchronized String + lookupKernelFunction(int address) + { + checkGoCalled(); + synchronized (LocalConsumer.class) { + return _lookupKernelFunction(new Integer(address)); + } + } + + public synchronized String + lookupKernelFunction(long address) + { + checkGoCalled(); + synchronized (LocalConsumer.class) { + return _lookupKernelFunction(new Long(address)); + } + } + + public synchronized String + lookupUserFunction(int pid, int address) + { + checkGoCalled(); + synchronized (LocalConsumer.class) { + return _lookupUserFunction(pid, new Integer(address)); + } + } + + public synchronized String + lookupUserFunction(int pid, long address) + { + checkGoCalled(); + synchronized (LocalConsumer.class) { + return _lookupUserFunction(pid, new Long(address)); + } + } + + public String + getVersion() + { + synchronized (LocalConsumer.class) { + return LocalConsumer._getVersion(); + } + } + + /** + * Called by native code. + */ + private void + nextProbeData(ProbeData probeData) throws ConsumerException + { + fireDataReceived(new DataEvent(this, probeData)); + } + + /** + * Called by native code. + */ + private void + dataDropped(Drop drop) throws ConsumerException + { + fireDataDropped(new DropEvent(this, drop)); + } + + /** + * Called by native code. + */ + private void + errorEncountered(Error error) throws ConsumerException + { + fireErrorEncountered(new ErrorEvent(this, error)); + } + + /** + * Called by native code. + */ + private void + processStateChanged(ProcessState processState) throws ConsumerException + { + fireProcessStateChanged(new ProcessEvent(this, processState)); + } + + protected void + fireDataReceived(DataEvent e) throws ConsumerException + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).dataReceived(e); + } + } + } + + protected void + fireDataDropped(DropEvent e) throws ConsumerException + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).dataDropped(e); + } + } + } + + protected void + fireErrorEncountered(ErrorEvent e) throws ConsumerException + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).errorEncountered(e); + } + } + } + + protected void + fireProcessStateChanged(ProcessEvent e) throws ConsumerException + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).processStateChanged(e); + } + } + } + + protected void + fireConsumerStarted(ConsumerEvent e) + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).consumerStarted(e); + } + } + } + + protected void + fireConsumerStopped(ConsumerEvent e) + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).consumerStopped(e); + } + } + } + + // Called by native code + private void + intervalBegan() + { + fireIntervalBegan(new ConsumerEvent(this, System.nanoTime())); + } + + protected void + fireIntervalBegan(ConsumerEvent e) + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).intervalBegan(e); + } + } + } + + // Called by native code + private void + intervalEnded() + { + fireIntervalEnded(new ConsumerEvent(this, System.nanoTime())); + } + + protected void + fireIntervalEnded(ConsumerEvent e) + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).intervalEnded(e); + } + } + } + + /** + * Gets a string representation of this consumer useful for logging + * and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(LocalConsumer.class.getName()); + synchronized (this) { + buf.append("[open = "); + buf.append(isOpen()); + buf.append(", enabled = "); + buf.append(isEnabled()); + buf.append(", running = "); + buf.append(isRunning()); + buf.append(", closed = "); + buf.append(isClosed()); + } + buf.append(']'); + return buf.toString(); + } + + /** + * Ensures that the {@link #close()} method of this consumer has + * been called before it is garbage-collected. The intended safety + * net is weak because the JVM does not guarantee that an object + * will be garbage-collected when it is no longer referenced. Users + * of the API should call {@code close()} to ensure that all + * resources associated with this consumer are reclaimed in a timely + * manner. + * + * @see #close() + */ + protected void + finalize() + { + close(); + } + + private String + getTag() + { + return super.toString(); + } + + // + // Uniquely identifies a consumer across systems so it is possible + // to validate that an object such as a Program passed to a remote + // client over a socket was created by this consumer and no other. + // + static class Identifier implements Serializable { + static final long serialVersionUID = 2183165132305302834L; + + // local identifier + private int id; + private long timestamp; + // remote identifier + private InetAddress localHost; + private String tag; // in case localHost not available + + private + Identifier(LocalConsumer consumer) + { + id = LocalConsumer.sequence++; + timestamp = System.currentTimeMillis(); + try { + localHost = InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + localHost = null; + } + tag = consumer.getTag(); + } + + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof Identifier) { + Identifier i = (Identifier)o; + return ((id == i.id) && + (timestamp == i.timestamp) && + ((localHost == null) ? (i.localHost == null) : + localHost.equals(i.localHost)) && + tag.equals(i.tag)); + } + return false; + } + + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + id; + hash = (37 * hash) + ((int)(timestamp ^ (timestamp >>> 32))); + hash = (37 * hash) + (localHost == null ? 0 : + localHost.hashCode()); + hash = (37 * hash) + tag.hashCode(); + return hash; + } + + @Override + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Identifier.class.getName()); + buf.append("[id = "); + buf.append(id); + buf.append(", timestamp = "); + buf.append(timestamp); + buf.append(", localHost = "); + buf.append(localHost); + buf.append(", tag = "); + buf.append(tag); + buf.append(']'); + return buf.toString(); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LogDistribution.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LogDistribution.java new file mode 100644 index 0000000000..fb37303a01 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LogDistribution.java @@ -0,0 +1,175 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A power-of-two logarithmic frequency distribution aggregated by the + * DTrace {@code quantize()} action. Aggregated values fall into + * consecutive ranges, each twice as large as the previous range. Each + * range, known as a bucket, begins at two to the power of <i>n</i> and + * ends at one less than the beginning of the next bucket, two to the + * power of <i>n + 1</i>. The zero bucket is the degenerate case and + * holds the frequency of the base value zero. For example, the first + * bucket after 0 starts at 1 (2 to the power of 0) and ends at 1 (one + * less than 2 to the power of 1). The next bucket starts at 2 (2 to + * the power of 1) and ends at 3 (one less than 2 to the power of 2). + * Each bucket frequency is incremented for each aggregated value that + * falls into its range. Buckets are typically identified by their + * lower bound: 1, 2, 4, 8, etc. Mirroring these are buckets with + * negative ranges: -1, -2, -4, -8, etc. The range of an entire {@code + * LogDistribution} is (<code>-2<sup>63</sup> .. + * 2<sup>63</sup></code>). + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see LinearDistribution + * @see Aggregation + * + * @author Tom Erickson + */ +public final class LogDistribution extends Distribution + implements Serializable, Comparable <LogDistribution> +{ + static final long serialVersionUID = -1279719751212721961L; + + static final int ZERO_BUCKET_INDEX = 63; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(LogDistribution.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"buckets"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Called by native C code + */ + private + LogDistribution(long[] buckets) + { + super(0, 2, buckets); // initializes using base 0, power of 2 + } + + /** + * Creates a logarithmic distribution with the given frequencies. + * Supports XML persistence. + * + * @param frequencies list of frequencies in bucket ranges bounded + * by consucutive powers of two + * @throws NullPointerException if {@code frequencies} is {@code + * null} + * @throws IllegalArgumentException if any bucket does not have the + * expected range (bounded by consecutive powers of two) + */ + public + LogDistribution(List <Bucket> frequencies) + { + super(frequencies); + initialize(); + } + + /** + * Gets a two element array: the first elelemt is the range minimum + * (inclusive), the second element is the range maximum (inclusive). + */ + @Override + long[] + getBucketRange(int i, int len, long base, long constant) + { + long min = LocalConsumer._quantizeBucket(i); + long max = (LocalConsumer._quantizeBucket(i + 1) - 1); + + long[] range = new long[] {min, max}; + return range; + } + + @Override + long[] + getBucketRange(int i, int len) + { + return getBucketRange(i, len, 0, 2); + } + + public Number + getValue() + { + double total = 0; + List <Distribution.Bucket> buckets = getBuckets(); + for (Distribution.Bucket bucket : buckets) { + total += ((double)bucket.getFrequency() * (double)bucket.getMin()); + } + return (new Double(total)); + } + + private long + getZeroBucketValue() + { + Distribution.Bucket b = get(ZERO_BUCKET_INDEX); + return b.getFrequency(); + } + + /** + * Compares the {@code double} values of {@link #getValue()} for + * overall magnitude, and if those are equal, compares the + * frequencies at the zero bucket (the bucket whose range has a + * minimum and maximum value of zero). + */ + public int + compareTo(LogDistribution d) + { + Number v1 = getValue(); + Number v2 = d.getValue(); + double d1 = v1.doubleValue(); + double d2 = v2.doubleValue(); + int cmp = (d1 < d2 ? -1 : (d1 > d2 ? 1 : 0)); + if (cmp == 0) { + long z1 = getZeroBucketValue(); + long z2 = d.getZeroBucketValue(); + cmp = (z1 < z2 ? -1 : (z1 > z2 ? 1 : 0)); + } + return (cmp); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + initialize(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MaxValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MaxValue.java new file mode 100644 index 0000000000..6cf1fa46de --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MaxValue.java @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code max()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class MaxValue extends AbstractAggregationValue { + static final long serialVersionUID = -7761988758448759253L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(MaxValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code max()} action. + * Supports XML persistence. + * + * @param v maximum of the aggregated values + */ + public + MaxValue(long v) + { + super(v); + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the maximum of the aggregated values. + * + * @return the maximum of the aggregated values + */ + public Long + getValue() + { + return (Long)super.getValue(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MinValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MinValue.java new file mode 100644 index 0000000000..1d82557a2c --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MinValue.java @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code min()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class MinValue extends AbstractAggregationValue { + static final long serialVersionUID = -3903522347795401289L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(MinValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code min()} action. + * Supports XML persistence. + * + * @param v minimum of the aggregated values + */ + public + MinValue(long v) + { + super(v); + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the minimum of the aggregated values. + * + * @return the minimum of the aggregated values + */ + public Long + getValue() + { + return (Long)super.getValue(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/NativeException.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/NativeException.java new file mode 100644 index 0000000000..bfb70d911a --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/NativeException.java @@ -0,0 +1,70 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * Wraps a java exception encountered in native code for the purpose of + * adding native source filename and line number, which are otherwise + * not included in the stack trace of the wrapped exception. + * + * @author Tom Erickson + */ +class NativeException extends RuntimeException { + static final long serialVersionUID = 4129171856987233185L; + + /** @serial */ + private String fileName; + /** @serial */ + private int lineNumber; + + public + NativeException(String file, int line, Throwable cause) + { + super(cause); + fileName = file; + lineNumber = line; + } + + public String + getMessage() + { + StringBuffer buf = new StringBuffer(); + buf.append(fileName); + buf.append(" line "); + buf.append(lineNumber); + Throwable cause = getCause(); + if (cause != null) { + String message = cause.getMessage(); + if (message != null) { + buf.append(" "); + buf.append(cause.getMessage()); + } + } + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Option.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Option.java new file mode 100644 index 0000000000..c81707c3cb --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Option.java @@ -0,0 +1,681 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.Serializable; +import java.io.ObjectInputStream; +import java.io.InvalidObjectException; +import java.io.IOException; +import java.beans.*; + +/** + * A DTrace option and its value. Compile-time options must be set + * before calling {@code Consumer} {@link Consumer#compile(String + * program, String[] macroArgs) compile(String program, ...)} or {@link + * Consumer#compile(File program, String[] macroArgs) compile(File + * program, ...)} in order to affect program compilation. Runtime + * options may be set anytime before calling {@code Consumer} {@link + * Consumer#go() go()}, and some of them may be changed while a consumer + * is running. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlis?a=view> + * <b>Options and Tunables</b></a> chapter of the <i>Solaris Dynamic + * Tracing Guide</i>. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class Option implements Serializable { + static final long serialVersionUID = 2734100173861424920L; + + /** + * Value returned by {@link Consumer#getOption(String option)} when + * the given boolean option is unset. + */ + public static final long UNSET = -2L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufpolicy} option when the {@link #VALUE_RING ring} + * buffer policy is set. + */ + public static final long BUFPOLICY_RING = 0L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufpolicy} option when the {@link #VALUE_FILL fill} + * buffer policy is set. + */ + public static final long BUFPOLICY_FILL = 1L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufpolicy} option when the default {@link + * #VALUE_SWITCH switch} buffer policy is set. + */ + public static final long BUFPOLICY_SWITCH = 2L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufresize} option when the default {@link #VALUE_AUTO + * auto} buffer resize policy is set. + */ + public static final long BUFRESIZE_AUTO = 0L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufresize} option when the {@link #VALUE_MANUAL + * manual} buffer resize policy is set. + */ + public static final long BUFRESIZE_MANUAL = 1L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Option.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"option", "value"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + // + // See lib/libdtrace/common/dt_options.c + // + + /** + * Gets a size option value indicating the given number of + * kilobytes. + * + * @param n number of kilobytes + * @return size option value indicating the given number of + * kilobytes + */ + public static String + kb(int n) + { + return (Integer.toString(n) + "k"); + } + + /** + * Gets a size option value indicating the given number of + * megabytes. + * + * @param n number of megabytes + * @return size option value indicating the given number of + * megabytes + */ + public static String + mb(int n) + { + return (Integer.toString(n) + "m"); + } + + /** + * Gets a size option value indicating the given number of + * gigabytes. + * + * @param n number of gigabytes + * @return size option value indicating the given number of + * gigabytes + */ + public static String + gb(int n) + { + return (Integer.toString(n) + "g"); + } + + /** + * Gets a size option value indicating the given number of + * terabytes. + * + * @param n number of terabytes + * @return size option value indicating the given number of + * terabytes + */ + public static String + tb(int n) + { + return (Integer.toString(n) + "t"); + } + + /** + * Gets a time option value indicating the given number of + * nanoseconds. + * + * @param n number of nanoseconds + * @return time option value indicating the given number of + * nanoseconds + */ + public static String + nanos(int n) + { + return (Integer.toString(n) + "ns"); + } + + /** + * Gets a time option value indicating the given number of + * microseconds. + * + * @param n number of microseconds + * @return time option value indicating the given number of + * microseconds + */ + public static String + micros(int n) + { + return (Integer.toString(n) + "us"); + } + + /** + * Gets a time option value indicating the given number of + * milliseconds. + * + * @param n number of milliseconds + * @return time option value indicating the given number of + * milliseconds + */ + public static String + millis(int n) + { + return (Integer.toString(n) + "ms"); + } + + /** + * Gets a time option value indicating the given number of seconds. + * + * @param n number of seconds + * @return time option value indicating the given number of seconds + */ + public static String + seconds(int n) + { + return (Integer.toString(n) + "s"); + } + + /** + * Gets a time option value indicating the given number of minutes. + * + * @param n number of minutes + * @return time option value indicating the given number of minutes + */ + public static String + minutes(int n) + { + return (Integer.toString(n) + "m"); + } + + /** + * Gets a time option value indicating the given number of hours. + * + * @param n number of hours + * @return time option value indicating the given number of hours + */ + public static String + hours(int n) + { + return (Integer.toString(n) + "h"); + } + + /** + * Gets a time option value indicating the given number of days. + * + * @param n number of days + * @return time option value indicating the given number of days + */ + public static String + days(int n) + { + return (Integer.toString(n) + "d"); + } + + /** + * Gets a time option value indicating the given rate per second. + * + * @param n number of cycles per second (hertz) + * @return time option value indicating rate per second + */ + public static String + hz(int n) + { + return (Integer.toString(n) + "hz"); + } + + /** + * May be passed to {@link Consumer#setOption(String option, String + * value)} to set a boolean option such as {@link #flowindent}. + * However, a more convenient way to set boolean options is {@link + * Consumer#setOption(String option)}. + */ + public static final String VALUE_SET = "set"; + + /** + * May be passed to {@link Consumer#setOption(String option, String + * value)} to unset a boolean option such as {@link #flowindent}. + * However, a more convenient way to unset boolean options is {@link + * Consumer#unsetOption(String option)}. + */ + public static final String VALUE_UNSET = "unset"; + + /** + * {@link #bufpolicy} value: use {@code ring} princical buffer + * policy. + */ + public static final String VALUE_RING = "ring"; + /** + * {@link #bufpolicy} value: use {@code fill} princical buffer + * policy. + */ + public static final String VALUE_FILL = "fill"; + /** + * {@link #bufpolicy} default value: use {@code switch} princical + * buffer policy. + */ + public static final String VALUE_SWITCH = "switch"; + + /** + * {@link #bufresize} default value: use {@code auto} buffer + * resizing policy. + */ + public static final String VALUE_AUTO = "auto"; + /** + * {@link #bufresize} value: use {@code manual} buffer resizing + * policy. + */ + public static final String VALUE_MANUAL = "manual"; + + // + // See lib/libdtrace/common/dt_options.c + // + + /** + * Set program attribute minimum (compile-time). The format of the + * option value is defined by the {@link + * InterfaceAttributes#toString()} method. + * + * @see Program#getInfo() + */ + public static final String amin = "amin"; + /** + * Do not require all macro args to be used (compile-time; no option + * value). + * + * @see Consumer#compile(String program, String[] macroArgs) + * @see Consumer#compile(File program, String[] macroArgs) + */ + public static final String argref = "argref"; + /** + * Run cpp(1) preprocessor on D script files (compile-time; no + * option value). + */ + public static final String cpp = "cpp"; + /** + * Used together with {@link #cpp} option, specifies which {@code + * cpp} to run by its pathname (compile-time). + */ + public static final String cpppath = "cpppath"; + /** + * Use zero (0) or empty string ("") as the value for unspecified macro args + * (compile-time; no option value). + * + * @see Consumer#compile(String program, String[] macroArgs) + * @see Consumer#compile(File program, String[] macroArgs) + */ + public static final String defaultargs = "defaultargs"; + /** + * Define symbol when invoking preprocssor (compile-time). + */ + public static final String define = "define"; + /** + * Permit compilation of empty D source files (compile-time; no + * option value). + */ + public static final String empty = "empty"; + /** + * Adds error tags to default error messages (compile-time; no + * option value). + */ + public static final String errtags = "errtags"; + /** + * Add include directory to preprocessor search path (compile-time). + */ + public static final String incdir = "incdir"; + /** + * Permit unresolved kernel symbols (compile-time; no option value). + */ + public static final String knodefs = "knodefs"; + /** + * Add library directory to library search path (compile-time). + */ + public static final String libdir = "libdir"; + /** + * Specify ISO C conformance settings for preprocessor + * (compile-time). + */ + public static final String stdc = "stdc"; + /** + * Undefine symbol when invoking preprocessor (compile-time). + */ + public static final String undef = "undef"; + /** + * Permit unresolved user symbols (compile-time; no option value). + */ + public static final String unodefs = "unodefs"; + /** + * Request specific version of native DTrace library (compile-time). + */ + public static final String version = "version"; + /** + * Permit probe definitions that match zero probes (compile-time; no + * option value). + */ + public static final String zdefs = "zdefs"; + + /** Rate of aggregation reading (time). Runtime option. */ + public static final String aggrate = "aggrate"; + /** Aggregation buffer size (size). Runtime option. */ + public static final String aggsize = "aggsize"; + /** + * Denotes that aggregation data should be sorted in tuple order, + * with ties broken by value order (no option value). Runtime + * option. + * + * @see AggregationRecord + * @see Option#aggsortkeypos + * @see Option#aggsortpos + * @see Option#aggsortrev + */ + public static final String aggsortkey = "aggsortkey"; + /** + * When multiple aggregation tuple elements are present, the + * position of the tuple element that should act as the primary sort + * key (zero-based index). Runtime option. + * + * @see Option#aggsortkey + * @see Option#aggsortpos + * @see Option#aggsortrev + */ + public static final String aggsortkeypos = "aggsortkeypos"; + /** + * When multiple aggregations are being printed, the position of the + * aggregation that should act as the primary sort key (zero-based + * index). Runtime option. + * <p> + * Here "position" refers to the position of the aggregation in the + * {@code printa()} argument list after the format string (if + * any). For example, given the following statement: + * <pre><code> + * printa("%d %@7d %@7d\n", @a, @b); + * </code></pre> + * setting {@code aggsortpos} to {@code "0"} indicates that output + * should be sorted using the values of {@code @a} as the primary + * sort key, while setting {@code aggsortpos} to {@code "1"} + * indicates that output should be sorted using the values of + * {@code @b} as the primary sort key. + * + * @see Option#aggsortkey + * @see Option#aggsortkeypos + * @see Option#aggsortrev + */ + public static final String aggsortpos = "aggsortpos"; + /** + * Denotes that aggregation data should be sorted in descending + * order (no option value). Runtime option. + * <p> + * The {@code aggsortrev} option is useful in combination with the + * {@code aggsortkey}, {@code aggsortkeypos}, and {@code aggsortpos} + * options, which define the ascending sort reversed by this option. + * + * @see Option#aggsortkey + * @see Option#aggsortkeypos + * @see Option#aggsortpos + */ + public static final String aggsortrev = "aggsortrev"; + /** Principal buffer size (size). Runtime option. */ + public static final String bufsize = "bufsize"; + /** + * Buffering policy ({@link #VALUE_SWITCH switch}, {@link + * #VALUE_FILL fill}, or {@link #VALUE_RING ring}). Runtime option. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhr?a=view> + * <b>Principal Buffer Policies</b></a> section of the + * <b>Buffers and Buffering</b> chapter of the <i>Solaris Dynamic + * Tracing Guide</i>. + */ + public static final String bufpolicy = "bufpolicy"; + /** + * Buffer resizing policy ({@link #VALUE_AUTO auto} or {@link + * #VALUE_MANUAL manual}). Runtime option. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhu?a=view> + * <b>Buffer Resizing Policy</b></a> section of the <b>Buffers + * and Buffering</b> chapter of the <i>Solaris Dynamic Tracing + * Guide</i>. + */ + public static final String bufresize = "bufresize"; + /** Cleaning rate (time). Runtime option. */ + public static final String cleanrate = "cleanrate"; + /** CPU on which to enable tracing (scalar). Runtime option. */ + public static final String cpu = "cpu"; + /** Permit destructive actions (no option value). Runtime option. */ + public static final String destructive = "destructive"; + /** Dynamic variable space size (size). Runtime option. */ + public static final String dynvarsize = "dynvarsize"; + /** + * Adds {@link Flow} information to generated {@link ProbeData} + * indicating direction of control flow (entry or return) across + * function boundaries and depth in call stack (no option value). + * Runtime option. + */ + public static final String flowindent = "flowindent"; + /** Number of speculations (scalar). Runtime option. */ + public static final String nspec = "nspec"; + /** + * Only output explicitly traced data (no option value). Makes no + * difference to generated {@link ProbeData}, but user apps may use + * the {@code quiet} flag as a rendering hint similar to the {@code + * -q} {@code dtrace(1M)} command option. Runtime option. + */ + public static final String quiet = "quiet"; + /** Speculation buffer size (size). Runtime option. */ + public static final String specsize = "specsize"; + /** Number of stack frames (scalar). Runtime option. */ + public static final String stackframes = "stackframes"; + /** Rate of status checking (time). Runtime option. */ + public static final String statusrate = "statusrate"; + /** String size (size). Runtime option. */ + public static final String strsize = "strsize"; + /** Rate of buffer switching (time). Runtime option. */ + public static final String switchrate = "switchrate"; + /** Number of user stack frames (scalar). Runtime option. */ + public static final String ustackframes = "ustackframes"; + + /** @serial */ + private final String option; + /** @serial */ + private final String value; + + /** + * Creates an option without an associated value. The created + * boolean option has the value {@link Option#VALUE_SET}. To + * specify that the named option be unset, use {@link + * Option#VALUE_UNSET}. + * + * @param opt DTrace option name + * @throws NullPointerException if the given option name is {@code + * null} + * @see #Option(String opt, String val) + */ + public + Option(String opt) + { + this(opt, Option.VALUE_SET); + } + + /** + * Creates an option with the given name and value. + * + * @param opt DTrace option name + * @param val DTrace option value + * @throws NullPointerException if the given option name or value is + * {@code null} + */ + public + Option(String opt, String val) + { + option = opt; + value = val; + validate(); + } + + private void + validate() + { + if (option == null) { + throw new NullPointerException("option name is null"); + } + if (value == null) { + throw new NullPointerException("option value is null"); + } + } + + /** + * Gets the option name. + * + * @return non-null option name + */ + public String + getOption() + { + return option; + } + + /** + * Gets the option value. + * + * @return option value, or {@code null} if no value is associated + * with the option + */ + public String + getValue() + { + return value; + } + + /** + * Compares the specified object with this option for equality. + * Defines equality as having equal names and values. + * + * @return {@code true} if and only if the specified object is an + * {@code Option} with the same name and the same value as this + * option. Option values are the same if they are both {@code null} + * or if they are equal as defined by {@link String#equals(Object o) + * String.equals()}. + */ + public boolean + equals(Object o) + { + if (o instanceof Option) { + Option opt = (Option)o; + return (option.equals(opt.option) && + value.equals(opt.value)); + } + return false; + } + + /** + * Overridden to ensure that equal options have equal hashcodes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + option.hashCode(); + hash = (37 * hash) + value.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this option useful for logging + * and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Option.class.getName()); + buf.append("[option = "); + buf.append(option); + buf.append(", value = "); + buf.append(value); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintaRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintaRecord.java new file mode 100644 index 0000000000..e24e31896b --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintaRecord.java @@ -0,0 +1,482 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; +import java.util.*; + +/** + * A record generated by the DTrace {@code printa()} action. Lists the + * aggregations passed to {@code printa()} and records the formatted + * output associated with each {@link Tuple}. If multiple aggregations + * were passed to the {@code printa()} action that generated this + * record, then the DTrace library tabulates the output, using a default + * format if no format string was specified. By default, the output + * string associated with a given {@code Tuple} includes a value from + * each aggregation, or zero wherever an aggregation has no value + * associated with that {@code Tuple}. For example, the D statements + * <pre><code> + * @a[123] = sum(1); + * @b[456] = sum(2); + * printa(@a, @b, @c); + * </code></pre> + * produce output for the tuples "123" and "456" similar to the + * following: + * <pre><code> + * 123 1 0 0 + * 456 0 2 0 + * </code></pre> + * The first column after the tuple contains values from {@code @a}, + * the next column contains values from {@code @b}, and the last + * column contains zeros because {@code @c} has neither a value + * associated with "123" nor a value associated with "456". + * <p> + * If a format string is passed to {@code printa()}, it may limit the + * aggregation data available in this record. For example, if the + * format string specifies a value placeholder for only one of two + * aggregations passed to {@code printa()}, then the resulting {@code + * PrintaRecord} will contain only one {@code Aggregation}. If no value + * placeholder is specified, or if the aggregation tuple is not + * completely specified, the resulting {@code PrintaRecord} will contain + * no aggregation data. However, the formatted output generated by the + * DTrace library is available in all cases. For details about + * {@code printa()} format strings, see the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidli3?a=view> + * <b>{@code printa()}</b></a> section of the <b>Output + * Formatting</b> chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class PrintaRecord implements Record, Serializable { + static final long serialVersionUID = -4174277639915895694L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(PrintaRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"snaptime", "aggregations", + "formattedStrings", "tuples", "output"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final long snaptime; + /** @serial */ + private List <Aggregation> aggregations; + /** @serial */ + private Map <Tuple, String> formattedStrings; + /** @serial */ + private List <Tuple> tuples; + private transient StringBuffer outputBuffer; + private transient String output; + private transient boolean formatted; + + /** + * Package level access, called by ProbeData. + */ + PrintaRecord(long snaptimeNanos, boolean isFormatString) + { + snaptime = snaptimeNanos; + aggregations = new ArrayList <Aggregation> (); + formattedStrings = new HashMap <Tuple, String> (); + tuples = new ArrayList <Tuple> (); + outputBuffer = new StringBuffer(); + formatted = isFormatString; + validate(); + } + + /** + * Creates a record with the given snaptime, aggregations, and + * formatted output. + * + * @param snaptimeNanos nanosecond timestamp of the snapshot used + * to create this {@code printa()} record + * @param aggs aggregations passed to the {@code printa()} action + * that generated this record + * @param formattedOutput the formatted output, if any, associated + * with each {@code Tuple} occurring in the aggregations belonging + * to this record, one formatted string per {@code Tuple}, or an + * empty or {@code null} map if an incomplete {@code printa()} + * format string caused aggregation tuples to be omitted from this + * record + * @param orderedTuples list of aggregation tuples in the same order + * generated by the native DTrace library (determined by the various + * "aggsort" options such as {@link Option#aggsortkey}) + * @param formattedOutputString {@code printa()} formatted string + * output in the same order generated by the native DTrace library + * (determined by the various "aggsort" options such as + * {@link Option#aggsortkey}) + * @throws NullPointerException if the given collection of + * aggregations is {@code null}, or if the given ordered lists of + * tuples or formatted strings are {@code null} + * @throws IllegalArgumentException if the given snaptime is + * negative + */ + public + PrintaRecord(long snaptimeNanos, Collection <Aggregation> aggs, + Map <Tuple, String> formattedOutput, + List <Tuple> orderedTuples, + String formattedOutputString) + { + snaptime = snaptimeNanos; + if (aggs != null) { + aggregations = new ArrayList <Aggregation> (aggs.size()); + aggregations.addAll(aggs); + } + if (formattedOutput != null) { + formattedStrings = new HashMap <Tuple, String> + (formattedOutput); + } + if (orderedTuples != null) { + tuples = new ArrayList <Tuple> (orderedTuples.size()); + tuples.addAll(orderedTuples); + } + output = formattedOutputString; + validate(); + } + + private void + validate() + { + if (snaptime < 0) { + throw new IllegalArgumentException("snaptime is negative"); + } + if (aggregations == null) { + throw new NullPointerException("aggregations list is null"); + } + Aggregation a; + for (int i = 0, len = aggregations.size(); i < len; ++i) { + a = aggregations.get(i); + if (a == null) { + throw new NullPointerException( + "null aggregation at index " + i); + } + } + if (tuples == null) { + throw new NullPointerException("ordered tuple list is null"); + } + if (output == null && outputBuffer == null) { + throw new NullPointerException("formatted output is null"); + } + } + + /** + * Gets the nanosecond timestamp of the aggregate snapshot used to + * create this {@code printa()} record. + * + * @return nanosecond timestamp + */ + public long + getSnaptime() + { + return snaptime; + } + + private Aggregation + getAggregationImpl(String name) + { + if (name == null) { + return null; + } + for (Aggregation a : aggregations) { + if (name.equals(a.getName())) { + return a; + } + } + return null; + } + + /** + * Gets the named aggregation. + * + * @return the named aggregation passed to {@code printa()}, or + * {@code null} if the named aggregation is not passed to {@code + * printa()}, or if it is omitted due to an incomplete {@code + * printa()} format string, or if it is empty (a future release of + * this API may represent an empty DTrace aggregation as a non-null + * {@code Aggregation} with no records; users of this API should not + * rely on a non-null return value to indicate a non-zero record + * count) + */ + public Aggregation + getAggregation(String name) + { + name = Aggregate.filterUnnamedAggregationName(name); + return getAggregationImpl(name); + } + + /** + * Gets a list of the aggregations passed to the {@code printa()} + * action that generated this record. The returned list is a copy, + * and modifying it has no effect on this record. Supports XML + * persistence. + * + * @return non-null, possibly empty list of aggregations belonging + * to this record (empty aggregations are excluded) + */ + public List <Aggregation> + getAggregations() + { + return new ArrayList <Aggregation> (aggregations); + } + + /** + * Gets the formatted string, if any, associated with the given + * aggregation tuple. + * + * @param key aggregation tuple + * @return the formatted string associated with the given + * aggregation tuple, or {@code null} if the given tuple does not + * exist in the aggregations belonging to this record or if it + * is omitted from this record due to an incomplete {@code printa()} + * format string + * @see #getFormattedStrings() + * @see #getOutput() + */ + public String + getFormattedString(Tuple key) + { + if (formattedStrings == null) { + return null; + } + return formattedStrings.get(key); + } + + /** + * Gets the formatted output, if any, associated with each {@code + * Tuple} occurring in the aggregations belonging to this record, + * one formatted string per {@code Tuple}. Gets an empty map if + * aggregation tuples are omitted from this record due to an + * incomplete {@code printa()} format string. The returned map is a + * copy and modifying it has no effect on this record. Supports XML + * persistence. + * + * @return a map of aggregation tuples and their associated + * formatted output strings, empty if aggregation tuples are omitted + * from this record due to an incomplete {@code printa(}) format + * string + * @see #getFormattedString(Tuple key) + * @see #getOutput() + */ + public Map <Tuple, String> + getFormattedStrings() + { + if (formattedStrings == null) { + return new HashMap <Tuple, String> (); + } + return new HashMap <Tuple, String> (formattedStrings); + } + + /** + * Gets an ordered list of this record's aggregation tuples. The + * returned list is a copy, and modifying it has no effect on this + * record. Supports XML persistence. + * + * @return a non-null list of this record's aggregation tuples in + * the order they were generated by the native DTrace library, as + * determined by the {@link Option#aggsortkey}, {@link + * Option#aggsortrev}, {@link Option#aggsortpos}, and {@link + * Option#aggsortkeypos} options + */ + public List <Tuple> + getTuples() + { + return new ArrayList <Tuple> (tuples); + } + + /** + * Gets this record's formatted output. Supports XML persistence. + * + * @return non-null formatted output in the order generated by the + * native DTrace library, as determined by the {@link + * Option#aggsortkey}, {@link Option#aggsortrev}, {@link + * Option#aggsortpos}, and {@link Option#aggsortkeypos} options + */ + public String + getOutput() + { + if (output == null) { + output = outputBuffer.toString(); + outputBuffer = null; + if ((output.length() == 0) && !formatted) { + output = "\n"; + } + } + return output; + } + + /** + * Package level access, called by ProbeData. + * + * @throws NullPointerException if aggregationName is null + * @throws IllegalStateException if this PrintaRecord has an + * aggregation matching the given name and it already has an + * AggregationRecord with the same tuple key as the given record. + */ + void + addRecord(String aggregationName, long aggid, AggregationRecord record) + { + if (formattedStrings == null) { + // printa() format string does not completely specify tuple + return; + } + + aggregationName = Aggregate.filterUnnamedAggregationName( + aggregationName); + Aggregation aggregation = getAggregationImpl(aggregationName); + if (aggregation == null) { + aggregation = new Aggregation(aggregationName, aggid); + aggregations.add(aggregation); + } + try { + aggregation.addRecord(record); + } catch (IllegalArgumentException e) { + Map <Tuple, AggregationRecord> map = aggregation.asMap(); + AggregationRecord r = map.get(record.getTuple()); + // + // The printa() format string may specify the value of the + // aggregating action multiple times. While that changes + // the resulting formatted string associated with the tuple, + // we ignore the attempt to add the redundant record to the + // aggregation. + // + if (!r.equals(record)) { + throw e; + } + } + } + + // + // Called from native code when the tuple is not completely + // specified in the printa() format string. + // + void + invalidate() + { + formattedStrings = null; + aggregations.clear(); + tuples.clear(); + } + + void + addFormattedString(Tuple tuple, String formattedString) + { + if (tuple != null && formattedStrings != null) { + if (formattedStrings.containsKey(tuple)) { + throw new IllegalArgumentException("A formatted string " + + "for tuple " + tuple + " already exists."); + } else { + formattedStrings.put(tuple, formattedString); + tuples.add(tuple); + } + } + outputBuffer.append(formattedString); + } + + /** + * Serialize this {@code PrintaRecord} instance. + * + * @serialData Serialized fields are emitted, followed by the + * formatted output string. + */ + private void + writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + if (output == null) { + s.writeObject(outputBuffer.toString()); + } else { + s.writeObject(output); + } + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + output = (String)s.readObject(); + // make defensive copy + if (aggregations != null) { + List <Aggregation> copy = new ArrayList <Aggregation> + (aggregations.size()); + copy.addAll(aggregations); + aggregations = copy; + } + if (formattedStrings != null) { + formattedStrings = new HashMap <Tuple, String> (formattedStrings); + } + if (tuples != null) { + List <Tuple> copy = new ArrayList <Tuple> (tuples.size()); + copy.addAll(tuples); + tuples = copy; + } + // check constructor invariants only after defensize copy + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this instance useful for logging + * and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(PrintaRecord.class.getName()); + buf.append("[snaptime = "); + buf.append(snaptime); + buf.append(", aggregations = "); + buf.append(aggregations); + buf.append(", formattedStrings = "); + buf.append(formattedStrings); + buf.append(", tuples = "); + buf.append(tuples); + buf.append(", output = "); + buf.append(getOutput()); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintfRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintfRecord.java new file mode 100644 index 0000000000..d7f5f45db6 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintfRecord.java @@ -0,0 +1,221 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; +import java.util.*; + +/** + * A formatted string generated by the DTrace {@code printf()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class PrintfRecord implements Record, Serializable { + static final long serialVersionUID = 727237355963977675L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(PrintfRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"records", "formattedString"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private List <ValueRecord> records; + /** @serial */ + private String formattedString; + + // package-level access, called by ProbeData + PrintfRecord() + { + records = new ArrayList <ValueRecord> (); + } + + /** + * Creates a record with the unformatted elements passed to the + * DTrace {@code printf()} action and the resulting formatted + * output. Supports XML persistence. + * + * @param v variable number of unformatted elements passed to the + * DTrace {@code printf()} action + * @param s formatted {@code printf()} output + * @throws NullPointerException if the given list or any of its + * elements is {@code null}, or if the given formatted string is + * {@code null} + */ + public + PrintfRecord(List <ValueRecord> v, String s) + { + formattedString = s; + records = new ArrayList <ValueRecord> (v.size()); + records.addAll(v); + validate(); + } + + private void + validate() + { + if (formattedString == null) { + throw new NullPointerException("formatted string is null"); + } + if (records == null) { + throw new NullPointerException("list of format args is null"); + } + for (ValueRecord r : records) { + if (r == null) { + throw new NullPointerException("format arg is null"); + } + } + } + + /** + * Called by ProbeData code to populate record list. + * + * @throws NullPointerException if o is null + */ + void + addUnformattedElement(Object o) + { + records.add(new ScalarRecord(o)); + } + + /** + * Gets the formatted string output of the DTrace {@code printf()} + * action. + * + * @return non-null formatted string output of the DTrace {@code + * printf()} action + */ + public String + getFormattedString() + { + return formattedString; + } + + /** + * Package level access; called by ProbeData + */ + void + setFormattedString(String s) + { + if (s == null) { + throw new NullPointerException("formatted string is null"); + } + formattedString = s; + } + + /** + * Gets the unfomatted elements passed to the DTrace {@code + * printf()} action after the format string. + * + * @return non-null, unmodifiable list of unformatted elements + * passed to the DTrace {@code printf()} action that generated this + * record, in the order they appear in the argument list after the + * format string + */ + public List <ValueRecord> + getRecords() + { + return Collections.unmodifiableList(records); + } + + /** + * Gets the number of DTrace {@code printf()} unformatted elements + * (arguments following the format string). For example, the + * following action + * <pre><code> + * printf("%s %d\n", "cat", 9); + * </code></pre> + * generates a {@code PrintfRecord} with a record count of two. + * + * @return the number of unformatted elements passed to the DTrace + * {@code printf()} action that generated this record. + */ + public int + getRecordCount() + { + return records.size(); + } + + /** + * Gets the unformatted element passed to the DTrace {@code + * printf()} action at the given offset in the {@code printf()} + * argument list after the format string, starting at offset zero + * for the first unformatted element. + * + * @return non-null record representing the unformatted {@code + * printf()} element at the given index (using the same order that + * they appear in the {@code printf()} argument list) + * @throws ArrayIndexOutOfBoundsException if the given index is + * out of range (index < 0 || index >= getRecordCount()) + */ + public ValueRecord + getRecord(int i) + { + return records.get(i); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Defensively copy record list before validating + if (records == null) { + throw new InvalidObjectException("record list is null"); + } + List <ValueRecord> copy = new ArrayList <ValueRecord> (records.size()); + copy.addAll(records); + records = copy; + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the formatted string output of the DTrace {@code printf()} + * action. + */ + public String + toString() + { + return formattedString; + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Probe.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Probe.java new file mode 100644 index 0000000000..9d80b7d2b6 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Probe.java @@ -0,0 +1,201 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A {@link ProbeDescription} identifying a single probe combined with + * information about the identified probe. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#listProbes(ProbeDescription filter) + * @see Consumer#listProgramProbes(Program program) + * + * @author Tom Erickson + */ +public final class Probe implements Serializable { + static final long serialVersionUID = 8917481979541675727L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Probe.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"description", "info"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final ProbeDescription description; + /** @serial */ + private final ProbeInfo info; + + /** + * Creates a {@code Probe} instance with the given identifying + * description and associated probe information. Supports XML + * persistence. + * + * @param probeDescription probe description that identifies a + * single DTrace probe + * @param probeInfo information about the identified probe, {@code + * null} indicating that the information could not be obtained + * @throws NullPointerException if the given probe description is + * {@code null} + */ + public + Probe(ProbeDescription probeDescription, ProbeInfo probeInfo) + { + description = probeDescription; + info = probeInfo; + validate(); + } + + private void + validate() + { + if (description == null) { + throw new NullPointerException("description is null"); + } + } + + /** + * Gets the probe description identifying a single probe described + * by this instance. + * + * @return non-null probe description matching a single probe on the + * system + */ + public ProbeDescription + getDescription() + { + return description; + } + + /** + * Gets information including attributes and argument types of the + * probe identified by {@link #getDescription()}. + * + * @return information about the probe identified by {@link + * #getDescription()}, or {@code null} if that information could not + * be obtained for any reason + */ + public ProbeInfo + getInfo() + { + return info; + } + + /** + * Compares the specified object with this {@code Probe} instance + * for equality. Defines equality as having the same probe + * description. + * + * @return {@code true} if and only if the specified object is also + * a {@code Probe} and both instances return equal values from + * {@link #getDescription()}. + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof Probe) { + Probe p = (Probe)o; + return description.equals(p.description); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return description.hashCode(); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Returns a string representation of this {@code Probe} useful for + * logging and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Probe.class.getName()); + buf.append("[description = "); + buf.append(description); + buf.append(", info = "); + buf.append(info); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeData.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeData.java new file mode 100644 index 0000000000..a8978be334 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeData.java @@ -0,0 +1,693 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * Data generated when a DTrace probe fires, contains one record for + * every record-generating action in the probe. (Some D actions, such + * as {@code clear()}, do not generate a {@code ProbeData} record.) A + * {@link Consumer} gets data from DTrace by registering a {@link + * ConsumerListener listener} to get probe data whenever a probe fires: + * <pre><code> + * Consumer consumer = new LocalConsumer(); + * consumer.addConsumerListener(new ConsumerAdapter() { + * public void dataReceived(DataEvent e) { + * ProbeData probeData = e.getProbeData(); + * System.out.println(probeData); + * } + * }); + * </code></pre> + * Getting DTrace to generate that probe data involves compiling, + * enabling, and running a D program: + * <pre><code> + * try { + * consumer.open(); + * consumer.compile(program); + * consumer.enable(); // instruments code at matching probe points + * consumer.go(); // non-blocking; generates probe data in background + * } catch (DTraceException e) { + * e.printStackTrace(); + * } + * </code></pre> + * Currently the {@code ProbeData} instance does not record a timestamp. + * If you need a timestamp, trace the built-in {@code timestamp} + * variable in your D program. (See the + * <a href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlfv?a=view> + * <b>Built-in Variables</b></a> section of the <b>Variables</b> chapter of + * the <i>Solaris Dynamic Tracing Guide</i>). + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#addConsumerListener(ConsumerListener l) + * @see ConsumerListener#dataReceived(DataEvent e) + * + * @author Tom Erickson + */ +public final class ProbeData implements Serializable, Comparable <ProbeData> { + static final long serialVersionUID = -7021504416192099215L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProbeData.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"enabledProbeID", "CPU", + "enabledProbeDescription", "flow", "records"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + private static Comparator <ProbeData> DEFAULT_CMP; + + static { + try { + DEFAULT_CMP = ProbeData.getComparator(KeyField.RECORDS, + KeyField.EPID); + } catch (Throwable e) { + e.printStackTrace(); + System.exit(1); + } + } + + /** @serial */ + private int epid; + /** @serial */ + private int cpu; + /** @serial */ + private ProbeDescription enabledProbeDescription; + /** @serial */ + private Flow flow; + // Scratch data, one element per native probedata->dtpda_edesc->dtepd_nrecs + // element, cleared after records list is fully populated. + private transient List <Object> nativeElements; + /** @serial */ + private List <Record> records; + + /** + * Enumerates the fields by which {@link ProbeData} may be sorted + * using the {@link #getComparator(KeyField[] f) getComparator()} + * convenience method. + */ + public enum KeyField { + /** Specifies {@link ProbeData#getCPU()} */ + CPU, + /** Specifies {@link ProbeData#getEnabledProbeDescription()} */ + PROBE, + /** Specifies {@link ProbeData#getEnabledProbeID()} */ + EPID, + /** Specifies {@link ProbeData#getRecords()} */ + RECORDS + } + + /** + * Called by native code. + */ + private + ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, + Flow f, int nativeElementCount) + { + epid = enabledProbeID; + cpu = cpuID; + enabledProbeDescription = p; + flow = f; + nativeElements = new ArrayList <Object> (nativeElementCount); + records = new ArrayList <Record> (); + validate(); + } + + /** + * Creates a probe data instance with the given properties and list + * of records. Supports XML persistence. + * + * @param enabledProbeID identifies the enabled probe that fired; + * the ID is generated by the native DTrace library to distinguish + * all probes enabled by the source consumer (as opposed to + * all probes on the system) + * @param cpuID non-negative ID, identifies the CPU on which the + * probe fired + * @param p identifies the enabled probe that fired + * @param f current state of control flow (entry or return and depth + * in call stack) at time of probe firing, included if {@link + * Option#flowindent flowindent} option used, {@code null} otherwise + * @param recordList list of records generated by D actions in the + * probe that fired, one record per action, may be empty + * @throws NullPointerException if the given probe description or + * list of records is {@code null} + */ + public + ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, + Flow f, List <Record> recordList) + { + epid = enabledProbeID; + cpu = cpuID; + enabledProbeDescription = p; + flow = f; + records = new ArrayList <Record> (recordList.size()); + records.addAll(recordList); + validate(); + } + + private void + validate() + { + if (enabledProbeDescription == null) { + throw new NullPointerException( + "enabled probe description is null"); + } + if (records == null) { + throw new NullPointerException("record list is null"); + } + } + + private void + addDataElement(Object o) + { + nativeElements.add(o); + } + + /** + * Called by native code. + */ + private void + addRecord(Record record) + { + records.add(record); + } + + /** + * Called by native code. + */ + private void + addTraceRecord(int i) + { + // trace() value is preceded by one null for every D program + // statement preceding trace() that is not a D action, such as + // assignment to a variable (results in a native probedata + // record with no data). + int len = nativeElements.size(); + Object o = null; + for (; ((o = nativeElements.get(i)) == null) && (i < len); ++i); + records.add(new ScalarRecord(o)); + } + + /** + * Called by native code. + */ + private void + addStackRecord(int i, String framesString) + { + int len = nativeElements.size(); + Object o = null; + for (; ((o = nativeElements.get(i)) == null) && (i < len); ++i); + StackValueRecord stack = (StackValueRecord)o; + StackFrame[] frames = KernelStackRecord.parse(framesString); + if (stack instanceof KernelStackRecord) { + ((KernelStackRecord)stack).setStackFrames(frames); + } else if (stack instanceof UserStackRecord) { + ((UserStackRecord)stack).setStackFrames(frames); + } else { + throw new IllegalStateException("no stack record at index " + i); + } + records.add(stack); + } + + /** + * Called by native code. + */ + private void + addPrintfRecord() + { + records.add(new PrintfRecord()); + } + + /** + * Called by native code. + */ + private void + addPrintaRecord(long snaptimeNanos, boolean isFormatString) + { + records.add(new PrintaRecord(snaptimeNanos, isFormatString)); + } + + private PrintaRecord + getLastPrinta() + { + ListIterator <Record> itr = records.listIterator(records.size()); + PrintaRecord printa = null; + Record record; + while (itr.hasPrevious() && (printa == null)) { + record = itr.previous(); + if (record instanceof PrintaRecord) { + printa = (PrintaRecord)record; + } + } + return printa; + } + + /** + * Called by native code. + */ + private void + addAggregationRecord(String aggregationName, long aggid, + AggregationRecord rec) + { + PrintaRecord printa = getLastPrinta(); + if (printa == null) { + throw new IllegalStateException( + "No PrintaRecord in this ProbeData"); + } + printa.addRecord(aggregationName, aggid, rec); + } + + /** + * Called by native code. + */ + private void + invalidatePrintaRecord() + { + PrintaRecord printa = getLastPrinta(); + if (printa == null) { + throw new IllegalStateException( + "No PrintaRecord in this ProbeData"); + } + printa.invalidate(); + } + + /** + * Called by native code. + */ + private void + addPrintaFormattedString(Tuple tuple, String s) + { + PrintaRecord printa = getLastPrinta(); + if (printa == null) { + throw new IllegalStateException( + "No PrintaRecord in this ProbeData"); + } + printa.addFormattedString(tuple, s); + } + + /** + * Called by native code. + */ + private void + addExitRecord(int i) + { + int len = nativeElements.size(); + Object o = null; + for (; ((o = nativeElements.get(i)) == null) && (i < len); ++i); + Integer exitStatus = (Integer)o; + records.add(new ExitRecord(exitStatus)); + } + + /** + * Called by native code. Attaches native probedata elements cached + * between the given first index and last index inclusive to the most + * recently added record if applicable. + */ + private void + attachRecordElements(int first, int last) + { + Record record = records.get(records.size() - 1); + if (record instanceof PrintfRecord) { + PrintfRecord printf = (PrintfRecord)record; + Object e; + for (int i = first; i <= last; ++i) { + e = nativeElements.get(i); + if (e == null) { + // printf() unformatted elements are preceded by one + // null for every D program statement preceding the + // printf() that is not a D action, such as + // assignment to a variable (generates a probedata + // record with no data). + continue; + } + printf.addUnformattedElement(e); + } + } + } + + /** + * Called by native code. + */ + void + clearNativeElements() + { + nativeElements = null; + } + + /** + * Called by native code. + */ + private void + setFormattedString(String s) + { + Record record = records.get(records.size() - 1); + if (record instanceof PrintfRecord) { + PrintfRecord printf = (PrintfRecord)record; + printf.setFormattedString(s); + } + } + + /** + * Convenience method, gets a comparator that sorts multiple {@link + * ProbeDescription} instances by the specified field or fields. If + * more than one sort field is specified, the probe data are sorted + * by the first field, and in case of a tie, by the second field, + * and so on, in the order that the fields are specified. + * + * @param f field specifiers given in descending order of sort + * priority; lower priority fields are only compared (as a tie + * breaker) when all higher priority fields are equal + * @return non-null probe data comparator that sorts by the + * specified sort fields in the given order + */ + public static Comparator <ProbeData> + getComparator(KeyField ... f) + { + return new Cmp(f); + } + + private static class Cmp implements Comparator <ProbeData> { + private KeyField[] sortFields; + + private + Cmp(KeyField ... f) + { + sortFields = f; + } + + public int + compare(ProbeData d1, ProbeData d2) + { + return ProbeData.compare(d1, d2, sortFields); + } + } + + /** + * @throws ClassCastException if records or their data are are not + * mutually comparable + */ + @SuppressWarnings("unchecked") + private static int + compareRecords(Record r1, Record r2) + { + int cmp; + if (r1 instanceof ScalarRecord) { + ScalarRecord t1 = ScalarRecord.class.cast(r1); + ScalarRecord t2 = ScalarRecord.class.cast(r2); + Comparable v1 = Comparable.class.cast(t1.getValue()); + Comparable v2 = Comparable.class.cast(t2.getValue()); + cmp = v1.compareTo(v2); + } else if (r1 instanceof PrintfRecord) { + PrintfRecord t1 = PrintfRecord.class.cast(r1); + PrintfRecord t2 = PrintfRecord.class.cast(r2); + String s1 = t1.toString(); + String s2 = t2.toString(); + cmp = s1.compareTo(s2); + } else if (r1 instanceof ExitRecord) { + ExitRecord e1 = ExitRecord.class.cast(r1); + ExitRecord e2 = ExitRecord.class.cast(r2); + int status1 = e1.getStatus(); + int status2 = e2.getStatus(); + cmp = (status1 < status2 ? -1 : (status1 > status2 ? 1 : 0)); + } else { + throw new IllegalArgumentException("Unexpected record type: " + + r1.getClass()); + } + + return cmp; + } + + /** + * @throws ClassCastException if lists are not mutually comparable + * because corresponding list elements are not comparable or the + * list themselves are different lengths + */ + private static int + compareRecordLists(ProbeData d1, ProbeData d2) + { + List <Record> list1 = d1.getRecords(); + List <Record> list2 = d2.getRecords(); + int len1 = list1.size(); + int len2 = list2.size(); + if (len1 != len2) { + throw new ClassCastException("Record lists of different " + + "length are not comparable (lengths are " + + len1 + " and " + len2 + ")."); + } + + int cmp; + Record r1; + Record r2; + + for (int i = 0; (i < len1) && (i < len2); ++i) { + r1 = list1.get(i); + r2 = list2.get(i); + + cmp = compareRecords(r1, r2); + if (cmp != 0) { + return cmp; + } + } + + return 0; + } + + private static int + compare(ProbeData d1, ProbeData d2, KeyField[] comparedFields) + { + int cmp; + for (KeyField f : comparedFields) { + switch (f) { + case CPU: + int cpu1 = d1.getCPU(); + int cpu2 = d2.getCPU(); + cmp = (cpu1 < cpu2 ? -1 : (cpu1 > cpu2 ? 1 : 0)); + break; + case PROBE: + ProbeDescription p1 = d1.getEnabledProbeDescription(); + ProbeDescription p2 = d2.getEnabledProbeDescription(); + cmp = p1.compareTo(p2); + break; + case EPID: + int epid1 = d1.getEnabledProbeID(); + int epid2 = d2.getEnabledProbeID(); + cmp = (epid1 < epid2 ? -1 : (epid1 > epid2 ? 1 : 0)); + break; + case RECORDS: + cmp = compareRecordLists(d1, d2); + break; + default: + throw new IllegalArgumentException( + "Unexpected sort field " + f); + } + + if (cmp != 0) { + return cmp; + } + } + + return 0; + } + + /** + * Gets the enabled probe ID. Identifies the enabled probe that + * fired and generated this {@code ProbeData}. The "epid" is + * different from {@link ProbeDescription#getID()} in that it + * identifies a probe among all probes enabled by the source {@link + * Consumer}, rather than among all the probes on the system. + * + * @return the enabled probe ID generated by the native DTrace + * library + */ + public int + getEnabledProbeID() + { + return epid; + } + + /** + * Gets the ID of the CPU on which the probe fired. + * + * @return ID of the CPU on which the probe fired + */ + public int + getCPU() + { + return cpu; + } + + /** + * Gets the enabled probe description. Identifies the enabled probe + * that fired and generated this {@code ProbeData}. + * + * @return non-null probe description + */ + public ProbeDescription + getEnabledProbeDescription() + { + return enabledProbeDescription; + } + + /** + * Gets the current state of control flow (function entry or return, + * and depth in call stack) at the time of the probe firing that + * generated this {@code ProbeData} instance, or {@code null} if + * such information was not requested with the {@code flowindent} + * option. + * + * @return a description of control flow across function boundaries, + * or {@code null} if {@code Consumer.getOption(Option.flowindent)} + * returns {@link Option#UNSET} + * @see Consumer#setOption(String option) + * @see Option#flowindent + */ + public Flow + getFlow() + { + return flow; + } + + /** + * Gets the records generated by the actions of the probe that + * fired, in the same order as the actions that generated the + * records. The returned list includes one record for every + * record-generating D action (some D actions, such as {@code + * clear()}, do not generate records). + * + * @return non-null, unmodifiable list view of the records belonging + * to this {@code ProbeData} in the order of the actions in the + * DTrace probe that generated them (record-producing actions are + * generally those that produce output, such as {@code printf()}, + * but also the {@code exit()} action) + */ + public List <Record> + getRecords() + { + return Collections.unmodifiableList(records); + } + + /** + * Natural ordering of probe data. Sorts probe data by records + * first, then if record data is equal, by enabled probe ID. + * + * @param d probe data to be compared with this probe data + * @return a negative number, zero, or a positive number as this + * probe data is less than, equal to, or greater than the given + * probe data + * @see ProbeData#getComparator(KeyField[] f) + * @throws NullPointerException if the given probe data is + * {@code null} + * @throws ClassCastException if record lists of both {@code + * ProbeData} instances are not mutually comparable because + * corresponding list elements are not comparable or the lists + * themselves are different lengths + */ + public int + compareTo(ProbeData d) + { + return DEFAULT_CMP.compare(this, d); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Defensively copy record list _before_ validating. + int len = records.size(); + ArrayList <Record> copy = new ArrayList <Record> (len); + copy.addAll(records); + records = copy; + // Check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this {@code ProbeData} instance + * useful for logging and not intended for display. The exact + * details of the representation are unspecified and subject to + * change, but the following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProbeData.class.getName()); + buf.append("[epid = "); + buf.append(epid); + buf.append(", cpu = "); + buf.append(cpu); + buf.append(", enabledProbeDescription = "); + buf.append(enabledProbeDescription); + buf.append(", flow = "); + buf.append(flow); + buf.append(", records = "); + + Record record; + Object value; + buf.append('['); + for (int i = 0; i < records.size(); ++i) { + if (i > 0) { + buf.append(", "); + } + record = records.get(i); + if (record instanceof ValueRecord) { + value = ((ValueRecord)record).getValue(); + if (value instanceof String) { + buf.append("\""); + buf.append((String)value); + buf.append("\""); + } else { + buf.append(record); + } + } else { + buf.append(record); + } + } + buf.append(']'); + + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeDescription.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeDescription.java new file mode 100644 index 0000000000..99928b6c9e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeDescription.java @@ -0,0 +1,467 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.text.ParseException; +import java.io.*; +import java.beans.*; + +/** + * A DTrace probe description consists of provider, module, function, + * and name. A single probe description may identify a single DTrace + * probe or match multiple probes. Any field may be wildcarded by + * omission (set to null) or set to a glob-style pattern: + * <pre> + * * Matches any string, including the null string + * ? Matches any single character + * [ ... ] Matches any one of the enclosed characters. A pair of + * characters separated by - matches any character + * between the pair, inclusive. If the first + * character after the [ is !, any character not + * enclosed in the set is matched. + * \ Interpret the next character as itself, without any + * special meaning + * </pre> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#listProbes(ProbeDescription filter) + * + * @author Tom Erickson + */ +public final class ProbeDescription implements Serializable, + Comparable <ProbeDescription> +{ + static final long serialVersionUID = 5978023304364513667L; + + /** + * Instance with empty provider, module, function, and name fields + * matches all DTrace probes on a system. + */ + public static final ProbeDescription EMPTY = + new ProbeDescription(null, null, null, null); + + private static final int ID_NONE = -1; + + /** + * Enumerates the provider, module, function, and name fields of a + * probe description. + */ + public enum Spec { + /** Probe provider */ + PROVIDER, + /** Probe module */ + MODULE, + /** Probe function */ + FUNCTION, + /** Probe name (unqualified) */ + NAME + }; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProbeDescription.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"id", "provider", "module", + "function", "name"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private int id = ID_NONE; // set by native code + + /** @serial */ + private final String provider; + /** @serial */ + private final String module; + /** @serial */ + private final String function; + /** @serial */ + private final String name; + + /** + * Creates a probe description that specifies only the unqualified + * probe name. + * + * @see ProbeDescription#ProbeDescription(String probeProvider, + * String probeModule, String probeFunction, String probeName) + */ + public + ProbeDescription(String probeName) + { + this(null, null, null, probeName); + } + + /** + * Creates a probe description that specifies the probe name + * qualified only by the function name. + * + * @see ProbeDescription#ProbeDescription(String probeProvider, + * String probeModule, String probeFunction, String probeName) + */ + public + ProbeDescription(String probeFunction, String probeName) + { + this(null, null, probeFunction, probeName); + } + + /** + * Creates a probe description that specifies the probe name + * qualified by the function name and module name. + * + * @see ProbeDescription#ProbeDescription(String probeProvider, + * String probeModule, String probeFunction, String probeName) + */ + public + ProbeDescription(String probeModule, String probeFunction, + String probeName) + { + this(null, probeModule, probeFunction, probeName); + } + + /** + * Creates a fully qualified probe description. If no pattern + * syntax is used and no field is omitted, the resulting description + * matches at most one DTrace probe. + * + * @param probeProvider provider name, may be null or empty to match + * all providers or use pattern syntax to match multiple providers + * @param probeModule module name, may be null or empty to match all + * modules or use pattern syntax to match multiple modules + * @param probeFunction function name, may be null or empty to match + * all functions or use pattern syntax to match multiple functions + * @param probeName unqualified probe name, may be null or empty to + * match all names or use pattern syntax to match multiple names + */ + public + ProbeDescription(String probeProvider, + String probeModule, + String probeFunction, + String probeName) + { + provider = ((probeProvider == null) ? "" : probeProvider); + module = ((probeModule == null) ? "" : probeModule); + function = ((probeFunction == null) ? "" : probeFunction); + name = ((probeName == null) ? "" : probeName); + } + + /** + * Supports XML persistence. + */ + public + ProbeDescription(int probeID, + String probeProvider, + String probeModule, + String probeFunction, + String probeName) + { + this(probeProvider, probeModule, probeFunction, probeName); + id = probeID; + } + + /** + * Generates a probe description from a string in the same format + * returned by {@link #toString()}. Parses the string from right to + * left. + * <pre><code> + * <i>provider:module:function:name</i> + * </code></pre> + * + * @return non-null probe description + * @throws ParseException if {@code s} does not have the expected + * format. The error offset is the index of the first unexpected + * character encountered starting from the last character and + * reading backwards. + * @throws NullPointerException if the given string is {@code null} + */ + public static ProbeDescription + parse(String s) throws ParseException + { + ProbeDescription p; + + // StringTokenizer and String.split() do not correctly handle + // the case of consecutive delimiters + List <String> list = new ArrayList <String> (); + int len = s.length(); + int npos = len; + char ch; + for (int i = (len - 1); i >= 0; --i) { + ch = s.charAt(i); + if (ch == ':') { + list.add(0, s.substring((i + 1), npos)); + npos = i; + } + } + list.add(0, s.substring(0, npos)); + + switch (list.size()) { + case 0: + p = EMPTY; + break; + case 1: + p = new ProbeDescription(list.get(0)); + break; + case 2: + p = new ProbeDescription(list.get(0), list.get(1)); + break; + case 3: + p = new ProbeDescription(list.get(0), list.get(1), + list.get(2)); + break; + case 4: + p = new ProbeDescription(list.get(0), list.get(1), + list.get(2), list.get(3)); + break; + default: + // get error offset (parsing right-to-left) + int offset = (s.length() - 4); + len = list.size(); + for (int i = (len - 1); i >= (len - 4); --i) { + offset -= list.get(i).length(); + } + throw new ParseException("Overspecified probe " + + "description: \"" + s + "\"", offset); + } + return p; + } + + /** + * Gets the probe ID. + * + * @return ID generated from a sequence by the native DTrace + * library, identifies the probe among all probes on the system + */ + public int + getID() + { + return id; + } + + /** + * Gets the provider name. + * + * @return non-null provider name, may be an empty string to + * indicate omission + */ + public String + getProvider() + { + return provider; + } + + /** + * Gets the module name. + * + * @return non-null module name, may be an empty string to indicate + * omission + */ + public String + getModule() + { + return module; + } + + /** + * Gets the function name. + * + * @return non-null function name, may be an empty string to + * indicate omission + */ + public String + getFunction() + { + return function; + } + + /** + * Gets the unqualified probe name. + * + * @return non-null probe name, may be an empty string to indicate + * omission + */ + public String + getName() + { + return name; + } + + /** + * Returns {@code true} if provider, module, function, and name are + * all omitted. An empty probe description matches all DTrace + * probes on a system. + * + * @return {@code true} if all probe fields are omitted, {@code + * false} otherwise + */ + public boolean + isEmpty() + { + if (provider.length() > 0) { + return false; + } + if (module.length() > 0) { + return false; + } + if (function.length() > 0) { + return false; + } + if (name.length() > 0) { + return false; + } + return true; + } + + /** + * Compares the specified object with this probe description for + * equality. Defines equality as having the same fields. Omitted + * fields must be omitted in both instances in order for them to be + * equal, but it makes no difference whether {@code null} or empty + * string was used to indicate omission. + * + * @return {@code true} if and only if all corresponding fields of + * both probe descriptions are either both omitted (null or empty) + * or else equal as defined by {@link String#equals(Object o) + * String.equals()} + */ + public boolean + equals(Object o) + { + if (o instanceof ProbeDescription) { + ProbeDescription p = (ProbeDescription)o; + if ((id == ID_NONE) || (p.id == ID_NONE)) { + return (compareTo(p) == 0); + } else { + return (id == p.id); + } + } + + return false; + } + + /** + * Defines the natural ordering of probe descriptions. Returns the + * natural ordering of the first unequal pair of corresponding + * fields (starting with the provider and continuing to the + * unqualified name only if all other fields are equal). + * Corresponding fields are equal if they are both omitted or both + * equal as defined by {@link String#equals(Object o) + * String.equals()}. It makes no difference if {@code null} or + * empty string is used to indicate omission. The behavior is + * consistent with the {@link #equals(Object o) equals()} method. + * + * @return -1, 0, or 1 as this probe description is less than, equal + * to, or greater than the given probe description + */ + public int + compareTo(ProbeDescription p) + { + int cmp = 0; + cmp = provider.compareTo(p.provider); + if (cmp == 0) { + cmp = module.compareTo(p.module); + if (cmp == 0) { + cmp = function.compareTo(p.function); + if (cmp == 0) { + cmp = name.compareTo(p.name); + } + } + } + return (cmp); + } + + /** + * Overridden to ensure that equal probe descriptions have equal + * hashcodes. + */ + @Override + public int + hashCode() + { + int hash = id; + if (hash != ID_NONE) { + return hash; + } + + hash = 17; + hash = (37 * hash) + provider.hashCode(); + hash = (37 * hash) + module.hashCode(); + hash = (37 * hash) + function.hashCode(); + hash = (37 * hash) + name.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + if (provider == null) { + throw new InvalidObjectException("provider is null"); + } + if (module == null) { + throw new InvalidObjectException("module is null"); + } + if (function == null) { + throw new InvalidObjectException("function is null"); + } + if (name == null) { + throw new InvalidObjectException("name is null"); + } + } + + /** + * Gets the string representation of this probe description. The + * format is as follows: + * <pre><code> + * <i>provider:module:function:name</i> + * </code></pre> + * Individual fields may be empty, but none of the three delimiting + * colons is ever omitted. If this instance uses pattern matching + * syntax to match multiple probes, that syntax is preserved in the + * string representation. + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(provider); + buf.append(':'); + buf.append(module); + buf.append(':'); + buf.append(function); + buf.append(':'); + buf.append(name); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeInfo.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeInfo.java new file mode 100644 index 0000000000..3d88c1f205 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeInfo.java @@ -0,0 +1,207 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * Probe stability information. Does not identify a probe, but gives + * information about a single probe identified by a {@link + * ProbeDescription}. A {@code ProbeDescription} can match multiple + * probes using pattern syntax (globbing) and wildcarding (field + * omission), but it does not normally make sense to associate a {@code + * ProbeInfo} with a {@code ProbeDescription} unless that description + * matches exactly one probe on the system. A {@link Probe} pairs a + * {@code ProbeDescription} with information about the DTrace probe it + * identifies. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#listProbeDetail(ProbeDescription filter) + * @see Consumer#listProgramProbeDetail(Program program) + * + * @author Tom Erickson + */ +public final class ProbeInfo implements Serializable { + static final long serialVersionUID = 1057402669978245904L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProbeInfo.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"probeAttributes", + "argumentAttributes"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final InterfaceAttributes probeAttributes; + /** @serial */ + private final InterfaceAttributes argumentAttributes; + + /** + * Creates a {@code ProbeInfo} instance from the given attributes. + * Supports XML persistence. + * + * @throws NullPointerException if any parameter is null + */ + public + ProbeInfo(InterfaceAttributes singleProbeAttributes, + InterfaceAttributes argAttributes) + { + probeAttributes = singleProbeAttributes; + argumentAttributes = argAttributes; + validate(); + } + + private void + validate() + { + if (probeAttributes == null) { + throw new NullPointerException("probeAttributes is null"); + } + if (argumentAttributes == null) { + throw new NullPointerException("argumentAttributes is null"); + } + } + + /** + * Gets the interface attributes of a probe. + * + * @return non-null attributes including stability levels and + * dependency class + */ + public InterfaceAttributes + getProbeAttributes() + { + return probeAttributes; + } + + /** + * Gets the interface attributes of the arguments to a probe. + * + * @return non-null attributes including stability levels and + * dependency class of the arguments to a probe + */ + public InterfaceAttributes + getArgumentAttributes() + { + return argumentAttributes; + } + + /** + * Compares the specified object with this {@code ProbeInfo} + * instance for equality. Defines equality as having equal probe + * attributes and equal argument attributes. + * + * @return {@code true} if and only if the specified object is also + * a {@code ProbeInfo} and both instances have the same attributes + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof ProbeInfo) { + ProbeInfo i = (ProbeInfo)o; + return (probeAttributes.equals(i.probeAttributes) && + argumentAttributes.equals(i.argumentAttributes)); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + probeAttributes.hashCode(); + hash = (37 * hash) + argumentAttributes.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Must copy before checking class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this {@code ProbeInfo} useful for + * logging and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProbeInfo.class.getName()); + buf.append("[probeAttributes = "); + buf.append(probeAttributes); + buf.append(", argumentAttributes = "); + buf.append(argumentAttributes); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessEvent.java new file mode 100644 index 0000000000..0012464e4c --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessEvent.java @@ -0,0 +1,118 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.EventObject; + +/** + * Notification that the state of a target process designated by {@link + * Consumer#createProcess(String command)} or {@link + * Consumer#grabProcess(int pid)} has changed. + * + * @see ConsumerListener#processStateChanged(ProcessEvent e) + * + * @author Tom Erickson + */ +public class ProcessEvent extends EventObject { + static final long serialVersionUID = -3779443761929558702L; + + /** @serial */ + private ProcessState processState; + + /** + * Creates a {@link ConsumerListener#processStateChanged(ProcessEvent e) + * processStateChanged()} event to notify listeners of a process + * state change. + * + * @param source the {@link Consumer} that is the source of this event + * @throws NullPointerException if the given process state is {@code + * null} + */ + public + ProcessEvent(Object source, ProcessState p) + { + super(source); + processState = p; + validate(); + } + + private void + validate() + { + if (processState == null) { + throw new NullPointerException("process state is null"); + } + } + + /** + * Gets the process state. + * + * @return non-null process state + */ + public ProcessState + getProcessState() + { + return processState; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this event useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProcessEvent.class.getName()); + buf.append("[source = "); + buf.append(getSource()); + buf.append(", processState = "); + buf.append(processState); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessState.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessState.java new file mode 100644 index 0000000000..576dcfff8e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessState.java @@ -0,0 +1,358 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * State of a target process designated by {@link + * Consumer#createProcess(String command)} or {@link + * Consumer#grabProcess(int pid)}. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see ConsumerListener#processStateChanged(ProcessEvent e) + * + * @author Tom Erickson + */ +public final class ProcessState implements Serializable { + static final long serialVersionUID = -3395911213431317292L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProcessState.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"processID", "state", + "terminationSignal", "terminationSignalName", + "exitStatus", "message"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + + protected Expression + instantiate(Object oldInstance, Encoder out) + { + ProcessState pstate = (ProcessState)oldInstance; + return new Expression(oldInstance, oldInstance.getClass(), + "new", new Object[] { pstate.getProcessID(), + pstate.getState().name(), + pstate.getTerminationSignal(), + pstate.getTerminationSignalName(), + pstate.getExitStatus(), + pstate.getMessage() }); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * State of a target process. + */ + public enum State { + /** Process is running. */ + RUN, + /** Process is stopped. */ + STOP, + /** Process is lost to control. */ + LOST, + /** Process is terminated (zombie). */ + UNDEAD, + /** Process is terminated (core file). */ + DEAD + } + + /** @serial */ + private int processID; + /** @serial */ + private State state; + /** @serial */ + private int terminationSignal; + /** @serial */ + private String terminationSignalName; + /** @serial */ + private Integer exitStatus; + /** @serial */ + private String message; + + /** + * Creates a {@code ProcessState} instance with the given state. + * + * @param pid non-negative target process ID + * @param processState target process state + * @param processTerminationSignal signal that terminated the target + * process, {@code -1} if the process was not terminated by a signal + * or if the terminating signal is unknown + * @param processTerminationSignalName name of the signal that + * terminated the target process, {@code null} if the process was + * not terminated by a signal or if the terminating signal is + * unknown + * @param processExitStatus target process exit status, {@code null} + * if the process has not exited or the exit status is unknown + * @param msg message included by DTrace, if any + * @throws NullPointerException if the given process state is {@code + * null} + * @throws IllegalArgumentException if the given process ID is negative + */ + public + ProcessState(int pid, State processState, + int processTerminationSignal, + String processTerminationSignalName, + Integer processExitStatus, String msg) + { + processID = pid; + state = processState; + terminationSignal = processTerminationSignal; + terminationSignalName = processTerminationSignalName; + exitStatus = processExitStatus; + message = msg; + validate(); + } + + /** + * Supports XML persistence. + * + * @see #ProcessState(int pid, State processState, int + * processTerminationSignal, String processTerminationSignalName, + * Integer processExitStatus, String msg) + * @throws IllegalArgumentException if there is no {@link + * ProcessState.State} value with the given state name. + */ + public + ProcessState(int pid, String processStateName, + int processTerminationSignal, + String processTerminationSignalName, + Integer processExitStatus, String msg) + { + processID = pid; + state = Enum.valueOf(State.class, processStateName); + terminationSignal = processTerminationSignal; + terminationSignalName = processTerminationSignalName; + exitStatus = processExitStatus; + message = msg; + validate(); + } + + private void + validate() + { + if (processID < 0) { + throw new IllegalArgumentException("pid is negative"); + } + if (state == null) { + throw new NullPointerException("process state is null"); + } + } + + /** + * Gets the process ID. + * + * @return non-negative target process ID + */ + public int + getProcessID() + { + return processID; + } + + /** + * Gets the process state. + * + * @return non-null target process state + */ + public State + getState() + { + return state; + } + + /** + * Gets the signal that terminated the process. + * + * @return termination signal, {@code -1} if the process was not + * terminated by a signal or if the terminating signal is unknown + */ + public int + getTerminationSignal() + { + return terminationSignal; + } + + /** + * Gets the name of the signal that terminated the process. + * + * @return termination signal name, {@code null} if the process was + * not terminated by a signal or if the terminating signal is + * unknown + */ + public String + getTerminationSignalName() + { + return terminationSignalName; + } + + /** + * Gets the process exit status. + * + * @return exit status, or {@code null} if the process has not + * exited or the exit status is unknown + */ + public Integer + getExitStatus() + { + return exitStatus; + } + + /** + * Called by native code. + */ + private void + setExitStatus(int status) + { + exitStatus = new Integer(status); + } + + /** + * Gets the message from DTrace describing this process state. + * + * @return DTrace message, or {@code null} if DTrace did not include + * a message with this process state + */ + public String + getMessage() + { + return message; + } + + /** + * Compares the specified object with this {@code ProcessState} + * instance for equality. Defines equality as having the same + * attributes. + * + * @return {@code true} if and only if the specified object is also + * a {@code ProcessState} and both instances have the same + * attributes + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof ProcessState) { + ProcessState s = (ProcessState)o; + return ((processID == s.processID) && + (state == s.state) && + (terminationSignal == s.terminationSignal) && + ((terminationSignalName == null) ? + (s.terminationSignalName == null) : + terminationSignalName.equals(s.terminationSignalName)) && + ((exitStatus == null) ? + (s.exitStatus == null) : + exitStatus.equals(s.exitStatus)) && + ((message == null) ? (s.message == null) : + message.equals(s.message))); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + processID; + hash = (37 * hash) + state.hashCode(); + hash = (37 * hash) + terminationSignal; + hash = (37 * hash) + (exitStatus == null ? 0 : + exitStatus.hashCode()); + hash = (37 * hash) + (message == null ? 0 : message.hashCode()); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this process state useful for + * logging and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProcessState.class.getName()); + buf.append("[pid = "); + buf.append(processID); + buf.append(", state = "); + buf.append(state); + buf.append(", terminationSignal = "); + buf.append(terminationSignal); + buf.append(", terminationSignalName = "); + buf.append(terminationSignalName); + buf.append(", exitStatus = "); + buf.append(exitStatus); + buf.append(", message = "); + buf.append(message); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Program.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Program.java new file mode 100644 index 0000000000..5a6ecce0a3 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Program.java @@ -0,0 +1,259 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; + +/** + * Identifies a compiled D program. This identifier is valid only on + * the {@link LocalConsumer} from which it was obtained. Some {@code + * Consumer} methods attach additional {@link ProgramInfo} to this + * identifier. + * <p> + * Not intended for persistence, since it identifies nothing after its + * source {@code LocalConsumer} closes. + * + * @see Consumer#compile(String program, String[] macroArgs) + * @see Consumer#compile(java.io.File program, String[] macroArgs) + * @see Consumer#enable(Program program) + * @see Consumer#getProgramInfo(Program program) + * @see Consumer#listProgramProbes(Program program) + * @see Consumer#listProgramProbeDetail(Program program) + * + * @author Tom Erickson + */ +public class Program implements Serializable { + static final long serialVersionUID = 364989786308628466L; + + /** + * Identifies this program among all of a consumer's programs. Set + * by native code. + * + * @serial + */ + private int id = -1; + + // Set by LocalConsumer.compile() + /** @serial */ + LocalConsumer.Identifier consumerID; + /** @serial */ + String contents; + + /** @serial */ + private ProgramInfo info; + + /** + * Called by native code + */ + private Program() + { + } + + // Called by LocalConsumer.compile() to ensure that only valid + // instances are made accessible to users. Similarly called by + // readObject to ensure that only valid instances are deserialized. + void + validate() + { + if (id < 0) { + throw new IllegalArgumentException("id is negative"); + } + if (consumerID == null) { + throw new NullPointerException("consumer ID is null"); + } + } + + /** + * Gets the full pre-compiled text of the identified program. + * + * @return the {@code String} passed to {@link + * Consumer#compile(String program, String[] macroArgs)}, or the + * contents of the {@code File} passed to {@link + * Consumer#compile(java.io.File program, String[] macroArgs)} + */ + public String + getContents() + { + return contents; + } + + /** + * Gets information about this compiled program provided by {@link + * Consumer#getProgramInfo(Program program)} or {@link + * Consumer#enable(Program program)}. + * + * @return information about this compiled program, or {@code null} + * if this {@code Program} has not been passed to {@link + * Consumer#getProgramInfo(Program program)} or {@link + * Consumer#enable(Program program)} + */ + public ProgramInfo + getInfo() + { + return info; + } + + /** + * Sets additional information about this compiled program, + * including program stability and matching probe count. Several + * {@code Consumer} methods attach such information to a given + * {@code Program} argument. The method is {@code public} to + * support implementations of the {@code Consumer} interface other + * than {@link LocalConsumer}. Although a {@code Program} can only + * be obtained from a {@code LocalConsumer}, other {@code Consumer} + * implemenations may provide a helpful layer of abstraction while + * using a {@code LocalConsumer} internally to compile DTrace + * programs. Users of the API are not otherwise expected to call + * the {@code setInfo()} method directly. + * + * @param programInfo optional additional information about this + * compiled program + * @see #getInfo() + * @see Consumer#enable(Program program) + * @see Consumer#getProgramInfo(Program program) + */ + public void + setInfo(ProgramInfo programInfo) + { + info = programInfo; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the contents of the given file as a string. + * + * @return non-null contents of the given file as a string + * @throws IOException if the method fails to read the contents of + * the given file + */ + static String + getProgramString(java.io.File programFile) throws IOException + { + if (programFile == null) { + return null; + } + + StringBuffer buf = new StringBuffer(); + InputStream in; + in = new BufferedInputStream(new FileInputStream(programFile)); + int i = in.read(); + while (i >= 0) { + buf.append((char)i); + i = in.read(); + } + + String s = buf.toString(); + return s; + } + + /** + * Gets a string representation of this {@code Program} instance + * useful for logging and not intended for display. The exact + * details of the representation are unspecified and subject to + * change, but the following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Program.class.getName()); + buf.append("[contents = "); + buf.append(contents); + buf.append(", info = "); + buf.append(info); + buf.append(']'); + return buf.toString(); + } + + /** + * Identifies a compiled D program, specifically one that has been + * compiled from a file. + */ + public static final class File extends Program { + // Set by LocalConsumer.compile() + /** @serial */ + java.io.File file; + + private + File() + { + } + + // Called by LocalConsumer.compile() to ensure that only valid + // instances are made accessible to users. Similarly called by + // readObject to ensure that only valid instances are deserialized. + void + validate() + { + super.validate(); + if (file == null) { + throw new NullPointerException("file is null"); + } + } + + /** + * Gets the program file. + * + * @return the {@code File} passed to {@link + * Consumer#compile(java.io.File program, String[] macroArgs)} + */ + public java.io.File + getFile() + { + return file; + } + + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Program.File.class.getName()); + buf.append("[super = "); + buf.append(super.toString()); + buf.append(", file = "); + buf.append(file); + buf.append(']'); + return buf.toString(); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProgramInfo.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProgramInfo.java new file mode 100644 index 0000000000..0de397a804 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProgramInfo.java @@ -0,0 +1,252 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * Information about a {@link Program} including stability and matching + * probe count. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#getProgramInfo(Program program) + * @see Consumer#enable(Program program) + * + * @author Tom Erickson + */ +public final class ProgramInfo implements Serializable { + static final long serialVersionUID = 663862981171935056L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProgramInfo.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] { "minimumProbeAttributes", + "minimumStatementAttributes", + "matchingProbeCount" }) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** @serial */ + private final InterfaceAttributes minimumProbeAttributes; + /** @serial */ + private final InterfaceAttributes minimumStatementAttributes; + /** @serial */ + private final int matchingProbeCount; + + /** + * Creates a {@code ProgamInfo} instance with the given properties. + * Supports XML persistence. + * + * @param minProbeAttr minimum stability levels of the + * program probe descriptions + * @param minStatementAttr minimum stability levels of the + * program action statements (including D variables) + * @param matchingProbes non-negative count of probes matching the + * program probe description + * @throws NullPointerException if {@code minProbeAttr} or {@code + * minStatementAttr} is {@code null} + * @throws IllegalArgumentException if {@code matchingProbes} is + * negative + */ + public + ProgramInfo(InterfaceAttributes minProbeAttr, + InterfaceAttributes minStatementAttr, + int matchingProbes) + { + // Called by native code. Any change to this constructor requires a + // similar change in the native invocation. + minimumProbeAttributes = minProbeAttr; + minimumStatementAttributes = minStatementAttr; + matchingProbeCount = matchingProbes; + validate(); + } + + private void + validate() + { + if (minimumProbeAttributes == null) { + throw new NullPointerException("minimumProbeAttributes is null"); + } + if (minimumStatementAttributes == null) { + throw new NullPointerException( + "minimumStatementAttributes is null"); + } + if (matchingProbeCount < 0) { + throw new IllegalArgumentException( + "matchingProbeCount is negative"); + } + } + + /** + * Gets the minimum stability levels of the probe descriptions used + * in a compiled {@link Program}. + * + * @return non-null interface attributes describing the minimum + * stability of the probe descriptions in a D program + */ + public InterfaceAttributes + getMinimumProbeAttributes() + { + return minimumProbeAttributes; + } + + /** + * Gets the minimum stability levels of the action statements + * including D variables used in a compiled {@link Program}. + * + * @return non-null interface attributes describing the minimum + * stability of the action statements (including D variables) in a D + * program + */ + public InterfaceAttributes + getMinimumStatementAttributes() + { + return minimumStatementAttributes; + } + + /** + * Gets the number of DTrace probes that match the probe + * descriptions in a compiled {@link Program}. This count may be + * very high for programs that use {@link ProbeDescription} + * wildcarding (field omission) and globbing (pattern matching + * syntax). + * + * @return non-negative count of probes on the system matching the + * program descriptions in a compiled D program + */ + public int + getMatchingProbeCount() + { + return matchingProbeCount; + } + + /** + * Compares the specified object with this program information for + * equality. Defines equality as having the same information, + * including stability attributes and matching probe counts. + * Different D programs may have equal program information. + * + * @return {@code true} if and only if the specified object is also + * a {@code ProgramInfo} instance and has all the same information + * as this instance + */ + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof ProgramInfo) { + ProgramInfo i = (ProgramInfo)o; + return (minimumProbeAttributes.equals( + i.minimumProbeAttributes) && + minimumStatementAttributes.equals( + i.minimumStatementAttributes) && + (matchingProbeCount == i.matchingProbeCount)); + } + return false; + } + + /** + * Overridden to ensure that equal {@code ProgramInfo} instances + * have equal hashcodes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + minimumProbeAttributes.hashCode(); + hash = (37 * hash) + minimumStatementAttributes.hashCode(); + hash = (37 * hash) + matchingProbeCount; + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Check constructor invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this {@code ProgramInfo} useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + @Override + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProgramInfo.class.getName()); + buf.append("[minimumProbeAttributes = "); + buf.append(minimumProbeAttributes); + buf.append(", minimumStatementAttributes = "); + buf.append(minimumStatementAttributes); + buf.append(", matchingProbeCount = "); + buf.append(matchingProbeCount); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Record.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Record.java new file mode 100644 index 0000000000..ad0444f9e2 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Record.java @@ -0,0 +1,36 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * A data record generated by DTrace. + * + * @author Tom Erickson + */ +public interface Record { +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ResourceLimitException.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ResourceLimitException.java new file mode 100644 index 0000000000..ada7e46513 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ResourceLimitException.java @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * Indicates that the user has requested something directly or + * indirectly that exceeds a configured limit. + * + * @author Tom Erickson + */ +class ResourceLimitException extends RuntimeException { + static final long serialVersionUID = -304127017066919362L; + + /** + * Creates a {@code ResourceLimitException} with the specified + * detail message. + * + * @param message the detail message pertaining to this exception + */ + public + ResourceLimitException(String message) + { + super(message); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ScalarRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ScalarRecord.java new file mode 100644 index 0000000000..2654a07327 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ScalarRecord.java @@ -0,0 +1,327 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.Arrays; +import java.beans.*; + +/** + * A traced D primitive generated by a DTrace action such as {@code + * trace()} or {@code tracemem()}, or else an element in a composite + * value generated by DTrace. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class ScalarRecord implements ValueRecord, Serializable { + static final long serialVersionUID = -34046471695050108L; + static final int RAW_BYTES_INDENT = 5; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ScalarRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final Object value; + + /** + * Creates a scalar record with the given DTrace primitive. + * + * @param v DTrace primitive data value + * @throws NullPointerException if the given value is null + * @throws ClassCastException if the given value is not a DTrace + * primitive type listed as a possible return value of {@link + * #getValue()} + */ + public + ScalarRecord(Object v) + { + value = v; + validate(); + } + + private void + validate() + { + if (value == null) { + throw new NullPointerException(); + } + // Short-circuit-evaluate common cases first + if (!((value instanceof Number) || + (value instanceof String) || + (value instanceof byte[]))) { + throw new ClassCastException("value is not a D primitive"); + } + } + + /** + * Gets the traced D primitive value of this record. + * + * @return a non-null value whose type is one of the following: + * <ul> + * <li>{@link Number}</li> + * <li>{@link String}</li> + * <li>byte[]</li> + * </ul> + */ + public Object + getValue() + { + return value; + } + + /** + * Compares the specified object with this record for equality. + * Defines equality as having the same value. + * + * @return {@code true} if and only if the specified object is also + * a {@code ScalarRecord} and the values returned by the {@link + * #getValue()} methods of both instances are equal, {@code false} + * otherwise. Values are compared using {@link + * java.lang.Object#equals(Object o) Object.equals()}, unless they + * are arrays of raw bytes, in which case they are compared using + * {@link java.util.Arrays#equals(byte[] a, byte[] a2)}. + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof ScalarRecord) { + ScalarRecord r = (ScalarRecord)o; + if (value instanceof byte[]) { + if (r.value instanceof byte[]) { + byte[] a1 = (byte[])value; + byte[] a2 = (byte[])r.value; + return Arrays.equals(a1, a2); + } + return false; + } + return value.equals(r.value); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hashcodes. + * + * @return {@link java.lang.Object#hashCode()} of {@link + * #getValue()}, or {@link java.util.Arrays#hashCode(byte[] a)} if + * the value is a raw byte array + */ + @Override + public int + hashCode() + { + if (value instanceof byte[]) { + return Arrays.hashCode((byte[])value); + } + return value.hashCode(); + } + + private static final int BYTE_SIGN_BIT = 1 << 7; + + /** + * Static utility for treating a byte as unsigned by converting it + * to int without sign extending. + */ + static int + unsignedByte(byte b) + { + if (b < 0) { + b ^= (byte)BYTE_SIGN_BIT; + return ((int)b) | BYTE_SIGN_BIT; + } + return (int)b; + } + + static String + hexString(int n, int width) + { + String s = Integer.toHexString(n); + int len = s.length(); + if (width < len) { + s = s.substring(len - width); + } else if (width > len) { + s = (spaces(width - len) + s); + } + return s; + } + + static String + spaces(int n) + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < n; ++i) { + buf.append(' '); + } + return buf.toString(); + } + + /** + * Represents a byte array as a table of unsigned byte values in hex, + * 16 per row ending in their corresponding character + * representations (or a period (.) for each unprintable + * character). Uses default indentation. + * + * @see ScalarRecord#rawBytesString(byte[] bytes, int indent) + */ + static String + rawBytesString(byte[] bytes) + { + return rawBytesString(bytes, RAW_BYTES_INDENT); + } + + /** + * Represents a byte array as a table of unsigned byte values in hex, + * 16 per row ending in their corresponding character + * representations (or a period (.) for each unprintable + * character). The table begins and ends with a newline, includes a + * header row, and uses a newline at the end of each row. + * + * @param bytes array of raw bytes treated as unsigned when + * converted to hex display + * @param indent number of spaces to indent each line of the + * returned string + * @return table representation of 16 bytes per row as hex and + * character values + */ + static String + rawBytesString(byte[] bytes, int indent) + { + // ported from libdtrace/common/dt_consume.c dt_print_bytes() + int i, j; + int u; + StringBuffer buf = new StringBuffer(); + String leftMargin = spaces(indent); + buf.append('\n'); + buf.append(leftMargin); + buf.append(" "); + for (i = 0; i < 16; i++) { + buf.append(" "); + buf.append("0123456789abcdef".charAt(i)); + } + buf.append(" 0123456789abcdef\n"); + int nbytes = bytes.length; + String hex; + for (i = 0; i < nbytes; i += 16) { + buf.append(leftMargin); + buf.append(hexString(i, 5)); + buf.append(':'); + + for (j = i; (j < (i + 16)) && (j < nbytes); ++j) { + buf.append(hexString(unsignedByte(bytes[j]), 3)); + } + + while ((j++ % 16) != 0) { + buf.append(" "); + } + + buf.append(" "); + + for (j = i; (j < (i + 16)) && (j < nbytes); ++j) { + u = unsignedByte(bytes[j]); + if ((u < ' ') || (u > '~')) { + buf.append('.'); + } else { + buf.append((char) u); + } + } + + buf.append('\n'); + } + + return buf.toString(); + } + + static String + valueToString(Object value) + { + String s; + if (value instanceof byte[]) { + s = rawBytesString((byte[])value); + } else { + s = value.toString(); + } + return s; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the natural string representation of the traced D primitive. + * + * @return the value of {@link Object#toString} when called on + * {@link #getValue()}; or if the value is an array of raw bytes, a + * table displaying 16 bytes per row in unsigned hex followed by the + * ASCII character representations of those bytes (each unprintable + * character is represented by a period (.)) + */ + public String + toString() + { + return ScalarRecord.valueToString(getValue()); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackFrame.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackFrame.java new file mode 100644 index 0000000000..e18b937f60 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackFrame.java @@ -0,0 +1,162 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * A single stack frame in a {@link StackValueRecord}. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class StackFrame implements Serializable { + static final long serialVersionUID = 8617210929132692711L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(StackFrame.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"frame"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final String frame; + + /** + * Creates a single stack frame. Supports XML persistence. + * + * @param f human-readable string representation of this stack frame + * @throws NullPointerException if the given string representation + * is {@code null} + */ + public + StackFrame(String f) + { + frame = f; + validate(); + } + + private void + validate() + { + if (frame == null) { + throw new NullPointerException("frame is null"); + } + } + + /** + * Gets the human-readable string representation of this stack + * frame. Supports XML persistence. + * + * @return the human-readable string representation of this stack frame. + */ + public String + getFrame() + { + return frame; + } + + /** + * Compares the specified object with this {@code StackFrame} for + * equality. Returns {@code true} if and only if the specified + * object is also a {@code StackFrame} and both instances have the + * same human-readable string representation. + * + * @return {@code true} if and only if the specified object is also + * a {@code StackFrame} and both instances have the same + * human-readable string representation + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof StackFrame) { + StackFrame s = (StackFrame)o; + return frame.equals(s.frame); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return frame.hashCode(); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the string representation of this stack frame, in this case + * the same value returned by {@link #getFrame()}. + */ + public String + toString() + { + return frame; + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackValueRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackValueRecord.java new file mode 100644 index 0000000000..682fe6b81c --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackValueRecord.java @@ -0,0 +1,104 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; + +/** + * A value generated by the DTrace {@code stack()}, {@code ustack()}, or + * {@code jstack()} action. + * + * @author Tom Erickson + */ +public interface StackValueRecord extends ValueRecord { + /** + * Gets a copy of this record's stack frames, or an empty array if + * this record's raw stack data was not converted to human-readable + * stack frames by DTrace. Raw stack data is not converted (i.e. + * human-readable stack frames are omitted) whenever a {@code + * printa()} format string is specified without including the {@code + * %k} placeholder for the stack value represented by this record. + * (The {@code stack()}, {@code ustack()}, and {@code jstack()} + * actions are all usable as members of an aggregation tuple.) See + * the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidli3?a=view> + * <b>{@code printa()}</b></a> section of the <b>Output + * Formatting</b> chapter of the <i>Solaris Dynamic Tracing + * Guide</i> for details about {@code printa()} format strings. + * Human-readable stack frames are generated by default if {@code + * printa()} is called without specifying a format string, or when + * using {@link Consumer#getAggregate()} as an alternative to {@code + * printa()}. They are also generated when {@code stack()}, {@code + * ustack()}, or {@code jstack()} is used as a stand-alone action + * outside of an aggregation tuple. + * <p> + * The returned array is a copy and modifying it has no effect on + * this record. Elements of the returned array are not {@code + * null}. + * + * @return a non-null, possibly empty array of this record's + * human-readable stack frames, none of which are {@code null} + */ + public StackFrame[] getStackFrames(); + + /** + * Gets the native DTrace representation of this record's stack as + * an array of raw bytes. The raw bytes are needed to distinguish + * stacks that have the same string representation but are + * considered distinct by DTrace. Duplicate stacks (stacks with the + * same human-readable stack frames) can have distinct raw stack + * data when program text is relocated. + * <p> + * Implementations of this interface use raw stack data to compute + * {@link Object#equals(Object o) equals()} and {@link + * Object#hashCode() hashCode()}. If the stack belongs to a user + * process, the raw bytes include the process ID. + * + * @return the native DTrace library's internal representation of + * this record's stack as a non-null array of bytes + */ + public byte[] getRawStackData(); + + /** + * Gets the raw bytes used to represent this record's stack value in + * the native DTrace library. + * + * @return {@link #getRawStackData()} + */ + public Object getValue(); + + /** + * Gets a read-only {@code List} view of this record's stack frames. + * The returned list implements {@link java.util.RandomAccess}. It + * is empty if {@link #getStackFrames()} returns an empty array. + * + * @return non-null, unmodifiable {@code List} view of this record's + * stack frames + */ + public List <StackFrame> asList(); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/SumValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/SumValue.java new file mode 100644 index 0000000000..256b1c76e0 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/SumValue.java @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code sum()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class SumValue extends AbstractAggregationValue { + static final long serialVersionUID = 4929338907817617943L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(SumValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code sum()} action. + * Supports XML persistence. + * + * @param v sum total of the aggregated values + */ + public + SumValue(long v) + { + super(v); + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the sum total of the aggregated values. + * + * @return the sum total of the aggregated values + */ + public Long + getValue() + { + return (Long)super.getValue(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Tuple.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Tuple.java new file mode 100644 index 0000000000..65743e657f --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Tuple.java @@ -0,0 +1,370 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.*; +import java.beans.*; +import java.util.*; + +/** + * Multi-element key to a value in an {@link Aggregation}. + * <p> + * Tuple equality is based on the length of each tuple and the equality + * of each corresponding element. The natural ordering of tuples is + * based on a lenient comparison designed not to throw exceptions when + * corresponding elements are not mutually comparable or the number of + * tuple elements differs. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class Tuple implements Serializable, Comparable <Tuple>, + Iterable<ValueRecord> +{ + static final long serialVersionUID = 5192674716869462720L; + + /** + * The empty tuple has zero elements and may be used to obtain the + * singleton {@link AggregationRecord} of a non-keyed {@link + * Aggregation}, such as the one derived from the D statement + * <code>@a = count()</code>. (In D, an aggregation without + * square brackets aggregates a single value.) + */ + public static final Tuple EMPTY = new Tuple(); + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Tuple.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"elements"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private java.util.List <ValueRecord> elements; + + private + Tuple() + { + // + // expected to be a short list (usually one to three elements) + // + elements = new ArrayList <ValueRecord> (4); + } + + /** + * Creates a tuple with the given elements in the given order. + * + * @param tupleElements ordered series of tuple elements + * @throws NullPointerException if the given array or any of its + * elements is {@code null} + */ + public + Tuple(ValueRecord ... tupleElements) + { + this(); + if (tupleElements == null) { + throw new NullPointerException("null array"); + } + for (ValueRecord r : tupleElements) { + if (r == null) { + throw new NullPointerException("null element"); + } + elements.add(r); + } + } + + /** + * Creates a tuple with the given element list in the given list + * order. + * + * @param tupleElements ordered list of tuple elements + * @throws NullPointerException if the given list or any of its + * elements is {@code null} + */ + public + Tuple(List <ValueRecord> tupleElements) + { + this(); + if (tupleElements == null) { + throw new NullPointerException("element list is null"); + } + for (ValueRecord r : tupleElements) { + if (r == null) { + throw new NullPointerException("null element"); + } + elements.add(r); + } + } + + /** + * Called by native code. + * + * @throws NullPointerException if element is null + * @throws IllegalArgumentException if element is neither a {@link + * ValueRecord} nor one of the DTrace primitive types returned by + * {@link ScalarRecord#getValue()} + */ + private void + addElement(Object element) + { + if (element == null) { + throw new NullPointerException("tuple element is null at " + + "index " + elements.size()); + } + if (element instanceof ValueRecord) { + elements.add(ValueRecord.class.cast(element)); + } else { + elements.add(new ScalarRecord(element)); + } + } + + /** + * Gets a modifiable list of this tuple's elements in the same order + * as their corresponding variables in the original D program tuple. + * Modifying the returned list has no effect on this tuple. + * Supports XML persistence. + * + * @return a modifiable list of this tuple's elements in the same order + * as their corresponding variables in the original D program tuple + */ + public List <ValueRecord> + getElements() + { + return new ArrayList <ValueRecord> (elements); + } + + /** + * Gets a read-only {@code List} view of this tuple. + * + * @return a read-only {@code List} view of this tuple + */ + public List <ValueRecord> + asList() + { + return Collections.unmodifiableList(elements); + } + + /** + * Gets the number of elements in this tuple. + * + * @return non-negative element count + */ + public int + size() + { + return elements.size(); + } + + /** + * Returns {@code true} if this tuple has no elements. + * + * @return {@code true} if this tuple has no elements, {@code false} + * otherwise + * @see Tuple#EMPTY + */ + public boolean + isEmpty() + { + return elements.isEmpty(); + } + + /** + * Gets the element at the given tuple index (starting at zero). + * + * @return non-null tuple element at the given zero-based index + */ + public ValueRecord + get(int index) + { + return elements.get(index); + } + + /** + * Gets an iterator over the elements of this tuple. + * + * @return an iterator over the elements of this tuple + */ + public Iterator<ValueRecord> + iterator() + { + return elements.iterator(); + } + + /** + * Compares the specified object with this {@code Tuple} instance + * for equality. Defines equality as having the same elements in + * the same order. + * + * @return {@code true} if and only if the specified object is of + * type {@code Tuple} and both instances have the same size and + * equal elements at corresponding tuple indexes + */ + public boolean + equals(Object o) + { + if (o instanceof Tuple) { + Tuple t = (Tuple)o; + return elements.equals(t.elements); + } + return false; + } + + /** + * Overridden to ensure that equals instances have equal hash codes. + */ + public int + hashCode() + { + return elements.hashCode(); + } + + // lenient sort does not throw exceptions + @SuppressWarnings("unchecked") + private int + compareObjects(Object o1, Object o2) + { + int cmp; + Class c1 = o1.getClass(); + Class c2 = o2.getClass(); + if (c1.isAssignableFrom(c2) && (o1 instanceof Comparable)) { + Comparable c = Comparable.class.cast(o1); + cmp = c.compareTo(c1.cast(o2)); + } else { + // Compare string values. If matching, compare object class. + String s1 = o1.toString(); + String s2 = o2.toString(); + cmp = s1.compareTo(s2); + if (cmp == 0) { + cmp = c1.getName().compareTo(c2.getName()); + } + } + return cmp; + } + + /** + * Defines the natural ordering of tuples. Uses a lenient algorithm + * designed not to throw exceptions. Sorts tuples by the natural + * ordering of corresponding elements, starting with the first pair + * of corresponding elements and comparing subsequent pairs only + * when all previous pairs are equal (as a tie breaker). If + * corresponding elements are not mutually comparable, it compares + * the string values of those elements, then if the string values + * are equal, it compares the class names of those elements' types. + * If all corresponding elements are equal, then the tuple with more + * elements sorts higher than the tuple with fewer elements. + * + * @return a negative integer, zero, or a postive integer as this + * tuple is less than, equal to, or greater than the given tuple + */ + public int + compareTo(Tuple t) + { + int cmp = 0; + int len = size(); + int tlen = t.size(); + ValueRecord rec; + ValueRecord trec; + Object val; + Object tval; + for (int i = 0; (cmp == 0) && (i < len) && (i < tlen); ++i) { + rec = get(i); + trec = t.get(i); + if (rec instanceof ScalarRecord) { + val = rec.getValue(); + } else { + val = rec; + } + if (trec instanceof ScalarRecord) { + tval = trec.getValue(); + } else { + tval = trec; + } + cmp = compareObjects(val, tval); + } + if (cmp == 0) { + cmp = (len < tlen ? -1 : (len > tlen ? 1 : 0)); + } + return cmp; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Make a defensive copy of elements + if (elements == null) { + throw new InvalidObjectException("element list is null"); + } + List <ValueRecord> copy = new ArrayList <ValueRecord> + (elements.size()); + copy.addAll(elements); + elements = copy; + // check class invariants + for (ValueRecord e : elements) { + if (e == null) { + throw new InvalidObjectException("null element"); + } + } + } + + /** + * Gets a string representation of this tuple's elements in the same + * format as that returned by {@link AbstractCollection#toString()}. + * The representation, although specified, is subject to change. + */ + public String + toString() + { + return elements.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/UserStackRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/UserStackRecord.java new file mode 100644 index 0000000000..7c6e64872b --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/UserStackRecord.java @@ -0,0 +1,327 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A value generated by the DTrace {@code ustack()} or {@code jstack()} + * action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class UserStackRecord implements StackValueRecord, + Serializable, Comparable <UserStackRecord> +{ + static final long serialVersionUID = -4195269026915862308L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(UserStackRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"processID", "stackFrames", "rawStackData"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + private transient KernelStackRecord stackRecord; + /** @serial */ + private final int processID; + + /** + * Called by native code. + */ + private + UserStackRecord(int pid, byte[] rawBytes) + { + stackRecord = new KernelStackRecord(rawBytes); + processID = pid; + validate(); + } + + /** + * Creates a {@code UserStackRecord} with the given stack frames, + * user process ID, and raw stack data. Supports XML persistence. + * + * @param frames array of human-readable stack frames, copied so + * that later modifying the given frames array will not affect this + * {@code UserStackRecord}; may be {@code null} or empty to indicate + * that the raw stack data was not converted to human-readable stack + * frames (see {@link StackValueRecord#getStackFrames()}) + * @param pid non-negative user process ID + * @param rawBytes array of raw bytes used to represent this stack + * value in the native DTrace library, needed to distinguish stacks + * that have the same display value but are considered distinct by + * DTrace; copied so that later modifying the given array will not + * affect this {@code UserStackRecord} + * @throws NullPointerException if the given array of raw bytes is + * {@code null} or if any element of the {@code frames} array is + * {@code null} + * @throws IllegalArgumentException if the given process ID is + * negative + */ + public + UserStackRecord(int pid, StackFrame[] frames, byte[] rawBytes) + { + stackRecord = new KernelStackRecord(frames, rawBytes); + processID = pid; + validate(); + } + + private void + validate() + { + if (processID < 0) { + throw new IllegalArgumentException("process ID is negative"); + } + } + + public StackFrame[] + getStackFrames() + { + return stackRecord.getStackFrames(); + } + + void + setStackFrames(StackFrame[] frames) + { + stackRecord.setStackFrames(frames); + } + + /** + * Gets the native DTrace representation of this record's stack as + * an array of raw bytes. The raw bytes include the process ID and + * are used in {@link #equals(Object o) equals()} and {@link + * #compareTo(UserStackRecord r) compareTo()} to test equality and + * to determine the natural ordering of user stack records. + * + * @return the native DTrace library's internal representation of + * this record's stack as a non-null array of bytes + */ + public byte[] + getRawStackData() + { + return stackRecord.getRawStackData(); + } + + /** + * Gets the raw bytes used to represent this record's stack value in + * the native DTrace library. To get a human-readable + * representation, call {@link #toString()}. + * + * @return {@link #getRawStackData()} + */ + public Object + getValue() + { + return stackRecord.getValue(); + } + + /** + * Gets the process ID associated with this record's user stack. + * + * @return non-negative pid + */ + public int + getProcessID() + { + return processID; + } + + public List <StackFrame> + asList() + { + return stackRecord.asList(); + } + + /** + * Gets a {@code KernelStackRecord} view of this record. + * + * @return non-null {@code KernelStackRecord} view of this record + */ + public KernelStackRecord + asKernelStackRecord() + { + return stackRecord; + } + + /** + * Compares the specified object with this {@code UserStackRecord} + * for equality. Returns {@code true} if and only if the specified + * object is also a {@code UserStackRecord} and both stacks have the + * same raw stack data (including process ID). + * <p> + * This implementation first checks if the specified object is this + * {@code UserStackRecord}. If so, it returns {@code true}. + * + * @return {@code true} if and only if the specified object is also + * a {@code UserStackRecord} and both stacks have the same raw stack + * data (including process ID) + */ + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof UserStackRecord) { + UserStackRecord r = (UserStackRecord)o; + // raw stack data includes the process ID, but the process + // ID passed to the constructor is not validated against the + // raw stack data; probably best for this class to test all + // of its state without making assumptions + return ((processID == r.processID) && + stackRecord.equals(r.stackRecord)); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return (stackRecord.hashCode() + processID); + } + + /** + * Compares this record with the given {@code UserStackRecord}. + * Compares process ID first, then if those are equal, compares the + * views returned by {@link #asKernelStackRecord()}. The {@code + * compareTo()} method is compatible with {@link #equals(Object o) + * equals()}. + * <p> + * This implementation first checks if the specified object is this + * {@code UserStackRecord}. If so, it returns {@code 0}. + * + * @return -1, 0, or 1 as this record is less than, equal to, or + * greater than the given record + */ + public int + compareTo(UserStackRecord r) + { + if (r == this) { + return 0; + } + + int cmp = 0; + cmp = ((processID < r.processID) ? -1 : + ((processID > r.processID) ? 1 : 0)); + if (cmp == 0) { + cmp = stackRecord.compareTo(r.stackRecord); + } + return cmp; + } + + /** + * Serialize this {@code UserStackRecord} instance. + * + * @serialData Serialized fields are emitted, followed first by this + * record's stack frames as an array of type {@link String}, then by + * this record's raw stack data as an array of bytes. + */ + private void + writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeObject(stackRecord.getStackFrames()); + s.writeObject(stackRecord.getRawStackData()); + } + + private void + readObject(ObjectInputStream s) throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + try { + StackFrame[] frames = (StackFrame[])s.readObject(); + byte[] rawBytes = (byte[])s.readObject(); + // defensively copies stack frames and raw bytes + stackRecord = new KernelStackRecord(frames, rawBytes); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the {@link KernelStackRecord#toString() string + * representation} of the view returned by {@link + * #asKernelStackRecord()} preceded by the user process ID on its + * own line. The process ID is in the format {@code process ID: + * pid} (where <i>pid</i> is a decimal integer) and is indented by + * the same number of spaces as the stack frames. The exact format + * is subject to change. + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + final int stackindent = KernelStackRecord.STACK_INDENT; + int i; + buf.append('\n'); + for (i = 0; i < KernelStackRecord.STACK_INDENT; ++i) { + buf.append(' '); + } + buf.append("process ID: "); + buf.append(processID); + buf.append(stackRecord.toString()); // starts with newline + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Utility.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Utility.java new file mode 100644 index 0000000000..5cd9c035d8 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Utility.java @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.File; + +/** + * Common functionality for all {@code org.opensolaris.os} subpackages. + */ +class Utility +{ + private static void + loadLibrary(String paths[], String name, boolean debug) + { + File file; + for (String p : paths) { + file = new File(p); + // Allows LD_LIBRARY_PATH to include relative paths + p = file.getAbsolutePath(); + try { + System.load(p + "/" + name); + if (debug) { + System.out.println("loaded " + name + " from " + p); + } + return; + } catch (UnsatisfiedLinkError e) { + } + } + throw (new UnsatisfiedLinkError("Unable to find " + name)); + } + + /** + * Loads a library. + */ + public static void + loadLibrary(String name, boolean debug) + { + String path = System.getProperty("java.library.path"); + path = path + ":/usr/lib/64"; /* Java bug 6254947 */ + String[] paths = path.split(":"); + + if (debug) { + String root = System.getenv("ROOT"); + if (root != null && root.length() > 0) { + System.out.println("Prepending $ROOT to library path."); + String[] npaths = new String[paths.length * 2]; + for (int i = 0; i < paths.length; i++) { + npaths[i] = root + "/" + paths[i]; + npaths[i + paths.length] = paths[i]; + } + paths = npaths; + } + } + + loadLibrary(paths, name, debug); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ValueRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ValueRecord.java new file mode 100644 index 0000000000..e0ea54e450 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ValueRecord.java @@ -0,0 +1,42 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * A data record generated by DTrace that contains a single value. + * + * @author Tom Erickson + */ +public interface ValueRecord extends Record { + /** + * Gets a traced value. + * + * @return non-null record value + */ + public abstract Object getValue(); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/package.html b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/package.html new file mode 100644 index 0000000000..92689cf5f2 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/package.html @@ -0,0 +1,62 @@ +<!-- + Copyright 2006 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + ident "%Z%%M% %I% %E% SMI" + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head/> +<body bgcolor="white"> + + Interface to the native DTrace library. + +<!-- commented out ... + +<h2>Package Specification</h2> + +##### FILL IN ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT ##### + +<ul> +<li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a> +</ul> + +end commented-out block --> + +<h2>Related Documentation</h2> + +For overviews, tutorials, examples, guides, and tool documentation, please see: + +<br><br> +<a href="http://docs.sun.com/db/doc/817-6223"> + <i>Solaris Dynamic Tracing Guide</i></a><br> +<a href="http://www.opensolaris.org/os/community/dtrace"> + OpenSolaris DTrace Website</a><br> +<a href="../../../../../html/JavaDTraceAPI.html" target="parent"> + API Diagram</a><br> +<a href="../../../../../html/fast.html" target="parent"> + Quick Start Guide</a><br> + +<!-- Put @see and @since tags down here. --> + +</body> +</html> diff --git a/usr/src/lib/libdtrace_jni/sparc/Makefile b/usr/src/lib/libdtrace_jni/sparc/Makefile new file mode 100644 index 0000000000..758b71741f --- /dev/null +++ b/usr/src/lib/libdtrace_jni/sparc/Makefile @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +MAPDIR = ../spec/sparc + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libdtrace_jni/sparcv9/Makefile b/usr/src/lib/libdtrace_jni/sparcv9/Makefile new file mode 100644 index 0000000000..59d59c4abc --- /dev/null +++ b/usr/src/lib/libdtrace_jni/sparcv9/Makefile @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +MAPDIR = ../spec/sparcv9 + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libdtrace_jni/spec/Makefile b/usr/src/lib/libdtrace_jni/spec/Makefile new file mode 100644 index 0000000000..a66eef302e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/spec/Makefile @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include $(SRC)/lib/Makefile.spec.arch diff --git a/usr/src/lib/libdtrace_jni/spec/Makefile.targ b/usr/src/lib/libdtrace_jni/spec/Makefile.targ new file mode 100644 index 0000000000..9be768f2eb --- /dev/null +++ b/usr/src/lib/libdtrace_jni/spec/Makefile.targ @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +.KEEP_STATE: + +LIBRARY = libdtrace_jni.a +VERS = .1 +OBJECTS = dtrace_jni.o diff --git a/usr/src/lib/libdtrace_jni/spec/amd64/Makefile b/usr/src/lib/libdtrace_jni/spec/amd64/Makefile new file mode 100644 index 0000000000..aced5376d3 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/spec/amd64/Makefile @@ -0,0 +1,37 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# To enable apptrace, comment out the next line +DISABLE_APPTRACE= $(POUND_SIGN) + +include ../Makefile.targ +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec + +$(DISABLE_APPTRACE)install: $(ROOTABILIB64) diff --git a/usr/src/lib/libdtrace_jni/spec/dtrace_jni.spec b/usr/src/lib/libdtrace_jni/spec/dtrace_jni.spec new file mode 100644 index 0000000000..ac34d71659 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/spec/dtrace_jni.spec @@ -0,0 +1,155 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1checkVersion +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1loadJniTable +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1open +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1compileString +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1compileFile +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1exec +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1getProgramInfo +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1setOption +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1getOption +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1checkProgramEnabling +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1isEnabled +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1go +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1stop +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1consume +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1interrupt +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1close +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1quantizeBucket +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupKernelFunction +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupUserFunction +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1getAggregate +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1createProcess +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1grabProcess +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbes +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbes +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbeDetail +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbeDetail +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1getVersion +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1openCount +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1getExecutableName +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1setMaximumConsumers +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1setDebug +version SUNWprivate_1.1 +end + +function Java_org_opensolaris_os_dtrace_LocalConsumer__1destroy +version SUNWprivate_1.1 +end diff --git a/usr/src/lib/libdtrace_jni/spec/i386/Makefile b/usr/src/lib/libdtrace_jni/spec/i386/Makefile new file mode 100644 index 0000000000..4e313b5aa8 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/spec/i386/Makefile @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# To enable apptrace, comment out the next line +DISABLE_APPTRACE= $(POUND_SIGN) + +include ../Makefile.targ +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec + +$(DISABLE_APPTRACE)install: $(ROOTABILIB) diff --git a/usr/src/lib/libdtrace_jni/spec/sparc/Makefile b/usr/src/lib/libdtrace_jni/spec/sparc/Makefile new file mode 100644 index 0000000000..0783a6dc00 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/spec/sparc/Makefile @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# To enable apptrace, comment out the next line +DISABLE_APPTRACE= $(POUND_SIGN) + +include ../Makefile.targ +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec + +$(DISABLE_APPTRACE)install: $(ROOTABILIB) diff --git a/usr/src/lib/libdtrace_jni/spec/sparcv9/Makefile b/usr/src/lib/libdtrace_jni/spec/sparcv9/Makefile new file mode 100644 index 0000000000..aced5376d3 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/spec/sparcv9/Makefile @@ -0,0 +1,37 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# To enable apptrace, comment out the next line +DISABLE_APPTRACE= $(POUND_SIGN) + +include ../Makefile.targ +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec + +$(DISABLE_APPTRACE)install: $(ROOTABILIB64) diff --git a/usr/src/lib/libdtrace_jni/spec/versions b/usr/src/lib/libdtrace_jni/spec/versions new file mode 100644 index 0000000000..3be654f018 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/spec/versions @@ -0,0 +1,43 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +i386 { + SUNWprivate_1.1; +} + +amd64 { + SUNWprivate_1.1; +} + +sparc { + SUNWprivate_1.1; +} + +sparcv9 { + SUNWprivate_1.1; +} diff --git a/usr/src/pkgdefs/SUNWdtrc/prototype_com b/usr/src/pkgdefs/SUNWdtrc/prototype_com index d4a10bb750..f270dbaa68 100644 --- a/usr/src/pkgdefs/SUNWdtrc/prototype_com +++ b/usr/src/pkgdefs/SUNWdtrc/prototype_com @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -151,3 +150,12 @@ f none usr/demo/dtrace/writetimeq.d 644 root bin f none usr/demo/dtrace/xioctl.d 644 root bin f none usr/demo/dtrace/xterm.d 644 root bin f none usr/demo/dtrace/xwork.d 644 root bin + +f none usr/lib/libdtrace_jni.so.1 755 root bin +s none usr/lib/libdtrace_jni.so=libdtrace_jni.so.1 +d none usr/share 755 root sys +d none usr/share/lib 755 root sys +d none usr/share/lib/java 755 root sys +f none usr/share/lib/java/dtrace.jar 644 root sys + +!include prototype_doc diff --git a/usr/src/pkgdefs/SUNWdtrc/prototype_doc b/usr/src/pkgdefs/SUNWdtrc/prototype_doc new file mode 100644 index 0000000000..520b6184f7 --- /dev/null +++ b/usr/src/pkgdefs/SUNWdtrc/prototype_doc @@ -0,0 +1,179 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +d none usr/share/lib/java/javadoc 0755 root other +d none usr/share/lib/java/javadoc/dtrace 0755 root other +d none usr/share/lib/java/javadoc/dtrace/examples 0755 root other +f none usr/share/lib/java/javadoc/dtrace/examples/TestAPI.java 0644 root other +f none usr/share/lib/java/javadoc/dtrace/examples/TestAPI2.java 0644 root other +f none usr/share/lib/java/javadoc/dtrace/examples/TestTarget.java 0644 root other +f none usr/share/lib/java/javadoc/dtrace/examples/hello.d 0644 root other +f none usr/share/lib/java/javadoc/dtrace/examples/intrstat.d 0644 root other +f none usr/share/lib/java/javadoc/dtrace/examples/syscall.d 0644 root other +f none usr/share/lib/java/javadoc/dtrace/examples/target.d 0644 root other +d none usr/share/lib/java/javadoc/dtrace/api 0755 root other +d none usr/share/lib/java/javadoc/dtrace/api/org 0755 root other +d none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris 0755 root other +d none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os 0755 root other +d none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace 0755 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Aggregate.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Aggregation.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/AggregationRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/AggregationValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/AvgValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Consumer.OpenFlag.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Consumer.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ConsumerAdapter.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ConsumerEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ConsumerException.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ConsumerListener.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/CountValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/DTraceException.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/DataEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Distribution.Bucket.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Distribution.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Drop.Kind.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Drop.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/DropEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Error.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ErrorEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ExceptionHandler.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ExitRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Flow.Kind.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Flow.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/InterfaceAttributes.DependencyClass.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/InterfaceAttributes.Stability.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/InterfaceAttributes.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/KernelStackRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/LinearDistribution.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/LocalConsumer.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/LogDistribution.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/MaxValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/MinValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Option.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/PrintaRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/PrintfRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Probe.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ProbeData.KeyField.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ProbeData.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ProbeDescription.Spec.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ProbeDescription.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ProbeInfo.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ProcessEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ProcessState.State.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ProcessState.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Program.File.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Program.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ProgramInfo.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Record.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ScalarRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/StackFrame.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/StackValueRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/SumValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/Tuple.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/UserStackRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/ValueRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/package-frame.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/package-summary.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/package-tree.html 0644 root other +d none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use 0755 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Aggregate.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Aggregation.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/AggregationRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/AggregationValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/AvgValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Consumer.OpenFlag.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Consumer.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ConsumerAdapter.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ConsumerEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ConsumerException.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ConsumerListener.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/CountValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/DTraceException.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/DataEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Distribution.Bucket.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Distribution.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Drop.Kind.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Drop.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/DropEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Error.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ErrorEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ExceptionHandler.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ExitRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Flow.Kind.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Flow.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/InterfaceAttributes.DependencyClass.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/InterfaceAttributes.Stability.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/InterfaceAttributes.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/KernelStackRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/LinearDistribution.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/LocalConsumer.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/LogDistribution.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/MaxValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/MinValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Option.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/PrintaRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/PrintfRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Probe.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ProbeData.KeyField.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ProbeData.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ProbeDescription.Spec.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ProbeDescription.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ProbeInfo.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ProcessEvent.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ProcessState.State.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ProcessState.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Program.File.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Program.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ProgramInfo.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Record.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ScalarRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/StackFrame.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/StackValueRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/SumValue.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/Tuple.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/UserStackRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/class-use/ValueRecord.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/org/opensolaris/os/dtrace/package-use.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/package-list 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/constant-values.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/serialized-form.html 0644 root other +d none usr/share/lib/java/javadoc/dtrace/api/resources 0755 root other +f none usr/share/lib/java/javadoc/dtrace/api/resources/inherit.gif 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/overview-tree.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/index-all.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/deprecated-list.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/allclasses-frame.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/allclasses-noframe.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/index.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/help-doc.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/api/stylesheet.css 0644 root other +d none usr/share/lib/java/javadoc/dtrace/images 0755 root other +f none usr/share/lib/java/javadoc/dtrace/images/JavaDTraceAPI.gif 0644 root other +d none usr/share/lib/java/javadoc/dtrace/html 0755 root other +f none usr/share/lib/java/javadoc/dtrace/html/JavaDTraceAPI.html 0644 root other +f none usr/share/lib/java/javadoc/dtrace/html/fast.html 0644 root other diff --git a/usr/src/pkgdefs/SUNWdtrc/prototype_i386 b/usr/src/pkgdefs/SUNWdtrc/prototype_i386 index f9d52af8d7..558d44e7bd 100644 --- a/usr/src/pkgdefs/SUNWdtrc/prototype_i386 +++ b/usr/src/pkgdefs/SUNWdtrc/prototype_i386 @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -49,3 +48,6 @@ f none usr/sbin/amd64/dtrace 555 root bin f none usr/sbin/amd64/intrstat 555 root bin f none usr/sbin/amd64/lockstat 555 root bin f none usr/sbin/amd64/plockstat 555 root bin + +f none usr/lib/amd64/libdtrace_jni.so.1 755 root bin +s none usr/lib/amd64/libdtrace_jni.so=libdtrace_jni.so.1 diff --git a/usr/src/pkgdefs/SUNWdtrc/prototype_sparc b/usr/src/pkgdefs/SUNWdtrc/prototype_sparc index 431fe87520..cf49180d36 100644 --- a/usr/src/pkgdefs/SUNWdtrc/prototype_sparc +++ b/usr/src/pkgdefs/SUNWdtrc/prototype_sparc @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -44,3 +43,6 @@ f none usr/sbin/sparcv9/dtrace 555 root bin f none usr/sbin/sparcv9/intrstat 555 root bin f none usr/sbin/sparcv9/lockstat 555 root bin f none usr/sbin/sparcv9/plockstat 555 root bin + +f none usr/lib/sparcv9/libdtrace_jni.so.1 755 root bin +s none usr/lib/sparcv9/libdtrace_jni.so=libdtrace_jni.so.1 |