summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdtrace_jni/java
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/lib/libdtrace_jni/java
parent5e985db5e665b4363a8154fb1870b3895ca33192 (diff)
downloadillumos-joyent-fb3fb4f3d76d55b64440afd0af72775dfad3bd1d.tar.gz
PSARC 2006/054 DTrace JNI Binding
6384263 PSARC 2006/054 DTrace JNI Binding
Diffstat (limited to 'usr/src/lib/libdtrace_jni/java')
-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
65 files changed, 14461 insertions, 0 deletions
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>