summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authortomee <none@none>2006-02-16 12:15:27 -0800
committertomee <none@none>2006-02-16 12:15:27 -0800
commitfb3fb4f3d76d55b64440afd0af72775dfad3bd1d (patch)
treebefe8fec5ad60327cead19dcdbe6773c3e7d00b5 /usr/src
parent5e985db5e665b4363a8154fb1870b3895ca33192 (diff)
downloadillumos-gate-fb3fb4f3d76d55b64440afd0af72775dfad3bd1d.tar.gz
PSARC 2006/054 DTrace JNI Binding
6384263 PSARC 2006/054 DTrace JNI Binding
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/lib/Makefile10
-rw-r--r--usr/src/lib/libdtrace_jni/Makefile67
-rw-r--r--usr/src/lib/libdtrace_jni/Makefile.com94
-rw-r--r--usr/src/lib/libdtrace_jni/amd64/Makefile34
-rw-r--r--usr/src/lib/libdtrace_jni/common/dtj_attr.c181
-rw-r--r--usr/src/lib/libdtrace_jni/common/dtj_consume.c2247
-rw-r--r--usr/src/lib/libdtrace_jni/common/dtj_error.c79
-rw-r--r--usr/src/lib/libdtrace_jni/common/dtj_jnitab.c853
-rw-r--r--usr/src/lib/libdtrace_jni/common/dtj_probe.c108
-rw-r--r--usr/src/lib/libdtrace_jni/common/dtj_util.c1506
-rw-r--r--usr/src/lib/libdtrace_jni/common/dtj_util.h376
-rw-r--r--usr/src/lib/libdtrace_jni/common/dtrace_jni.c1893
-rw-r--r--usr/src/lib/libdtrace_jni/common/dtrace_jni.h491
-rw-r--r--usr/src/lib/libdtrace_jni/i386/Makefile33
-rw-r--r--usr/src/lib/libdtrace_jni/java/Makefile242
-rw-r--r--usr/src/lib/libdtrace_jni/java/docs/examples/TestAPI.java62
-rw-r--r--usr/src/lib/libdtrace_jni/java/docs/examples/TestAPI2.java80
-rw-r--r--usr/src/lib/libdtrace_jni/java/docs/examples/TestTarget.java81
-rwxr-xr-xusr/src/lib/libdtrace_jni/java/docs/examples/hello.d33
-rw-r--r--usr/src/lib/libdtrace_jni/java/docs/examples/intrstat.d43
-rw-r--r--usr/src/lib/libdtrace_jni/java/docs/examples/syscall.d39
-rw-r--r--usr/src/lib/libdtrace_jni/java/docs/examples/target.d33
-rw-r--r--usr/src/lib/libdtrace_jni/java/docs/html/JavaDTraceAPI.html207
-rw-r--r--usr/src/lib/libdtrace_jni/java/docs/html/fast.html542
-rw-r--r--usr/src/lib/libdtrace_jni/java/docs/images/JavaDTraceAPI.gifbin0 -> 27447 bytes
-rw-r--r--usr/src/lib/libdtrace_jni/java/manifest/dtrace.mf11
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AbstractAggregationValue.java108
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregate.java278
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregateSpec.java163
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregation.java368
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationRecord.java208
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationValue.java52
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AvgValue.java154
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Consumer.java751
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerAdapter.java79
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerEvent.java71
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerException.java107
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerListener.java127
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/CountValue.java106
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DTraceException.java46
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DataEvent.java117
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Distribution.java620
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Drop.java274
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DropEvent.java114
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Error.java324
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ErrorEvent.java116
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExceptionHandler.java54
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExitRecord.java94
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Flow.java234
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/InterfaceAttributes.java529
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelStackRecord.java382
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LinearDistribution.java291
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LocalConsumer.java1332
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LogDistribution.java175
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MaxValue.java81
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MinValue.java81
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/NativeException.java70
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Option.java681
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintaRecord.java482
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintfRecord.java221
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Probe.java201
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeData.java693
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeDescription.java467
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeInfo.java207
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessEvent.java118
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessState.java358
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Program.java259
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProgramInfo.java252
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Record.java36
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ResourceLimitException.java50
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ScalarRecord.java327
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackFrame.java162
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackValueRecord.java104
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/SumValue.java81
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Tuple.java370
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/UserStackRecord.java327
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Utility.java82
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ValueRecord.java42
-rw-r--r--usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/package.html62
-rw-r--r--usr/src/lib/libdtrace_jni/sparc/Makefile33
-rw-r--r--usr/src/lib/libdtrace_jni/sparcv9/Makefile34
-rw-r--r--usr/src/lib/libdtrace_jni/spec/Makefile29
-rw-r--r--usr/src/lib/libdtrace_jni/spec/Makefile.targ33
-rw-r--r--usr/src/lib/libdtrace_jni/spec/amd64/Makefile37
-rw-r--r--usr/src/lib/libdtrace_jni/spec/dtrace_jni.spec155
-rw-r--r--usr/src/lib/libdtrace_jni/spec/i386/Makefile36
-rw-r--r--usr/src/lib/libdtrace_jni/spec/sparc/Makefile36
-rw-r--r--usr/src/lib/libdtrace_jni/spec/sparcv9/Makefile37
-rw-r--r--usr/src/lib/libdtrace_jni/spec/versions43
-rw-r--r--usr/src/pkgdefs/SUNWdtrc/prototype_com16
-rw-r--r--usr/src/pkgdefs/SUNWdtrc/prototype_doc179
-rw-r--r--usr/src/pkgdefs/SUNWdtrc/prototype_i38610
-rw-r--r--usr/src/pkgdefs/SUNWdtrc/prototype_sparc10
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 &lt; 1) {
+ System.err.println("Usage: java TestAPI &lt;script&gt; [ 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 &lt; Record &gt; 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-&gt;ts = vtimestamp;
+ }
+
+ sdt:::interrupt-complete
+ / self-&gt;ts &amp;&amp; arg0 /
+ {
+ this-&gt;devi = (struct dev_info *)arg0;
+ @counts[stringof(`devnamesp[this-&gt;devi-&gt;devi_major].dn_name),
+ this-&gt;devi-&gt;devi_instance, cpu] = count();
+ @times[stringof(`devnamesp[this-&gt;devi-&gt;devi_major].dn_name),
+ this-&gt;devi-&gt;devi_instance, cpu] = sum(vtimestamp - self-&gt;ts);
+ self-&gt;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
new file mode 100644
index 0000000000..a3a7aa871f
--- /dev/null
+++ b/usr/src/lib/libdtrace_jni/java/docs/images/JavaDTraceAPI.gif
Binary files differ
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>
+ * &#64;a[123] = sum(1);
+ * &#64;b[456] = sum(2);
+ * printa(&#64;a, &#64;b, &#64;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 (&#46;) 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 (&#46;) 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>&#64;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