diff options
| author | tomee <none@none> | 2006-02-16 12:15:27 -0800 |
|---|---|---|
| committer | tomee <none@none> | 2006-02-16 12:15:27 -0800 |
| commit | fb3fb4f3d76d55b64440afd0af72775dfad3bd1d (patch) | |
| tree | befe8fec5ad60327cead19dcdbe6773c3e7d00b5 /usr/src/lib/libdtrace_jni/java | |
| parent | 5e985db5e665b4363a8154fb1870b3895ca33192 (diff) | |
| download | illumos-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')
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 < 1) { + System.err.println("Usage: java TestAPI <script> [ macroargs... ]"); + System.exit(2); + } + + File file = new File(args[0]); + String[] macroArgs = new String[args.length - 1]; + System.arraycopy(args, 1, macroArgs, 0, (args.length - 1));</font> + + Consumer consumer = new LocalConsumer(); + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + System.out.println(e.getProbeData()); + } + }); +<font color=#aaaaaa> + try {</font> + consumer.open(); + consumer.compile(file, macroArgs); + consumer.enable(); + consumer.go();<font color=#aaaaaa> + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + }</font> +</tt></pre> +<br> +Compile the test program as follows: +<pre><tt> + java -cp dtrace.jar TestAPI.java +</tt></pre> +<br> +<h4><a name="Running_hello.d_Script"></a>Running the <tt>hello.d</tt> Script</h4> +Now we need a D script for the program to run. The following is a +simple example that prints "hello, world" and exits:<br> +<b>D script (<a href="../examples/hello.d">hello.d</a>)</b> +<pre><tt> + dtrace:::BEGIN + { + trace("hello, world"); + exit(0); + } +</tt></pre> +<br> +Run as follows:<br> +On i86:<br> +<pre><tt> + java -cp .:dtrace.jar TestAPI hello.d +</tt></pre> +<br> +On sparc you need to add the <tt>-d64</tt> option to java: +<pre><tt> + java -d64 -cp .:dtrace.jar TestAPI hello.d</span><br> +</tt></pre> +<br> +You may need to set <tt>LD_LIBRARY_PATH</tt> so that Java can find +<tt>libdtrace_jni.so</tt> on your system. The output should look like +this: +<pre><tt> + org.opensolaris.os.dtrace.ProbeData[epid = 1, cpu = 1, + enabledProbeDescription = dtrace:::BEGIN, flow = null, records = + ["hello, world", 0]] +</tt></pre> +<br> +There is one record in the <a +href="../api/org/opensolaris/os/dtrace/ProbeData.html"><tt>ProbeData</tt></a> +for each action in the D script. The first record is generated by the +<tt>trace()</tt> action. The second is generated by the <tt>exit()</tt> +action. For prettier output, you could change the <tt>ConsumerAdapter <a + href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#dataReceived%28org.opensolaris.os.dtrace.DataEvent%29">dataReceived()</a></tt> +implementation as follows: +<pre><tt><font color=#aaaaaa> + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + // System.out.println(e.getProbeData());</font> + ProbeData data = e.getProbeData(); + java.util.List < Record > records = data.getRecords(); + for (Record r : records) { + if (r instanceof ExitRecord) { + } else { + System.out.println(r); + } + }<font color=#aaaaaa> + } + });</font> +</tt></pre> +<br> +<h2><a name="Aggregations"></a>Aggregations</h2> +The example Java program can just as easily run a more complex script, +such as an aggregation:<br> +<b>D script (<a href="../examples/syscall.d">syscall.d</a>)</b> +<pre><tt> + syscall:::entry + / execname == $$1 / + { + @[probefunc] = count(); + } + + profile:::tick-1sec + { + printa(@); + clear(@); + } +</tt></pre> +<br> +The above script uses the <tt>$$1</tt> macro variable as a placeholder +for whatever executable you'd like to trace. See the <a +href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidliq?a=view><b> +Macro Arguments</b></a> section of the <b>Scripting</b> chapter of the +<i>Solaris Dynamic Tracing Guide</i>. Using two dollar signs (<tt>$$1</tt>) +instead of one (<tt>$1</tt>) forces expansion of the macro variable to +type string.<br> +<br> +To run the example Java program using the above D script, you need to +specify an argument to the <tt>execname</tt> placeholder. To be sure of +getting output, let's use "java":<br> +On i86: +<pre><tt> + java -cp .:dtrace.jar TestAPI syscall.d java +</tt></pre> +<br> +On sparc: +<pre><tt> + java -d64 -cp .:dtrace.jar TestAPI syscall.d java +</tt></pre> +<br> +A data record generated by the <tt>printa()</tt> action is printed to +the console once every second. It contains counts of system calls by +function name made by java. No record is generated by the +<tt>clear()</tt> action.<br> +<br> +If you omit the argument to the <tt>execname</tt> placeholder, the +program fails to compile and the API throws the following exception: +<pre><tt> + org.opensolaris.os.dtrace.DTraceException: failed to compile script + syscall.d: line 2: macro argument $$1 is not defined + at org.opensolaris.os.dtrace.LocalConsumer._compileFile(Native Method) + at org.opensolaris.os.dtrace.LocalConsumer.compile(LocalConsumer.java:342) + at TestAPI.main(TestAPI.java:26) +</tt></pre> +<br> +A DTrace script may have more than one aggregation. In that case, each +aggregation needs a distinct name:<br> +<b>D script (<a href="../examples/intrstat.d">intrstat.d</a>)</b> +<pre><tt> + sdt:::interrupt-start + { + self->ts = vtimestamp; + } + + sdt:::interrupt-complete + / self->ts && arg0 / + { + this->devi = (struct dev_info *)arg0; + @counts[stringof(`devnamesp[this->devi->devi_major].dn_name), + this->devi->devi_instance, cpu] = count(); + @times[stringof(`devnamesp[this->devi->devi_major].dn_name), + this->devi->devi_instance, cpu] = sum(vtimestamp - self->ts); + self->ts = 0; + } +</tt></pre> +<br> +The <tt>@counts</tt> and <tt>@times</tt> aggregations both accumulate +values for each unique combination of device name, device instance, and +CPU (a three-element tuple). In this example we drop the <tt>tick</tt> +probe to demonstrate a more convenient way to get aggregation data +without the use of the <tt>printa()</tt> action. The <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#getAggregate%28%29"> +<tt>getAggregate()</tt></a> method allows us to get all aggregations at +once on a programmatic interval.<br> +<b>Java program (<a href="../examples/TestAPI2.java">TestAPI2.java</a>)</b> +<pre><tt><font color=#aaaaaa> + ... + + try { + consumer.open(); + consumer.compile(file, macroArgs); + consumer.enable(); + consumer.go();</font> + + Aggregate a; + do { + Thread.sleep(1000); // 1 second + a = consumer.getAggregate(); + if (!a.asMap().isEmpty()) { + System.out.println(a); + } + } while (consumer.isRunning());<font color=#aaaaaa> + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + ...</font> +</tt></pre> +<br> +Compile and run: +<pre><tt> + javac -cp dtrace.jar TestAPI2.java +</tt></pre> +<br> +On i86: +<pre><tt> + java -cp .:dtrace.jar TestAPI2 intrstat.d +</tt></pre> +<br> +On sparc: +<pre><tt> + java -d64 -cp .:dtrace.jar TestAPI2 intrstat.d +</tt></pre> +<br> +Try removing the <tt>tick</tt> probe from the <tt>syscall.d</tt> example +and running it again with the above modification (<tt>TestAPI2</tt>).<br> +<br> +By default, the requested aggregate includes every aggregation and +accumulates running totals. To display values per time interval +(instead of running totals), clear the aggregations each time you call +<tt>getAggregate()</tt>. Clearing an aggregation resets all counts to +zero without removing any tuples. The following modification to the +example above clears all aggregations: +<pre><tt><font color=#aaaaaa> + // a = consumer.getAggregate();</font> + a = consumer.getAggregate(null, null); // included, cleared +</tt></pre> +<br> +Each <tt>Set</tt> of aggregation names, <tt>included</tt> and +<tt>cleared</tt>, specifies <i>all</i> aggregations if <tt>null</tt> and +no aggregations if empty. Any subset is possible. However, if an +aggregation has ever been used in the <tt>printa()</tt> action, it is no +longer available to the <tt>getAggregate()</tt> method.<br> +<br> +Be aware that you cannot call <tt>getAggregate()</tt> on an interval +faster that the <tt>aggrate</tt> setting. See the <a +href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlis?a=view> +<b>Options and Tunables</b></a> chapter of the <i>Solaris Dynamic +Tracing Guide</i>. See also the <a +href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhf?a=view> +<b>Minimizing Drops</b></a> section of the <b>Aggregations</b> chapter +for specific information about the <tt>aggrate</tt> option. The default +<tt>aggrate</tt> is once per second. Here's an example of how you might +double the <tt>aggrate</tt> to minimize drops: +<pre><tt> + consumer.setOption(Option.aggrate, Option.millis(500)); // every half second +</tt></pre> +<br> +Even a single drop terminates the consumer unless you override the <a +href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#dataDropped%28org.opensolaris.os.dtrace.DropEvent%29"> +<tt>dataDropped()</tt></a> method of <tt>ConsumerAdapter</tt> to handle +drops differently. To avoid drops, it is probably better to increase +the <tt>aggsize</tt> option, since increasing the <tt>aggrate</tt> makes +the consumer work harder. In most cases, the <tt>aggrate</tt> should +only be increased when you need to update a display of aggregation data +more frequently than once per second. Many runtime options, including +<tt>aggrate</tt>, can be changed dynamically while the consumer is +running.<br> +<br> +It's also worth mentioning that a D aggregation may omit square +brackets and aggregate only a single value: +<pre><tt> + @total = count(); +</tt></pre> +<br> +The resulting singleton <a +href="../api/org/opensolaris/os/dtrace/Aggregation.html"> +<tt>Aggregation</tt></a> instance has one record that may be obtained as +follows: +<pre><tt> + Aggregate a = consumer.getAggregate(); + Aggregation total = a.getAggregation("total"); + AggregationRecord totalRecord = total.getRecord(Tuple.EMPTY); +</tt></pre> +<br> +<h2><a name="Target_Process"></a>Target Process ID</h2> +In addition to supporting macro arguments (see the <tt>syscall.d</tt> +aggregation example above), the Java DTrace API also supports the +<tt>$target</tt> macro variable. (See the <a +href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlir?a=view> +<b>Target Process ID</b></a> section of the <b>Scripting</b> chapter of +the <i>Solaris Dynamic Tracing Guide</i>.) This allows you to trace a +process from the very beginning of its execution, rather than sometime +after you manually obtain its process ID. The API does this by creating +a process that is initially suspended and allowed to start only after <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#go%28%29"> +<tt>go()</tt></a> has initiated tracing. For example, we might want to +aggregate all the system calls from start to finish made by the +<tt>date</tt> command:<br> +<b>D script (<a href="../examples/target.d">target.d</a>)</b> +<pre><tt> + syscall:::entry + / pid == $target / + { + @[probefunc] = count(); + } +</tt></pre> +<br> +A modified version of the <tt>TestAPI.java</tt> program adds the <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#createProcess%28java.lang.String%29"> +<tt>createProcess()</tt></a> call to execute the given command but +prevent it from starting until the consumer is running:<br> +<b>Java program (<a href="../examples/TestTarget.java">TestTarget.java</a>)</b> +<pre><tt><font color=#aaaaaa> + ... + consumer.open();</font> + consumer.createProcess(command);<font color=#aaaaaa> + consumer.compile(file); + consumer.enable(); + consumer.go(); + ...</font> +</tt></pre> +<br> +It also overrides the <a +href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#processStateChanged%28org.opensolaris.os.dtrace.ProcessEvent%29"> +<tt>processStateChanged()</tt></a> method of the +<tt>ConsumerAdapter</tt> to print a notification when the process has +ended: +<pre><tt><font color=#aaaaaa> + ... + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + System.out.println(e.getProbeData()); + } + public void consumerStopped(ConsumerEvent e) { + try { + Aggregate a = consumer.getAggregate(); + for (Aggregation agg : a.asMap().values()) { + for (AggregationRecord rec : agg.asMap().values()) { + System.out.println(rec.getTuple() + " " + + rec.getValue()); + } + } + } catch (Exception x) { + x.printStackTrace(); + System.exit(1); + } + consumer.close(); + }</font> + public void processStateChanged(ProcessEvent e) { + System.out.println(e.getProcessState()); + }<font color=#aaaaaa> + }); + ...</font> +</tt></pre> +<br> +Compile and run: +<pre><tt> + javac -cp dtrace.jar TestTarget.java +</tt></pre> +<br> +On i86: +<pre><tt> + java -cp .:dtrace.jar TestTarget target.d date +</tt></pre> +<br> +On sparc: +<pre><tt> + java -d64 -cp .:dtrace.jar TestTarget target.d date +</tt></pre> +<br> +The consumer exits automatically when the target <tt>date</tt> process +completes.<br> +<h2><a name="Closing_Consumers"></a>Closing Consumers</h2> +An application using the Java DTrace API may run multiple consumers +simultaneously. When a consumer stops running, the programmer is +responsible for closing it in order to release the system resources it +holds. A consumer may stop running for any of the following reasons: +<ul> + <li>It was stopped explicitly by a call to its <a +href=../api/org/opensolaris/os/dtrace/Consumer.html#stop()> +<tt>stop()</tt></a> method</li> + <li>It encountered the <tt>exit()</tt> action</li> + <li>Its <tt>$target</tt> process or processes (if any) all completed</li> + <li>It encountered an exception</li> +</ul> +By default, an exception prints a stack trace to <tt>stderr</tt> before +notifying listeners that the consumer has stopped. You can define +different behavior by setting an <a +href=../api/org/opensolaris/os/dtrace/ExceptionHandler.html> +<tt>ExceptionHandler</tt></a>, but the consumer is still stopped.<br> +<br> +The same listener that receives probe data generated by DTrace is also +notified when the consumer stops. This is a good place to close the +consumer: +<pre><tt><font color=#aaaaaa> + consumer.addConsumerListener(new ConsumerAdapter() { + public void dataReceived(DataEvent e) { + System.out.println(e.getProbeData()); + }</font> + public void consumerStopped(ConsumerEvent e) { + Consumer consumer = (Consumer)e.getSource(); + consumer.close(); + } + }<font color=#aaaaaa> + });</font> +</tt></pre> +<br> +This releases the resources held by the consumer in all cases, i.e. +after it exits for <i>any</i> of the reasons listed above.<br> +<br> +You can request the last aggregate snapshot made by a stopped consumer, +as long as it has not yet been closed: +<pre><tt> + Aggregate a = consumer.getAggregate(); +</tt></pre> +<br> +Note however that any aggregation that has already appeared in a <a +href=../api/org/opensolaris/os/dtrace/PrintaRecord.html> +<tt>PrintaRecord</tt></a> as a result of the <tt>printa()</tt> action +action will not be included in the requested aggregate. +<h2><a name="Learning_DTrace"></a>Learning More</h2> +<br> +The <a href="http://www.opensolaris.org/os/community/dtrace/"> +OpenSolaris DTrace page</a> has links to resources to help you learn +DTrace. In particular, you should read the <a +href="http://docs.sun.com/db/doc/817-6223"><i>Solaris Dynamic Tracing + Guide</i></a>.<br> +<br> +Try the example Java programs on this page with other D scripts. You +need not remove <tt>#!/usr/sbin/dtrace -s</tt> from the top of an +executable script. You may want to remove <tt>profile:::tick*</tt> +clauses if you plan to use the <tt>Consumer</tt> <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#getAggregate%28%29"> +<tt>getAggregate()</tt></a> method and control the aggregating interval +programmatically. If the script uses the pre-compiler, you will need to +call the <tt>Consumer</tt> <a +href="../api/org/opensolaris/os/dtrace/Consumer.html#setOption%28java.lang.String%29"> +<tt>setOption()</tt></a> method with the <a +href="../api/org/opensolaris/os/dtrace/Option.html#cpp"> +<tt>Option.cpp</tt></a> argument.<br> +<br> +To quickly familiarize yourself with the Java DTrace API, take a look at +the overview <a href="JavaDTraceAPI.html">diagram</a>.<br> +<br> +<a href="#Quick_Start_Guide_to_the_Java_DTrace_API_">Back to top</a><br> +<br> +</body> +</html> diff --git a/usr/src/lib/libdtrace_jni/java/docs/images/JavaDTraceAPI.gif b/usr/src/lib/libdtrace_jni/java/docs/images/JavaDTraceAPI.gif Binary files differnew file mode 100644 index 0000000000..a3a7aa871f --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/docs/images/JavaDTraceAPI.gif diff --git a/usr/src/lib/libdtrace_jni/java/manifest/dtrace.mf b/usr/src/lib/libdtrace_jni/java/manifest/dtrace.mf new file mode 100644 index 0000000000..46fc8c8207 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/manifest/dtrace.mf @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Specification-Title: Java DTrace API +Specification-Version: 1.0 +Implementation-Title: org.opensolaris.os.dtrace +Implementation-Vendor: Sun Microsystems, Inc. + +Name: org/opensolaris/os/dtrace/ +Sealed: true + +Name: org/opensolaris/os/support/ +Sealed: true diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AbstractAggregationValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AbstractAggregationValue.java new file mode 100644 index 0000000000..a136c287a5 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AbstractAggregationValue.java @@ -0,0 +1,108 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.Serializable; +import java.beans.*; + +/** + * A numeric value generated by a D aggregating action such as {@code + * count()} or {@code sum()}. + * <p> + * Immutable. + * + * @author Tom Erickson + */ +abstract class AbstractAggregationValue + implements AggregationValue, Serializable +{ + static final long serialVersionUID = 2340811719178724026L; + + /** @serial */ + private final Number value; + + public + AbstractAggregationValue(long v) + { + value = new Long(v); + } + + public + AbstractAggregationValue(double v) + { + value = new Double(v); + } + + public Number + getValue() + { + return value; + } + + /** + * Compares the specified object with this aggregation value for + * equality. Defines equality as having the same type and the same + * numeric value. + * + * @return {@code true} if and only if the specified object is an + * aggregation value of the same {@code Class} as this value, and + * both values return equal numbers from {@link #getValue()}. + */ + public boolean + equals(Object o) + { + if (o instanceof AbstractAggregationValue) { + AbstractAggregationValue v = (AbstractAggregationValue)o; + return (value.equals(v.value) && + (getClass() == v.getClass())); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return value.hashCode(); + } + + /** + * Gets the string representation of {@link #getValue()}. + * + * @return the string representation of {@link #getValue()} returned + * by {@link Object#toString()} + */ + public String + toString() + { + return value.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregate.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregate.java new file mode 100644 index 0000000000..23aa3af99b --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregate.java @@ -0,0 +1,278 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.beans.*; +import java.io.*; + +/** + * A consistent snapshot of all aggregations requested by a single + * {@link Consumer}. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#getAggregate() + * + * @author Tom Erickson + */ +public final class Aggregate implements Serializable +{ + static final long serialVersionUID = 3180340417154076628L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Aggregate.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"snaptime", "aggregations"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final long snaptime; + + // Map must not have same name as named PersistenceDelegate property + // ("aggregations"), otherwise it gets confused for a bean property + // and XMLDecoder calls the constructor with a Map instead of the + // value of the getAggregations() method. + + private transient Map <String, Aggregation> map; + + /** + * Called by native code. + */ + private + Aggregate(long snaptimeNanos) + { + snaptime = snaptimeNanos; + map = new HashMap <String, Aggregation> (); + } + + /** + * Creates an aggregate with the given snaptime and aggregations. + * Supports XML persistence. + * + * @param snaptimeNanos nanosecond timestamp when this aggregate was + * snapped + * @param aggregations unordered collection of aggregations + * belonging to this aggregate + * @throws NullPointerException if the given collection of + * aggregations is {@code null} + */ + public + Aggregate(long snaptimeNanos, Collection <Aggregation> aggregations) + { + snaptime = snaptimeNanos; + mapAggregations(aggregations); + } + + // assumes map is not yet created + private void + mapAggregations(Collection <Aggregation> aggregations) + { + int capacity = (int)(((float)aggregations.size() * 3.0f) / 2.0f); + // avoid rehashing and optimize lookup; will never be modified + map = new HashMap <String, Aggregation> (capacity, 1.0f); + for (Aggregation a : aggregations) { + map.put(a.getName(), a); + } + } + + /** + * Gets the nanosecond timestamp of this aggregate snapshot. + * + * @return nanosecond timestamp of this aggregate snapshot + */ + public long + getSnaptime() + { + return snaptime; + } + + /** + * Gets an unordered list of all aggregations in this aggregate + * snapshot. The list is easily sortable using {@link + * java.util.Collections#sort(List list, Comparator c)} provided any + * user-defined ordering. Modifying the returned list has no effect + * on this aggregate. Supports XML persistence. + * + * @return modifiable unordered list of all aggregations in this + * aggregate snapshot; list is non-null and possibly empty + */ + public List <Aggregation> + getAggregations() + { + // Must return an instance of a public, mutable class in order + // to support XML persistence. + List <Aggregation> list = new ArrayList <Aggregation> (map.size()); + list.addAll(map.values()); + return list; + } + + /** + * Gets the aggregation with the given name if it exists in this + * aggregate snapshot. + * + * @param name the name of the desired aggregation, or empty string + * to request the unnamed aggregation. In D, the unnamed + * aggregation is used anytime a name does not follow the + * aggregation symbol '{@code @}', for example: + * <pre> {@code @ = count();}</pre> as opposed to + * <pre> {@code @counts = count()}</pre> resulting in an + * {@code Aggregation} with the name "counts". + * + * @return {@code null} if no aggregation by the given name exists + * in this aggregate + * @see Aggregation#getName() + */ + public Aggregation + getAggregation(String name) + { + // This was decided March 18, 2005 in a meeting with the DTrace + // team that calling getAggregation() with underbar should + // return the unnamed aggregation (same as calling with empty + // string). Underbar is used to identify the unnamed + // aggregation in libdtrace; in the jave API it is identifed by + // the empty string. The API never presents underbar but + // accepts it as input (just converts underbar to empty string + // everywhere it sees it). + name = Aggregate.filterUnnamedAggregationName(name); + return map.get(name); + } + + /** + * In the native DTrace library, the unnamed aggregation {@code @} + * is given the name {@code _} (underbar). The Java DTrace API does + * not expose this implementation detail but instead identifies the + * unnamed aggregation with the empty string. Here we convert the + * name of the unnamed aggregation at the earliest opportunity. + * <p> + * Package level access. Called by this class and PrintaRecord when + * adding the Aggregation abstraction on top of native aggregation + * records. + */ + static String + filterUnnamedAggregationName(String name) + { + if ((name != null) && name.equals("_")) { + return ""; + } + return name; + } + + /** + * Gets a read-only {@code Map} view of this aggregate. + * + * @return a read-only {@code Map} view of this aggregate keyed by + * aggregation name + */ + public Map <String, Aggregation> + asMap() + { + return Collections. <String, Aggregation> unmodifiableMap(map); + } + + /** + * Called by native code. + * + * @throws IllegalStateException if the aggregation with the given + * name already has a record with the same tuple key as the given + * record. + */ + private void + addRecord(String aggregationName, long aggid, AggregationRecord rec) + { + aggregationName = Aggregate.filterUnnamedAggregationName( + aggregationName); + Aggregation aggregation = getAggregation(aggregationName); + if (aggregation == null) { + aggregation = new Aggregation(aggregationName, aggid); + map.put(aggregationName, aggregation); + } + aggregation.addRecord(rec); + } + + /** + * Serialize this {@code Aggregate} instance. + * + * @serialData Serialized fields are emitted, followed by a {@link + * java.util.List} of {@link Aggregation} instances. + */ + private void + writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeObject(getAggregations()); + } + + @SuppressWarnings("unchecked") + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // cannot cast to parametric type without compiler warning + List <Aggregation> aggregations = (List)s.readObject(); + // load serialized form into private map as a defensive copy + mapAggregations(aggregations); + // check class invariants after defensive copy + } + + /** + * Gets a string representation of this aggregate snapshot useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Aggregate.class.getName()); + buf.append("[snaptime = "); + buf.append(snaptime); + buf.append(", aggregations = "); + List <Aggregation> a = getAggregations(); + Collections.sort(a, new Comparator <Aggregation> () { + public int compare(Aggregation a1, Aggregation a2) { + return a1.getName().compareTo(a2.getName()); + } + }); + buf.append(Arrays.toString(a.toArray())); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregateSpec.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregateSpec.java new file mode 100644 index 0000000000..656c6f6b15 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregateSpec.java @@ -0,0 +1,163 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; + +/** + * Implementation detail used by {@link Consumer#getAggregate()}. + * Package level access. + * + * @author Tom Erickson + */ +class AggregateSpec { + private Set <String> includedAggregationNames; + private Set <String> clearedAggregationNames; + + AggregateSpec() + { + includedAggregationNames = new HashSet <String> (); + clearedAggregationNames = new HashSet <String> (); + } + + public boolean + isIncludeByDefault() + { + return (includedAggregationNames == null); + } + + public boolean + isClearByDefault() + { + return (clearedAggregationNames == null); + } + + public void + setIncludeByDefault(boolean include) + { + if (include) { + includedAggregationNames = null; + } else if (includedAggregationNames == null) { + includedAggregationNames = new HashSet <String> (); + } + } + + public void + setClearByDefault(boolean clear) + { + if (clear) { + clearedAggregationNames = null; + } else if (clearedAggregationNames == null) { + clearedAggregationNames = new HashSet <String> (); + } + } + + /** + * Specifies which aggregations to include in an aggregate snapshot. + * If none are specified, all aggregations are included. A snapshot + * is read-consistent across all included aggregations. + * + * @see Consumer#getAggregate(AggregateSpec spec) + */ + public void + addIncludedAggregationName(String name) + { + if (includedAggregationNames == null) { + includedAggregationNames = new HashSet <String> (); + } + includedAggregationNames.add(name); + } + + /** + * Specifies which aggregations to clear after snapping the + * aggregate. If none are specified, no aggregations are cleared. + * <p> + * Aggregations are cleared immediately after they are snapped + * before any more data can be accumulated in order to prevent loss + * of data between snapshots. + * + * @see Consumer#getAggregate(AggregateSpec spec) + */ + public void + addClearedAggregationName(String name) + { + if (clearedAggregationNames == null) { + clearedAggregationNames = new HashSet <String> (); + } + clearedAggregationNames.add(name); + } + + public Set <String> + getIncludedAggregationNames() + { + if (includedAggregationNames == null) { + return Collections. <String> emptySet(); + } + return Collections.unmodifiableSet(includedAggregationNames); + } + + public Set <String> + getClearedAggregationNames() + { + if (clearedAggregationNames == null) { + return Collections. <String> emptySet(); + } + return Collections.unmodifiableSet(clearedAggregationNames); + } + + public boolean + isIncluded(String aggregationName) + { + return ((includedAggregationNames == null) || + includedAggregationNames.contains(aggregationName)); + } + + public boolean + isCleared(String aggregationName) + { + return ((clearedAggregationNames == null) || + clearedAggregationNames.contains(aggregationName)); + } + + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(AggregateSpec.class.getName()); + buf.append("[includedAggregationNames = "); + buf.append(Arrays.toString(getIncludedAggregationNames().toArray())); + buf.append(", clearedAggregationNames = "); + buf.append(Arrays.toString(getClearedAggregationNames().toArray())); + buf.append(", includeByDefault = "); + buf.append(isIncludeByDefault()); + buf.append(", clearByDefault = "); + buf.append(isClearByDefault()); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregation.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregation.java new file mode 100644 index 0000000000..f88532819a --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Aggregation.java @@ -0,0 +1,368 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.beans.*; +import java.io.*; + +/** + * A snapshot of a DTrace aggregation. The name of an {@code + * Aggregation} instance matches the source declaration, for example + * <pre> {@code @a[execname] = count();}</pre> + * results in an {@code Aggregation} named "a" (the name does not + * include the preceding {@code @}). For convenience, a single + * aggregation can remain unnamed (multiple aggregations in the same D + * program need distinct names). The unnamed aggregation results in an + * {@code Aggregation} instance whose name is the empty string, for + * example + * <pre> {@code @[execname] = count();}</pre> + * An aggregation can list more than one variable in square brackets in + * order to accumulate a value for each unique combination, or {@link + * Tuple}. Each tuple instance is associated with its accumulated + * {@link AggregationValue} in an {@link AggregationRecord}. For + * example + * <pre> {@code @counts[execname, probefunc, cpu] = count();}</pre> + * results in an {@code Aggregation} named "counts" containing records + * each pairing a {@link CountValue} to a three-element {@code Tuple}. + * It is also possible to omit the square brackets, for example + * <pre> {@code @a = count();}</pre> + * results in an {@code Aggregation} named "a" with only a single record + * keyed to the empty tuple ({@link Tuple#EMPTY}). + * <p> + * For more information, see the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlh7?a=view> + * <b>Aggregations</b></a> chapter of the <i>Solaris Dynamic Tracing + * Guide</i>. Also, the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlfv?a=view> + * <b>Built-in Variables</b></a> section of the <b>Variables</b> chapter + * describes variables like {@code execname}, {@code probefunc}, and + * {@code cpu} useful for aggregating. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregate + * @see PrintaRecord + * + * @author Tom Erickson + */ +public final class Aggregation implements Serializable { + static final long serialVersionUID = 2340811719178724026L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Aggregation.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"name", "id", "records"}) + { + @Override + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return ((newInstance != null) && (oldInstance != null) && + (oldInstance.getClass() == newInstance.getClass())); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** @serial */ + private String name; + /** @serial */ + private final long id; + private transient Map <Tuple, AggregationRecord> map; + + /** + * Package-level access, called by Aggregate + */ + Aggregation(String aggregationName, long aggregationID) + { + name = Aggregate.filterUnnamedAggregationName(aggregationName); + id = aggregationID; + map = new HashMap <Tuple, AggregationRecord> (); + } + + /** + * Creates an aggregation with the given name, ID, and records. + * Supports XML persistence. + * + * @param aggregationName the name of this aggregation, empty string + * if this aggregation is unnamed + * @param aggregationID ID generated from a sequence by the native + * DTrace library + * @param aggregationRecords unordered collection of records + * belonging to this aggregation + * @throws NullPointerException if the specified name or list of + * records is {@code null} + * @throws IllegalArgumentException if any record has an empty + * tuple, unless it is the only record in the given collection (only + * a singleton generated by an aggregation without square brackets + * uses {@link Tuple#EMPTY} as a key) + * @see #getRecord(Tuple key) + */ + public + Aggregation(String aggregationName, long aggregationID, + Collection <AggregationRecord> aggregationRecords) + { + name = Aggregate.filterUnnamedAggregationName(aggregationName); + id = aggregationID; + mapRecords(aggregationRecords); + validate(); + } + + // assumes map is not yet created + private void + mapRecords(Collection <AggregationRecord> records) + { + int capacity = (int)(((float)records.size() * 3.0f) / 2.0f); + // avoid rehashing and optimize lookup; will never be modified + map = new HashMap <Tuple, AggregationRecord> (capacity, 1.0f); + for (AggregationRecord record : records) { + map.put(record.getTuple(), record); + } + } + + private void + validate() + { + if (name == null) { + throw new NullPointerException("name is null"); + } + for (AggregationRecord r : map.values()) { + if ((r.getTuple().size() == 0) && (map.size() > 1)) { + throw new IllegalArgumentException("empty tuple " + + "allowed only in singleton aggregation"); + } + } + } + + /** + * Gets the name of this aggregation. + * + * @return the name of this aggregation exactly as it appears in the + * D program minus the preceding {@code @}, or an empty string if + * the aggregation is unnamed, for example: + * <pre> {@code @[execname] = count();}</pre> + */ + public String + getName() + { + return name; + } + + /** + * Gets the D compiler-generated ID of this aggregation. + * + * @return the D compiler-generated ID + */ + public long + getID() + { + return id; + } + + /** + * Gets an unordered list of this aggregation's records. The list + * is easily sortable using {@link java.util.Collections#sort(List + * list, Comparator c)} provided any user-defined ordering. + * Modifying the returned list has no effect on this aggregation. + * Supports XML persistence. + * + * @return a newly created list that copies this aggregation's + * records by reference in no particular order + */ + public List <AggregationRecord> + getRecords() + { + List <AggregationRecord> list = + new ArrayList <AggregationRecord> (map.values()); + return list; + } + + /** + * Package level access, called by Aggregate and PrintaRecord. + * + * @throws IllegalArgumentException if this aggregation already + * contains a record with the same tuple key as the given record + */ + void + addRecord(AggregationRecord record) + { + Tuple key = record.getTuple(); + if (map.put(key, record) != null) { + throw new IllegalArgumentException("already contains a record " + + "with tuple " + key); + } + } + + /** + * Gets a read-only {@code Map} view of this aggregation. + * + * @return a read-only {@code Map} view of this aggregation + */ + public Map <Tuple, AggregationRecord> + asMap() + { + return Collections.unmodifiableMap(map); + } + + /** + * Compares the specified object with this aggregation for equality. + * Defines equality as having equal names and equal records. + * + * @return {@code true} if and only if the specified object is an + * {@code Aggregation} with the same name as this aggregation and + * the {@code Map} views of both aggregations returned by {@link + * #asMap()} are equal as defined by {@link + * AbstractMap#equals(Object o) AbstractMap.equals()} + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof Aggregation) { + Aggregation a = (Aggregation)o; + return (name.equals(a.name) && + (map.equals(a.map))); // same mappings + } + return false; + } + + /** + * Overridden to ensure that equal aggregations have equal hash + * codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + name.hashCode(); + hash = (37 * hash) + map.hashCode(); + return hash; + } + + /** + * Gets the record associated with the given key, or the singleton + * record of an aggregation declared without square brackets if + * {@code key} is {@code null} or empty. + * + * @param key The record key, or an empty tuple (see {@link + * Tuple#EMPTY}) to obtain the value from a <i>singleton</i> (a + * non-keyed instance with only a single value) generated from a + * DTrace aggregation declarated without square brackets, for + * example: + * <pre> {@code @a = count();}</pre> + * @return the record associated with the given key, or {@code null} + * if no record in this aggregation is associated with the given key + */ + public AggregationRecord + getRecord(Tuple key) + { + if (key == null) { + key = Tuple.EMPTY; + } + return map.get(key); + } + + /** + * Serialize this {@code Aggregation} instance. + * + * @serialData Serialized fields are emitted, followed by a {@link + * java.util.List} of {@link AggregationRecord} instances. + */ + private void + writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeObject(getRecords()); + } + + @SuppressWarnings("unchecked") + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // cannot cast to parametric type without compiler warning + List <AggregationRecord> records = (List)s.readObject(); + // load serialized form into private map as a defensive copy + mapRecords(records); + // Check class invariants (only after defensive copy) + name = Aggregate.filterUnnamedAggregationName(name); + validate(); + } + + /** + * Gets a string representation of this aggregation useful for + * logging and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + @Override + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Aggregation.class.getName()); + buf.append("[name = "); + buf.append(name); + buf.append(", id = "); + buf.append(id); + buf.append(", records = "); + List <AggregationRecord> recordList = getRecords(); + // Sort by tuple so that equal aggregations have equal strings + Collections.sort(recordList, new Comparator <AggregationRecord> () { + public int compare(AggregationRecord r1, AggregationRecord r2) { + Tuple t1 = r1.getTuple(); + Tuple t2 = r2.getTuple(); + return t1.compareTo(t2); + } + }); + buf.append('['); + boolean first = true; + for (AggregationRecord record : recordList) { + if (first) { + first = false; + } else { + buf.append(", "); + } + buf.append(record); + } + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationRecord.java new file mode 100644 index 0000000000..4ab439c4c5 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationRecord.java @@ -0,0 +1,208 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A single key-value pair in a DTrace aggregation. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class AggregationRecord implements Record, Serializable { + static final long serialVersionUID = -8439277589555814411L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(AggregationRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"tuple", "value"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private Tuple tuple; + /** @serial */ + private AggregationValue value; + + /** + * Creates an aggregation record with the given key and value. + * Supports XML persistence. + * + * @param tupleKey aggregation tuple, may be empty (see {@link + * Tuple#EMPTY}) to indicate that this record's value belongs to an + * unkeyed aggregation declared without square brackets, for + * example: <pre> {@code @a = count();}</pre> + * @param recordValue aggregated value associated with the given + * tuple + * @throws NullPointerException if the given key or value is + * {@code null} + */ + public + AggregationRecord(Tuple tupleKey, AggregationValue recordValue) + { + tuple = tupleKey; + value = recordValue; + validate(); + } + + private void + validate() + { + if (tuple == null) { + throw new NullPointerException("key is null"); + } + if (value == null) { + throw new NullPointerException("value is null"); + } + } + + /** + * Gets the multi-element key associated with {@link + * #getValue()}. + * + * @return non-null, possibly empty tuple + * @see Aggregation#getRecord(Tuple key) + */ + public Tuple + getTuple() + { + return tuple; + } + + /** + * Gets the value associated with {@link #getTuple()}. Values + * generated by the DTrace actions {@code count()}, {@code sum()}, + * {@code avg()}, {@code min()}, and {@code max()} are of type + * {@link Long}. Values generated by the DTrace actions {@code + * quantize(}) and {@code lquantize()} are of type {@link + * Distribution}. + * + * @return non-null value keyed to {@link #getTuple()} + */ + public AggregationValue + getValue() + { + return value; + } + + /** + * Compares the specified object with this aggregation record for + * equality. Defines equality as having the same tuple and value. + * + * @return {@code true} if and only if the specified object is an + * {@code AggregationRecord} and both records have equal tuples as + * defined by {@link Tuple#equals(Object o)} and equal values as + * defined by the implementation of {@link AggregationValue} + */ + public boolean + equals(Object o) + { + if (o instanceof AggregationRecord) { + AggregationRecord r = (AggregationRecord)o; + return (tuple.equals(r.tuple) && + value.equals(r.value)); + } + return false; + } + + /** + * Overridden to ensure that equal records have equal hash codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + tuple.hashCode(); + hash = (37 * hash) + value.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this aggregation record useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + @Override + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(AggregationRecord.class.getName()); + buf.append("[tuple = "); + buf.append(tuple); + buf.append(", value = "); + buf.append(value); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationValue.java new file mode 100644 index 0000000000..1bc2b176c1 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AggregationValue.java @@ -0,0 +1,52 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * A value accumulated by an aggregating DTrace action such as {@code + * count()} or {@code sum()}. Each {@code AggregationValue} is + * associated with a {@link Tuple} in an {@link AggregationRecord}. In + * other words it is a value in a key-value pair (each pair representing + * an entry in a DTrace aggregation). + * <p> + * This value may be a single number or consist of multiple numbers, + * such as a value distribution. In the latter case, it still has a + * single, composite value useful for display and/or comparison. + * + * @see AggregationRecord + * + * @author Tom Erickson + */ +public interface AggregationValue { + /** + * Gets the numeric value of this instance. + * + * @return non-null numeric value + */ + public Number getValue(); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AvgValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AvgValue.java new file mode 100644 index 0000000000..bd1257a009 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/AvgValue.java @@ -0,0 +1,154 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code avg()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class AvgValue extends AbstractAggregationValue { + static final long serialVersionUID = 1633169020110237906L; + + /** @serial */ + private final long total; + /** @serial */ + private final long count; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(AvgValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value", "total", "count"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code avg()} action. + * Supports XML persistence. + * + * @param v average + * @param averagedTotal sum total of all values averaged + * @param averagedValueCount number of values averaged + * @throws IllegalArgumentException if the given count is negative + * or if the given average is not the value expected for the given + * total and count + */ + public + AvgValue(long v, long averagedTotal, long averagedValueCount) + { + super(v); + total = averagedTotal; + count = averagedValueCount; + validate(); + } + + private void + validate() + { + if (count < 0) { + throw new IllegalArgumentException("count is negative"); + } + long average = super.getValue().longValue(); + if (count == 0) { + if (average != 0) { + throw new IllegalArgumentException( + "count of values is zero, average is non-zero (" + + average + ")"); + } + } else { + if (average != (total / count)) { + throw new IllegalArgumentException( + getValue().toString() + " is not the expected " + + "average of total " + total + " and count " + + count); + } + } + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the average of the aggregated values. + * + * @return average of the aggregated values, equal to <code>({@link + * #getTotal()} / {@link #getCount()})</code> rounded down + */ + public Long + getValue() + { + return (Long)super.getValue(); + } + + /** + * Gets the sum total of the aggregated values. + * + * @return the sum total of the aggregated values + */ + public long + getTotal() + { + return total; + } + + /** + * Gets the number of aggregated values included in the average. + * + * @return the number of aggregated values included in the average + */ + public long + getCount() + { + return count; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Consumer.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Consumer.java new file mode 100644 index 0000000000..6b6b47681d --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Consumer.java @@ -0,0 +1,751 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.*; + +/** + * Interface to the native DTrace library, each instance is a single + * DTrace consumer. To consume the output of DTrace program actions, + * {@link #addConsumerListener(ConsumerListener l) register a probe data + * listener}. To get a snapshot of all aggregations in a D program on + * your own programmatic interval without relying on DTrace actions to + * generate that output, use the {@link #getAggregate()} method. + * + * @see ProbeData + * @see Aggregate + * + * @author Tom Erickson + */ +public interface Consumer { + + /** + * Optional flags passed to {@link #open(Consumer.OpenFlag[] flags) + * open()}. + */ + public enum OpenFlag { + /** + * Generate 32-bit D programs. {@code ILP32} and {@link + * Consumer.OpenFlag#LP64 LP64} are mutually exclusive. + */ + ILP32, + /** + * Generate 64-bit D programs. {@code LP64} and {@link + * Consumer.OpenFlag#ILP32 ILP32} are mutually exclusive. + */ + LP64, + }; + + /** + * Opens this DTrace consumer. Optional flags indicate behaviors + * that can only be set at the time of opening. Most optional + * behaviors are set using {@link #setOption(String option, String + * value) setOption()} after opening the consumer. In the great + * majority of cases, the consumer is opened without specifying any + * flags: + * <pre> {@code consumer.open();}</pre> + * Subsequent calls to set options, compile DTrace programs, enable + * probes, and run this consumer may be made from any thread. + * + * @throws NullPointerException if any of the given open flags is + * {@code null} + * @throws IllegalArgumentException if any of the given flags are + * mutually exlusive + * @throws IllegalStateException if this consumer is closed or has + * already been opened + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #compile(File program, String[] macroArgs) + * @see #compile(String program, String[] macroArgs) + * @see #enable() + * @see #go() + */ + public void open(OpenFlag ... flags) throws DTraceException; + + /** + * Compiles the given D program string. Optional macro arguments + * replace corresponding numbered macro variables in the D program + * starting at {@code $1}. + * + * @param program program string + * @param macroArgs macro substitutions for <i>$n</i> placeholders + * embedded in the given D program: {@code macroArgs[0]} replaces + * all occurrences of {@code $1}, {@code macroArgs[1]} replaces all + * occurrences of {@code $2}, and so on. {@code $0} is + * automatically replaced by the executable name and should not be + * included in the {@code macroArgs} parameter. See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidliq?a=view> + * <b>Macro Arguments</b></a> section of the <b>Scripting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * @return a non-null {@code Program} identifier that may be passed + * to {@link #enable(Program program) enable()} + * @throws NullPointerException if the given program string or any + * of the given macro arguments is {@code null} + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #go()}, or if the + * consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #compile(File program, String[] macroArgs) + */ + public Program compile(String program, String ... macroArgs) + throws DTraceException; + + /** + * Compiles the given D program file. Optional macro arguments + * replace corresponding numbered macro variables in the D program + * starting at {@code $1}. + * + * @param program program file + * @param macroArgs macro substitutions for <i>$n</i> placeholders + * embedded in the given D program: {@code macroArgs[0]} replaces + * all occurrences of {@code $1}, {@code macroArgs[1]} replaces all + * occurrences of {@code $2}, and so on. {@code $0} is + * automatically set to the name of the given file and should not be + * included in the {@code macroArgs} parameter. See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidliq?a=view> + * <b>Macro Arguments</b></a> section of the <b>Scripting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * @return a non-null {@code Program} identifier that may be passed + * to {@link #enable(Program program) enable()} + * @throws NullPointerException if the given program file or any of + * the given macro arguments is {@code null} + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #go()}, or if the + * consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @throws FileNotFoundException if the given program file cannot be + * opened + * @throws IOException if an I/O error occurs while reading the + * contents of the given program file + * @throws SecurityException if a security manager exists and its + * {@code checkRead()} method denies read access to the file + * @see #compile(String program, String[] macroArgs) + */ + public Program compile(File program, String ... macroArgs) + throws DTraceException, IOException, SecurityException; + + /** + * Enables all DTrace probes compiled by this consumer. Call {@code + * enable()} with no argument to enable everything this consumer has + * compiled so far (most commonly a single program, the only one to + * be compiled). Call with one {@link Program} at a time if you + * need information about enabled probes specific to each program. + * + * @throws IllegalStateException if called before compiling at least + * one program, or if any compiled program is already enabled, or if + * {@link #go()} was already called, or if this consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #enable(Program program) + */ + public void enable() throws DTraceException; + + /** + * Enables DTrace probes matching the given program and attaches + * information about those probes to the given program. A probe + * matched multiple times (within the same D program or in multiple + * D programs) triggers the actions associated with each matching + * occurrence every time that probe fires. + * + * @param program A {@code Program} identifier returned by {@link + * #compile(String program, String[] macroArgs) compile(String + * program, ...)} or {@link #compile(File program, String[] + * macroArgs) compile(File program, ...)}: If the given program is + * {@code null}, the call has the same behavior as {@link #enable()} + * with no argument; if the given program is non-null, the call + * enables only those probes matching that program. In the latter + * case, the {@code Program} parameter is modified as a way of + * passing back information about the given program and its matching + * probes, including program stability. + * @throws IllegalArgumentException if the given program is non-null + * and not compiled by this {@code Consumer} + * @throws IllegalStateException if the given program is already + * enabled (or if the given program is {@code null} and <i>any</i> + * program is already enabled), or if {@link #go()} was already + * called, or if this consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #compile(String program, String[] macroArgs) + * @see #compile(File program, String[] macroArgs) + * @see #enable() + * @see #getProgramInfo(Program program) + */ + public void enable(Program program) throws DTraceException; + + /** + * Attaches information about matching DTrace probes to the given + * program. Attaches the same information to the given program as + * that attached by {@link #enable(Program program)} but without + * enabling the probes. + * + * @throws NullPointerException if the given program is {@code null} + * @throws IllegalArgumentException if the given program was not + * compiled by this {@code Consumer} + * @throws IllegalStateException if called after {@link #close()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #compile(String program, String[] macroArgs) + * @see #compile(File program, String[] macroArgs) + * @see #enable(Program program) + */ + public void getProgramInfo(Program program) throws DTraceException; + + /** + * Sets a boolean option. + * + * @throws NullPointerException if the given option is {@code null} + * @throws DTraceException if a value is expected for the given + * option, or if the option is otherwise invalid + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #close()}, or if + * the given option is a boolean compile-time option and {@link + * #go()} has already been called (see {@link Option} for a + * breakdown of runtime and compile-time options) + * @see #setOption(String option, String value) + * @see #unsetOption(String option) + */ + public void setOption(String option) throws DTraceException; + + /** + * Unsets a boolean option. + * + * @throws NullPointerException if the given option is {@code null} + * @throws DTraceException if the given option is not a boolean + * option, or if the option is otherwise invalid + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #close()}, or if + * the given option is a boolean compile-time option and {@link + * #go()} has already been called (see {@link Option} for a + * breakdown of runtime and compile-time options) + * @see #setOption(String option) + */ + public void unsetOption(String option) throws DTraceException; + + /** + * Sets the value of a DTrace option. If the given option affects + * compile-time behavior, it must be set before calling {@link + * #compile(String program, String[] macroArgs) compile(String + * program, ...)} or {@link #compile(File program, String[] + * macroArgs) compile(File program, ...)} in order to have an effect + * on compilation. Some runtime options including {@link + * Option#switchrate switchrate} and {@link Option#aggrate aggrate} + * are settable while a consumer is running; others must be set + * before calling {@link #go()}. See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlis?a=view> + * <b>Options and Tunables</b></a> chapter of the <i>Solaris Dynamic + * Guide</i> for information about specific options. + * + * @throws NullPointerException if the given option or value is + * {@code null} + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #close()}, or if + * the given option is a boolean compile-time option and {@code + * go()} has already been called (see {@link Option} for a breakdown + * of runtime and compile-time options) + * @throws DTraceException for any of the following: + * <ul><li>The option is invalid</li> + * <li>The value is invalid for the given option</li> + * <li>{@code go()} has been called to start this consumer, and the + * option is not settable on a running consumer (some runtime + * options, including {@link Option#switchrate switchrate} and + * {@link Option#aggrate aggrate} are settable while the consumer is + * running)</li></ul> + * + * @see #open(OpenFlag[] flags) + * @see #getOption(String option) + * @see Option + */ + public void setOption(String option, String value) throws DTraceException; + + /** + * Gets the value of a DTrace option. + * + * @throws NullPointerException if the given option is {@code null} + * @throws IllegalStateException if called before {@link + * #open(OpenFlag[] flags) open()} or after {@link #close()} + * @throws DTraceException if the given option is invalid + * @return the value of the given DTrace option: If the given option + * is a boolean option and is currently unset, the returned value is + * {@link Option#UNSET}. If the given option is a <i>size</i> + * option, the returned value is in bytes. If the given option is a + * <i>time</i> option, the returned value is in nanoseconds. If the + * given option is {@link Option#bufpolicy bufpolicy}, the returned + * value is one of {@link Option#BUFPOLICY_RING BUFPOLICY_RING}, + * {@link Option#BUFPOLICY_FILL BUFPOLICY_FILL}, or {@link + * Option#BUFPOLICY_SWITCH BUFPOLICY_SWITCH}. If the given option + * is {@link Option#bufresize bufresize}, the returned value is one + * of {@link Option#BUFRESIZE_AUTO BUFRESIZE_AUTO} or {@link + * Option#BUFRESIZE_MANUAL BUFRESIZE_MANUAL}. + * + * @see #setOption(String option) + * @see #unsetOption(String option) + * @see #setOption(String option, String value) + * @see Option + */ + public long getOption(String option) throws DTraceException; + + /** + * Reports whether or not this consumer is open. + * + * @return {@code true} if and only if {@link #open(OpenFlag[] + * flags) open()} has been called on this consumer and {@link + * #close()} has not + */ + public boolean isOpen(); + + /** + * Reports whether or not it is valid to call {@link #go()}. + * + * @return {@code true} if and only if at least one program has been + * compiled, all compiled programs have been enabled, {@code go()} + * has not already been called, and {@link #close()} has not been + * called + */ + public boolean isEnabled(); + + /** + * Reports whether or not this consumer is running. There may be a + * delay after calling {@link #go()} before this consumer actually + * starts running (listeners are notified by the {@link + * ConsumerListener#consumerStarted(ConsumerEvent e) + * consumerStarted()} method). + * + * @return {@code true} if this consumer is running, {@code false} + * otherwise + */ + public boolean isRunning(); + + /** + * Reports whether or not this consumer is closed. A closed + * consumer cannot be reopened. + * <p> + * Note that a closed consumer is different from a consumer that has + * not yet been opened. + * + * @return {@code true} if {@link #close()} has been called on this + * consumer, {@code false} otherwise + */ + public boolean isClosed(); + + /** + * Begin tracing and start a background thread to consume generated + * probe data. + * + * @throws IllegalStateException if not {@link #isEnabled()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #go(ExceptionHandler h) + * @see #open(OpenFlag[] flags) + * @see #compile(String program, String[] macroArgs) + * @see #compile(File program, String[] macroArgs) + * @see #enable() + * @see #stop() + * @see #close() + */ + public void go() throws DTraceException; + + /** + * Begin tracing and start a background thread to consume generated + * probe data. Handle any exception thrown in the consumer thread + * with the given handler. + * + * @throws IllegalStateException if not {@link #isEnabled()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #go() + */ + public void go(ExceptionHandler h) throws DTraceException; + + /** + * Stops all tracing, as well as the background thread started by + * {@link #go()} to consume generated probe data. A stopped + * consumer cannot be restarted. It is necessary to {@code close()} + * a stopped consumer to release the system resources it holds. + * <p> + * A consumer may stop on its own in response to the {@code exit()} + * action (see <b>{@code exit()}</b> in the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhm?a=view> + * <b>Special Actions</b></a> section of the <b>Actions and + * Subroutines</b> chapter of the <i>Solaris Dynamic Tracing + * Guide</i>). Similarly, a consumer stops automatically if it has + * at least one target process and all its target processes have + * completed (see {@link #createProcess(String command) + * createProcess()} and {@link #grabProcess(int pid) + * grabProcess()}). A consumer also stops automatically if it + * encounters an exception while consuming probe data. In these + * cases it is not necessary to call {@code stop()}. If a consumer + * stops for any reason (an explicit call to {@code stop()} or any + * of the reasons just given), listeners are notified through the + * {@link ConsumerListener#consumerStopped(ConsumerEvent e) + * consumerStopped()} method. + * <p> + * Note that a call to {@code stop()} blocks until the background + * thread started by {@code go()} actually stops. After {@code + * stop()} returns, a call to {@link #isRunning()} returns {@code + * false}. If a {@code DTraceException} is thrown while stopping + * this consumer, it is handled by the handler passed to {@link + * #go(ExceptionHandler h)} (or a default handler if none is + * specified). + * + * @throws IllegalStateException if called before {@link #go()} or + * if {@code stop()} was already called + * @see #go() + * @see #close() + */ + public void stop(); + + /** + * Closes an open consumer and releases the system resources it was + * holding. If the consumer is running, {@code close()} will {@link + * #stop()} it automatically. A closed consumer cannot be + * reopened. Closing a consumer that has not yet been opened makes + * it illegal to open that consumer afterwards. It is a no-op to + * call {@code close()} on a consumer that is already closed. + * + * @see #open(OpenFlag[] flags) + */ + public void close(); + + /** + * Adds a listener for probe data generated by this consumer. + */ + public void addConsumerListener(ConsumerListener l); + + /** + * Removes a listener for probe data generated by this consumer. + */ + public void removeConsumerListener(ConsumerListener l); + + /** + * Gets a snapshot of all aggregations except those that have + * already been captured in a {@link PrintaRecord}. Does not clear + * any aggregation. + * <p> + * Provides a programmatic alternative to the {@code printa(}) + * action (see <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhv?a=view> + * <b>{@code printa()}</b></a> in the <b>Output Formatting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>). + * + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #getAggregate(Set includedAggregationNames, Set + * clearedAggregationNames) + */ + public Aggregate getAggregate() throws DTraceException; + + /** + * Gets a snapshot of all the specified aggregations except those + * that have already been captured in a {@link PrintaRecord}. Does + * not clear any aggregation. + * <p> + * Provides a programmatic alternative to the {@code printa(}) + * action (see <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhv?a=view> + * <b>{@code printa()}</b></a> in the <b>Output Formatting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>). + * + * @param includedAggregationNames if {@code null}, all available + * aggregations are included; if non-null, only those aggregations + * specifically named by the given set are included + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #getAggregate(Set includedAggregationNames, Set + * clearedAggregationNames) + */ + public Aggregate getAggregate(Set <String> includedAggregationNames) + throws DTraceException; + + /** + * Gets a snapshot of all the specified aggregations except those + * that have already been captured in a {@link PrintaRecord}, with + * the side effect of atomically clearing any subset of those + * aggregations. Clearing an aggregation resets all of its values + * to zero without removing any of its keys. Leave aggregations + * uncleared to get running totals, otherwise specify that an + * aggregation be cleared to get values per time interval. Note + * that once an aggregation is captured in a {@code PrintaRecord} + * (as a result of the {@code printa()} action), it is no longer + * available to the {@code getAggregate()} method. + * <p> + * Provides a programmatic alternative to the {@code printa(}) (see + * <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhv?a=view> + * <b>{@code printa()}</b></a> in the <b>Output Formatting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>) and {@code + * clear()} actions. + * + * @param includedAggregationNames if {@code null}, all available + * aggregations are included; if non-null, only those aggregations + * specifically named by the given set are included + * @param clearedAggregationNames if {@code null}, all available + * aggregations are cleared; if non-null, only those aggregations + * specifically named by the given set are cleared + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @throws DTraceException if an exception occurs in the native + * DTrace library + */ + public Aggregate getAggregate(Set <String> includedAggregationNames, + Set <String> clearedAggregationNames) throws DTraceException; + + /** + * Creates a process by executing the given command on the system + * and returns the created process ID. The created process is + * suspended until calling {@link #go()} so that the process waits + * to do anything until this consumer has started tracing (allowing + * a process to be traced from the very beginning of its execution). + * The macro variable {@code $target} in a D program will be + * replaced by the process ID of the created process. When the + * created process exits, this consumer notifies listeners through + * the {@link ConsumerListener#processStateChanged(ProcessEvent e) + * processStateChanged()} method. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlir?a=view> + * <b>Target Process ID</b></a> section of the <b>Scripting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * + * @param command a string whose first token is assumed to be the + * name of the command and whose subsequent tokens are the arguments + * to that command. + * @return ID of the created process (pid) + * @throws NullPointerException if the given command is {@code nul}l + * @throws IllegalArgumentException if the given command is empty or + * contains only whitespace + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if the process cannot be created + * @see #grabProcess(int pid) + */ + public int createProcess(String command) throws DTraceException; + + /** + * Grabs the specified process and caches its symbol tables. The + * macro variable {@code $target} in a D program will be replaced by + * the process ID of the grabbed process. When the specified + * process exits, this consumer notifies listeners through the + * {@link ConsumerListener#processStateChanged(ProcessEvent e) + * processStateChanged()} method. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlir?a=view> + * <b>Target Process ID</b></a> section of the <b>Scripting</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * + * @param pid process ID of the process to be grabbed + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if the process cannot be grabbed + * @see #createProcess(String command) + */ + public void grabProcess(int pid) throws DTraceException; + + /** + * Lists probes that match the given probe description. See {@link + * ProbeDescription} for information about pattern syntax and + * wildcarding. + * + * @param filter use {@link ProbeDescription#EMPTY} to get all + * probes, otherwise get only those probes that match the given + * filter + * @return a non-null list of probe descriptions + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #open(OpenFlag[] flags) + * @see #close() + * @see #listProbeDetail(ProbeDescription filter) + * @see #listProgramProbes(Program program) + */ + public List <ProbeDescription> listProbes(ProbeDescription filter) + throws DTraceException; + + /** + * Lists probes that match the given probe description and includes + * detail such as stability information about each listed probe. + * + * @param filter use {@link ProbeDescription#EMPTY} to get all + * probes, otherwise get only those probes that match the given + * filter + * @return a non-null list of probe detail + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #listProbes(ProbeDescription filter) + * @see #listProgramProbeDetail(Program program) + */ + public List <Probe> listProbeDetail(ProbeDescription filter) + throws DTraceException; + + /** + * Lists probes that match the given compiled program. A probe + * matches a D program if that program contains any matching probe + * description. + * + * @param program a {@code Program} identifier returned by {@link + * #compile(String program, String[] macroArgs) compile(String + * program, ...)} or {@link #compile(File program, String[] + * macroArgs) compile(File program, ...)} + * @return a non-null list of probe descriptions + * @throws NullPointerException if the given program identifier is + * {@code null} + * @throws IllegalArgumentException if the specified program was not + * compiled by this consumer + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #listProbes(ProbeDescription filter) + */ + public List <ProbeDescription> listProgramProbes(Program program) + throws DTraceException; + + /** + * Lists probes that match the given compiled program and includes + * detail such as stability information about each listed probe. + * + * @param program a {@code Program} identifier returned by {@link + * #compile(String program, String[] macroArgs) compile(String + * program, ...)} or {@link #compile(File program, String[] + * macroArgs) compile(File program, ...)} + * @return a non-null list of probe detail + * @throws NullPointerException if the given program identifier is + * {@code null} + * @throws IllegalArgumentException if the specified program was not + * compiled by this consumer + * @throws IllegalStateException if called before {@link + * #open(Consumer.OpenFlag[] flags) open()} or after {@link #go()}, + * or if the consumer is closed + * @throws DTraceException if an exception occurs in the native + * DTrace library + * @see #listProgramProbes(Program program) + * @see #listProbeDetail(ProbeDescription filter) + */ + public List <Probe> listProgramProbeDetail(Program program) + throws DTraceException; + + /** + * Gets the kernel function name for the given 32-bit kernel + * address. + * + * @param address 32-bit kernel function address, such as the value + * of a {@link Tuple} member in an {@link AggregationRecord} to be + * converted for display + * @return the result of kernel function lookup as one of the + * following:<ul><li>{@code module`function}</li> + * <li>{@code module`function+offset}</li> + * <li>{@code module`address}</li> + * <li>{@code address}</li></ul> where {@code module} and {@code + * function} are names, and {@code offset} and {@code address} are + * integers in hexadecimal format preceded by "{@code 0x}". {@code + * offset} is the number of bytes from the beginning of the + * function, included when non-zero. {@code address} is simply the + * hex form of the input paramater, returned when function lookup + * fails. The exact details of this format are subject to change. + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @see #lookupKernelFunction(long address) + */ + public String lookupKernelFunction(int address); + + /** + * Gets the kernel function name for the given 64-bit kernel + * address. + * + * @param address 64-bit kernel function address + * @return kernel function name + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @see #lookupKernelFunction(int address) + */ + public String lookupKernelFunction(long address); + + /** + * Gets the user function name for the given 32-bit user address and + * process ID. + * + * @param pid ID of the user process containing the addressed + * function + * @param address 32-bit user function address, such as the value + * of a {@link Tuple} member in an {@link AggregationRecord} to be + * converted for display. + * @return result of user function lookup as one of the + * following:<ul> <li>{@code module`function}</li> + * <li>{@code module`function+offset}</li> + * <li>{@code module`address}</li> + * <li>{@code address}</li></ul> where {@code module} and {@code + * function} are names, and {@code offset} and {@code address} are + * integers in hexadecimal format preceded by "{@code 0x}". {@code + * offset} is the number of bytes from the beginning of the + * function, included when non-zero. {@code address} is simply the + * hex form of the input paramater, returned when function lookup + * fails. The exact details of this format are subject to change. + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @see #lookupUserFunction(int pid, long address) + */ + public String lookupUserFunction(int pid, int address); + + /** + * Gets the user function name for the given 64-bit user address and + * process ID. + * + * @param pid ID of the user process containing the addressed + * function + * @param address 64-bit user function address + * @return user function name + * @throws IllegalStateException if called before {@link #go()} or + * after {@link #close()} + * @see #lookupUserFunction(int pid, int address) + */ + public String lookupUserFunction(int pid, long address); + + /** + * Gets the version of the native DTrace library. + * + * @return version string generated by the native DTrace library + * (same as the output of {@code dtrace(1M)} with the {@code -V} + * option) + */ + public String getVersion(); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerAdapter.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerAdapter.java new file mode 100644 index 0000000000..f12cba5541 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerAdapter.java @@ -0,0 +1,79 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * An abstract adapter class for getting events from a {@link Consumer}. + * The methods in this class are empty except for a few that implement + * the default behavior of terminating a consumer by throwing an + * exception. This class exists as a convenience for implementing + * consumer listeners. + * + * @see Consumer#addConsumerListener(ConsumerListener l) + * + * @author Tom Erickson + */ +public abstract class ConsumerAdapter implements ConsumerListener { + /** Empty method */ + public void dataReceived(DataEvent e) throws ConsumerException {} + + /** + * Terminates a running {@link Consumer} by throwing an exception. + * + * @throws ConsumerException + */ + public void + dataDropped(DropEvent e) throws ConsumerException + { + Drop drop = e.getDrop(); + throw new ConsumerException(drop.getDefaultMessage(), drop); + } + + /** + * Terminates a running {@link Consumer} by throwing an exception. + * + * @throws ConsumerException + */ + public void + errorEncountered(ErrorEvent e) throws ConsumerException + { + Error error = e.getError(); + throw new ConsumerException(error.getDefaultMessage(), error); + } + + /** Empty method */ + public void processStateChanged(ProcessEvent e) throws ConsumerException {} + /** Empty method */ + public void consumerStarted(ConsumerEvent e) {} + /** Empty method */ + public void consumerStopped(ConsumerEvent e) {} + /** Empty method */ + public void intervalBegan(ConsumerEvent e) {} + /** Empty method */ + public void intervalEnded(ConsumerEvent e) {} +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerEvent.java new file mode 100644 index 0000000000..555e30caeb --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerEvent.java @@ -0,0 +1,71 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.EventObject; + +/** + * An event indicating a state change in a DTrace {@link Consumer}. + * + * @see ConsumerListener + * + * @author Tom Erickson + */ +public class ConsumerEvent extends EventObject { + static final long serialVersionUID = 1659441401142401810L; + + /** @serial */ + private long timestamp; + + /** + * Creates a consumer event with the given source {@link Consumer} + * and nanosecond timestamp. + * + * @param source the {@link Consumer} that is the source of the + * event + * @param timeNanos nanosecond timestamp of this event + */ + public + ConsumerEvent(Object source, long timeNanos) + { + super(source); + timestamp = timeNanos; + } + + /** + * Gets the nanosecond timestamp of this event. + * + * @return nanosecond timestamp of the event on the system where the + * consumer opened a native DTrace library handle + */ + public long + getTimestamp() + { + return timestamp; + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerException.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerException.java new file mode 100644 index 0000000000..cd6437c169 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerException.java @@ -0,0 +1,107 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * Exception thrown by a {@link ConsumerListener} to terminate a running + * {@link Consumer}. + * + * @author Tom Erickson + */ +public class ConsumerException extends Exception { + static final long serialVersionUID = -2125855097525822644L; + + /** @serial */ + private Object notificationObject; + + /** + * Creates a consumer exception with the given message. + * + * @see #ConsumerException(String message, Object + * dtraceNotificationObject) + */ + public + ConsumerException(String message) + { + super(message); + } + + /** + * Creates an exception thrown by a {@link ConsumerListener} + * implementation to terminate a running {@link Consumer}, usually + * in response to a drop or an error reported by the native DTrace + * library. Optionally includes the object reported by the native + * DTrace library so it can be used by an {@link ExceptionHandler} + * to display details about why the consumer terminated. + * + * @param message default display message explaining why the + * consumer was terminated. + * @param notification usually the object passed to a {@link + * ConsumerListener} from DTrace that prompted this exception. The + * notification could be any of the following: <ul> <li>a {@link + * Drop} passed to {@link ConsumerListener#dataDropped(DropEvent e) + * dataDropped()}</li> <li>an {@link Error} passed to {@link + * ConsumerListener#errorEncountered(ErrorEvent e) + * errorEncountered()}</li> <li>a {@link ProcessState} passed to + * {@link ConsumerListener#processStateChanged(ProcessEvent e) + * processStateChanged()}</li> </ul> or it could be a user-defined + * object that describes anything unexpected in {@link + * ConsumerListener#dataReceived(DataEvent e) dataReceived()} or + * that defines an arbitrary error threshold. An {@link + * ExceptionHandler} should be defined to handle any type of + * notification object set by user code. May be {@code null}. + * @see Consumer#go(ExceptionHandler h) + */ + public + ConsumerException(String message, Object notification) + { + super(message); + notificationObject = notification; + } + + /** + * Gets the optional object from the {@link ConsumerListener} that + * communicates to the {@link ExceptionHandler} why the listener + * threw this exception. Usually this is the object from DTrace + * (such as an {@link org.opensolaris.os.dtrace.Error Error}) that + * prompted the exception, simply forwarded to the exception + * handler. + * + * @return an object that communicates to the {@link + * ExceptionHandler} why the {@link ConsumerListener} threw this + * exception, may be {@code null} + * @see Consumer#go(ExceptionHandler h) + * @see #ConsumerException(String message, + * Object dtraceNotificationObject) + */ + public Object + getNotificationObject() + { + return notificationObject; + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerListener.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerListener.java new file mode 100644 index 0000000000..d207705678 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ConsumerListener.java @@ -0,0 +1,127 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.EventListener; + +/** + * Listener for data generated by a single DTrace {@link Consumer}. + * + * @author Tom Erickson + */ +public interface ConsumerListener extends EventListener { + /** + * Called whenever a DTrace probe fires (that is, once for each + * instance of {@link ProbeData} generated by DTrace). Identifies + * the probe and provides data generated by the probe's actions. To + * terminate the consumer in the event of unexpected data, throw a + * {@link ConsumerException} from this method. + * + * @throws ConsumerException if the implementation should terminate + * the running consumer + */ + public void dataReceived(DataEvent e) throws ConsumerException; + + /** + * Called when traced data is dropped because of inadequate buffer + * space. To terminate the consumer in the event of a drop, throw + * a {@link ConsumerException} from this method. + * + * @throws ConsumerException if the implementation should terminate + * the running consumer + */ + public void dataDropped(DropEvent e) throws ConsumerException; + + /** + * Called when an error is encountered in the native DTrace library + * while tracing probe data. To terminate the consumer, throw a + * {@link ConsumerException} from this method. + * + * @throws ConsumerException if the implementation should terminate + * the running consumer + */ + public void errorEncountered(ErrorEvent e) throws ConsumerException; + + /** + * Called when the state of a target process changes. To terminate + * the consumer in the event of unexpected process state, throw a + * {@link ConsumerException} from this method. + * + * @throws ConsumerException if the implementation should terminate + * the running consumer + * @see Consumer#createProcess(String command) + * @see Consumer#grabProcess(int pid) + */ + public void processStateChanged(ProcessEvent e) throws ConsumerException; + + /** + * Called once when the source {@link Consumer} is successfully + * started in response to {@link Consumer#go()}. + * + * @see #consumerStopped(ConsumerEvent e) + */ + public void consumerStarted(ConsumerEvent e); + + /** + * Called once when the source {@link Consumer} is stopped, + * indicating that this listener should expect no further events. + * Called only if there was a prior call to {@link + * #consumerStarted(ConsumerEvent e) consumerStarted()}, that is, + * only if the consumer was successfully started by a call to {@link + * Consumer#go()}. Guaranteed to be called whether the consumer was + * stopped by request (via {@link Consumer#stop()}), terminated + * normally as a result of the DTrace {@code exit()} action or the + * completion of all target processes, or terminated abnormally + * because of an exception. It is necessary to call {@link + * Consumer#close()} to release any system resources still held by + * the stopped consumer. + * + * @see #consumerStarted(ConsumerEvent e) + */ + public void consumerStopped(ConsumerEvent e); + + /** + * Called when the source {@link Consumer} wakes up to process its + * buffer of traced probe data. + * + * @see #intervalEnded(ConsumerEvent e) + */ + public void intervalBegan(ConsumerEvent e); + + /** + * Called when the source {@link Consumer} finishes processing its + * buffer of traced probe data and is about to sleep until the next + * interval. The rate of consumption may be controlled with the + * {@link Option#switchrate switchrate} and {@link Option#aggrate + * aggrate} options (see {@link Consumer#setOption(String option, + * String value)}). + * + * @see #intervalBegan(ConsumerEvent e) + */ + public void intervalEnded(ConsumerEvent e); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/CountValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/CountValue.java new file mode 100644 index 0000000000..d7c6c39924 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/CountValue.java @@ -0,0 +1,106 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code count()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class CountValue extends AbstractAggregationValue { + static final long serialVersionUID = 5948954123445410783L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(CountValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code count()} action. + * Supports XML persistence. + * + * @param v aggregated value count + * @throws IllegalArgumentException if the given count is negative + */ + public + CountValue(long v) + { + super(v); + validate(); + } + + private void + validate() + { + long count = super.getValue().longValue(); + if (count < 0) { + throw new IllegalArgumentException("count is negative"); + } + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the number of aggregated values. + * + * @return the number of aggregated values + */ + public Long + getValue() + { + return (Long)super.getValue(); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DTraceException.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DTraceException.java new file mode 100644 index 0000000000..9ed5366fd7 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DTraceException.java @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * Exception in the native DTrace library. + * + * @author Tom Erickson + */ +public class DTraceException extends Exception { + static final long serialVersionUID = -4048327080469337476L; + + /** + * Creates a DTrace exception with the given message. + */ + public + DTraceException(String message) + { + super(message); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DataEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DataEvent.java new file mode 100644 index 0000000000..e863d127c1 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DataEvent.java @@ -0,0 +1,117 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.EventObject; + +/** + * An event used to pass probe data generated by a DTrace {@link + * Consumer} to interested listeners. + * + * @see ConsumerListener#dataReceived(DataEvent e) + * + * @author Tom Erickson + */ +public class DataEvent extends EventObject { + static final long serialVersionUID = 3068774547474769821L; + + /** @serial */ + private ProbeData probeData; + + /** + * Creates a {@link ConsumerListener#dataReceived(DataEvent e) + * dataReceived()} event that conveys data generated by DTrace from + * a single probe firing. + * + * @throws NullPointerException if the given probe data is {@code + * null} + */ + public + DataEvent(Object source, ProbeData generatedData) + { + super(source); + probeData = generatedData; + validate(); + } + + private void + validate() + { + if (probeData == null) { + throw new NullPointerException("probe data is null"); + } + } + + /** + * Gets the data generated by DTrace from a single probe firing. + * + * @return non-null probe data generated by DTrace from a single + * probe firing + */ + public ProbeData + getProbeData() + { + return probeData; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this event useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(DataEvent.class.getName()); + buf.append("[source = "); + buf.append(getSource()); + buf.append(", probeData = "); + buf.append(probeData); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Distribution.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Distribution.java new file mode 100644 index 0000000000..7557047aa5 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Distribution.java @@ -0,0 +1,620 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A frequency distribution aggregated by the DTrace {@code quantize()} + * or {@code lquantize()} action. Each aggregated value falls into a + * range known as a bucket and counts toward the frequency of that + * bucket. Bucket ranges are consecutive, with the maximum of one + * bucket's range always one less than the minimum of the next bucket's + * range. By convention each bucket is identified by the minimum of its + * range. + * + * @author Tom Erickson + */ +public abstract class Distribution implements AggregationValue, + Iterable <Distribution.Bucket>, Serializable +{ + static final long serialVersionUID = 1186243118882654932L; + + /** @serial */ + private List <Bucket> buckets; + private transient double total; + private transient boolean initialized; + + /** + * Package level access, called by subclasses LinearDistribution and + * LogDistribution, but not available outside the API. + * + * @param base the lower bound of this distribution, or zero if not + * applicable + * @param constant the constant term of the distribution function + * used to calculate the lower bound of any bucket given the lower + * bound of the previous bucket, for example the step in a linear + * distribution or the log base in a logarithmic distribution. + * @param frequencies for each bucket, the number of aggregated + * values falling into that bucket's range; each element must be a + * positive integer + * @throws NullPointerException if frequencies is null + * @throws IllegalArgumentException if any element of frequencies + * does not have the expected range as defined by checkBucketRange() + */ + Distribution(long base, long constant, long[] frequencies) + { + total = 0; + long frequency; + for (int i = 0, len = frequencies.length; i < len; ++i) { + frequency = frequencies[i]; + total += frequency; + } + + buckets = createBuckets(base, constant, frequencies); + initialized = true; + } + + /** + * Supports XML persistence of subclasses. Sub-class implementation + * must call initialize() after setting any state specific to that + * subclass for determining bucket ranges. + * + * @throws NullPointerException if frequencies is null + * @throws IllegalArgumentException if any element of frequencies + * does not have the expected range as defined by checkBucketRange() + */ + Distribution(List <Bucket> frequencies) + { + // defensively copy frequencies list + int len = frequencies.size(); + // don't need gratuitous capacity % added by constructor that + // takes a Collection argument; list will not be modified + buckets = new ArrayList <Bucket> (len); + buckets.addAll(frequencies); + } + + final void + initialize() + { + // Called by constructor and readObject() (deserialization). + // 1. Check class invariants, throw exception if deserialized + // state inconsistent with a Distribution that can result + // from the public constructor. + // 2. Compute total (transient property derived from buckets) + total = 0; + long frequency; + Bucket bucket; + int len = buckets.size(); + for (int i = 0; i < len; ++i) { + bucket = buckets.get(i); + frequency = bucket.getFrequency(); + // relies on package-private getBucketRange() + // implementation + checkBucketRange(i, len, bucket); + total += frequency; + } + initialized = true; + } + + // Must be called by public instance methods (since the AbstractList + // methods all depend on get() and size(), it is sufficient to call + // checkInit() only in those inherited methods). + private void + checkInit() + { + if (!initialized) { + throw new IllegalStateException("Uninitialized"); + } + } + + /** + * Gets a two element array: the first elelemt is the range minimum + * (inclusive), the second element is the range maximum (inclusive). + * Implemented by subclasses LinearDistribution and LogDistribution + * to define bucket ranges for this distribution and not available + * outside the API. Used by the private general purpose constructor + * called from native code. Implementation must not use + * subclass-specific state, since subclass state has not yet been + * allocated. + * + * @see #Distribution(long base, long constant, long[] frequencies) + */ + abstract long[] + getBucketRange(int i, int len, long base, long constant); + + /** + * Used by public constructors and deserialization only after + * state specific to the subclass is available to the method. + */ + abstract long[] + getBucketRange(int i, int len); + + private List <Distribution.Bucket> + createBuckets(long base, long constant, long[] frequencies) + { + int len = frequencies.length; + Bucket bucket; + List <Bucket> buckets = new ArrayList <Bucket> (len); + long min; // current bucket + long max; // next bucket minus one + long[] range; // two element array: { min, max } + + for (int i = 0; i < len; ++i) { + range = getBucketRange(i, len, base, constant); + min = range[0]; + max = range[1]; + bucket = new Distribution.Bucket(min, max, frequencies[i]); + buckets.add(bucket); + } + + return buckets; + } + + /** + * Validates that bucket has the expected range for the given bucket + * index. Uses {@code base} and {@code constant} constructor args + * to check invariants specific to each subclass, since state + * derived from these args in a subclass is not yet available in the + * superclass constructor. + * + * @throws IllegalArgumentException if bucket does not have the + * expected range for the given bucket index {@code i} + */ + private void + checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket, + long base, long constant) + { + long[] range = getBucketRange(i, bucketCount, base, constant); + checkBucketRange(i, bucket, range); + } + + private void + checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket) + { + long[] range = getBucketRange(i, bucketCount); + checkBucketRange(i, bucket, range); + } + + private void + checkBucketRange(int i, Distribution.Bucket bucket, long[] range) + { + long min = range[0]; + long max = range[1]; + + if (bucket.getMin() != min) { + throw new IllegalArgumentException("bucket min " + + bucket.getMin() + " at index " + i + ", expected " + min); + } + if (bucket.getMax() != max) { + throw new IllegalArgumentException("bucket max " + + bucket.getMax() + " at index " + i + ", expected " + max); + } + } + + /** + * Gets a modifiable list of this distribution's buckets ordered by + * bucket range. Modifying the returned list has no effect on this + * distribution. Supports XML persistence. + * + * @return a modifiable list of this distribution's buckets ordered + * by bucket range + */ + public List <Bucket> + getBuckets() + { + checkInit(); + return new ArrayList <Bucket> (buckets); + } + + /** + * Gets a read-only {@code List} view of this ditribution. + * + * @return a read-only {@code List} view of this ditribution + */ + public List <Bucket> + asList() + { + checkInit(); + return Collections.unmodifiableList(buckets); + } + + /** + * Gets the number of buckets in this distribution. + * + * @return non-negative bucket count + */ + public int + size() + { + checkInit(); + return buckets.size(); + } + + /** + * Gets the bucket at the given distribution index (starting at + * zero). + * + * @return non-null distribution bucket at the given zero-based + * index + */ + public Bucket + get(int index) + { + checkInit(); + return buckets.get(index); + } + + /** + * Gets an iterator over the buckets of this distribution. + * + * @return an iterator over the buckets of this distribution + */ + public Iterator<Bucket> + iterator() + { + checkInit(); + return buckets.iterator(); + } + + /** + * Compares the specified object with this {@code Distribution} + * instance for equality. Defines equality as having the same + * buckets with the same values. + * + * @return {@code true} if and only if the specified object is of + * type {@code Distribution} and both instances have the same size + * and equal buckets at corresponding distribution indexes + */ + public boolean + equals(Object o) + { + checkInit(); + if (o instanceof Distribution) { + Distribution d = (Distribution)o; + return buckets.equals(d.buckets); + } + return false; + } + + /** + * Overridden to ensure that equals instances have equal hash codes. + */ + public int + hashCode() + { + checkInit(); + return buckets.hashCode(); + } + + /** + * Gets the total frequency across all buckets. + * + * @return sum of the frequency of all buckets in this distribution + */ + public double + getTotal() + { + checkInit(); + return total; + } + + /** + * Gets the numeric value of this distribution used to compare + * distributions by overall magnitude, defined as the sum total of + * each bucket's frequency times the minimum of its range. + */ + public abstract Number getValue(); + + /** + * Called by native code + */ + private void + normalizeBuckets(long normal) + { + for (Bucket b : buckets) { + b.frequency /= normal; + } + } + + /** + * A range inclusive at both endpoints and a count of aggregated + * values that fall in that range. Buckets in a {@link + * Distribution} are consecutive, such that the max of one bucket is + * always one less than the min of the next bucket (or {@link + * Long#MAX_VALUE} if it is the last bucket in the {@code + * Distribution}). + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + */ + public static final class Bucket implements Serializable { + static final long serialVersionUID = 4863264115375406295L; + + /** @serial */ + private final long min; + /** @serial */ + private final long max; + /** @serial */ + private long frequency; // non-final so native code can normalize + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Bucket.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"min", "max", "frequency"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + (oldInstance.getClass() == + newInstance.getClass())); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a distribution bucket with the given range and + * frequency. + * + * @param rangeMinimumInclusive sets the lower bound (inclusive) + * returned by {@link #getMin()} + * @param rangeMaximumInclusive sets the upper bound (inclusive) + * returned by {@link #getMax()} + * @param valuesInRange sets the value frequency in this + * bucket's range returned by {@link #getFrequency()} + * @throws IllegalArgumentException if {@code + * rangeMaximumInclusive} is less than {@code + * rangeMinimumInclusive} + */ + public + Bucket(long rangeMinimumInclusive, long rangeMaximumInclusive, + long valuesInRange) + { + if (rangeMaximumInclusive < rangeMinimumInclusive) { + throw new IllegalArgumentException("upper bound " + + rangeMaximumInclusive + " is less than lower bound " + + rangeMinimumInclusive); + } + + min = rangeMinimumInclusive; + max = rangeMaximumInclusive; + frequency = valuesInRange; + } + + /** + * Gets the lower bound of this bucket's range (inclusive). + */ + public long + getMin() + { + return min; + } + + /** + * Gets the upper bound of this bucket's range (inclusive). + */ + public long + getMax() + { + return max; + } + + /** + * Gets the number of values in a {@link Distribution} that fall + * into the range defined by this bucket. + */ + public long + getFrequency() + { + return frequency; + } + + /** + * Compares the specified object with this distribution bucket + * for equality. Defines equality of two distribution buckets + * as having the same range and the same frequency. + * + * @return false if the specified object is not a {@code + * Distribution.Bucket} + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof Bucket) { + Bucket b = (Bucket)o; + return ((min == b.min) && + (max == b.max) && + (frequency == b.frequency)); + } + return false; + } + + /** + * Overridden to ensure that equal buckets have equal hashcodes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + ((int)(min ^ (min >>> 32))); + hash = (37 * hash) + ((int)(max ^ (max >>> 32))); + hash = (37 * hash) + ((int)(frequency ^ (frequency >>> 32))); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants (as constructor does) + if (max < min) { + throw new InvalidObjectException("upper bound " + + max + " is less than lower bound " + min); + } + } + + /** + * Gets a string representation of this distribution bucket + * useful for logging and not intended for display. The exact + * details of the representation are unspecified and subject to + * change, but the following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Bucket.class.getName()); + buf.append("[min = "); + buf.append(min); + buf.append(", max = "); + buf.append(max); + buf.append(", frequency = "); + buf.append(frequency); + buf.append(']'); + return buf.toString(); + } + } + + /** + * Gets a list of buckets of interest by excluding empty buckets at + * both ends of the distribution. Leaves one empty bucket on each + * end if possible to convey the distribution context more + * effectively in a display. + * + * @return an unmodifiable sublist that includes the range starting + * from the first bucket with a non-zero frequency and ending with + * the last bucket with a non-zero frequency, plus one empty bucket + * before and after that range if possible + */ + public List <Bucket> + getDisplayRange() + { + checkInit(); + int min = -1; + int max = -1; + int len = size(); + Bucket b; + // Get first non-empty bucket + for (int i = 0; i < len; ++i) { + b = buckets.get(i); + if (b.getFrequency() > 0L) { + min = i; + break; + } + } + if (min < 0) { + return Collections. <Bucket> emptyList(); + } + // Get last non-empty bucket + for (int i = (len - 1); i >= 0; --i) { + b = buckets.get(i); + if (b.getFrequency() > 0L) { + max = i; + break; + } + } + // If possible, pad non-empty range with one empty bucket at + // each end. + if (min > 0) { + --min; + } + if (max < (len - 1)) { + ++max; + } + + // subList inclusive at low index, exclusive at high index + return Collections. <Bucket> + unmodifiableList(buckets).subList(min, max + 1); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + + // Defensively copy buckets _before_ validating. Subclass + // validates by calling initialize() after reading any state + // specific to that subclass needed for validation. + int len = buckets.size(); + ArrayList <Bucket> copy = new ArrayList <Bucket> (len); + copy.addAll(buckets); + buckets = copy; + } + + /** + * Gets a string representation of this {@code Distribution} useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + checkInit(); + StringBuffer buf = new StringBuffer(); + buf.append(Distribution.class.getName()); + buf.append("[buckets = "); + List <Bucket> list = getDisplayRange(); + if (list.isEmpty()) { + buf.append("<empty>"); + } else { + buf.append(Arrays.toString(getDisplayRange().toArray())); + } + buf.append(", total = "); + buf.append(getTotal()); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Drop.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Drop.java new file mode 100644 index 0000000000..a0ade2a93e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Drop.java @@ -0,0 +1,274 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.Serializable; +import java.io.*; +import java.beans.*; + +/** + * Detail about one or more records dropped by DTrace (not reported to + * {@link ConsumerListener#dataReceived(DataEvent e) + * ConsumerListener.dataReceived()}) due to inadequte buffer space. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see ConsumerListener#dataDropped(DropEvent e) + * + * @author Tom Erickson + */ +public final class Drop implements Serializable { + static final long serialVersionUID = 26653827678657381L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Drop.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"CPU", "kind", "count", "total", + "defaultMessage"}) + { + protected Expression + instantiate(Object oldInstance, Encoder out) + { + Drop drop = (Drop)oldInstance; + return new Expression(oldInstance, oldInstance.getClass(), + "new", new Object[] { drop.getCPU(), + drop.getKind().name(), drop.getCount(), + drop.getTotal(), drop.getDefaultMessage() }); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** + * Indicates what kind of buffer space experienced the data drop + * (such as principal buffer or aggregation buffer) and possibly a + * reason. + */ + public enum Kind { + /** Drop to principal buffer */ + PRINCIPAL("Principal buffer"), + /** Drop to aggregation buffer */ + AGGREGATION("Aggregation"), + /** Dynamic drop */ + DYNAMIC("Dynamic"), + /** Dynamic drop due to rinsing */ + DYNRINSE("Dynamic (rinse)"), + /** Dynamic drop due to dirtiness */ + DYNDIRTY("Dynamic (dirty)"), + /** Speculative drop */ + SPEC("Speculation"), + /** Speculative drop due to business */ + SPECBUSY("Speculation (busy)"), + /** Speculative drop due to unavailability */ + SPECUNAVAIL("Speculation (unavailable)"), + /** Stack string table overflow */ + STKSTROVERFLOW("Stack string table overflow"), + /** Error in ERROR probe */ + DBLERROR("error in ERROR probe"), + /** Unrecognized value from native DTrace library */ + UNKNOWN("Unknown"); + + private String s; + + private + Kind(String displayString) + { + s = displayString; + } + + /** + * Overridden to get the default display value. To + * internationalize the display value, use {@link Enum#name()} + * instead as an I18N lookup key. + */ + public String + toString() + { + return s; + } + } + + /** @serial */ + private final int cpu; + /** @serial */ + private final Kind kind; + /** @serial */ + private final long count; + /** @serial */ + private final long total; + /** @serial */ + private final String defaultMessage; + + /** + * Creates a {@code Drop} instance with the given CPU, drop kind, + * drop counts, and default message. Supports XML persistence. + * + * @param dropCPU cpu where drops occurred + * @param dropKindName name of enumeration value indicating the kind + * of buffer space where the drop occurred and possibly a reason + * @param dropCount number of drops + * @param totalDrops total number of drops since the source {@link + * Consumer} started running + * @param defaultDropMessage drop message provided by DTrace + * @throws IllegalArgumentException if there is no {@code Drop.Kind} + * value with the given name or if {@code dropCount} or {@code + * totalDrops} is negative + * @throws NullPointerException if the given {@code Drop.Kind} name + * or default message is {@code null} + */ + public + Drop(int dropCPU, String dropKindName, long dropCount, long totalDrops, + String defaultDropMessage) + { + cpu = dropCPU; + kind = Enum.valueOf(Kind.class, dropKindName); + count = dropCount; + total = totalDrops; + defaultMessage = defaultDropMessage; + validate(); + } + + private void + validate() + { + if (count < 0) { + throw new IllegalArgumentException("count is negative"); + } + if (total < 0) { + throw new IllegalArgumentException("total is negative"); + } + if (defaultMessage == null) { + throw new NullPointerException("default message is null"); + } + } + + /** + * Gets the CPU where the drops occurred. + * + * @return non-negative CPU ID, or a negative number if the CPU is + * unknown + */ + public int + getCPU() + { + return cpu; + } + + /** + * Gets the kind of drop for all drops included in {@link + * #getCount()}. + * + * @return non-null drop kind + */ + public Kind + getKind() + { + return kind; + } + + /** + * Gets the number of drops reported by this {@code Drop} instance. + * + * @return non-negative drop count + */ + public long + getCount() + { + return count; + } + + /** + * Gets the total number of drops since the source {@link Consumer} + * started running. + * + * @return non-negative drop total since tracing started + */ + public long + getTotal() + { + return total; + } + + /** + * Gets the message provided by DTrace. + * + * @return non-null message provided by DTrace + */ + public String + getDefaultMessage() + { + return defaultMessage; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this drop instance, not intended + * for display. The exact details of the representation are + * unspecified and subject to change, but the following format may + * be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Drop.class.getName()); + buf.append("[cpu = "); + buf.append(cpu); + buf.append(", kind = "); + buf.append(kind); + buf.append(", count = "); + buf.append(count); + buf.append(", total = "); + buf.append(total); + buf.append(", defaultMessage = "); + buf.append(defaultMessage); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DropEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DropEvent.java new file mode 100644 index 0000000000..3a621d26ef --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/DropEvent.java @@ -0,0 +1,114 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.EventObject; + +/** + * Notification that DTrace has dropped data due to inadequate buffer + * space. + * + * @see ConsumerListener#dataDropped(DropEvent e) + * + * @author Tom Erickson + */ +public class DropEvent extends EventObject { + static final long serialVersionUID = 5454623535426339134L; + + /** @serial */ + private Drop drop; + + /** + * Creates a {@link ConsumerListener#dataDropped(DropEvent e) + * dataDropped()} event that reports dropped data. + * + * @throws NullPointerException if the given drop is {@code null} + */ + public + DropEvent(Object source, Drop dataDrop) + { + super(source); + drop = dataDrop; + validate(); + } + + private void + validate() + { + if (drop == null) { + throw new NullPointerException("drop is null"); + } + } + + /** + * Gets the drop information generated by DTrace. + * + * @return non-null drop information generated by DTrace + */ + public Drop + getDrop() + { + return drop; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this event useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(DropEvent.class.getName()); + buf.append("[source = "); + buf.append(getSource()); + buf.append(", drop = "); + buf.append(drop); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Error.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Error.java new file mode 100644 index 0000000000..b08bdb6692 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Error.java @@ -0,0 +1,324 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * An error encountered in the native DTrace library while tracing probe + * data. Each of the fault name constants beginning with {@code + * DTRACEFLT_} identifies a specific fault with a name that is + * guaranteed not to change across API versions. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see ConsumerListener#errorEncountered(ErrorEvent e) + * + * @author Tom Erickson + */ +public final class Error implements Serializable { + static final long serialVersionUID = 5069931629562700614L; + + /** + * Invalid address. + */ + public static final String DTRACEFLT_BADADDR = "DTRACEFLT_BADADDR"; + /** + * Invalid alignment. + */ + public static final String DTRACEFLT_BADALIGN = "DTRACEFLT_BADALIGN"; + /** + * Illegal operation. + */ + public static final String DTRACEFLT_ILLOP = "DTRACEFLT_ILLOP"; + /** + * Divide-by-zero. + */ + public static final String DTRACEFLT_DIVZERO = "DTRACEFLT_DIVZERO"; + /** + * Out of scratch space. + */ + public static final String DTRACEFLT_NOSCRATCH = "DTRACEFLT_NOSCRATCH"; + /** + * Invalid kernel access. + */ + public static final String DTRACEFLT_KPRIV = "DTRACEFLT_KPRIV"; + /** + * Invalid user access. + */ + public static final String DTRACEFLT_UPRIV = "DTRACEFLT_UPRIV"; + /** + * Tuple stack overflow. + */ + public static final String DTRACEFLT_TUPOFLOW = "DTRACEFLT_TUPOFLOW"; + /** + * Library-level fault. + */ + public static final String DTRACEFLT_LIBRARY = "DTRACEFLT_LIBRARY"; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Error.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"probeDescription", + "enabledProbeID", "CPU", "action", "offset", + "fault", "address", "defaultMessage"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** @serial */ + private final ProbeDescription probeDescription; + /** @serial */ + private final int epid; + /** @serial */ + private final int cpu; + /** @serial */ + private final int action; + /** @serial */ + private final int offset; + /** @serial */ + private final String fault; + /** @serial */ + private final long address; + /** @serial */ + private final String defaultMessage; + + /** + * Creates a DTrace error with the given properties. Supports XML + * persistence. + * + * @param pdesc probe description that identifies the error-inducing + * probe among all the probes on the system + * @param enabledProbeID identifies the error-inducing probe among + * all probes enabled by the same {@link Consumer} + * @param errorCPU non-negative ID of the CPU where the error was + * encountered, or a negative number if the CPU is unknown + * @param errorAction integer that identifies the error-inducing + * action as the nth action (starting at one) in the error-inducing + * probe, or zero if the error is in the predicate rather than in an + * action + * @param errorOffset error offset in compiled DTrace Intermediate + * Format (DIF), or a negative number if the offset is not available + * @param faultName name of the specific fault, or {@code null} + * if the fault is unknown to the Java DTrace API + * @param faultAddress address of fault, or -1 if address is not + * applicable to the specific fault + * @param errorMessage default message from the native DTrace + * library preconstructed from the properties of this error + * @throws NullPointerException if the given probe description or + * default message is {@code null} + */ + public + Error(ProbeDescription pdesc, int enabledProbeID, int errorCPU, + int errorAction, int errorOffset, String faultName, + long faultAddress, String errorMessage) + { + probeDescription = pdesc; + epid = enabledProbeID; + cpu = errorCPU; + action = errorAction; + offset = errorOffset; + fault = faultName; + address = faultAddress; + defaultMessage = errorMessage; + validate(); + } + + private void + validate() + { + if (probeDescription == null) { + throw new NullPointerException( + "enabled probe description is null"); + } + if (defaultMessage == null) { + throw new NullPointerException("default message is null"); + } + } + + /** + * Gets the probe description that identifies the error-inducing + * probe among all the probes on the system. + * + * @return non-null probe description + */ + public ProbeDescription + getProbeDescription() + { + return probeDescription; + } + + /** + * Gets the enabled probe ID. The "epid" is different from {@link + * ProbeDescription#getID()} because it identifies a probe among all + * the probes enabled by a {@link Consumer}, rather than among all + * the probes on the system. + * + * @return the enabled probe ID + */ + public int + getEnabledProbeID() + { + return epid; + } + + /** + * Gets the CPU that encountered the error. + * + * @return non-negative CPU ID, or a negative number if the CPU is + * unknown + */ + public int + getCPU() + { + return cpu; + } + + /** + * Gets the error-inducing action as the <i>nth</i> action (starting + * at one) in the error-inducing probe, or zero if the error is in + * the predicate rather than in an action. Note that some actions + * in a D program consist of multiple actions internally within the + * DTrace library. + * + * @return zero if the error is in the probe predicate, otherwise + * the <i>nth</i> action (<i>n</i> starting at one) from the start + * of the probe that induced the error + */ + public int + getAction() + { + return action; + } + + /** + * Gets the error offset in compiled DTrace Intermediate Format + * (DIF), or a negative number if the offset is not available. + * + * @return the error offset in compiled DTrace Intermediate Format + * (DIF), or a negative number if the offset is not available + */ + public int + getOffset() + { + return offset; + } + + /** + * Gets the name identifying the specific fault. The names are + * guaranteed not to change across API versions as long as the fault + * cases they identify still exist. + * + * @return name of the specific fault or {@code null} if the + * fault is unknown to the Java DTrace API + */ + public String + getFault() + { + return fault; + } + + /** + * Gets the address of the fault, if any. + * + * @return address of fault, or -1 if address is not applicable to + * the specific fault (the fault is not one of {@link + * #DTRACEFLT_BADADDR} or {@link #DTRACEFLT_BADALIGN}) + */ + public long + getAddress() + { + return address; + } + + /** + * Gets the default message from the native DTrace library + * preconstructed from the properties of this error. + * + * @return non-null preconstructed message + */ + public String + getDefaultMessage() + { + return defaultMessage; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this error useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Error.class.getName()); + buf.append("[probeDescription = "); + buf.append(probeDescription); + buf.append(", epid = "); + buf.append(epid); + buf.append(", cpu = "); + buf.append(cpu); + buf.append(", action = "); + buf.append(action); + buf.append(", offset = "); + buf.append(offset); + buf.append(", fault = "); + buf.append(fault); + buf.append(", address = "); + buf.append(address); + buf.append(", defaultMessage = "); + buf.append(defaultMessage); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ErrorEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ErrorEvent.java new file mode 100644 index 0000000000..d956705ac5 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ErrorEvent.java @@ -0,0 +1,116 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.EventObject; + +/** + * Notification that DTrace has encountered an error. + * + * @see ConsumerListener#errorEncountered(ErrorEvent e) + * + * @author Tom Erickson + */ +public class ErrorEvent extends EventObject { + static final long serialVersionUID = 2236600660422267215L; + + /** @serial */ + private Error error; + + /** + * Creates an {@link ConsumerListener#errorEncountered(ErrorEvent e) + * errorEncountered()} event that reports an error encountered in + * the native DTrace library during tracing. + * + * @param source the {@link Consumer} that is the source of this event + * @param dtraceError the error encountered by DTrace + * @throws NullPointerException if the given error is {@code null} + */ + public + ErrorEvent(Object source, Error dtraceError) + { + super(source); + error = dtraceError; + validate(); + } + + private void + validate() + { + if (error == null) { + throw new NullPointerException("error is null"); + } + } + + /** + * Gets the error reported by DTrace. + * + * @return non-null error reported by DTrace + */ + public Error + getError() + { + return error; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this event useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ErrorEvent.class.getName()); + buf.append("[source = "); + buf.append(getSource()); + buf.append(", error = "); + buf.append(error); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExceptionHandler.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExceptionHandler.java new file mode 100644 index 0000000000..e73c443ed3 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExceptionHandler.java @@ -0,0 +1,54 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * User-defined application behavior after an exception terminates + * a running DTrace consumer. The {@link Consumer} that threw the + * exception is stopped automatically whether or not an {@code + * ExceptionHandler} is set, but a handler must be set to do anything + * other than print a stack trace to {@code stderr}. + * + * @see Consumer#go(ExceptionHandler handler) + * + * @author Tom Erickson + */ +public interface ExceptionHandler { + /** + * Defines what to do after an exception terminates a running {@link + * Consumer}. For example, a handler might be implemented to + * display details about what went wrong. + * + * @param e a {@link DTraceException} if encountered in the native + * DTrace library, a {@link ConsumerException} if thrown from a + * {@link ConsumerListener} method to terminate the consumer, or a + * {@link RuntimeException} to indicate an unexpected error in the + * Java DTrace API. + */ + public void handleException(Throwable e); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExitRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExitRecord.java new file mode 100644 index 0000000000..7a046e555e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ExitRecord.java @@ -0,0 +1,94 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.Serializable; +import java.beans.*; + +/** + * A record indicating that the DTrace {@code exit()} action is about to + * stop the source {@link Consumer}. The exit status is whatever value + * was passed to the {@code exit()} action in the D program. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class ExitRecord implements Record, Serializable { + static final long serialVersionUID = -2062716683135961493L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ExitRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"status"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final int status; + + /** + * Creates an exit record with the given status. + * + * @param exitStatus value passed to the D {@code exit()} action + */ + public + ExitRecord(int exitStatus) + { + status = exitStatus; + } + + /** + * Gets the exit status of a DTrace {@link Consumer}. + * + * @return the value passed to the D {@code exit()} action + */ + public int + getStatus() + { + return status; + } + + /** + * Gets a string representation of the exit status. + * + * @return the string form of {@link #getStatus()} returned by + * {@link Integer#toString(int i)} + */ + public String + toString() + { + return Integer.toString(status); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Flow.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Flow.java new file mode 100644 index 0000000000..8c74c8d02d --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Flow.java @@ -0,0 +1,234 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * Description of control flow across function boundaries including + * direction (entry or return) and depth in the call stack. This + * information is added to {@link ProbeData} instances only when the + * {@link Option#flowindent flowindent} option is used: + * <pre><code> + * Consumer consumer = new LocalConsumer(); + * consumer.open(); + * consumer.setOption(Option.flowindent); + * ... + * </code></pre> + * See the <a + * href="http://docs.sun.com/app/docs/doc/817-6223/6mlkidlk1?a=view"> + * <b>Examples</b></a> section of the <b>{@code fbt} + * Provider</b> chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#setOption(String option) + * @see Option#flowindent + * + * @author Tom Erickson + */ +public final class Flow implements Serializable { + static final long serialVersionUID = -9178272444872063901L; + + /** + * Indicates direction of flow across a boundary, such as entering + * or returing from a function. + */ + public enum Kind { + /** Entry into a function. */ + ENTRY, + /** Return from a function. */ + RETURN, + /** No function boundary crossed. */ + NONE + } + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Flow.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"kind", "depth"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + + protected Expression + instantiate(Object oldInstance, Encoder out) + { + Flow flow = (Flow)oldInstance; + return new Expression(oldInstance, oldInstance.getClass(), + "new", new Object[] { flow.getKind().name(), + flow.getDepth() }); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** @serial */ + private final Kind kind; + /** @serial */ + private final int depth; + + /** + * Creates a {@code Flow} instance with the given flow kind and + * depth. Supports XML persistence. + * + * @param flowKindName name of enumeration value indicating the + * direction of flow + * @param flowDepth current depth in the call stack + * @throws IllegalArgumentException if there is no {@code Flow.Kind} + * value with the given name or if the given {@code flowDepth} is + * negative + * @throws NullPointerException if the given {@code Flow.Kind} name + * is {@code null} + */ + public + Flow(String flowKindName, int flowDepth) + { + kind = Enum.valueOf(Kind.class, flowKindName); + depth = flowDepth; + if (depth < 0) { + throw new IllegalArgumentException("depth is negative"); + } + } + + /** + * Gets the direction of the flow of control (entry or return) + * across a function boundary. + * + * @return non-null flow kind indicating direction of flow (entry or + * return) across a function boundary + */ + public Kind + getKind() + { + return kind; + } + + /** + * Gets the current depth in the call stack. + * + * @return A non-negative sum of the function entries minus the + * function returns up until the moment described by this control + * flow instance. For example, if the traced flow of control + * entered two functions but only returned from one, the depth is + * one (2 entries minus 1 return). + */ + public int + getDepth() + { + return depth; + } + + /** + * Compares the specified object with this {@code Flow} instance for + * equality. Defines equality as having the same flow kind and + * depth. + * + * @return {@code true} if and only if the specified object is of + * type {@code Flow} and both instances have equal flow kind and + * depth. + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof Flow) { + Flow f = (Flow)o; + return ((kind == f.kind) && (depth == f.depth)); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + kind.hashCode(); + hash = (37 * hash) + depth; + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + if (kind == null) { + throw new InvalidObjectException("kind is null"); + } + if (depth < 0) { + throw new InvalidObjectException("depth is negative"); + } + } + + /** + * Gets a string representation of this {@code Flow} instance useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Flow.class.getName()); + buf.append("[kind = "); + buf.append(kind); + buf.append(", depth = "); + buf.append(depth); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/InterfaceAttributes.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/InterfaceAttributes.java new file mode 100644 index 0000000000..aa7ab73a81 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/InterfaceAttributes.java @@ -0,0 +1,529 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * Triplet of attributes consisting of two stability levels and a + * dependency class. Attributes may vary independently. They use + * labels described in the {@code attributes(5)} man page to help set + * expectations for what kinds of changes might occur in different kinds + * of future releases. The D compiler includes features to dynamically + * compute the stability levels of D programs you create. For more + * information, refer to the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlnp?a=view> + * <b>Stability</b></a> chapter of the <i>Solaris Dynamic Tracing + * Guide</i>. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#getProgramInfo(Program program) + * @see Consumer#enable(Program program) + * @see Consumer#listProbes(ProbeDescription filter) + * @see Consumer#listProgramProbes(Program program) + * + * @author Tom Erickson + */ +public final class InterfaceAttributes implements Serializable { + static final long serialVersionUID = -2814012588381562694L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(InterfaceAttributes.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"nameStability", "dataStability", + "dependencyClass" }) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + + protected Expression + instantiate(Object oldInstance, Encoder out) + { + InterfaceAttributes attr = (InterfaceAttributes) + oldInstance; + return new Expression(oldInstance, oldInstance.getClass(), + "new", new Object[] { + attr.getNameStability().name(), + attr.getDataStability().name(), + attr.getDependencyClass().name() }); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** + * Interface stability level. Assists developers in making risk + * assessments when developing scripts and tools based on DTrace by + * indicating how likely an interface or DTrace entity is to change + * in a future release or patch. + */ + public enum Stability { + /** + * The interface is private to DTrace itself and represents an + * implementation detail of DTrace. Internal interfaces might + * change in minor or micro releases. + */ + INTERNAL("Internal"), + /** + * The interface is private to Sun and represents an interface + * developed for use by other Sun products that is not yet + * publicly documented for use by customers and ISVs. Private + * interfaces might change in minor or micro releases. + */ + PRIVATE("Private"), + /** + * The interface is supported in the current release but is + * scheduled to be removed, most likely in a future minor + * release. When support of an interface is to be discontinued, + * Sun will attempt to provide notification before discontinuing + * the interface. The D compiler might produce warning messages + * if you attempt to use an Obsolete interface. + */ + OBSOLETE("Obsolete"), + /** + * The interface is controlled by an entity other than Sun. At + * Sun's discretion, Sun can deliver updated and possibly + * incompatible versions as part of any release, subject to + * their availability from the controlling entity. Sun makes no + * claims regarding either the source or binary compatibility + * for External interfaces between two releases. Applications + * based on these interfaces might not work in future releases, + * including patches that contain External interfaces. + */ + EXTERNAL("External"), + /** + * The interface is provided to give developers early access to + * new or rapidly changing technology or to an implementation + * artifact that is essential for observing or debugging system + * behavior for which a more stable solution is anticipated in + * the future. Sun makes no claims about either source of + * binary compatibility for Unstable interfaces from one minor + * release to another. + */ + UNSTABLE("Unstable"), + /** + * The interface might eventually become Standard or Stable but + * is still in transition. Sun will make reasonable efforts to + * ensure compatibility with previous releases as it eveolves. + * When non-upward compatible changes become necessary, they + * will occur in minor and major releases. These changes will + * be avoided in micro releases whenever possible. If such a + * change is necessary, it will be documented in the release + * notes for the affected release, and when feasible, Sun will + * provide migration aids for binary compatibility and continued + * D program development. + */ + EVOLVING("Evolving"), + /** + * The interface is a mature interface under Sun's control. Sun + * will try to avoid non-upward-compatible changes to these + * interfaces, especially in minor or micro releases. If + * support of a Stable interface must be discontinued, Sun will + * attempt to provide notification and the stability level + * changes to Obsolete. + */ + STABLE("Stable"), + /** + * The interface complies with an industry standard. The + * corresponding documentation for the interface will describe + * the standard to which the interface conforms. Standards are + * typically controlled by a standards development organization, + * and changes can be made to the interface in accordance with + * approved changes to the standard. This stability level can + * also apply to interfaces that have been adopted (without a + * formal standard) by an industry convention. Support is + * provided for only the specified versions of a standard; + * support for later versions is not guaranteed. If the + * standards development organization approves a + * non-upward-compatible change to a Standard interface that Sun + * decides to support, Sun will announce a compatibility and + * migration strategy. + */ + STANDARD("Standard"); + + private String s; + + private + Stability(String displayName) + { + s = displayName; + } + + /** + * Overridden to get the default display value. To + * internationalize the display value, use {@link + * java.lang.Enum#name()} instead as a lookup key. + */ + @Override + public String + toString() + { + return s; + } + } + + /** + * Architectural dependency class. Tells whether an interface is + * common to all Solaris platforms and processors, or whether the + * interface is associated with a particular architecture such as + * SPARC processors only. + */ + public enum DependencyClass { + // Note that the compareTo() method depends on the order in + // which the instances are instantiated + + /** + * The interface has an unknown set of architectural dependencies. + * DTrace does not necessarily know the architectural dependencies of + * all entities, such as data types defined in the operating system + * implementation. The Unknown label is typically applied to interfaces + * of very low stability for which dependencies cannot be computed. The + * interface might not be available when using DTrace on <i>any</i> + * architecture other than the one you are currently using. + */ + UNKNOWN("Unknown"), + /** + * The interface is specific to the CPU model of the current + * system. You can use the {@code psrinfo(1M)} utility's {@code + * -v} option to display the current CPU model and + * implementation names. Interfaces with CPU model dependencies + * might not be available on other CPU implementations, even if + * those CPUs export the same instruction set architecture + * (ISA). For example, a CPU-dependent interface on an + * UltraSPARC-III+ microprocessor might not be available on an + * UltraSPARC-II microprocessor, even though both processors + * support the SPARC instruction set. + */ + CPU("CPU"), + /** + * The interface is specific to the hardware platform of the current + * system. A platform typically associates a set of system components + * and architectural characteristics such as a set of supported CPU + * models with a system name such as <code>SUNW, + * Ultra-Enterprise-10000</code>. You can display the current + * platform name using the {@code uname(1)} {@code -i} option. + * The interface might not be available on other hardware + * platforms. + */ + PLATFORM("Platform"), + /** + * The interface is specific to the hardware platform group of the + * current system. A platform group typically associates a set of + * platforms with related characteristics together under a single name, + * such as {@code sun4u}. You can display the current platform + * group name using the {@code uname(1)} {@code -m} option. The + * interface is available on other platforms in the platform + * group, but might not be available on hardware platforms that + * are not members of the group. + */ + GROUP("Group"), + /** + * The interface is specific to the instruction set architecture (ISA) + * supported by the microprocessor on this system. The ISA describes a + * specification for software that can be executed on the + * microprocessor, including details such as assembly language + * instructions and registers. You can display the native + * instruction sets supported by the system using the {@code + * isainfo(1)} utility. The interface might not be supported on + * systems that do not export any of of the same instruction + * sets. For example, an ISA-dependent interface on a Solaris + * SPARC system might not be supported on a Solaris x86 system. + */ + ISA("ISA"), + /** + * The interface is common to all Solaris systems regardless of the + * underlying hardware. DTrace programs and layered applications that + * depend only on Common interfaces can be executed and deployed on + * other Solaris systems with the same Solaris and DTrace revisions. + * The majority of DTrace interfaces are Common, so you can use them + * wherever you use Solaris. + */ + COMMON("Common"); + + private String s; + + private + DependencyClass(String displayString) + { + s = displayString; + } + + /** + * Overridden to get the default display value. To + * internationalize the display value, use {@link + * java.lang.Enum#name()} instead as a lookup key. + */ + @Override + public String + toString() + { + return s; + } + } + + /** @serial */ + private Stability nameStability; + /** @serial */ + private Stability dataStability; + /** @serial */ + private DependencyClass dependencyClass; + + /** + * Called by native code. + */ + private + InterfaceAttributes() + { + } + + /** + * Creates an interface attribute triplet from the given attributes. + * + * @param nameStabilityAttribute the stability level of the + * interface associated with its name in a D program + * @param dataStabilityAttribute stability of the data format used + * by the interface and any associated data semantics + * @param dependencyClassAttribute describes whether the interface + * is specific to the current operating platform or microprocessor + * @throws NullPointerException if any parameter is {@code null} + */ + public + InterfaceAttributes(Stability nameStabilityAttribute, + Stability dataStabilityAttribute, + DependencyClass dependencyClassAttribute) + { + nameStability = nameStabilityAttribute; + dataStability = dataStabilityAttribute; + dependencyClass = dependencyClassAttribute; + validate(); + } + + /** + * Creates an interface attribute triplet from the given attribute + * names. Supports XML persistence. + * + * @throws NullPointerException if any parameter is {@code null} + * @throws IllegalArgumentException if any parameter fails to match + * an enumerated stability value + */ + public + InterfaceAttributes(String nameStabilityAttributeName, + String dataStabilityAttributeName, + String dependencyClassAttributeName) + { + this(Enum.valueOf(Stability.class, nameStabilityAttributeName), + Enum.valueOf(Stability.class, dataStabilityAttributeName), + Enum.valueOf(DependencyClass.class, + dependencyClassAttributeName)); + // validate() unnecessary because Enum.valueOf() has already + // thrown the exception + } + + private void + validate() + { + if (nameStability == null) { + throw new NullPointerException("nameStability is null"); + } + if (dataStability == null) { + throw new NullPointerException("dataStability is null"); + } + if (dependencyClass == null) { + throw new NullPointerException("dependencyClass is null"); + } + } + + /** + * Gets the stabiltiy level of an interface associated with its name + * as it appears in a D program. For example, the {@code execname} + * D variable is a {@link Stability#STABLE STABLE} name: Sun + * guarantees this identifier will continue to be supported in D + * programs according to the rules described for Stable interfaces. + * + * @return the stability level of an interface associated with its + * name as it appears in a D program + */ + public Stability + getNameStability() + { + return nameStability; + } + + /** + * Called by native code. + */ + private void + setNameStability(String s) + { + nameStability = Enum.valueOf(Stability.class, s); + } + + /** + * Gets the stability level of the data format used by an interface + * and any associated data semantics. For example, the {@code pid} + * D variable is a {@link Stability#STABLE STABLE} interface: + * process IDs are a stable concept in Solaris, and Sun guarantees + * that the {@code pid} variable will be of type {@code pid_t} with + * the semantic that it is set to the process ID corresponding to + * the thread that fired a given probe in accordance with the rules + * described for Stable interfaces. + * + * @return the stability level of the data format used by an + * interface and any associated data semantics. + */ + public Stability + getDataStability() + { + return dataStability; + } + + /** + * Called by native code. + */ + private void + setDataStability(String s) + { + dataStability = Enum.valueOf(Stability.class, s); + } + + /** + * Gets the interface dependency class. + * + * @return the dependency class describing whether the interface is + * specific to the current operating platform or microprocessor + */ + public DependencyClass + getDependencyClass() + { + return dependencyClass; + } + + /** + * Called by native code. + */ + private void + setDependencyClass(String s) + { + dependencyClass = Enum.valueOf(DependencyClass.class, s); + } + + /** + * Compares the specified object with this attribute triplet for + * equality. Defines equality as having the same attributes. + * + * @return {@code true} if and only if the specified object is also + * an {@code InterfaceAttributes} instance and has all the same + * attributes as this instance. + */ + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof InterfaceAttributes) { + InterfaceAttributes a = (InterfaceAttributes)o; + return ((nameStability == a.nameStability) && + (dataStability == a.dataStability) && + (dependencyClass == a.dependencyClass)); + } + return false; + } + + /** + * Overridden to ensure that equal {@code InterfaceAttributes} + * instances have equal hashcodes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + nameStability.hashCode(); + hash = (37 * hash) + dataStability.hashCode(); + hash = (37 * hash) + dependencyClass.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Check constructor invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the string representation of this triplet of interface + * attributes. The format follows the convention described in the + * <a href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlnt?a=view> + * <b>Interface Attributes</b></a> section of the <b>Stability</b> + * chapter of the <i>Solaris Dynamic Tracing Guide</i>. The + * attributes appear in the following order, separated by slashes: + * <pre><code> + * <i>name-stability / data-stability / dependency-class</i> + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(nameStability); + buf.append(" / "); + buf.append(dataStability); + buf.append(" / "); + buf.append(dependencyClass); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelStackRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelStackRecord.java new file mode 100644 index 0000000000..95b5fd2bd0 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelStackRecord.java @@ -0,0 +1,382 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.util.regex.Pattern; +import java.beans.*; + +/** + * A value generated by the DTrace {@code stack()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class KernelStackRecord implements StackValueRecord, + Serializable, Comparable <KernelStackRecord> +{ + static final long serialVersionUID = 8616454544771346573L; + static final int STACK_INDENT = 14; + static final StackFrame[] EMPTY_FRAMES = new StackFrame[] {}; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(KernelStackRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"stackFrames", "rawStackData"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Splits formatted call stack generated by DTrace stack() and + * ustack() actions into tokens delimited by whitespace. Matches + * any number of whitespace characters on either side of a newline. + * Can't assume that a line has no whitespace characters. A java + * stack might have the line "StubRoutines (1)", which must not get + * split into two tokens. + */ + static final Pattern STACK_TOKENIZER = Pattern.compile("\\s*\n\\s*"); + + /** + * Called by JNI layer to convert a stack formatted by the native + * DTrace library into an unformatted array of stack frames. + * + * @param s string representation of stack data generated by the D + * {@code stack()}, {@code ustack()}, or {@code jstack()} action + * @return array of human-readable stack frames + */ + static StackFrame[] + parse(String s) + { + // + // First trim the leading whitespace to avoid an initial empty + // element in the returned array. + // + s = s.trim(); + StackFrame[] frames; + if (s.length() == 0) { + frames = EMPTY_FRAMES; + } else { + String[] f = STACK_TOKENIZER.split(s); + int n = f.length; + frames = new StackFrame[n]; + for (int i = 0; i < n; ++i) { + frames[i] = new StackFrame(f[i]); + } + } + return frames; + } + + /** @serial */ + private StackFrame[] stackFrames; + /** @serial */ + private byte[] rawStackData; + + /** + * Called by native code and by UserStackRecord (in its constructor + * called by native code). + * + * @throws NullPointerException if rawBytes is {@code null} + */ + KernelStackRecord(byte[] rawBytes) + { + // No need for defensive copy; native code will not modify input + // raw bytes. + rawStackData = rawBytes; + if (rawStackData == null) { + throw new NullPointerException("raw stack data is null"); + } + } + + /** + * Creates a {@code KernelStackRecord} with the given stack frames + * and raw stack data. Supports XML persistence. + * + * @param frames array of human-readable stack frames, copied so + * that later modifying the given frames array will not affect this + * {@code KernelStackRecord}; may be {@code null} or empty to + * indicate that the raw stack data was not converted to + * human-readable stack frames (see {@link + * StackValueRecord#getStackFrames()}) + * @param rawBytes array of raw bytes used to represent this stack + * value in the native DTrace library, needed to distinguish stacks + * that have the same display value but are considered distinct by + * DTrace; copied so that later modifying the given array will not + * affect this {@code KernelStackRecord} + * @throws NullPointerException if the given array of raw bytes is + * {@code null} or if any element of the {@code frames} array is + * {@code null} + */ + public + KernelStackRecord(StackFrame[] frames, byte[] rawBytes) + { + if (frames != null) { + stackFrames = (StackFrame[])frames.clone(); + } + if (rawBytes != null) { + rawStackData = (byte[])rawBytes.clone(); + } + validate(); + } + + private void + validate() + { + if (rawStackData == null) { + throw new NullPointerException("raw stack data is null"); + } + // stackFrames may be null; if non-null, cannot contain null + // elements + if (stackFrames != null) { + for (StackFrame f : stackFrames) { + if (f == null) { + throw new NullPointerException("stack frame is null"); + } + } + } + } + + public StackFrame[] + getStackFrames() + { + if (stackFrames == null) { + return EMPTY_FRAMES; + } + return (StackFrame[])stackFrames.clone(); + } + + /** + * Called by native code and by UserStackRecord in its + * setStackFrames() method. + */ + void + setStackFrames(StackFrame[] frames) + { + // No need for defensive copy; native code will not modify input + // frames. + stackFrames = frames; + validate(); + } + + /** + * Gets the native DTrace representation of this record's stack as + * an array of raw bytes. The raw bytes are used in {@link + * #equals(Object o) equals()} and {@link + * #compareTo(KernelStackRecord r) compareTo()} to test equality and + * to determine the natural ordering of kernel stack records. + * + * @return the native DTrace library's internal representation of + * this record's stack as a non-null array of bytes + */ + public byte[] + getRawStackData() + { + return (byte[])rawStackData.clone(); + } + + /** + * Gets the raw bytes used to represent this record's stack value in + * the native DTrace library. To get a human-readable + * representation, call {@link #toString()}. + * + * @return {@link #getRawStackData()} + */ + public Object + getValue() + { + return (byte[])rawStackData.clone(); + } + + public List <StackFrame> + asList() + { + if (stackFrames == null) { + return Collections. <StackFrame> emptyList(); + } + return Collections.unmodifiableList(Arrays.asList(stackFrames)); + } + + /** + * Compares the specified object with this {@code KernelStackRecord} + * for equality. Returns {@code true} if and only if the specified + * object is also a {@code KernelStackRecord} and both records have + * the same raw stack data. + * <p> + * This implementation first checks if the specified object is this + * {@code KernelStackRecord}. If so, it returns {@code true}. + * + * @return {@code true} if and only if the specified object is also + * a {@code KernelStackRecord} and both records have the same raw + * stack data + */ + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof KernelStackRecord) { + KernelStackRecord r = (KernelStackRecord)o; + return Arrays.equals(rawStackData, r.rawStackData); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return Arrays.hashCode(rawStackData); + } + + /** + * Compares this record with the given stack record. Compares the + * first unequal pair of bytes at the same index in each record's + * raw stack data, or if all corresponding bytes are equal, compares + * the length of each record's array of raw stack data. The {@code + * compareTo()} method is compatible with {@link #equals(Object o) + * equals()}. + * <p> + * This implementation first checks if the specified record is this + * {@code KernelStackRecord}. If so, it returns {@code 0}. + * + * @return -1, 0, or 1 as this record's raw stack data is less than, + * equal to, or greater than the given record's raw stack data. + */ + public int + compareTo(KernelStackRecord r) + { + if (r == this) { + return 0; + } + + int len1 = rawStackData.length; + int len2 = r.rawStackData.length; + int cmp = 0; + for (int i = 0; (cmp == 0) && (i < len1) && (i < len2); ++i) { + cmp = ((rawStackData[i] < r.rawStackData[i]) ? -1 : + ((rawStackData[i] > r.rawStackData[i]) ? 1 : 0)); + } + if (cmp == 0) { + cmp = ((len1 < len2) ? -1 : ((len1 > len2) ? 1 : 0)); + } + return cmp; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Make a defensive copy of stack frames and raw bytes + if (stackFrames != null) { + stackFrames = (StackFrame[])stackFrames.clone(); + } + if (rawStackData != null) { + rawStackData = (byte[])rawStackData.clone(); + } + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a multi-line string representation of a stack with one frame + * per line. Each line is of the format {@code + * module`function+offset}, where {@code module} and {@code + * function} are symbol names and offset is a hex integer preceded + * by {@code 0x}. For example: {@code genunix`open+0x19}. The + * offset (and the preceding '+' sign) are omitted if offset is + * zero. If function name lookup fails, the raw pointer value is + * used instead. In that case, the module name (and the ` + * delimiter) may or may not be present, depending on whether or not + * module lookup also fails, and a raw pointer value appears in + * place of {@code function+offset} as a hex value preceded by + * {@code 0x}. The format just described, not including surrounding + * whitespce, is defined in the native DTrace library and is as + * stable as that library definition. Each line is indented by an + * equal (unspecified) number of spaces. + * <p> + * If human-readable stack frames are not available (see {@link + * #getStackFrames()}), a table represenation of {@link + * #getRawStackData()} is returned instead. The table displays 16 + * bytes per row in unsigned hex followed by the ASCII character + * representations of those bytes. Each unprintable character is + * represented by a period (.). + */ + @Override + public String + toString() + { + StackFrame[] frames = getStackFrames(); + if (frames.length == 0) { + return ScalarRecord.rawBytesString(rawStackData); + } + + StringBuffer buf = new StringBuffer(); + buf.append('\n'); + for (StackFrame f : frames) { + for (int i = 0; i < STACK_INDENT; ++i) { + buf.append(' '); + } + buf.append(f); + buf.append('\n'); + } + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LinearDistribution.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LinearDistribution.java new file mode 100644 index 0000000000..3b52960864 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LinearDistribution.java @@ -0,0 +1,291 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A linear frequency distribution aggregated by the DTrace {@code + * lquantize()} action. Aggregated values fall into consecutive ranges + * bounded by the step parameter of the {@code lquantize()} action. + * Each range, known as a bucket, begins at the {@code lquantize()} + * lower bound, or base, plus a multiple of the {@code lquantize()} + * step, unless it is the first bucket, which is the frequency of all + * aggregated values less than the base. The last bucket counts all + * aggregated values greater than or equal to the {@code lquantize()} + * upper bound. For example + * <pre> {@code @ = lquantize(n, 0, 100, 10);}</pre> + * results in a distribution with a base of 0, an upper bound of 100, + * and a step of 10. It has twelve buckets starting with {@code n < 0} + * and ending with {@code n >= 100}. The buckets in between are {@code + * 0 .. 9}, {@code 10 .. 19}, etc. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see LogDistribution + * @see Aggregation + * + * @author Tom Erickson + */ +public final class LinearDistribution extends Distribution + implements Serializable, Comparable <LinearDistribution> +{ + static final long serialVersionUID = 7100080045858770132L; + + /** @serial */ + private long base; + /** @serial */ + private long step; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(LinearDistribution.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"base", "step", "buckets" }); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Called by native C code + */ + private + LinearDistribution(long lowerBound, long frequencyStep, + long[] frequencies) + { + // initializes using lowerBound and frequencyStep + super(lowerBound, frequencyStep, frequencies); + base = lowerBound; + step = frequencyStep; + } + + /** + * Creates a linear distribution with the given base, step, and + * frequencies. Supports XML persistence. + * + * @param lowerBound also known as the <i>base</i>; the minimum of + * the second bucket in this distribution (the first bucket contains + * the frequency of everything lower than the base) + * @param bucketStep the distance between the lower bound of one + * bucket and the lower bound of the next consecutive bucket + * (excluding the first bucket) + * @param frequencies list of frequencies in each bucket range + * @throws NullPointerException if {@code frequencies} is {@code + * null} + * @throws IllegalArgumentException if the given step is less than + * one, or if any bucket does not have the expected range + * (consecutive steps) + */ + public + LinearDistribution(long lowerBound, long bucketStep, + List <Bucket> frequencies) + { + super(frequencies); // makes defensive copy + base = lowerBound; + step = bucketStep; + initialize(); // checks class invariants, calculates total + if (step < 1) { + throw new IllegalArgumentException("step is less than one"); + } + } + + /** + * Gets a two element array: the first elelemt is the range minimum + * (inclusive), the second element is the range maximum (inclusive). + */ + @Override + long[] + getBucketRange(int i, int len, long base, long step) + { + long min; + long max; + if (i == 0) { + // first bucket is everything less than the base + min = Long.MIN_VALUE; + } else { + min = (base + ((i - 1) * step)); + } + + if (i == (len - 1)) { + // last bucket is everything greater than or equal to + // the upper bound + max = Long.MAX_VALUE; + } else { + max = ((base + (i * step)) - 1); + } + + long[] range = new long[] {min, max}; + return range; + } + + @Override + long[] + getBucketRange(int i, int len) + { + return getBucketRange(i, len, base, step); + } + + /** + * Gets the lower bound of this distribution. In a linear + * distribution, the first bucket holds the frequency of all values + * less than the base, so the base is the minimum of the second + * bucket's range. + * + * @return the lower bound of this distribution + */ + public long + getBase() + { + return base; + } + + /** + * Gets the difference between the lower bounds of consecutive + * buckets after the first. + * + * @return the step between the lower bounds of consecutive buckets + * after the first + */ + public long + getStep() + { + return step; + } + + public Number + getValue() + { + double total = 0; + List <Distribution.Bucket> buckets = getBuckets(); + int len = buckets.size(); + Distribution.Bucket bucket; + + if (len > 0) { + bucket = buckets.get(0); + total = (double)bucket.getFrequency() * (double)(getBase() - 1); + } + for (int i = 1; i < (len - 1); ++i) { + bucket = buckets.get(i); + total += (double)bucket.getFrequency() * (double)bucket.getMin(); + } + if (len > 1) { + bucket = buckets.get(len - 1); + // There doesn't seem to be any reason to add one to the + // minimum of the last bucket range, but that's how it's + // implemented in libdtrace dt_aggregate.c. + total += (double)bucket.getFrequency() * + (double)(bucket.getMin() + 1); + } + return (new Double(total)); + } + + private long + getZeroBucketValue() + { + for (Distribution.Bucket b : this) { + if (b.getMin() == 0) { + return b.getFrequency(); + } + } + return 0; + } + + /** + * Compares the {@code double} values of {@link #getValue()} for + * overall magnitude, and if those are equal, compares the + * frequencies at zero if the distributions include a bucket whose + * range has a minimum of zero. + */ + public int + compareTo(LinearDistribution d) + { + Number v1 = getValue(); + Number v2 = d.getValue(); + double d1 = v1.doubleValue(); + double d2 = v2.doubleValue(); + int cmp = (d1 < d2 ? -1 : (d1 > d2 ? 1 : 0)); + if (cmp == 0) { + long z1 = getZeroBucketValue(); + long z2 = d.getZeroBucketValue(); + cmp = (z1 < z2 ? -1 : (z1 > z2 ? 1 : 0)); + } + return (cmp); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + try { + initialize(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + if (step < 1) { + throw new InvalidObjectException("step is less than one"); + } + } + + /** + * Gets a string representation of this linear distribution useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(LinearDistribution.class.toString()); + buf.append("[base = "); + buf.append(getBase()); + buf.append(", step = "); + buf.append(getStep()); + buf.append(", buckets = "); + List <Bucket> list = getDisplayRange(); + if (list.isEmpty()) { + buf.append("<empty>"); + } else { + buf.append(Arrays.toString(getDisplayRange().toArray())); + } + buf.append(", total = "); + buf.append(getTotal()); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LocalConsumer.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LocalConsumer.java new file mode 100644 index 0000000000..455944a000 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LocalConsumer.java @@ -0,0 +1,1332 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.*; +import java.net.InetAddress; +import java.net.UnknownHostException; +import javax.swing.event.EventListenerList; +import java.util.logging.*; + +/** + * Interface to the native DTrace library, each instance is a single + * DTrace consumer. + * + * @author Tom Erickson + */ +public class LocalConsumer implements Consumer { + // + // Implementation notes: + // + // libdtrace is *not* thread-safe. You cannot make multiple calls + // into it simultaneously from different threads, even if those + // threads are operating on different dtrace_hdl_t's. Calls to + // libdtrace are synchronized on a global lock, LocalConsumer.class. + + static Logger logger = Logger.getLogger(LocalConsumer.class.getName()); + + private static final int DTRACE_JNI_VERSION = 1; + + private static final Option[] DEFAULT_OPTIONS = new Option[] { + new Option(Option.bufsize, Option.kb(256)), + new Option(Option.aggsize, Option.kb(256)), + }; + + private static native void _loadJniTable(); + + private static boolean debug; + private static int maxConsumers; + + static { + // configure logging + logger.addHandler(new ConsoleHandler()); + logger.setLevel(Level.OFF); + + // Undocumented configuration options using java -Doption=value + String property; + property = System.getProperty("JAVA_DTRACE_MAX_CONSUMERS"); + if (property != null && property.length() != 0) { + try { + maxConsumers = Integer.parseInt(property); + } catch (NumberFormatException e) { + e.printStackTrace(); + System.exit(1); + } + } + property = System.getProperty("JAVA_DTRACE_API_DEBUG"); + if (property != null && property.length() != 0) { + try { + int i = Integer.parseInt(property); + debug = (i != 0); + } catch (NumberFormatException e) { + System.err.println("Warning: property ignored: " + + "JAVA_DTRACE_API_DEBUG=" + property); + } + } + + Utility.loadLibrary("libdtrace_jni.so.1", debug); + + _checkVersion(DTRACE_JNI_VERSION); + if (maxConsumers > 0) { + _setMaximumConsumers(maxConsumers); + } + _setDebug(debug); + + // + // Last of all in case configuration options affect the loading + // of the JNI table. + // + _loadJniTable(); + } + + // Native JNI interface (see lib/libdtrace_jni/dtrace_jni.c) + private static native void _checkVersion(int version); + private native void _open(OpenFlag[] flags) throws DTraceException; + private native Program _compileString(String program, String[] args) + throws DTraceException; + private native Program.File _compileFile(String path, String[] args) + throws DTraceException; + private native void _exec(Program program) throws DTraceException; + private native void _getProgramInfo(Program program) + throws DTraceException; + private native void _setOption(String option, String value) + throws DTraceException; + private native long _getOption(String option) throws DTraceException; + private native boolean _isEnabled(); + private native void _checkProgramEnabling(); + private native void _go() throws DTraceException; + private native void _stop() throws DTraceException; + private native void _consume() throws DTraceException; + private native void _interrupt(); + private native void _close(); + private native Aggregate _getAggregate(AggregateSpec spec) + throws DTraceException; + private native int _createProcess(String cmd) throws DTraceException; + private native void _grabProcess(int pid) throws DTraceException; + private native void _listProbes(List <ProbeDescription> probeList, + ProbeDescription filter); + private native void _listProbeDetail(List <Probe> probeList, + ProbeDescription filter); + private native void _listCompiledProbes( + List <ProbeDescription> probeList, Program program); + private native void _listCompiledProbeDetail( + List <Probe> probeList, Program program); + private static native String _getVersion(); + private static native int _openCount(); + // + // Releases memory held in the JNI layer after dtrace_close() has + // released critical system resources like file descriptors, and + // calls to libdtrace are no longer needed (or possible). + // + private native void _destroy(); + // Called by LogDistribution + static native long _quantizeBucket(int i); + // + // Cannot be static because the necessary dtrace handle is specific + // to this Consumer. + // + private native String _lookupKernelFunction(Number address); + private native String _lookupUserFunction(int pid, Number address); + private static native String _getExecutableName(); + + // Undocumented configuration options + private static native void _setMaximumConsumers(int max); + private static native void _setDebug(boolean debug); + + protected EventListenerList listenerList; + protected ExceptionHandler exceptionHandler; + + private int _handle = -1; // native C identifier (do not modify) + private final Identifier id; // java identifier + + private enum State { + INIT, + OPEN, + COMPILED, + GO, + STARTED, + STOPPED, + CLOSED + } + + private State state = State.INIT; + private boolean stopCalled; + + // + // Per-consumer lock used in native code to prevent conflict between + // the native consumer loop and the getAggregate() thread without + // locking this LocalConsumer. A distinct per-consumer lock allows + // the stop() method to be synchronized without causing deadlock + // when the consumer loop grabs the per-consumer lock before + // dtrace_work(). + // + private Object consumerLock; + // + // Synchronization lock used to ensure that stop() does not return + // until this consumer is actually stopped. + // + private Object stopLock; + private boolean workEnded; + + private static int sequence = 0; + + /** + * Creates a consumer that interacts with the native DTrace library + * on the local system. + */ + public + LocalConsumer() + { + id = new LocalConsumer.Identifier(this); + consumerLock = new Object(); + stopLock = new Object(); + listenerList = new EventListenerList(); + } + + /** + * Called by native C code only + */ + private int + getHandle() + { + return _handle; + } + + /** + * Called by native C code only + */ + private void + setHandle(int n) + { + _handle = n; + } + + public synchronized void + open(OpenFlag ... flags) throws DTraceException + { + if (state == State.CLOSED) { + throw new IllegalStateException("cannot reopen a closed consumer"); + } + if (state != State.INIT) { + throw new IllegalStateException("consumer already open"); + } + + for (OpenFlag flag : flags) { + if (flag == null) { + throw new NullPointerException("open flag is null"); + } + } + + synchronized (LocalConsumer.class) { + _open(flags); + } + + state = State.OPEN; + setOptions(DEFAULT_OPTIONS); + + if (logger.isLoggable(Level.INFO)) { + logger.info("consumer table count: " + _openCount()); + } + } + + private synchronized void + checkCompile() + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: // caller may compile more than one program + break; + case GO: + case STARTED: + throw new IllegalStateException("go() already called"); + case STOPPED: + throw new IllegalStateException("consumer stopped"); + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + } + + public synchronized Program + compile(String program, String ... macroArgs) throws DTraceException + { + if (program == null) { + throw new NullPointerException("program string is null"); + } + checkCompile(); + Program p = null; + + String[] argv = null; + if (macroArgs != null) { + for (String macroArg : macroArgs) { + if (macroArg == null) { + throw new NullPointerException("macro argument is null"); + } + } + argv = new String[macroArgs.length + 1]; + synchronized (LocalConsumer.class) { + // + // Could be an application with an embedded JVM, not + // necessarily "java". + // + argv[0] = _getExecutableName(); + } + System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length); + } else { + synchronized (LocalConsumer.class) { + argv = new String[] { _getExecutableName() }; + } + } + synchronized (LocalConsumer.class) { + p = _compileString(program, argv); + } + p.consumerID = id; + p.contents = program; + p.validate(); + state = State.COMPILED; + + return p; + } + + public synchronized Program + compile(File program, String ... macroArgs) throws DTraceException, + IOException, SecurityException + { + if (program == null) { + throw new NullPointerException("program file is null"); + } + if (!program.canRead()) { + throw new FileNotFoundException("failed to open " + + program.getName()); + } + checkCompile(); + Program.File p = null; + + String[] argv = null; + if (macroArgs != null) { + for (String macroArg : macroArgs) { + if (macroArg == null) { + throw new NullPointerException("macro argument is null"); + } + } + argv = new String[macroArgs.length + 1]; + argv[0] = program.getPath(); + System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length); + } else { + macroArgs = new String[] { program.getPath() }; + } + synchronized (LocalConsumer.class) { + p = _compileFile(program.getPath(), argv); + } + p.consumerID = id; + p.contents = Program.getProgramString(program); + p.file = program; + p.validate(); + state = State.COMPILED; + + return p; + } + + private synchronized void + checkProgram(Program program) + { + if (program == null) { + throw new NullPointerException("program is null"); + } + if (!id.equals(program.consumerID)) { + throw new IllegalArgumentException("program not compiled " + + "by this consumer"); + } + } + + public void + enable() throws DTraceException + { + enable(null); + } + + public synchronized void + enable(Program program) throws DTraceException + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + throw new IllegalStateException("no compiled program"); + case COMPILED: + break; + case GO: + case STARTED: + throw new IllegalStateException("go() already called"); + case STOPPED: + throw new IllegalStateException("consumer stopped"); + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + + // Compile all programs if null + if (program != null) { + checkProgram(program); + } + + // + // Left to native code to throw IllegalArgumentException if the + // program is already enabled, since only the native code knows + // the enabled state. + // + synchronized (LocalConsumer.class) { + _exec(program); + } + } + + public synchronized void + getProgramInfo(Program program) throws DTraceException + { + checkProgram(program); + if (state == State.CLOSED) { + throw new IllegalStateException("consumer closed"); + } + + // + // The given program was compiled by this consumer, so we can + // assert the following: + // + assert ((state != State.INIT) && (state != State.OPEN)); + + synchronized (LocalConsumer.class) { + _getProgramInfo(program); + } + } + + private void + setOptions(Option[] options) throws DTraceException + { + for (Option o : options) { + setOption(o.getOption(), o.getValue()); + } + } + + public void + setOption(String option) throws DTraceException + { + setOption(option, Option.VALUE_SET); + } + + public void + unsetOption(String option) throws DTraceException + { + setOption(option, Option.VALUE_UNSET); + } + + public synchronized void + setOption(String option, String value) throws DTraceException + { + if (option == null) { + throw new NullPointerException("option is null"); + } + if (value == null) { + throw new NullPointerException("option value is null"); + } + + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + case GO: + case STARTED: // Some options can be set on a running consumer + case STOPPED: // Allowed (may affect getAggregate()) + break; + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + + synchronized (LocalConsumer.class) { + _setOption(option, value); + } + } + + public synchronized long + getOption(String option) throws DTraceException + { + if (option == null) { + throw new NullPointerException("option is null"); + } + + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + case GO: + case STARTED: + case STOPPED: + break; + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + + long value; + synchronized (LocalConsumer.class) { + value = _getOption(option); + } + return value; + } + + public final synchronized boolean + isOpen() + { + return ((state != State.INIT) && (state != State.CLOSED)); + } + + public final synchronized boolean + isEnabled() + { + if (state != State.COMPILED) { + return false; + } + + return _isEnabled(); + } + + public final synchronized boolean + isRunning() + { + return (state == State.STARTED); + } + + public final synchronized boolean + isClosed() + { + return (state == State.CLOSED); + } + + /** + * Called in the runnable target of the thread returned by {@link + * #createThread()} to run this DTrace consumer. + * + * @see #createThread() + */ + protected final void + work() + { + try { + synchronized (this) { + if (state != State.GO) { + // + // stop() was called after go() but before the + // consumer started + // + return; // executes finally block before returning + } + + state = State.STARTED; + fireConsumerStarted(new ConsumerEvent(this, + System.nanoTime())); + } + + // + // We should not prevent other consumers from running + // concurrently while this consumer blocks on the native + // consumer loop. Instead, native code will acquire the + // LocalConsumer.class monitor as needed before calling + // libdtrace functions. + // + _consume(); + + } catch (Throwable e) { + if (exceptionHandler != null) { + exceptionHandler.handleException(e); + } else { + e.printStackTrace(); + } + } finally { + synchronized (stopLock) { + // Notify the stop() method to stop waiting + workEnded = true; + stopLock.notifyAll(); + } + + // Waits for stop() to finish + synchronized (this) { + // + // The stop() method may have updated the state and + // notified listeners already. Note that whoever + // updates the state and notifies listeners must be + // holding the lock on this LocalConsumer. If we update + // the state and notify listeners here while stop() is + // waiting, counting on stop() to hold the lock for us + // in the meantime, then a ConsumerListener could + // deadlock the application by calling a synchronized + // LocalConsumer method. + // + if (state == State.STARTED) { + state = State.STOPPED; + fireConsumerStopped(new ConsumerEvent(this, + System.nanoTime())); + } + } + } + } + + /** + * Creates the background thread started by {@link #go()} to run + * this consumer. Override this method if you need to set + * non-default {@code Thread} options or create the thread in a + * {@code ThreadGroup}. If you don't need to create the thread + * yourself, set the desired options on {@code super.createThread()} + * before returning it. Otherwise, the {@code Runnable} target of + * the created thread must call {@link #work()} in order to run this + * DTrace consumer. For example, to modify the default background + * consumer thread: + * <pre><code> + * protected Thread + * createThread() + * { + * Thread t = super.createThread(); + * t.setPriority(Thread.MIN_PRIORITY); + * return t; + * } + * </code></pre> + * Or if you need to create your own thread: + * <pre></code> + * protected Thread + * createThread() + * { + * Runnable target = new Runnable() { + * public void run() { + * work(); + * } + * }; + * String name = "Consumer " + UserApplication.sequence++; + * Thread t = new Thread(UserApplication.threadGroup, + * target, name); + * return t; + * } + * </code></pre> + * Do not start the returned thread, otherwise {@code go()} will + * throw an {@link IllegalThreadStateException} when it tries to + * start the returned thread a second time. + */ + protected Thread + createThread() + { + Thread t = new Thread(new Runnable() { + public void run() { + work(); + } + }, "DTrace consumer " + id); + return t; + } + + /** + * @inheritDoc + * @throws IllegalThreadStateException if a subclass calls {@link + * Thread#start()} on the value of {@link #createThread()} + * @see #createThread() + */ + public void + go() throws DTraceException + { + go(null); + } + + /** + * @inheritDoc + * @throws IllegalThreadStateException if a subclass calls {@link + * Thread#start()} on the value of {@link #createThread()} + * @see #createThread() + */ + public synchronized void + go(ExceptionHandler h) throws DTraceException + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + throw new IllegalStateException("no compiled program"); + case COMPILED: + // + // Throws IllegalStateException if not all compiled programs are + // also enabled. Does not make any calls to libdtrace. + // + _checkProgramEnabling(); + break; + case GO: + case STARTED: + throw new IllegalStateException("go() already called"); + case STOPPED: + throw new IllegalStateException("consumer stopped"); + case CLOSED: + throw new IllegalStateException("consumer closed"); + default: + throw new IllegalArgumentException("unknown state: " + state); + } + + synchronized (LocalConsumer.class) { + _go(); + } + + state = State.GO; + exceptionHandler = h; + Thread t = createThread(); + t.start(); + } + + public synchronized void + stop() + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + throw new IllegalStateException("go() not called"); + case GO: + try { + synchronized (LocalConsumer.class) { + _stop(); + } + state = State.STOPPED; + } catch (DTraceException e) { + if (exceptionHandler != null) { + exceptionHandler.handleException(e); + } else { + e.printStackTrace(); + } + } + break; + case STARTED: + // + // Calls no libdtrace methods, so no synchronization is + // needed. Sets a native flag that causes the consumer + // thread to exit the consumer loop and call native + // dtrace_stop() at the end of the current interval + // (after grabbing the global Consumer.class lock + // required for any libdtrace call). + // + _interrupt(); + + synchronized (stopLock) { + // + // If the work() thread got the stopLock first, then + // we have nothing to wait for. (Calling wait() on + // the stopLock can only deadlock this LocalConsumer + // in that case.) However, we still need to notify + // listeners here, since we're holding the lock on + // this LocalConsumer. + // + while (!workEnded) { + try { + stopLock.wait(); + } catch (InterruptedException e) { + logger.warning(e.toString()); + // do nothing but re-check the condition for waiting + } + } + } + + state = State.STOPPED; + fireConsumerStopped(new ConsumerEvent(this, + System.nanoTime())); + break; + case STOPPED: + // + // The work() thread that runs the native consumer loop + // may have terminated because of the exit() action in a + // DTrace program. In that case, a RuntimeException is + // inappropriate because there is no misuse of the API + // Creating a new checked exception type to handle this + // case seems to offer no benefit for the trouble to the + // caller. Instead, the situation calls for stop() to + // be quietly tolerant. + // + if (stopCalled) { + throw new IllegalStateException("consumer already stopped"); + } + logger.info("consumer already stopped"); + break; + case CLOSED: + throw new IllegalStateException("consumer closed"); + default: + throw new IllegalArgumentException("unknown state: " + state); + } + + stopCalled = true; + } + + public synchronized void + close() + { + if ((state == State.INIT) || (state == State.CLOSED)) { + state = State.CLOSED; + return; + } + + if ((state == State.STARTED) || (state == State.GO)) { + stop(); + } + + synchronized (LocalConsumer.class) { + _close(); + } + _destroy(); + state = State.CLOSED; + + if (logger.isLoggable(Level.INFO)) { + logger.info("consumer table count: " + _openCount()); + } + } + + public void + addConsumerListener(ConsumerListener l) + { + listenerList.add(ConsumerListener.class, l); + } + + public void + removeConsumerListener(ConsumerListener l) + { + listenerList.remove(ConsumerListener.class, l); + } + + public Aggregate + getAggregate() throws DTraceException + { + // include all, clear none + return getAggregate(null, Collections. <String> emptySet()); + } + + public Aggregate + getAggregate(Set <String> includedAggregationNames) + throws DTraceException + { + return getAggregate(includedAggregationNames, + Collections. <String> emptySet()); + } + + public Aggregate + getAggregate(Set <String> includedAggregationNames, + Set <String> clearedAggregationNames) + throws DTraceException + { + AggregateSpec spec = new AggregateSpec(); + + if (includedAggregationNames == null) { + spec.setIncludeByDefault(true); + } else { + spec.setIncludeByDefault(false); + for (String included : includedAggregationNames) { + spec.addIncludedAggregationName(included); + } + } + + if (clearedAggregationNames == null) { + spec.setClearByDefault(true); + } else { + spec.setClearByDefault(false); + for (String cleared : clearedAggregationNames) { + spec.addClearedAggregationName(cleared); + } + } + + return getAggregate(spec); + } + + private synchronized Aggregate + getAggregate(AggregateSpec spec) throws DTraceException + { + // + // It should be possible to request aggregation data after a + // consumer has stopped but not after it has been closed. + // + checkGoCalled(); + + // + // Getting the aggregate is a time-consuming request that should not + // prevent other consumers from running concurrently. Instead, + // native code will acquire the LocalConsumer.class monitor as + // needed before calling libdtrace functions. + // + Aggregate aggregate = _getAggregate(spec); + return aggregate; + } + + private synchronized void + checkGoCalled() + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + throw new IllegalStateException("go() not called"); + case GO: + case STARTED: + case STOPPED: + break; + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + } + + private synchronized void + checkGoNotCalled() + { + switch (state) { + case INIT: + throw new IllegalStateException("consumer not open"); + case OPEN: + case COMPILED: + break; + case GO: + case STARTED: + throw new IllegalStateException("go() already called"); + case STOPPED: + throw new IllegalStateException("consumer stopped"); + case CLOSED: + throw new IllegalStateException("consumer closed"); + } + } + + public synchronized int + createProcess(String command) throws DTraceException + { + if (command == null) { + throw new NullPointerException("command is null"); + } + + checkGoNotCalled(); + + int pid; + synchronized (LocalConsumer.class) { + pid = _createProcess(command); + } + return pid; + } + + public synchronized void + grabProcess(int pid) throws DTraceException + { + checkGoNotCalled(); + + synchronized (LocalConsumer.class) { + _grabProcess(pid); + } + } + + public synchronized List <ProbeDescription> + listProbes(ProbeDescription filter) throws DTraceException + { + checkGoNotCalled(); + List <ProbeDescription> probeList = + new LinkedList <ProbeDescription> (); + if (filter == ProbeDescription.EMPTY) { + filter = null; + } + synchronized (LocalConsumer.class) { + _listProbes(probeList, filter); + } + return probeList; + } + + public synchronized List <Probe> + listProbeDetail(ProbeDescription filter) throws DTraceException + { + checkGoNotCalled(); + List <Probe> probeList = new LinkedList <Probe> (); + if (filter == ProbeDescription.EMPTY) { + filter = null; + } + synchronized (LocalConsumer.class) { + _listProbeDetail(probeList, filter); + } + return probeList; + } + + public synchronized List <ProbeDescription> + listProgramProbes(Program program) throws DTraceException + { + checkProgram(program); + checkGoNotCalled(); + List <ProbeDescription> probeList = + new LinkedList <ProbeDescription> (); + synchronized (LocalConsumer.class) { + _listCompiledProbes(probeList, program); + } + return probeList; + } + + public synchronized List <Probe> + listProgramProbeDetail(Program program) throws DTraceException + { + checkProgram(program); + checkGoNotCalled(); + List <Probe> probeList = new LinkedList <Probe> (); + synchronized (LocalConsumer.class) { + _listCompiledProbeDetail(probeList, program); + } + return probeList; + } + + public synchronized String + lookupKernelFunction(int address) + { + checkGoCalled(); + synchronized (LocalConsumer.class) { + return _lookupKernelFunction(new Integer(address)); + } + } + + public synchronized String + lookupKernelFunction(long address) + { + checkGoCalled(); + synchronized (LocalConsumer.class) { + return _lookupKernelFunction(new Long(address)); + } + } + + public synchronized String + lookupUserFunction(int pid, int address) + { + checkGoCalled(); + synchronized (LocalConsumer.class) { + return _lookupUserFunction(pid, new Integer(address)); + } + } + + public synchronized String + lookupUserFunction(int pid, long address) + { + checkGoCalled(); + synchronized (LocalConsumer.class) { + return _lookupUserFunction(pid, new Long(address)); + } + } + + public String + getVersion() + { + synchronized (LocalConsumer.class) { + return LocalConsumer._getVersion(); + } + } + + /** + * Called by native code. + */ + private void + nextProbeData(ProbeData probeData) throws ConsumerException + { + fireDataReceived(new DataEvent(this, probeData)); + } + + /** + * Called by native code. + */ + private void + dataDropped(Drop drop) throws ConsumerException + { + fireDataDropped(new DropEvent(this, drop)); + } + + /** + * Called by native code. + */ + private void + errorEncountered(Error error) throws ConsumerException + { + fireErrorEncountered(new ErrorEvent(this, error)); + } + + /** + * Called by native code. + */ + private void + processStateChanged(ProcessState processState) throws ConsumerException + { + fireProcessStateChanged(new ProcessEvent(this, processState)); + } + + protected void + fireDataReceived(DataEvent e) throws ConsumerException + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).dataReceived(e); + } + } + } + + protected void + fireDataDropped(DropEvent e) throws ConsumerException + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).dataDropped(e); + } + } + } + + protected void + fireErrorEncountered(ErrorEvent e) throws ConsumerException + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).errorEncountered(e); + } + } + } + + protected void + fireProcessStateChanged(ProcessEvent e) throws ConsumerException + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).processStateChanged(e); + } + } + } + + protected void + fireConsumerStarted(ConsumerEvent e) + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).consumerStarted(e); + } + } + } + + protected void + fireConsumerStopped(ConsumerEvent e) + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).consumerStopped(e); + } + } + } + + // Called by native code + private void + intervalBegan() + { + fireIntervalBegan(new ConsumerEvent(this, System.nanoTime())); + } + + protected void + fireIntervalBegan(ConsumerEvent e) + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).intervalBegan(e); + } + } + } + + // Called by native code + private void + intervalEnded() + { + fireIntervalEnded(new ConsumerEvent(this, System.nanoTime())); + } + + protected void + fireIntervalEnded(ConsumerEvent e) + { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ConsumerListener.class) { + ((ConsumerListener)listeners[i + 1]).intervalEnded(e); + } + } + } + + /** + * Gets a string representation of this consumer useful for logging + * and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(LocalConsumer.class.getName()); + synchronized (this) { + buf.append("[open = "); + buf.append(isOpen()); + buf.append(", enabled = "); + buf.append(isEnabled()); + buf.append(", running = "); + buf.append(isRunning()); + buf.append(", closed = "); + buf.append(isClosed()); + } + buf.append(']'); + return buf.toString(); + } + + /** + * Ensures that the {@link #close()} method of this consumer has + * been called before it is garbage-collected. The intended safety + * net is weak because the JVM does not guarantee that an object + * will be garbage-collected when it is no longer referenced. Users + * of the API should call {@code close()} to ensure that all + * resources associated with this consumer are reclaimed in a timely + * manner. + * + * @see #close() + */ + protected void + finalize() + { + close(); + } + + private String + getTag() + { + return super.toString(); + } + + // + // Uniquely identifies a consumer across systems so it is possible + // to validate that an object such as a Program passed to a remote + // client over a socket was created by this consumer and no other. + // + static class Identifier implements Serializable { + static final long serialVersionUID = 2183165132305302834L; + + // local identifier + private int id; + private long timestamp; + // remote identifier + private InetAddress localHost; + private String tag; // in case localHost not available + + private + Identifier(LocalConsumer consumer) + { + id = LocalConsumer.sequence++; + timestamp = System.currentTimeMillis(); + try { + localHost = InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + localHost = null; + } + tag = consumer.getTag(); + } + + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof Identifier) { + Identifier i = (Identifier)o; + return ((id == i.id) && + (timestamp == i.timestamp) && + ((localHost == null) ? (i.localHost == null) : + localHost.equals(i.localHost)) && + tag.equals(i.tag)); + } + return false; + } + + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + id; + hash = (37 * hash) + ((int)(timestamp ^ (timestamp >>> 32))); + hash = (37 * hash) + (localHost == null ? 0 : + localHost.hashCode()); + hash = (37 * hash) + tag.hashCode(); + return hash; + } + + @Override + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Identifier.class.getName()); + buf.append("[id = "); + buf.append(id); + buf.append(", timestamp = "); + buf.append(timestamp); + buf.append(", localHost = "); + buf.append(localHost); + buf.append(", tag = "); + buf.append(tag); + buf.append(']'); + return buf.toString(); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LogDistribution.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LogDistribution.java new file mode 100644 index 0000000000..fb37303a01 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LogDistribution.java @@ -0,0 +1,175 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A power-of-two logarithmic frequency distribution aggregated by the + * DTrace {@code quantize()} action. Aggregated values fall into + * consecutive ranges, each twice as large as the previous range. Each + * range, known as a bucket, begins at two to the power of <i>n</i> and + * ends at one less than the beginning of the next bucket, two to the + * power of <i>n + 1</i>. The zero bucket is the degenerate case and + * holds the frequency of the base value zero. For example, the first + * bucket after 0 starts at 1 (2 to the power of 0) and ends at 1 (one + * less than 2 to the power of 1). The next bucket starts at 2 (2 to + * the power of 1) and ends at 3 (one less than 2 to the power of 2). + * Each bucket frequency is incremented for each aggregated value that + * falls into its range. Buckets are typically identified by their + * lower bound: 1, 2, 4, 8, etc. Mirroring these are buckets with + * negative ranges: -1, -2, -4, -8, etc. The range of an entire {@code + * LogDistribution} is (<code>-2<sup>63</sup> .. + * 2<sup>63</sup></code>). + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see LinearDistribution + * @see Aggregation + * + * @author Tom Erickson + */ +public final class LogDistribution extends Distribution + implements Serializable, Comparable <LogDistribution> +{ + static final long serialVersionUID = -1279719751212721961L; + + static final int ZERO_BUCKET_INDEX = 63; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(LogDistribution.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"buckets"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Called by native C code + */ + private + LogDistribution(long[] buckets) + { + super(0, 2, buckets); // initializes using base 0, power of 2 + } + + /** + * Creates a logarithmic distribution with the given frequencies. + * Supports XML persistence. + * + * @param frequencies list of frequencies in bucket ranges bounded + * by consucutive powers of two + * @throws NullPointerException if {@code frequencies} is {@code + * null} + * @throws IllegalArgumentException if any bucket does not have the + * expected range (bounded by consecutive powers of two) + */ + public + LogDistribution(List <Bucket> frequencies) + { + super(frequencies); + initialize(); + } + + /** + * Gets a two element array: the first elelemt is the range minimum + * (inclusive), the second element is the range maximum (inclusive). + */ + @Override + long[] + getBucketRange(int i, int len, long base, long constant) + { + long min = LocalConsumer._quantizeBucket(i); + long max = (LocalConsumer._quantizeBucket(i + 1) - 1); + + long[] range = new long[] {min, max}; + return range; + } + + @Override + long[] + getBucketRange(int i, int len) + { + return getBucketRange(i, len, 0, 2); + } + + public Number + getValue() + { + double total = 0; + List <Distribution.Bucket> buckets = getBuckets(); + for (Distribution.Bucket bucket : buckets) { + total += ((double)bucket.getFrequency() * (double)bucket.getMin()); + } + return (new Double(total)); + } + + private long + getZeroBucketValue() + { + Distribution.Bucket b = get(ZERO_BUCKET_INDEX); + return b.getFrequency(); + } + + /** + * Compares the {@code double} values of {@link #getValue()} for + * overall magnitude, and if those are equal, compares the + * frequencies at the zero bucket (the bucket whose range has a + * minimum and maximum value of zero). + */ + public int + compareTo(LogDistribution d) + { + Number v1 = getValue(); + Number v2 = d.getValue(); + double d1 = v1.doubleValue(); + double d2 = v2.doubleValue(); + int cmp = (d1 < d2 ? -1 : (d1 > d2 ? 1 : 0)); + if (cmp == 0) { + long z1 = getZeroBucketValue(); + long z2 = d.getZeroBucketValue(); + cmp = (z1 < z2 ? -1 : (z1 > z2 ? 1 : 0)); + } + return (cmp); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + initialize(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MaxValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MaxValue.java new file mode 100644 index 0000000000..6cf1fa46de --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MaxValue.java @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code max()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class MaxValue extends AbstractAggregationValue { + static final long serialVersionUID = -7761988758448759253L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(MaxValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code max()} action. + * Supports XML persistence. + * + * @param v maximum of the aggregated values + */ + public + MaxValue(long v) + { + super(v); + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the maximum of the aggregated values. + * + * @return the maximum of the aggregated values + */ + public Long + getValue() + { + return (Long)super.getValue(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MinValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MinValue.java new file mode 100644 index 0000000000..1d82557a2c --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/MinValue.java @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code min()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class MinValue extends AbstractAggregationValue { + static final long serialVersionUID = -3903522347795401289L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(MinValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code min()} action. + * Supports XML persistence. + * + * @param v minimum of the aggregated values + */ + public + MinValue(long v) + { + super(v); + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the minimum of the aggregated values. + * + * @return the minimum of the aggregated values + */ + public Long + getValue() + { + return (Long)super.getValue(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/NativeException.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/NativeException.java new file mode 100644 index 0000000000..bfb70d911a --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/NativeException.java @@ -0,0 +1,70 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * Wraps a java exception encountered in native code for the purpose of + * adding native source filename and line number, which are otherwise + * not included in the stack trace of the wrapped exception. + * + * @author Tom Erickson + */ +class NativeException extends RuntimeException { + static final long serialVersionUID = 4129171856987233185L; + + /** @serial */ + private String fileName; + /** @serial */ + private int lineNumber; + + public + NativeException(String file, int line, Throwable cause) + { + super(cause); + fileName = file; + lineNumber = line; + } + + public String + getMessage() + { + StringBuffer buf = new StringBuffer(); + buf.append(fileName); + buf.append(" line "); + buf.append(lineNumber); + Throwable cause = getCause(); + if (cause != null) { + String message = cause.getMessage(); + if (message != null) { + buf.append(" "); + buf.append(cause.getMessage()); + } + } + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Option.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Option.java new file mode 100644 index 0000000000..c81707c3cb --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Option.java @@ -0,0 +1,681 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.Serializable; +import java.io.ObjectInputStream; +import java.io.InvalidObjectException; +import java.io.IOException; +import java.beans.*; + +/** + * A DTrace option and its value. Compile-time options must be set + * before calling {@code Consumer} {@link Consumer#compile(String + * program, String[] macroArgs) compile(String program, ...)} or {@link + * Consumer#compile(File program, String[] macroArgs) compile(File + * program, ...)} in order to affect program compilation. Runtime + * options may be set anytime before calling {@code Consumer} {@link + * Consumer#go() go()}, and some of them may be changed while a consumer + * is running. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlis?a=view> + * <b>Options and Tunables</b></a> chapter of the <i>Solaris Dynamic + * Tracing Guide</i>. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class Option implements Serializable { + static final long serialVersionUID = 2734100173861424920L; + + /** + * Value returned by {@link Consumer#getOption(String option)} when + * the given boolean option is unset. + */ + public static final long UNSET = -2L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufpolicy} option when the {@link #VALUE_RING ring} + * buffer policy is set. + */ + public static final long BUFPOLICY_RING = 0L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufpolicy} option when the {@link #VALUE_FILL fill} + * buffer policy is set. + */ + public static final long BUFPOLICY_FILL = 1L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufpolicy} option when the default {@link + * #VALUE_SWITCH switch} buffer policy is set. + */ + public static final long BUFPOLICY_SWITCH = 2L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufresize} option when the default {@link #VALUE_AUTO + * auto} buffer resize policy is set. + */ + public static final long BUFRESIZE_AUTO = 0L; + + /** + * Value returned by {@link Consumer#getOption(String option)} for + * the {@link #bufresize} option when the {@link #VALUE_MANUAL + * manual} buffer resize policy is set. + */ + public static final long BUFRESIZE_MANUAL = 1L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Option.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"option", "value"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + // + // See lib/libdtrace/common/dt_options.c + // + + /** + * Gets a size option value indicating the given number of + * kilobytes. + * + * @param n number of kilobytes + * @return size option value indicating the given number of + * kilobytes + */ + public static String + kb(int n) + { + return (Integer.toString(n) + "k"); + } + + /** + * Gets a size option value indicating the given number of + * megabytes. + * + * @param n number of megabytes + * @return size option value indicating the given number of + * megabytes + */ + public static String + mb(int n) + { + return (Integer.toString(n) + "m"); + } + + /** + * Gets a size option value indicating the given number of + * gigabytes. + * + * @param n number of gigabytes + * @return size option value indicating the given number of + * gigabytes + */ + public static String + gb(int n) + { + return (Integer.toString(n) + "g"); + } + + /** + * Gets a size option value indicating the given number of + * terabytes. + * + * @param n number of terabytes + * @return size option value indicating the given number of + * terabytes + */ + public static String + tb(int n) + { + return (Integer.toString(n) + "t"); + } + + /** + * Gets a time option value indicating the given number of + * nanoseconds. + * + * @param n number of nanoseconds + * @return time option value indicating the given number of + * nanoseconds + */ + public static String + nanos(int n) + { + return (Integer.toString(n) + "ns"); + } + + /** + * Gets a time option value indicating the given number of + * microseconds. + * + * @param n number of microseconds + * @return time option value indicating the given number of + * microseconds + */ + public static String + micros(int n) + { + return (Integer.toString(n) + "us"); + } + + /** + * Gets a time option value indicating the given number of + * milliseconds. + * + * @param n number of milliseconds + * @return time option value indicating the given number of + * milliseconds + */ + public static String + millis(int n) + { + return (Integer.toString(n) + "ms"); + } + + /** + * Gets a time option value indicating the given number of seconds. + * + * @param n number of seconds + * @return time option value indicating the given number of seconds + */ + public static String + seconds(int n) + { + return (Integer.toString(n) + "s"); + } + + /** + * Gets a time option value indicating the given number of minutes. + * + * @param n number of minutes + * @return time option value indicating the given number of minutes + */ + public static String + minutes(int n) + { + return (Integer.toString(n) + "m"); + } + + /** + * Gets a time option value indicating the given number of hours. + * + * @param n number of hours + * @return time option value indicating the given number of hours + */ + public static String + hours(int n) + { + return (Integer.toString(n) + "h"); + } + + /** + * Gets a time option value indicating the given number of days. + * + * @param n number of days + * @return time option value indicating the given number of days + */ + public static String + days(int n) + { + return (Integer.toString(n) + "d"); + } + + /** + * Gets a time option value indicating the given rate per second. + * + * @param n number of cycles per second (hertz) + * @return time option value indicating rate per second + */ + public static String + hz(int n) + { + return (Integer.toString(n) + "hz"); + } + + /** + * May be passed to {@link Consumer#setOption(String option, String + * value)} to set a boolean option such as {@link #flowindent}. + * However, a more convenient way to set boolean options is {@link + * Consumer#setOption(String option)}. + */ + public static final String VALUE_SET = "set"; + + /** + * May be passed to {@link Consumer#setOption(String option, String + * value)} to unset a boolean option such as {@link #flowindent}. + * However, a more convenient way to unset boolean options is {@link + * Consumer#unsetOption(String option)}. + */ + public static final String VALUE_UNSET = "unset"; + + /** + * {@link #bufpolicy} value: use {@code ring} princical buffer + * policy. + */ + public static final String VALUE_RING = "ring"; + /** + * {@link #bufpolicy} value: use {@code fill} princical buffer + * policy. + */ + public static final String VALUE_FILL = "fill"; + /** + * {@link #bufpolicy} default value: use {@code switch} princical + * buffer policy. + */ + public static final String VALUE_SWITCH = "switch"; + + /** + * {@link #bufresize} default value: use {@code auto} buffer + * resizing policy. + */ + public static final String VALUE_AUTO = "auto"; + /** + * {@link #bufresize} value: use {@code manual} buffer resizing + * policy. + */ + public static final String VALUE_MANUAL = "manual"; + + // + // See lib/libdtrace/common/dt_options.c + // + + /** + * Set program attribute minimum (compile-time). The format of the + * option value is defined by the {@link + * InterfaceAttributes#toString()} method. + * + * @see Program#getInfo() + */ + public static final String amin = "amin"; + /** + * Do not require all macro args to be used (compile-time; no option + * value). + * + * @see Consumer#compile(String program, String[] macroArgs) + * @see Consumer#compile(File program, String[] macroArgs) + */ + public static final String argref = "argref"; + /** + * Run cpp(1) preprocessor on D script files (compile-time; no + * option value). + */ + public static final String cpp = "cpp"; + /** + * Used together with {@link #cpp} option, specifies which {@code + * cpp} to run by its pathname (compile-time). + */ + public static final String cpppath = "cpppath"; + /** + * Use zero (0) or empty string ("") as the value for unspecified macro args + * (compile-time; no option value). + * + * @see Consumer#compile(String program, String[] macroArgs) + * @see Consumer#compile(File program, String[] macroArgs) + */ + public static final String defaultargs = "defaultargs"; + /** + * Define symbol when invoking preprocssor (compile-time). + */ + public static final String define = "define"; + /** + * Permit compilation of empty D source files (compile-time; no + * option value). + */ + public static final String empty = "empty"; + /** + * Adds error tags to default error messages (compile-time; no + * option value). + */ + public static final String errtags = "errtags"; + /** + * Add include directory to preprocessor search path (compile-time). + */ + public static final String incdir = "incdir"; + /** + * Permit unresolved kernel symbols (compile-time; no option value). + */ + public static final String knodefs = "knodefs"; + /** + * Add library directory to library search path (compile-time). + */ + public static final String libdir = "libdir"; + /** + * Specify ISO C conformance settings for preprocessor + * (compile-time). + */ + public static final String stdc = "stdc"; + /** + * Undefine symbol when invoking preprocessor (compile-time). + */ + public static final String undef = "undef"; + /** + * Permit unresolved user symbols (compile-time; no option value). + */ + public static final String unodefs = "unodefs"; + /** + * Request specific version of native DTrace library (compile-time). + */ + public static final String version = "version"; + /** + * Permit probe definitions that match zero probes (compile-time; no + * option value). + */ + public static final String zdefs = "zdefs"; + + /** Rate of aggregation reading (time). Runtime option. */ + public static final String aggrate = "aggrate"; + /** Aggregation buffer size (size). Runtime option. */ + public static final String aggsize = "aggsize"; + /** + * Denotes that aggregation data should be sorted in tuple order, + * with ties broken by value order (no option value). Runtime + * option. + * + * @see AggregationRecord + * @see Option#aggsortkeypos + * @see Option#aggsortpos + * @see Option#aggsortrev + */ + public static final String aggsortkey = "aggsortkey"; + /** + * When multiple aggregation tuple elements are present, the + * position of the tuple element that should act as the primary sort + * key (zero-based index). Runtime option. + * + * @see Option#aggsortkey + * @see Option#aggsortpos + * @see Option#aggsortrev + */ + public static final String aggsortkeypos = "aggsortkeypos"; + /** + * When multiple aggregations are being printed, the position of the + * aggregation that should act as the primary sort key (zero-based + * index). Runtime option. + * <p> + * Here "position" refers to the position of the aggregation in the + * {@code printa()} argument list after the format string (if + * any). For example, given the following statement: + * <pre><code> + * printa("%d %@7d %@7d\n", @a, @b); + * </code></pre> + * setting {@code aggsortpos} to {@code "0"} indicates that output + * should be sorted using the values of {@code @a} as the primary + * sort key, while setting {@code aggsortpos} to {@code "1"} + * indicates that output should be sorted using the values of + * {@code @b} as the primary sort key. + * + * @see Option#aggsortkey + * @see Option#aggsortkeypos + * @see Option#aggsortrev + */ + public static final String aggsortpos = "aggsortpos"; + /** + * Denotes that aggregation data should be sorted in descending + * order (no option value). Runtime option. + * <p> + * The {@code aggsortrev} option is useful in combination with the + * {@code aggsortkey}, {@code aggsortkeypos}, and {@code aggsortpos} + * options, which define the ascending sort reversed by this option. + * + * @see Option#aggsortkey + * @see Option#aggsortkeypos + * @see Option#aggsortpos + */ + public static final String aggsortrev = "aggsortrev"; + /** Principal buffer size (size). Runtime option. */ + public static final String bufsize = "bufsize"; + /** + * Buffering policy ({@link #VALUE_SWITCH switch}, {@link + * #VALUE_FILL fill}, or {@link #VALUE_RING ring}). Runtime option. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhr?a=view> + * <b>Principal Buffer Policies</b></a> section of the + * <b>Buffers and Buffering</b> chapter of the <i>Solaris Dynamic + * Tracing Guide</i>. + */ + public static final String bufpolicy = "bufpolicy"; + /** + * Buffer resizing policy ({@link #VALUE_AUTO auto} or {@link + * #VALUE_MANUAL manual}). Runtime option. + * <p> + * See the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhu?a=view> + * <b>Buffer Resizing Policy</b></a> section of the <b>Buffers + * and Buffering</b> chapter of the <i>Solaris Dynamic Tracing + * Guide</i>. + */ + public static final String bufresize = "bufresize"; + /** Cleaning rate (time). Runtime option. */ + public static final String cleanrate = "cleanrate"; + /** CPU on which to enable tracing (scalar). Runtime option. */ + public static final String cpu = "cpu"; + /** Permit destructive actions (no option value). Runtime option. */ + public static final String destructive = "destructive"; + /** Dynamic variable space size (size). Runtime option. */ + public static final String dynvarsize = "dynvarsize"; + /** + * Adds {@link Flow} information to generated {@link ProbeData} + * indicating direction of control flow (entry or return) across + * function boundaries and depth in call stack (no option value). + * Runtime option. + */ + public static final String flowindent = "flowindent"; + /** Number of speculations (scalar). Runtime option. */ + public static final String nspec = "nspec"; + /** + * Only output explicitly traced data (no option value). Makes no + * difference to generated {@link ProbeData}, but user apps may use + * the {@code quiet} flag as a rendering hint similar to the {@code + * -q} {@code dtrace(1M)} command option. Runtime option. + */ + public static final String quiet = "quiet"; + /** Speculation buffer size (size). Runtime option. */ + public static final String specsize = "specsize"; + /** Number of stack frames (scalar). Runtime option. */ + public static final String stackframes = "stackframes"; + /** Rate of status checking (time). Runtime option. */ + public static final String statusrate = "statusrate"; + /** String size (size). Runtime option. */ + public static final String strsize = "strsize"; + /** Rate of buffer switching (time). Runtime option. */ + public static final String switchrate = "switchrate"; + /** Number of user stack frames (scalar). Runtime option. */ + public static final String ustackframes = "ustackframes"; + + /** @serial */ + private final String option; + /** @serial */ + private final String value; + + /** + * Creates an option without an associated value. The created + * boolean option has the value {@link Option#VALUE_SET}. To + * specify that the named option be unset, use {@link + * Option#VALUE_UNSET}. + * + * @param opt DTrace option name + * @throws NullPointerException if the given option name is {@code + * null} + * @see #Option(String opt, String val) + */ + public + Option(String opt) + { + this(opt, Option.VALUE_SET); + } + + /** + * Creates an option with the given name and value. + * + * @param opt DTrace option name + * @param val DTrace option value + * @throws NullPointerException if the given option name or value is + * {@code null} + */ + public + Option(String opt, String val) + { + option = opt; + value = val; + validate(); + } + + private void + validate() + { + if (option == null) { + throw new NullPointerException("option name is null"); + } + if (value == null) { + throw new NullPointerException("option value is null"); + } + } + + /** + * Gets the option name. + * + * @return non-null option name + */ + public String + getOption() + { + return option; + } + + /** + * Gets the option value. + * + * @return option value, or {@code null} if no value is associated + * with the option + */ + public String + getValue() + { + return value; + } + + /** + * Compares the specified object with this option for equality. + * Defines equality as having equal names and values. + * + * @return {@code true} if and only if the specified object is an + * {@code Option} with the same name and the same value as this + * option. Option values are the same if they are both {@code null} + * or if they are equal as defined by {@link String#equals(Object o) + * String.equals()}. + */ + public boolean + equals(Object o) + { + if (o instanceof Option) { + Option opt = (Option)o; + return (option.equals(opt.option) && + value.equals(opt.value)); + } + return false; + } + + /** + * Overridden to ensure that equal options have equal hashcodes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + option.hashCode(); + hash = (37 * hash) + value.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this option useful for logging + * and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Option.class.getName()); + buf.append("[option = "); + buf.append(option); + buf.append(", value = "); + buf.append(value); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintaRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintaRecord.java new file mode 100644 index 0000000000..e24e31896b --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintaRecord.java @@ -0,0 +1,482 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; +import java.util.*; + +/** + * A record generated by the DTrace {@code printa()} action. Lists the + * aggregations passed to {@code printa()} and records the formatted + * output associated with each {@link Tuple}. If multiple aggregations + * were passed to the {@code printa()} action that generated this + * record, then the DTrace library tabulates the output, using a default + * format if no format string was specified. By default, the output + * string associated with a given {@code Tuple} includes a value from + * each aggregation, or zero wherever an aggregation has no value + * associated with that {@code Tuple}. For example, the D statements + * <pre><code> + * @a[123] = sum(1); + * @b[456] = sum(2); + * printa(@a, @b, @c); + * </code></pre> + * produce output for the tuples "123" and "456" similar to the + * following: + * <pre><code> + * 123 1 0 0 + * 456 0 2 0 + * </code></pre> + * The first column after the tuple contains values from {@code @a}, + * the next column contains values from {@code @b}, and the last + * column contains zeros because {@code @c} has neither a value + * associated with "123" nor a value associated with "456". + * <p> + * If a format string is passed to {@code printa()}, it may limit the + * aggregation data available in this record. For example, if the + * format string specifies a value placeholder for only one of two + * aggregations passed to {@code printa()}, then the resulting {@code + * PrintaRecord} will contain only one {@code Aggregation}. If no value + * placeholder is specified, or if the aggregation tuple is not + * completely specified, the resulting {@code PrintaRecord} will contain + * no aggregation data. However, the formatted output generated by the + * DTrace library is available in all cases. For details about + * {@code printa()} format strings, see the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidli3?a=view> + * <b>{@code printa()}</b></a> section of the <b>Output + * Formatting</b> chapter of the <i>Solaris Dynamic Tracing Guide</i>. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class PrintaRecord implements Record, Serializable { + static final long serialVersionUID = -4174277639915895694L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(PrintaRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"snaptime", "aggregations", + "formattedStrings", "tuples", "output"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final long snaptime; + /** @serial */ + private List <Aggregation> aggregations; + /** @serial */ + private Map <Tuple, String> formattedStrings; + /** @serial */ + private List <Tuple> tuples; + private transient StringBuffer outputBuffer; + private transient String output; + private transient boolean formatted; + + /** + * Package level access, called by ProbeData. + */ + PrintaRecord(long snaptimeNanos, boolean isFormatString) + { + snaptime = snaptimeNanos; + aggregations = new ArrayList <Aggregation> (); + formattedStrings = new HashMap <Tuple, String> (); + tuples = new ArrayList <Tuple> (); + outputBuffer = new StringBuffer(); + formatted = isFormatString; + validate(); + } + + /** + * Creates a record with the given snaptime, aggregations, and + * formatted output. + * + * @param snaptimeNanos nanosecond timestamp of the snapshot used + * to create this {@code printa()} record + * @param aggs aggregations passed to the {@code printa()} action + * that generated this record + * @param formattedOutput the formatted output, if any, associated + * with each {@code Tuple} occurring in the aggregations belonging + * to this record, one formatted string per {@code Tuple}, or an + * empty or {@code null} map if an incomplete {@code printa()} + * format string caused aggregation tuples to be omitted from this + * record + * @param orderedTuples list of aggregation tuples in the same order + * generated by the native DTrace library (determined by the various + * "aggsort" options such as {@link Option#aggsortkey}) + * @param formattedOutputString {@code printa()} formatted string + * output in the same order generated by the native DTrace library + * (determined by the various "aggsort" options such as + * {@link Option#aggsortkey}) + * @throws NullPointerException if the given collection of + * aggregations is {@code null}, or if the given ordered lists of + * tuples or formatted strings are {@code null} + * @throws IllegalArgumentException if the given snaptime is + * negative + */ + public + PrintaRecord(long snaptimeNanos, Collection <Aggregation> aggs, + Map <Tuple, String> formattedOutput, + List <Tuple> orderedTuples, + String formattedOutputString) + { + snaptime = snaptimeNanos; + if (aggs != null) { + aggregations = new ArrayList <Aggregation> (aggs.size()); + aggregations.addAll(aggs); + } + if (formattedOutput != null) { + formattedStrings = new HashMap <Tuple, String> + (formattedOutput); + } + if (orderedTuples != null) { + tuples = new ArrayList <Tuple> (orderedTuples.size()); + tuples.addAll(orderedTuples); + } + output = formattedOutputString; + validate(); + } + + private void + validate() + { + if (snaptime < 0) { + throw new IllegalArgumentException("snaptime is negative"); + } + if (aggregations == null) { + throw new NullPointerException("aggregations list is null"); + } + Aggregation a; + for (int i = 0, len = aggregations.size(); i < len; ++i) { + a = aggregations.get(i); + if (a == null) { + throw new NullPointerException( + "null aggregation at index " + i); + } + } + if (tuples == null) { + throw new NullPointerException("ordered tuple list is null"); + } + if (output == null && outputBuffer == null) { + throw new NullPointerException("formatted output is null"); + } + } + + /** + * Gets the nanosecond timestamp of the aggregate snapshot used to + * create this {@code printa()} record. + * + * @return nanosecond timestamp + */ + public long + getSnaptime() + { + return snaptime; + } + + private Aggregation + getAggregationImpl(String name) + { + if (name == null) { + return null; + } + for (Aggregation a : aggregations) { + if (name.equals(a.getName())) { + return a; + } + } + return null; + } + + /** + * Gets the named aggregation. + * + * @return the named aggregation passed to {@code printa()}, or + * {@code null} if the named aggregation is not passed to {@code + * printa()}, or if it is omitted due to an incomplete {@code + * printa()} format string, or if it is empty (a future release of + * this API may represent an empty DTrace aggregation as a non-null + * {@code Aggregation} with no records; users of this API should not + * rely on a non-null return value to indicate a non-zero record + * count) + */ + public Aggregation + getAggregation(String name) + { + name = Aggregate.filterUnnamedAggregationName(name); + return getAggregationImpl(name); + } + + /** + * Gets a list of the aggregations passed to the {@code printa()} + * action that generated this record. The returned list is a copy, + * and modifying it has no effect on this record. Supports XML + * persistence. + * + * @return non-null, possibly empty list of aggregations belonging + * to this record (empty aggregations are excluded) + */ + public List <Aggregation> + getAggregations() + { + return new ArrayList <Aggregation> (aggregations); + } + + /** + * Gets the formatted string, if any, associated with the given + * aggregation tuple. + * + * @param key aggregation tuple + * @return the formatted string associated with the given + * aggregation tuple, or {@code null} if the given tuple does not + * exist in the aggregations belonging to this record or if it + * is omitted from this record due to an incomplete {@code printa()} + * format string + * @see #getFormattedStrings() + * @see #getOutput() + */ + public String + getFormattedString(Tuple key) + { + if (formattedStrings == null) { + return null; + } + return formattedStrings.get(key); + } + + /** + * Gets the formatted output, if any, associated with each {@code + * Tuple} occurring in the aggregations belonging to this record, + * one formatted string per {@code Tuple}. Gets an empty map if + * aggregation tuples are omitted from this record due to an + * incomplete {@code printa()} format string. The returned map is a + * copy and modifying it has no effect on this record. Supports XML + * persistence. + * + * @return a map of aggregation tuples and their associated + * formatted output strings, empty if aggregation tuples are omitted + * from this record due to an incomplete {@code printa(}) format + * string + * @see #getFormattedString(Tuple key) + * @see #getOutput() + */ + public Map <Tuple, String> + getFormattedStrings() + { + if (formattedStrings == null) { + return new HashMap <Tuple, String> (); + } + return new HashMap <Tuple, String> (formattedStrings); + } + + /** + * Gets an ordered list of this record's aggregation tuples. The + * returned list is a copy, and modifying it has no effect on this + * record. Supports XML persistence. + * + * @return a non-null list of this record's aggregation tuples in + * the order they were generated by the native DTrace library, as + * determined by the {@link Option#aggsortkey}, {@link + * Option#aggsortrev}, {@link Option#aggsortpos}, and {@link + * Option#aggsortkeypos} options + */ + public List <Tuple> + getTuples() + { + return new ArrayList <Tuple> (tuples); + } + + /** + * Gets this record's formatted output. Supports XML persistence. + * + * @return non-null formatted output in the order generated by the + * native DTrace library, as determined by the {@link + * Option#aggsortkey}, {@link Option#aggsortrev}, {@link + * Option#aggsortpos}, and {@link Option#aggsortkeypos} options + */ + public String + getOutput() + { + if (output == null) { + output = outputBuffer.toString(); + outputBuffer = null; + if ((output.length() == 0) && !formatted) { + output = "\n"; + } + } + return output; + } + + /** + * Package level access, called by ProbeData. + * + * @throws NullPointerException if aggregationName is null + * @throws IllegalStateException if this PrintaRecord has an + * aggregation matching the given name and it already has an + * AggregationRecord with the same tuple key as the given record. + */ + void + addRecord(String aggregationName, long aggid, AggregationRecord record) + { + if (formattedStrings == null) { + // printa() format string does not completely specify tuple + return; + } + + aggregationName = Aggregate.filterUnnamedAggregationName( + aggregationName); + Aggregation aggregation = getAggregationImpl(aggregationName); + if (aggregation == null) { + aggregation = new Aggregation(aggregationName, aggid); + aggregations.add(aggregation); + } + try { + aggregation.addRecord(record); + } catch (IllegalArgumentException e) { + Map <Tuple, AggregationRecord> map = aggregation.asMap(); + AggregationRecord r = map.get(record.getTuple()); + // + // The printa() format string may specify the value of the + // aggregating action multiple times. While that changes + // the resulting formatted string associated with the tuple, + // we ignore the attempt to add the redundant record to the + // aggregation. + // + if (!r.equals(record)) { + throw e; + } + } + } + + // + // Called from native code when the tuple is not completely + // specified in the printa() format string. + // + void + invalidate() + { + formattedStrings = null; + aggregations.clear(); + tuples.clear(); + } + + void + addFormattedString(Tuple tuple, String formattedString) + { + if (tuple != null && formattedStrings != null) { + if (formattedStrings.containsKey(tuple)) { + throw new IllegalArgumentException("A formatted string " + + "for tuple " + tuple + " already exists."); + } else { + formattedStrings.put(tuple, formattedString); + tuples.add(tuple); + } + } + outputBuffer.append(formattedString); + } + + /** + * Serialize this {@code PrintaRecord} instance. + * + * @serialData Serialized fields are emitted, followed by the + * formatted output string. + */ + private void + writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + if (output == null) { + s.writeObject(outputBuffer.toString()); + } else { + s.writeObject(output); + } + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + output = (String)s.readObject(); + // make defensive copy + if (aggregations != null) { + List <Aggregation> copy = new ArrayList <Aggregation> + (aggregations.size()); + copy.addAll(aggregations); + aggregations = copy; + } + if (formattedStrings != null) { + formattedStrings = new HashMap <Tuple, String> (formattedStrings); + } + if (tuples != null) { + List <Tuple> copy = new ArrayList <Tuple> (tuples.size()); + copy.addAll(tuples); + tuples = copy; + } + // check constructor invariants only after defensize copy + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this instance useful for logging + * and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(PrintaRecord.class.getName()); + buf.append("[snaptime = "); + buf.append(snaptime); + buf.append(", aggregations = "); + buf.append(aggregations); + buf.append(", formattedStrings = "); + buf.append(formattedStrings); + buf.append(", tuples = "); + buf.append(tuples); + buf.append(", output = "); + buf.append(getOutput()); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintfRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintfRecord.java new file mode 100644 index 0000000000..d7f5f45db6 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/PrintfRecord.java @@ -0,0 +1,221 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; +import java.util.*; + +/** + * A formatted string generated by the DTrace {@code printf()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class PrintfRecord implements Record, Serializable { + static final long serialVersionUID = 727237355963977675L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(PrintfRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"records", "formattedString"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private List <ValueRecord> records; + /** @serial */ + private String formattedString; + + // package-level access, called by ProbeData + PrintfRecord() + { + records = new ArrayList <ValueRecord> (); + } + + /** + * Creates a record with the unformatted elements passed to the + * DTrace {@code printf()} action and the resulting formatted + * output. Supports XML persistence. + * + * @param v variable number of unformatted elements passed to the + * DTrace {@code printf()} action + * @param s formatted {@code printf()} output + * @throws NullPointerException if the given list or any of its + * elements is {@code null}, or if the given formatted string is + * {@code null} + */ + public + PrintfRecord(List <ValueRecord> v, String s) + { + formattedString = s; + records = new ArrayList <ValueRecord> (v.size()); + records.addAll(v); + validate(); + } + + private void + validate() + { + if (formattedString == null) { + throw new NullPointerException("formatted string is null"); + } + if (records == null) { + throw new NullPointerException("list of format args is null"); + } + for (ValueRecord r : records) { + if (r == null) { + throw new NullPointerException("format arg is null"); + } + } + } + + /** + * Called by ProbeData code to populate record list. + * + * @throws NullPointerException if o is null + */ + void + addUnformattedElement(Object o) + { + records.add(new ScalarRecord(o)); + } + + /** + * Gets the formatted string output of the DTrace {@code printf()} + * action. + * + * @return non-null formatted string output of the DTrace {@code + * printf()} action + */ + public String + getFormattedString() + { + return formattedString; + } + + /** + * Package level access; called by ProbeData + */ + void + setFormattedString(String s) + { + if (s == null) { + throw new NullPointerException("formatted string is null"); + } + formattedString = s; + } + + /** + * Gets the unfomatted elements passed to the DTrace {@code + * printf()} action after the format string. + * + * @return non-null, unmodifiable list of unformatted elements + * passed to the DTrace {@code printf()} action that generated this + * record, in the order they appear in the argument list after the + * format string + */ + public List <ValueRecord> + getRecords() + { + return Collections.unmodifiableList(records); + } + + /** + * Gets the number of DTrace {@code printf()} unformatted elements + * (arguments following the format string). For example, the + * following action + * <pre><code> + * printf("%s %d\n", "cat", 9); + * </code></pre> + * generates a {@code PrintfRecord} with a record count of two. + * + * @return the number of unformatted elements passed to the DTrace + * {@code printf()} action that generated this record. + */ + public int + getRecordCount() + { + return records.size(); + } + + /** + * Gets the unformatted element passed to the DTrace {@code + * printf()} action at the given offset in the {@code printf()} + * argument list after the format string, starting at offset zero + * for the first unformatted element. + * + * @return non-null record representing the unformatted {@code + * printf()} element at the given index (using the same order that + * they appear in the {@code printf()} argument list) + * @throws ArrayIndexOutOfBoundsException if the given index is + * out of range (index < 0 || index >= getRecordCount()) + */ + public ValueRecord + getRecord(int i) + { + return records.get(i); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Defensively copy record list before validating + if (records == null) { + throw new InvalidObjectException("record list is null"); + } + List <ValueRecord> copy = new ArrayList <ValueRecord> (records.size()); + copy.addAll(records); + records = copy; + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the formatted string output of the DTrace {@code printf()} + * action. + */ + public String + toString() + { + return formattedString; + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Probe.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Probe.java new file mode 100644 index 0000000000..9d80b7d2b6 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Probe.java @@ -0,0 +1,201 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A {@link ProbeDescription} identifying a single probe combined with + * information about the identified probe. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#listProbes(ProbeDescription filter) + * @see Consumer#listProgramProbes(Program program) + * + * @author Tom Erickson + */ +public final class Probe implements Serializable { + static final long serialVersionUID = 8917481979541675727L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Probe.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"description", "info"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final ProbeDescription description; + /** @serial */ + private final ProbeInfo info; + + /** + * Creates a {@code Probe} instance with the given identifying + * description and associated probe information. Supports XML + * persistence. + * + * @param probeDescription probe description that identifies a + * single DTrace probe + * @param probeInfo information about the identified probe, {@code + * null} indicating that the information could not be obtained + * @throws NullPointerException if the given probe description is + * {@code null} + */ + public + Probe(ProbeDescription probeDescription, ProbeInfo probeInfo) + { + description = probeDescription; + info = probeInfo; + validate(); + } + + private void + validate() + { + if (description == null) { + throw new NullPointerException("description is null"); + } + } + + /** + * Gets the probe description identifying a single probe described + * by this instance. + * + * @return non-null probe description matching a single probe on the + * system + */ + public ProbeDescription + getDescription() + { + return description; + } + + /** + * Gets information including attributes and argument types of the + * probe identified by {@link #getDescription()}. + * + * @return information about the probe identified by {@link + * #getDescription()}, or {@code null} if that information could not + * be obtained for any reason + */ + public ProbeInfo + getInfo() + { + return info; + } + + /** + * Compares the specified object with this {@code Probe} instance + * for equality. Defines equality as having the same probe + * description. + * + * @return {@code true} if and only if the specified object is also + * a {@code Probe} and both instances return equal values from + * {@link #getDescription()}. + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof Probe) { + Probe p = (Probe)o; + return description.equals(p.description); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return description.hashCode(); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Returns a string representation of this {@code Probe} useful for + * logging and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Probe.class.getName()); + buf.append("[description = "); + buf.append(description); + buf.append(", info = "); + buf.append(info); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeData.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeData.java new file mode 100644 index 0000000000..a8978be334 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeData.java @@ -0,0 +1,693 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * Data generated when a DTrace probe fires, contains one record for + * every record-generating action in the probe. (Some D actions, such + * as {@code clear()}, do not generate a {@code ProbeData} record.) A + * {@link Consumer} gets data from DTrace by registering a {@link + * ConsumerListener listener} to get probe data whenever a probe fires: + * <pre><code> + * Consumer consumer = new LocalConsumer(); + * consumer.addConsumerListener(new ConsumerAdapter() { + * public void dataReceived(DataEvent e) { + * ProbeData probeData = e.getProbeData(); + * System.out.println(probeData); + * } + * }); + * </code></pre> + * Getting DTrace to generate that probe data involves compiling, + * enabling, and running a D program: + * <pre><code> + * try { + * consumer.open(); + * consumer.compile(program); + * consumer.enable(); // instruments code at matching probe points + * consumer.go(); // non-blocking; generates probe data in background + * } catch (DTraceException e) { + * e.printStackTrace(); + * } + * </code></pre> + * Currently the {@code ProbeData} instance does not record a timestamp. + * If you need a timestamp, trace the built-in {@code timestamp} + * variable in your D program. (See the + * <a href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlfv?a=view> + * <b>Built-in Variables</b></a> section of the <b>Variables</b> chapter of + * the <i>Solaris Dynamic Tracing Guide</i>). + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#addConsumerListener(ConsumerListener l) + * @see ConsumerListener#dataReceived(DataEvent e) + * + * @author Tom Erickson + */ +public final class ProbeData implements Serializable, Comparable <ProbeData> { + static final long serialVersionUID = -7021504416192099215L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProbeData.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"enabledProbeID", "CPU", + "enabledProbeDescription", "flow", "records"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + private static Comparator <ProbeData> DEFAULT_CMP; + + static { + try { + DEFAULT_CMP = ProbeData.getComparator(KeyField.RECORDS, + KeyField.EPID); + } catch (Throwable e) { + e.printStackTrace(); + System.exit(1); + } + } + + /** @serial */ + private int epid; + /** @serial */ + private int cpu; + /** @serial */ + private ProbeDescription enabledProbeDescription; + /** @serial */ + private Flow flow; + // Scratch data, one element per native probedata->dtpda_edesc->dtepd_nrecs + // element, cleared after records list is fully populated. + private transient List <Object> nativeElements; + /** @serial */ + private List <Record> records; + + /** + * Enumerates the fields by which {@link ProbeData} may be sorted + * using the {@link #getComparator(KeyField[] f) getComparator()} + * convenience method. + */ + public enum KeyField { + /** Specifies {@link ProbeData#getCPU()} */ + CPU, + /** Specifies {@link ProbeData#getEnabledProbeDescription()} */ + PROBE, + /** Specifies {@link ProbeData#getEnabledProbeID()} */ + EPID, + /** Specifies {@link ProbeData#getRecords()} */ + RECORDS + } + + /** + * Called by native code. + */ + private + ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, + Flow f, int nativeElementCount) + { + epid = enabledProbeID; + cpu = cpuID; + enabledProbeDescription = p; + flow = f; + nativeElements = new ArrayList <Object> (nativeElementCount); + records = new ArrayList <Record> (); + validate(); + } + + /** + * Creates a probe data instance with the given properties and list + * of records. Supports XML persistence. + * + * @param enabledProbeID identifies the enabled probe that fired; + * the ID is generated by the native DTrace library to distinguish + * all probes enabled by the source consumer (as opposed to + * all probes on the system) + * @param cpuID non-negative ID, identifies the CPU on which the + * probe fired + * @param p identifies the enabled probe that fired + * @param f current state of control flow (entry or return and depth + * in call stack) at time of probe firing, included if {@link + * Option#flowindent flowindent} option used, {@code null} otherwise + * @param recordList list of records generated by D actions in the + * probe that fired, one record per action, may be empty + * @throws NullPointerException if the given probe description or + * list of records is {@code null} + */ + public + ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, + Flow f, List <Record> recordList) + { + epid = enabledProbeID; + cpu = cpuID; + enabledProbeDescription = p; + flow = f; + records = new ArrayList <Record> (recordList.size()); + records.addAll(recordList); + validate(); + } + + private void + validate() + { + if (enabledProbeDescription == null) { + throw new NullPointerException( + "enabled probe description is null"); + } + if (records == null) { + throw new NullPointerException("record list is null"); + } + } + + private void + addDataElement(Object o) + { + nativeElements.add(o); + } + + /** + * Called by native code. + */ + private void + addRecord(Record record) + { + records.add(record); + } + + /** + * Called by native code. + */ + private void + addTraceRecord(int i) + { + // trace() value is preceded by one null for every D program + // statement preceding trace() that is not a D action, such as + // assignment to a variable (results in a native probedata + // record with no data). + int len = nativeElements.size(); + Object o = null; + for (; ((o = nativeElements.get(i)) == null) && (i < len); ++i); + records.add(new ScalarRecord(o)); + } + + /** + * Called by native code. + */ + private void + addStackRecord(int i, String framesString) + { + int len = nativeElements.size(); + Object o = null; + for (; ((o = nativeElements.get(i)) == null) && (i < len); ++i); + StackValueRecord stack = (StackValueRecord)o; + StackFrame[] frames = KernelStackRecord.parse(framesString); + if (stack instanceof KernelStackRecord) { + ((KernelStackRecord)stack).setStackFrames(frames); + } else if (stack instanceof UserStackRecord) { + ((UserStackRecord)stack).setStackFrames(frames); + } else { + throw new IllegalStateException("no stack record at index " + i); + } + records.add(stack); + } + + /** + * Called by native code. + */ + private void + addPrintfRecord() + { + records.add(new PrintfRecord()); + } + + /** + * Called by native code. + */ + private void + addPrintaRecord(long snaptimeNanos, boolean isFormatString) + { + records.add(new PrintaRecord(snaptimeNanos, isFormatString)); + } + + private PrintaRecord + getLastPrinta() + { + ListIterator <Record> itr = records.listIterator(records.size()); + PrintaRecord printa = null; + Record record; + while (itr.hasPrevious() && (printa == null)) { + record = itr.previous(); + if (record instanceof PrintaRecord) { + printa = (PrintaRecord)record; + } + } + return printa; + } + + /** + * Called by native code. + */ + private void + addAggregationRecord(String aggregationName, long aggid, + AggregationRecord rec) + { + PrintaRecord printa = getLastPrinta(); + if (printa == null) { + throw new IllegalStateException( + "No PrintaRecord in this ProbeData"); + } + printa.addRecord(aggregationName, aggid, rec); + } + + /** + * Called by native code. + */ + private void + invalidatePrintaRecord() + { + PrintaRecord printa = getLastPrinta(); + if (printa == null) { + throw new IllegalStateException( + "No PrintaRecord in this ProbeData"); + } + printa.invalidate(); + } + + /** + * Called by native code. + */ + private void + addPrintaFormattedString(Tuple tuple, String s) + { + PrintaRecord printa = getLastPrinta(); + if (printa == null) { + throw new IllegalStateException( + "No PrintaRecord in this ProbeData"); + } + printa.addFormattedString(tuple, s); + } + + /** + * Called by native code. + */ + private void + addExitRecord(int i) + { + int len = nativeElements.size(); + Object o = null; + for (; ((o = nativeElements.get(i)) == null) && (i < len); ++i); + Integer exitStatus = (Integer)o; + records.add(new ExitRecord(exitStatus)); + } + + /** + * Called by native code. Attaches native probedata elements cached + * between the given first index and last index inclusive to the most + * recently added record if applicable. + */ + private void + attachRecordElements(int first, int last) + { + Record record = records.get(records.size() - 1); + if (record instanceof PrintfRecord) { + PrintfRecord printf = (PrintfRecord)record; + Object e; + for (int i = first; i <= last; ++i) { + e = nativeElements.get(i); + if (e == null) { + // printf() unformatted elements are preceded by one + // null for every D program statement preceding the + // printf() that is not a D action, such as + // assignment to a variable (generates a probedata + // record with no data). + continue; + } + printf.addUnformattedElement(e); + } + } + } + + /** + * Called by native code. + */ + void + clearNativeElements() + { + nativeElements = null; + } + + /** + * Called by native code. + */ + private void + setFormattedString(String s) + { + Record record = records.get(records.size() - 1); + if (record instanceof PrintfRecord) { + PrintfRecord printf = (PrintfRecord)record; + printf.setFormattedString(s); + } + } + + /** + * Convenience method, gets a comparator that sorts multiple {@link + * ProbeDescription} instances by the specified field or fields. If + * more than one sort field is specified, the probe data are sorted + * by the first field, and in case of a tie, by the second field, + * and so on, in the order that the fields are specified. + * + * @param f field specifiers given in descending order of sort + * priority; lower priority fields are only compared (as a tie + * breaker) when all higher priority fields are equal + * @return non-null probe data comparator that sorts by the + * specified sort fields in the given order + */ + public static Comparator <ProbeData> + getComparator(KeyField ... f) + { + return new Cmp(f); + } + + private static class Cmp implements Comparator <ProbeData> { + private KeyField[] sortFields; + + private + Cmp(KeyField ... f) + { + sortFields = f; + } + + public int + compare(ProbeData d1, ProbeData d2) + { + return ProbeData.compare(d1, d2, sortFields); + } + } + + /** + * @throws ClassCastException if records or their data are are not + * mutually comparable + */ + @SuppressWarnings("unchecked") + private static int + compareRecords(Record r1, Record r2) + { + int cmp; + if (r1 instanceof ScalarRecord) { + ScalarRecord t1 = ScalarRecord.class.cast(r1); + ScalarRecord t2 = ScalarRecord.class.cast(r2); + Comparable v1 = Comparable.class.cast(t1.getValue()); + Comparable v2 = Comparable.class.cast(t2.getValue()); + cmp = v1.compareTo(v2); + } else if (r1 instanceof PrintfRecord) { + PrintfRecord t1 = PrintfRecord.class.cast(r1); + PrintfRecord t2 = PrintfRecord.class.cast(r2); + String s1 = t1.toString(); + String s2 = t2.toString(); + cmp = s1.compareTo(s2); + } else if (r1 instanceof ExitRecord) { + ExitRecord e1 = ExitRecord.class.cast(r1); + ExitRecord e2 = ExitRecord.class.cast(r2); + int status1 = e1.getStatus(); + int status2 = e2.getStatus(); + cmp = (status1 < status2 ? -1 : (status1 > status2 ? 1 : 0)); + } else { + throw new IllegalArgumentException("Unexpected record type: " + + r1.getClass()); + } + + return cmp; + } + + /** + * @throws ClassCastException if lists are not mutually comparable + * because corresponding list elements are not comparable or the + * list themselves are different lengths + */ + private static int + compareRecordLists(ProbeData d1, ProbeData d2) + { + List <Record> list1 = d1.getRecords(); + List <Record> list2 = d2.getRecords(); + int len1 = list1.size(); + int len2 = list2.size(); + if (len1 != len2) { + throw new ClassCastException("Record lists of different " + + "length are not comparable (lengths are " + + len1 + " and " + len2 + ")."); + } + + int cmp; + Record r1; + Record r2; + + for (int i = 0; (i < len1) && (i < len2); ++i) { + r1 = list1.get(i); + r2 = list2.get(i); + + cmp = compareRecords(r1, r2); + if (cmp != 0) { + return cmp; + } + } + + return 0; + } + + private static int + compare(ProbeData d1, ProbeData d2, KeyField[] comparedFields) + { + int cmp; + for (KeyField f : comparedFields) { + switch (f) { + case CPU: + int cpu1 = d1.getCPU(); + int cpu2 = d2.getCPU(); + cmp = (cpu1 < cpu2 ? -1 : (cpu1 > cpu2 ? 1 : 0)); + break; + case PROBE: + ProbeDescription p1 = d1.getEnabledProbeDescription(); + ProbeDescription p2 = d2.getEnabledProbeDescription(); + cmp = p1.compareTo(p2); + break; + case EPID: + int epid1 = d1.getEnabledProbeID(); + int epid2 = d2.getEnabledProbeID(); + cmp = (epid1 < epid2 ? -1 : (epid1 > epid2 ? 1 : 0)); + break; + case RECORDS: + cmp = compareRecordLists(d1, d2); + break; + default: + throw new IllegalArgumentException( + "Unexpected sort field " + f); + } + + if (cmp != 0) { + return cmp; + } + } + + return 0; + } + + /** + * Gets the enabled probe ID. Identifies the enabled probe that + * fired and generated this {@code ProbeData}. The "epid" is + * different from {@link ProbeDescription#getID()} in that it + * identifies a probe among all probes enabled by the source {@link + * Consumer}, rather than among all the probes on the system. + * + * @return the enabled probe ID generated by the native DTrace + * library + */ + public int + getEnabledProbeID() + { + return epid; + } + + /** + * Gets the ID of the CPU on which the probe fired. + * + * @return ID of the CPU on which the probe fired + */ + public int + getCPU() + { + return cpu; + } + + /** + * Gets the enabled probe description. Identifies the enabled probe + * that fired and generated this {@code ProbeData}. + * + * @return non-null probe description + */ + public ProbeDescription + getEnabledProbeDescription() + { + return enabledProbeDescription; + } + + /** + * Gets the current state of control flow (function entry or return, + * and depth in call stack) at the time of the probe firing that + * generated this {@code ProbeData} instance, or {@code null} if + * such information was not requested with the {@code flowindent} + * option. + * + * @return a description of control flow across function boundaries, + * or {@code null} if {@code Consumer.getOption(Option.flowindent)} + * returns {@link Option#UNSET} + * @see Consumer#setOption(String option) + * @see Option#flowindent + */ + public Flow + getFlow() + { + return flow; + } + + /** + * Gets the records generated by the actions of the probe that + * fired, in the same order as the actions that generated the + * records. The returned list includes one record for every + * record-generating D action (some D actions, such as {@code + * clear()}, do not generate records). + * + * @return non-null, unmodifiable list view of the records belonging + * to this {@code ProbeData} in the order of the actions in the + * DTrace probe that generated them (record-producing actions are + * generally those that produce output, such as {@code printf()}, + * but also the {@code exit()} action) + */ + public List <Record> + getRecords() + { + return Collections.unmodifiableList(records); + } + + /** + * Natural ordering of probe data. Sorts probe data by records + * first, then if record data is equal, by enabled probe ID. + * + * @param d probe data to be compared with this probe data + * @return a negative number, zero, or a positive number as this + * probe data is less than, equal to, or greater than the given + * probe data + * @see ProbeData#getComparator(KeyField[] f) + * @throws NullPointerException if the given probe data is + * {@code null} + * @throws ClassCastException if record lists of both {@code + * ProbeData} instances are not mutually comparable because + * corresponding list elements are not comparable or the lists + * themselves are different lengths + */ + public int + compareTo(ProbeData d) + { + return DEFAULT_CMP.compare(this, d); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Defensively copy record list _before_ validating. + int len = records.size(); + ArrayList <Record> copy = new ArrayList <Record> (len); + copy.addAll(records); + records = copy; + // Check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this {@code ProbeData} instance + * useful for logging and not intended for display. The exact + * details of the representation are unspecified and subject to + * change, but the following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProbeData.class.getName()); + buf.append("[epid = "); + buf.append(epid); + buf.append(", cpu = "); + buf.append(cpu); + buf.append(", enabledProbeDescription = "); + buf.append(enabledProbeDescription); + buf.append(", flow = "); + buf.append(flow); + buf.append(", records = "); + + Record record; + Object value; + buf.append('['); + for (int i = 0; i < records.size(); ++i) { + if (i > 0) { + buf.append(", "); + } + record = records.get(i); + if (record instanceof ValueRecord) { + value = ((ValueRecord)record).getValue(); + if (value instanceof String) { + buf.append("\""); + buf.append((String)value); + buf.append("\""); + } else { + buf.append(record); + } + } else { + buf.append(record); + } + } + buf.append(']'); + + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeDescription.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeDescription.java new file mode 100644 index 0000000000..99928b6c9e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeDescription.java @@ -0,0 +1,467 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.text.ParseException; +import java.io.*; +import java.beans.*; + +/** + * A DTrace probe description consists of provider, module, function, + * and name. A single probe description may identify a single DTrace + * probe or match multiple probes. Any field may be wildcarded by + * omission (set to null) or set to a glob-style pattern: + * <pre> + * * Matches any string, including the null string + * ? Matches any single character + * [ ... ] Matches any one of the enclosed characters. A pair of + * characters separated by - matches any character + * between the pair, inclusive. If the first + * character after the [ is !, any character not + * enclosed in the set is matched. + * \ Interpret the next character as itself, without any + * special meaning + * </pre> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#listProbes(ProbeDescription filter) + * + * @author Tom Erickson + */ +public final class ProbeDescription implements Serializable, + Comparable <ProbeDescription> +{ + static final long serialVersionUID = 5978023304364513667L; + + /** + * Instance with empty provider, module, function, and name fields + * matches all DTrace probes on a system. + */ + public static final ProbeDescription EMPTY = + new ProbeDescription(null, null, null, null); + + private static final int ID_NONE = -1; + + /** + * Enumerates the provider, module, function, and name fields of a + * probe description. + */ + public enum Spec { + /** Probe provider */ + PROVIDER, + /** Probe module */ + MODULE, + /** Probe function */ + FUNCTION, + /** Probe name (unqualified) */ + NAME + }; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProbeDescription.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"id", "provider", "module", + "function", "name"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private int id = ID_NONE; // set by native code + + /** @serial */ + private final String provider; + /** @serial */ + private final String module; + /** @serial */ + private final String function; + /** @serial */ + private final String name; + + /** + * Creates a probe description that specifies only the unqualified + * probe name. + * + * @see ProbeDescription#ProbeDescription(String probeProvider, + * String probeModule, String probeFunction, String probeName) + */ + public + ProbeDescription(String probeName) + { + this(null, null, null, probeName); + } + + /** + * Creates a probe description that specifies the probe name + * qualified only by the function name. + * + * @see ProbeDescription#ProbeDescription(String probeProvider, + * String probeModule, String probeFunction, String probeName) + */ + public + ProbeDescription(String probeFunction, String probeName) + { + this(null, null, probeFunction, probeName); + } + + /** + * Creates a probe description that specifies the probe name + * qualified by the function name and module name. + * + * @see ProbeDescription#ProbeDescription(String probeProvider, + * String probeModule, String probeFunction, String probeName) + */ + public + ProbeDescription(String probeModule, String probeFunction, + String probeName) + { + this(null, probeModule, probeFunction, probeName); + } + + /** + * Creates a fully qualified probe description. If no pattern + * syntax is used and no field is omitted, the resulting description + * matches at most one DTrace probe. + * + * @param probeProvider provider name, may be null or empty to match + * all providers or use pattern syntax to match multiple providers + * @param probeModule module name, may be null or empty to match all + * modules or use pattern syntax to match multiple modules + * @param probeFunction function name, may be null or empty to match + * all functions or use pattern syntax to match multiple functions + * @param probeName unqualified probe name, may be null or empty to + * match all names or use pattern syntax to match multiple names + */ + public + ProbeDescription(String probeProvider, + String probeModule, + String probeFunction, + String probeName) + { + provider = ((probeProvider == null) ? "" : probeProvider); + module = ((probeModule == null) ? "" : probeModule); + function = ((probeFunction == null) ? "" : probeFunction); + name = ((probeName == null) ? "" : probeName); + } + + /** + * Supports XML persistence. + */ + public + ProbeDescription(int probeID, + String probeProvider, + String probeModule, + String probeFunction, + String probeName) + { + this(probeProvider, probeModule, probeFunction, probeName); + id = probeID; + } + + /** + * Generates a probe description from a string in the same format + * returned by {@link #toString()}. Parses the string from right to + * left. + * <pre><code> + * <i>provider:module:function:name</i> + * </code></pre> + * + * @return non-null probe description + * @throws ParseException if {@code s} does not have the expected + * format. The error offset is the index of the first unexpected + * character encountered starting from the last character and + * reading backwards. + * @throws NullPointerException if the given string is {@code null} + */ + public static ProbeDescription + parse(String s) throws ParseException + { + ProbeDescription p; + + // StringTokenizer and String.split() do not correctly handle + // the case of consecutive delimiters + List <String> list = new ArrayList <String> (); + int len = s.length(); + int npos = len; + char ch; + for (int i = (len - 1); i >= 0; --i) { + ch = s.charAt(i); + if (ch == ':') { + list.add(0, s.substring((i + 1), npos)); + npos = i; + } + } + list.add(0, s.substring(0, npos)); + + switch (list.size()) { + case 0: + p = EMPTY; + break; + case 1: + p = new ProbeDescription(list.get(0)); + break; + case 2: + p = new ProbeDescription(list.get(0), list.get(1)); + break; + case 3: + p = new ProbeDescription(list.get(0), list.get(1), + list.get(2)); + break; + case 4: + p = new ProbeDescription(list.get(0), list.get(1), + list.get(2), list.get(3)); + break; + default: + // get error offset (parsing right-to-left) + int offset = (s.length() - 4); + len = list.size(); + for (int i = (len - 1); i >= (len - 4); --i) { + offset -= list.get(i).length(); + } + throw new ParseException("Overspecified probe " + + "description: \"" + s + "\"", offset); + } + return p; + } + + /** + * Gets the probe ID. + * + * @return ID generated from a sequence by the native DTrace + * library, identifies the probe among all probes on the system + */ + public int + getID() + { + return id; + } + + /** + * Gets the provider name. + * + * @return non-null provider name, may be an empty string to + * indicate omission + */ + public String + getProvider() + { + return provider; + } + + /** + * Gets the module name. + * + * @return non-null module name, may be an empty string to indicate + * omission + */ + public String + getModule() + { + return module; + } + + /** + * Gets the function name. + * + * @return non-null function name, may be an empty string to + * indicate omission + */ + public String + getFunction() + { + return function; + } + + /** + * Gets the unqualified probe name. + * + * @return non-null probe name, may be an empty string to indicate + * omission + */ + public String + getName() + { + return name; + } + + /** + * Returns {@code true} if provider, module, function, and name are + * all omitted. An empty probe description matches all DTrace + * probes on a system. + * + * @return {@code true} if all probe fields are omitted, {@code + * false} otherwise + */ + public boolean + isEmpty() + { + if (provider.length() > 0) { + return false; + } + if (module.length() > 0) { + return false; + } + if (function.length() > 0) { + return false; + } + if (name.length() > 0) { + return false; + } + return true; + } + + /** + * Compares the specified object with this probe description for + * equality. Defines equality as having the same fields. Omitted + * fields must be omitted in both instances in order for them to be + * equal, but it makes no difference whether {@code null} or empty + * string was used to indicate omission. + * + * @return {@code true} if and only if all corresponding fields of + * both probe descriptions are either both omitted (null or empty) + * or else equal as defined by {@link String#equals(Object o) + * String.equals()} + */ + public boolean + equals(Object o) + { + if (o instanceof ProbeDescription) { + ProbeDescription p = (ProbeDescription)o; + if ((id == ID_NONE) || (p.id == ID_NONE)) { + return (compareTo(p) == 0); + } else { + return (id == p.id); + } + } + + return false; + } + + /** + * Defines the natural ordering of probe descriptions. Returns the + * natural ordering of the first unequal pair of corresponding + * fields (starting with the provider and continuing to the + * unqualified name only if all other fields are equal). + * Corresponding fields are equal if they are both omitted or both + * equal as defined by {@link String#equals(Object o) + * String.equals()}. It makes no difference if {@code null} or + * empty string is used to indicate omission. The behavior is + * consistent with the {@link #equals(Object o) equals()} method. + * + * @return -1, 0, or 1 as this probe description is less than, equal + * to, or greater than the given probe description + */ + public int + compareTo(ProbeDescription p) + { + int cmp = 0; + cmp = provider.compareTo(p.provider); + if (cmp == 0) { + cmp = module.compareTo(p.module); + if (cmp == 0) { + cmp = function.compareTo(p.function); + if (cmp == 0) { + cmp = name.compareTo(p.name); + } + } + } + return (cmp); + } + + /** + * Overridden to ensure that equal probe descriptions have equal + * hashcodes. + */ + @Override + public int + hashCode() + { + int hash = id; + if (hash != ID_NONE) { + return hash; + } + + hash = 17; + hash = (37 * hash) + provider.hashCode(); + hash = (37 * hash) + module.hashCode(); + hash = (37 * hash) + function.hashCode(); + hash = (37 * hash) + name.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + if (provider == null) { + throw new InvalidObjectException("provider is null"); + } + if (module == null) { + throw new InvalidObjectException("module is null"); + } + if (function == null) { + throw new InvalidObjectException("function is null"); + } + if (name == null) { + throw new InvalidObjectException("name is null"); + } + } + + /** + * Gets the string representation of this probe description. The + * format is as follows: + * <pre><code> + * <i>provider:module:function:name</i> + * </code></pre> + * Individual fields may be empty, but none of the three delimiting + * colons is ever omitted. If this instance uses pattern matching + * syntax to match multiple probes, that syntax is preserved in the + * string representation. + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(provider); + buf.append(':'); + buf.append(module); + buf.append(':'); + buf.append(function); + buf.append(':'); + buf.append(name); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeInfo.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeInfo.java new file mode 100644 index 0000000000..3d88c1f205 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeInfo.java @@ -0,0 +1,207 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * Probe stability information. Does not identify a probe, but gives + * information about a single probe identified by a {@link + * ProbeDescription}. A {@code ProbeDescription} can match multiple + * probes using pattern syntax (globbing) and wildcarding (field + * omission), but it does not normally make sense to associate a {@code + * ProbeInfo} with a {@code ProbeDescription} unless that description + * matches exactly one probe on the system. A {@link Probe} pairs a + * {@code ProbeDescription} with information about the DTrace probe it + * identifies. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#listProbeDetail(ProbeDescription filter) + * @see Consumer#listProgramProbeDetail(Program program) + * + * @author Tom Erickson + */ +public final class ProbeInfo implements Serializable { + static final long serialVersionUID = 1057402669978245904L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProbeInfo.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"probeAttributes", + "argumentAttributes"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final InterfaceAttributes probeAttributes; + /** @serial */ + private final InterfaceAttributes argumentAttributes; + + /** + * Creates a {@code ProbeInfo} instance from the given attributes. + * Supports XML persistence. + * + * @throws NullPointerException if any parameter is null + */ + public + ProbeInfo(InterfaceAttributes singleProbeAttributes, + InterfaceAttributes argAttributes) + { + probeAttributes = singleProbeAttributes; + argumentAttributes = argAttributes; + validate(); + } + + private void + validate() + { + if (probeAttributes == null) { + throw new NullPointerException("probeAttributes is null"); + } + if (argumentAttributes == null) { + throw new NullPointerException("argumentAttributes is null"); + } + } + + /** + * Gets the interface attributes of a probe. + * + * @return non-null attributes including stability levels and + * dependency class + */ + public InterfaceAttributes + getProbeAttributes() + { + return probeAttributes; + } + + /** + * Gets the interface attributes of the arguments to a probe. + * + * @return non-null attributes including stability levels and + * dependency class of the arguments to a probe + */ + public InterfaceAttributes + getArgumentAttributes() + { + return argumentAttributes; + } + + /** + * Compares the specified object with this {@code ProbeInfo} + * instance for equality. Defines equality as having equal probe + * attributes and equal argument attributes. + * + * @return {@code true} if and only if the specified object is also + * a {@code ProbeInfo} and both instances have the same attributes + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof ProbeInfo) { + ProbeInfo i = (ProbeInfo)o; + return (probeAttributes.equals(i.probeAttributes) && + argumentAttributes.equals(i.argumentAttributes)); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + probeAttributes.hashCode(); + hash = (37 * hash) + argumentAttributes.hashCode(); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Must copy before checking class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this {@code ProbeInfo} useful for + * logging and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProbeInfo.class.getName()); + buf.append("[probeAttributes = "); + buf.append(probeAttributes); + buf.append(", argumentAttributes = "); + buf.append(argumentAttributes); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessEvent.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessEvent.java new file mode 100644 index 0000000000..0012464e4c --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessEvent.java @@ -0,0 +1,118 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.EventObject; + +/** + * Notification that the state of a target process designated by {@link + * Consumer#createProcess(String command)} or {@link + * Consumer#grabProcess(int pid)} has changed. + * + * @see ConsumerListener#processStateChanged(ProcessEvent e) + * + * @author Tom Erickson + */ +public class ProcessEvent extends EventObject { + static final long serialVersionUID = -3779443761929558702L; + + /** @serial */ + private ProcessState processState; + + /** + * Creates a {@link ConsumerListener#processStateChanged(ProcessEvent e) + * processStateChanged()} event to notify listeners of a process + * state change. + * + * @param source the {@link Consumer} that is the source of this event + * @throws NullPointerException if the given process state is {@code + * null} + */ + public + ProcessEvent(Object source, ProcessState p) + { + super(source); + processState = p; + validate(); + } + + private void + validate() + { + if (processState == null) { + throw new NullPointerException("process state is null"); + } + } + + /** + * Gets the process state. + * + * @return non-null process state + */ + public ProcessState + getProcessState() + { + return processState; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this event useful for logging and + * not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProcessEvent.class.getName()); + buf.append("[source = "); + buf.append(getSource()); + buf.append(", processState = "); + buf.append(processState); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessState.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessState.java new file mode 100644 index 0000000000..576dcfff8e --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProcessState.java @@ -0,0 +1,358 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * State of a target process designated by {@link + * Consumer#createProcess(String command)} or {@link + * Consumer#grabProcess(int pid)}. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see ConsumerListener#processStateChanged(ProcessEvent e) + * + * @author Tom Erickson + */ +public final class ProcessState implements Serializable { + static final long serialVersionUID = -3395911213431317292L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProcessState.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"processID", "state", + "terminationSignal", "terminationSignalName", + "exitStatus", "message"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + + protected Expression + instantiate(Object oldInstance, Encoder out) + { + ProcessState pstate = (ProcessState)oldInstance; + return new Expression(oldInstance, oldInstance.getClass(), + "new", new Object[] { pstate.getProcessID(), + pstate.getState().name(), + pstate.getTerminationSignal(), + pstate.getTerminationSignalName(), + pstate.getExitStatus(), + pstate.getMessage() }); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * State of a target process. + */ + public enum State { + /** Process is running. */ + RUN, + /** Process is stopped. */ + STOP, + /** Process is lost to control. */ + LOST, + /** Process is terminated (zombie). */ + UNDEAD, + /** Process is terminated (core file). */ + DEAD + } + + /** @serial */ + private int processID; + /** @serial */ + private State state; + /** @serial */ + private int terminationSignal; + /** @serial */ + private String terminationSignalName; + /** @serial */ + private Integer exitStatus; + /** @serial */ + private String message; + + /** + * Creates a {@code ProcessState} instance with the given state. + * + * @param pid non-negative target process ID + * @param processState target process state + * @param processTerminationSignal signal that terminated the target + * process, {@code -1} if the process was not terminated by a signal + * or if the terminating signal is unknown + * @param processTerminationSignalName name of the signal that + * terminated the target process, {@code null} if the process was + * not terminated by a signal or if the terminating signal is + * unknown + * @param processExitStatus target process exit status, {@code null} + * if the process has not exited or the exit status is unknown + * @param msg message included by DTrace, if any + * @throws NullPointerException if the given process state is {@code + * null} + * @throws IllegalArgumentException if the given process ID is negative + */ + public + ProcessState(int pid, State processState, + int processTerminationSignal, + String processTerminationSignalName, + Integer processExitStatus, String msg) + { + processID = pid; + state = processState; + terminationSignal = processTerminationSignal; + terminationSignalName = processTerminationSignalName; + exitStatus = processExitStatus; + message = msg; + validate(); + } + + /** + * Supports XML persistence. + * + * @see #ProcessState(int pid, State processState, int + * processTerminationSignal, String processTerminationSignalName, + * Integer processExitStatus, String msg) + * @throws IllegalArgumentException if there is no {@link + * ProcessState.State} value with the given state name. + */ + public + ProcessState(int pid, String processStateName, + int processTerminationSignal, + String processTerminationSignalName, + Integer processExitStatus, String msg) + { + processID = pid; + state = Enum.valueOf(State.class, processStateName); + terminationSignal = processTerminationSignal; + terminationSignalName = processTerminationSignalName; + exitStatus = processExitStatus; + message = msg; + validate(); + } + + private void + validate() + { + if (processID < 0) { + throw new IllegalArgumentException("pid is negative"); + } + if (state == null) { + throw new NullPointerException("process state is null"); + } + } + + /** + * Gets the process ID. + * + * @return non-negative target process ID + */ + public int + getProcessID() + { + return processID; + } + + /** + * Gets the process state. + * + * @return non-null target process state + */ + public State + getState() + { + return state; + } + + /** + * Gets the signal that terminated the process. + * + * @return termination signal, {@code -1} if the process was not + * terminated by a signal or if the terminating signal is unknown + */ + public int + getTerminationSignal() + { + return terminationSignal; + } + + /** + * Gets the name of the signal that terminated the process. + * + * @return termination signal name, {@code null} if the process was + * not terminated by a signal or if the terminating signal is + * unknown + */ + public String + getTerminationSignalName() + { + return terminationSignalName; + } + + /** + * Gets the process exit status. + * + * @return exit status, or {@code null} if the process has not + * exited or the exit status is unknown + */ + public Integer + getExitStatus() + { + return exitStatus; + } + + /** + * Called by native code. + */ + private void + setExitStatus(int status) + { + exitStatus = new Integer(status); + } + + /** + * Gets the message from DTrace describing this process state. + * + * @return DTrace message, or {@code null} if DTrace did not include + * a message with this process state + */ + public String + getMessage() + { + return message; + } + + /** + * Compares the specified object with this {@code ProcessState} + * instance for equality. Defines equality as having the same + * attributes. + * + * @return {@code true} if and only if the specified object is also + * a {@code ProcessState} and both instances have the same + * attributes + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof ProcessState) { + ProcessState s = (ProcessState)o; + return ((processID == s.processID) && + (state == s.state) && + (terminationSignal == s.terminationSignal) && + ((terminationSignalName == null) ? + (s.terminationSignalName == null) : + terminationSignalName.equals(s.terminationSignalName)) && + ((exitStatus == null) ? + (s.exitStatus == null) : + exitStatus.equals(s.exitStatus)) && + ((message == null) ? (s.message == null) : + message.equals(s.message))); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + processID; + hash = (37 * hash) + state.hashCode(); + hash = (37 * hash) + terminationSignal; + hash = (37 * hash) + (exitStatus == null ? 0 : + exitStatus.hashCode()); + hash = (37 * hash) + (message == null ? 0 : message.hashCode()); + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this process state useful for + * logging and not intended for display. The exact details of the + * representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProcessState.class.getName()); + buf.append("[pid = "); + buf.append(processID); + buf.append(", state = "); + buf.append(state); + buf.append(", terminationSignal = "); + buf.append(terminationSignal); + buf.append(", terminationSignalName = "); + buf.append(terminationSignalName); + buf.append(", exitStatus = "); + buf.append(exitStatus); + buf.append(", message = "); + buf.append(message); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Program.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Program.java new file mode 100644 index 0000000000..5a6ecce0a3 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Program.java @@ -0,0 +1,259 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; + +/** + * Identifies a compiled D program. This identifier is valid only on + * the {@link LocalConsumer} from which it was obtained. Some {@code + * Consumer} methods attach additional {@link ProgramInfo} to this + * identifier. + * <p> + * Not intended for persistence, since it identifies nothing after its + * source {@code LocalConsumer} closes. + * + * @see Consumer#compile(String program, String[] macroArgs) + * @see Consumer#compile(java.io.File program, String[] macroArgs) + * @see Consumer#enable(Program program) + * @see Consumer#getProgramInfo(Program program) + * @see Consumer#listProgramProbes(Program program) + * @see Consumer#listProgramProbeDetail(Program program) + * + * @author Tom Erickson + */ +public class Program implements Serializable { + static final long serialVersionUID = 364989786308628466L; + + /** + * Identifies this program among all of a consumer's programs. Set + * by native code. + * + * @serial + */ + private int id = -1; + + // Set by LocalConsumer.compile() + /** @serial */ + LocalConsumer.Identifier consumerID; + /** @serial */ + String contents; + + /** @serial */ + private ProgramInfo info; + + /** + * Called by native code + */ + private Program() + { + } + + // Called by LocalConsumer.compile() to ensure that only valid + // instances are made accessible to users. Similarly called by + // readObject to ensure that only valid instances are deserialized. + void + validate() + { + if (id < 0) { + throw new IllegalArgumentException("id is negative"); + } + if (consumerID == null) { + throw new NullPointerException("consumer ID is null"); + } + } + + /** + * Gets the full pre-compiled text of the identified program. + * + * @return the {@code String} passed to {@link + * Consumer#compile(String program, String[] macroArgs)}, or the + * contents of the {@code File} passed to {@link + * Consumer#compile(java.io.File program, String[] macroArgs)} + */ + public String + getContents() + { + return contents; + } + + /** + * Gets information about this compiled program provided by {@link + * Consumer#getProgramInfo(Program program)} or {@link + * Consumer#enable(Program program)}. + * + * @return information about this compiled program, or {@code null} + * if this {@code Program} has not been passed to {@link + * Consumer#getProgramInfo(Program program)} or {@link + * Consumer#enable(Program program)} + */ + public ProgramInfo + getInfo() + { + return info; + } + + /** + * Sets additional information about this compiled program, + * including program stability and matching probe count. Several + * {@code Consumer} methods attach such information to a given + * {@code Program} argument. The method is {@code public} to + * support implementations of the {@code Consumer} interface other + * than {@link LocalConsumer}. Although a {@code Program} can only + * be obtained from a {@code LocalConsumer}, other {@code Consumer} + * implemenations may provide a helpful layer of abstraction while + * using a {@code LocalConsumer} internally to compile DTrace + * programs. Users of the API are not otherwise expected to call + * the {@code setInfo()} method directly. + * + * @param programInfo optional additional information about this + * compiled program + * @see #getInfo() + * @see Consumer#enable(Program program) + * @see Consumer#getProgramInfo(Program program) + */ + public void + setInfo(ProgramInfo programInfo) + { + info = programInfo; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the contents of the given file as a string. + * + * @return non-null contents of the given file as a string + * @throws IOException if the method fails to read the contents of + * the given file + */ + static String + getProgramString(java.io.File programFile) throws IOException + { + if (programFile == null) { + return null; + } + + StringBuffer buf = new StringBuffer(); + InputStream in; + in = new BufferedInputStream(new FileInputStream(programFile)); + int i = in.read(); + while (i >= 0) { + buf.append((char)i); + i = in.read(); + } + + String s = buf.toString(); + return s; + } + + /** + * Gets a string representation of this {@code Program} instance + * useful for logging and not intended for display. The exact + * details of the representation are unspecified and subject to + * change, but the following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Program.class.getName()); + buf.append("[contents = "); + buf.append(contents); + buf.append(", info = "); + buf.append(info); + buf.append(']'); + return buf.toString(); + } + + /** + * Identifies a compiled D program, specifically one that has been + * compiled from a file. + */ + public static final class File extends Program { + // Set by LocalConsumer.compile() + /** @serial */ + java.io.File file; + + private + File() + { + } + + // Called by LocalConsumer.compile() to ensure that only valid + // instances are made accessible to users. Similarly called by + // readObject to ensure that only valid instances are deserialized. + void + validate() + { + super.validate(); + if (file == null) { + throw new NullPointerException("file is null"); + } + } + + /** + * Gets the program file. + * + * @return the {@code File} passed to {@link + * Consumer#compile(java.io.File program, String[] macroArgs)} + */ + public java.io.File + getFile() + { + return file; + } + + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(Program.File.class.getName()); + buf.append("[super = "); + buf.append(super.toString()); + buf.append(", file = "); + buf.append(file); + buf.append(']'); + return buf.toString(); + } + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProgramInfo.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProgramInfo.java new file mode 100644 index 0000000000..0de397a804 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProgramInfo.java @@ -0,0 +1,252 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * Information about a {@link Program} including stability and matching + * probe count. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Consumer#getProgramInfo(Program program) + * @see Consumer#enable(Program program) + * + * @author Tom Erickson + */ +public final class ProgramInfo implements Serializable { + static final long serialVersionUID = 663862981171935056L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ProgramInfo.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] { "minimumProbeAttributes", + "minimumStatementAttributes", + "matchingProbeCount" }) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + e.printStackTrace(); + } + } + + /** @serial */ + private final InterfaceAttributes minimumProbeAttributes; + /** @serial */ + private final InterfaceAttributes minimumStatementAttributes; + /** @serial */ + private final int matchingProbeCount; + + /** + * Creates a {@code ProgamInfo} instance with the given properties. + * Supports XML persistence. + * + * @param minProbeAttr minimum stability levels of the + * program probe descriptions + * @param minStatementAttr minimum stability levels of the + * program action statements (including D variables) + * @param matchingProbes non-negative count of probes matching the + * program probe description + * @throws NullPointerException if {@code minProbeAttr} or {@code + * minStatementAttr} is {@code null} + * @throws IllegalArgumentException if {@code matchingProbes} is + * negative + */ + public + ProgramInfo(InterfaceAttributes minProbeAttr, + InterfaceAttributes minStatementAttr, + int matchingProbes) + { + // Called by native code. Any change to this constructor requires a + // similar change in the native invocation. + minimumProbeAttributes = minProbeAttr; + minimumStatementAttributes = minStatementAttr; + matchingProbeCount = matchingProbes; + validate(); + } + + private void + validate() + { + if (minimumProbeAttributes == null) { + throw new NullPointerException("minimumProbeAttributes is null"); + } + if (minimumStatementAttributes == null) { + throw new NullPointerException( + "minimumStatementAttributes is null"); + } + if (matchingProbeCount < 0) { + throw new IllegalArgumentException( + "matchingProbeCount is negative"); + } + } + + /** + * Gets the minimum stability levels of the probe descriptions used + * in a compiled {@link Program}. + * + * @return non-null interface attributes describing the minimum + * stability of the probe descriptions in a D program + */ + public InterfaceAttributes + getMinimumProbeAttributes() + { + return minimumProbeAttributes; + } + + /** + * Gets the minimum stability levels of the action statements + * including D variables used in a compiled {@link Program}. + * + * @return non-null interface attributes describing the minimum + * stability of the action statements (including D variables) in a D + * program + */ + public InterfaceAttributes + getMinimumStatementAttributes() + { + return minimumStatementAttributes; + } + + /** + * Gets the number of DTrace probes that match the probe + * descriptions in a compiled {@link Program}. This count may be + * very high for programs that use {@link ProbeDescription} + * wildcarding (field omission) and globbing (pattern matching + * syntax). + * + * @return non-negative count of probes on the system matching the + * program descriptions in a compiled D program + */ + public int + getMatchingProbeCount() + { + return matchingProbeCount; + } + + /** + * Compares the specified object with this program information for + * equality. Defines equality as having the same information, + * including stability attributes and matching probe counts. + * Different D programs may have equal program information. + * + * @return {@code true} if and only if the specified object is also + * a {@code ProgramInfo} instance and has all the same information + * as this instance + */ + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof ProgramInfo) { + ProgramInfo i = (ProgramInfo)o; + return (minimumProbeAttributes.equals( + i.minimumProbeAttributes) && + minimumStatementAttributes.equals( + i.minimumStatementAttributes) && + (matchingProbeCount == i.matchingProbeCount)); + } + return false; + } + + /** + * Overridden to ensure that equal {@code ProgramInfo} instances + * have equal hashcodes. + */ + @Override + public int + hashCode() + { + int hash = 17; + hash = (37 * hash) + minimumProbeAttributes.hashCode(); + hash = (37 * hash) + minimumStatementAttributes.hashCode(); + hash = (37 * hash) + matchingProbeCount; + return hash; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Check constructor invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets a string representation of this {@code ProgramInfo} useful + * for logging and not intended for display. The exact details of + * the representation are unspecified and subject to change, but the + * following format may be regarded as typical: + * <pre><code> + * class-name[property1 = value1, property2 = value2] + * </code></pre> + */ + @Override + public String + toString() + { + StringBuffer buf = new StringBuffer(); + buf.append(ProgramInfo.class.getName()); + buf.append("[minimumProbeAttributes = "); + buf.append(minimumProbeAttributes); + buf.append(", minimumStatementAttributes = "); + buf.append(minimumStatementAttributes); + buf.append(", matchingProbeCount = "); + buf.append(matchingProbeCount); + buf.append(']'); + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Record.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Record.java new file mode 100644 index 0000000000..ad0444f9e2 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Record.java @@ -0,0 +1,36 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * A data record generated by DTrace. + * + * @author Tom Erickson + */ +public interface Record { +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ResourceLimitException.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ResourceLimitException.java new file mode 100644 index 0000000000..ada7e46513 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ResourceLimitException.java @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * Indicates that the user has requested something directly or + * indirectly that exceeds a configured limit. + * + * @author Tom Erickson + */ +class ResourceLimitException extends RuntimeException { + static final long serialVersionUID = -304127017066919362L; + + /** + * Creates a {@code ResourceLimitException} with the specified + * detail message. + * + * @param message the detail message pertaining to this exception + */ + public + ResourceLimitException(String message) + { + super(message); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ScalarRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ScalarRecord.java new file mode 100644 index 0000000000..2654a07327 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ScalarRecord.java @@ -0,0 +1,327 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.Arrays; +import java.beans.*; + +/** + * A traced D primitive generated by a DTrace action such as {@code + * trace()} or {@code tracemem()}, or else an element in a composite + * value generated by DTrace. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class ScalarRecord implements ValueRecord, Serializable { + static final long serialVersionUID = -34046471695050108L; + static final int RAW_BYTES_INDENT = 5; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(ScalarRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final Object value; + + /** + * Creates a scalar record with the given DTrace primitive. + * + * @param v DTrace primitive data value + * @throws NullPointerException if the given value is null + * @throws ClassCastException if the given value is not a DTrace + * primitive type listed as a possible return value of {@link + * #getValue()} + */ + public + ScalarRecord(Object v) + { + value = v; + validate(); + } + + private void + validate() + { + if (value == null) { + throw new NullPointerException(); + } + // Short-circuit-evaluate common cases first + if (!((value instanceof Number) || + (value instanceof String) || + (value instanceof byte[]))) { + throw new ClassCastException("value is not a D primitive"); + } + } + + /** + * Gets the traced D primitive value of this record. + * + * @return a non-null value whose type is one of the following: + * <ul> + * <li>{@link Number}</li> + * <li>{@link String}</li> + * <li>byte[]</li> + * </ul> + */ + public Object + getValue() + { + return value; + } + + /** + * Compares the specified object with this record for equality. + * Defines equality as having the same value. + * + * @return {@code true} if and only if the specified object is also + * a {@code ScalarRecord} and the values returned by the {@link + * #getValue()} methods of both instances are equal, {@code false} + * otherwise. Values are compared using {@link + * java.lang.Object#equals(Object o) Object.equals()}, unless they + * are arrays of raw bytes, in which case they are compared using + * {@link java.util.Arrays#equals(byte[] a, byte[] a2)}. + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof ScalarRecord) { + ScalarRecord r = (ScalarRecord)o; + if (value instanceof byte[]) { + if (r.value instanceof byte[]) { + byte[] a1 = (byte[])value; + byte[] a2 = (byte[])r.value; + return Arrays.equals(a1, a2); + } + return false; + } + return value.equals(r.value); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hashcodes. + * + * @return {@link java.lang.Object#hashCode()} of {@link + * #getValue()}, or {@link java.util.Arrays#hashCode(byte[] a)} if + * the value is a raw byte array + */ + @Override + public int + hashCode() + { + if (value instanceof byte[]) { + return Arrays.hashCode((byte[])value); + } + return value.hashCode(); + } + + private static final int BYTE_SIGN_BIT = 1 << 7; + + /** + * Static utility for treating a byte as unsigned by converting it + * to int without sign extending. + */ + static int + unsignedByte(byte b) + { + if (b < 0) { + b ^= (byte)BYTE_SIGN_BIT; + return ((int)b) | BYTE_SIGN_BIT; + } + return (int)b; + } + + static String + hexString(int n, int width) + { + String s = Integer.toHexString(n); + int len = s.length(); + if (width < len) { + s = s.substring(len - width); + } else if (width > len) { + s = (spaces(width - len) + s); + } + return s; + } + + static String + spaces(int n) + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < n; ++i) { + buf.append(' '); + } + return buf.toString(); + } + + /** + * Represents a byte array as a table of unsigned byte values in hex, + * 16 per row ending in their corresponding character + * representations (or a period (.) for each unprintable + * character). Uses default indentation. + * + * @see ScalarRecord#rawBytesString(byte[] bytes, int indent) + */ + static String + rawBytesString(byte[] bytes) + { + return rawBytesString(bytes, RAW_BYTES_INDENT); + } + + /** + * Represents a byte array as a table of unsigned byte values in hex, + * 16 per row ending in their corresponding character + * representations (or a period (.) for each unprintable + * character). The table begins and ends with a newline, includes a + * header row, and uses a newline at the end of each row. + * + * @param bytes array of raw bytes treated as unsigned when + * converted to hex display + * @param indent number of spaces to indent each line of the + * returned string + * @return table representation of 16 bytes per row as hex and + * character values + */ + static String + rawBytesString(byte[] bytes, int indent) + { + // ported from libdtrace/common/dt_consume.c dt_print_bytes() + int i, j; + int u; + StringBuffer buf = new StringBuffer(); + String leftMargin = spaces(indent); + buf.append('\n'); + buf.append(leftMargin); + buf.append(" "); + for (i = 0; i < 16; i++) { + buf.append(" "); + buf.append("0123456789abcdef".charAt(i)); + } + buf.append(" 0123456789abcdef\n"); + int nbytes = bytes.length; + String hex; + for (i = 0; i < nbytes; i += 16) { + buf.append(leftMargin); + buf.append(hexString(i, 5)); + buf.append(':'); + + for (j = i; (j < (i + 16)) && (j < nbytes); ++j) { + buf.append(hexString(unsignedByte(bytes[j]), 3)); + } + + while ((j++ % 16) != 0) { + buf.append(" "); + } + + buf.append(" "); + + for (j = i; (j < (i + 16)) && (j < nbytes); ++j) { + u = unsignedByte(bytes[j]); + if ((u < ' ') || (u > '~')) { + buf.append('.'); + } else { + buf.append((char) u); + } + } + + buf.append('\n'); + } + + return buf.toString(); + } + + static String + valueToString(Object value) + { + String s; + if (value instanceof byte[]) { + s = rawBytesString((byte[])value); + } else { + s = value.toString(); + } + return s; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the natural string representation of the traced D primitive. + * + * @return the value of {@link Object#toString} when called on + * {@link #getValue()}; or if the value is an array of raw bytes, a + * table displaying 16 bytes per row in unsigned hex followed by the + * ASCII character representations of those bytes (each unprintable + * character is represented by a period (.)) + */ + public String + toString() + { + return ScalarRecord.valueToString(getValue()); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackFrame.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackFrame.java new file mode 100644 index 0000000000..e18b937f60 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackFrame.java @@ -0,0 +1,162 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.beans.*; + +/** + * A single stack frame in a {@link StackValueRecord}. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class StackFrame implements Serializable { + static final long serialVersionUID = 8617210929132692711L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(StackFrame.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"frame"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private final String frame; + + /** + * Creates a single stack frame. Supports XML persistence. + * + * @param f human-readable string representation of this stack frame + * @throws NullPointerException if the given string representation + * is {@code null} + */ + public + StackFrame(String f) + { + frame = f; + validate(); + } + + private void + validate() + { + if (frame == null) { + throw new NullPointerException("frame is null"); + } + } + + /** + * Gets the human-readable string representation of this stack + * frame. Supports XML persistence. + * + * @return the human-readable string representation of this stack frame. + */ + public String + getFrame() + { + return frame; + } + + /** + * Compares the specified object with this {@code StackFrame} for + * equality. Returns {@code true} if and only if the specified + * object is also a {@code StackFrame} and both instances have the + * same human-readable string representation. + * + * @return {@code true} if and only if the specified object is also + * a {@code StackFrame} and both instances have the same + * human-readable string representation + */ + @Override + public boolean + equals(Object o) + { + if (o instanceof StackFrame) { + StackFrame s = (StackFrame)o; + return frame.equals(s.frame); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return frame.hashCode(); + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the string representation of this stack frame, in this case + * the same value returned by {@link #getFrame()}. + */ + public String + toString() + { + return frame; + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackValueRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackValueRecord.java new file mode 100644 index 0000000000..682fe6b81c --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/StackValueRecord.java @@ -0,0 +1,104 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; + +/** + * A value generated by the DTrace {@code stack()}, {@code ustack()}, or + * {@code jstack()} action. + * + * @author Tom Erickson + */ +public interface StackValueRecord extends ValueRecord { + /** + * Gets a copy of this record's stack frames, or an empty array if + * this record's raw stack data was not converted to human-readable + * stack frames by DTrace. Raw stack data is not converted (i.e. + * human-readable stack frames are omitted) whenever a {@code + * printa()} format string is specified without including the {@code + * %k} placeholder for the stack value represented by this record. + * (The {@code stack()}, {@code ustack()}, and {@code jstack()} + * actions are all usable as members of an aggregation tuple.) See + * the <a + * href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidli3?a=view> + * <b>{@code printa()}</b></a> section of the <b>Output + * Formatting</b> chapter of the <i>Solaris Dynamic Tracing + * Guide</i> for details about {@code printa()} format strings. + * Human-readable stack frames are generated by default if {@code + * printa()} is called without specifying a format string, or when + * using {@link Consumer#getAggregate()} as an alternative to {@code + * printa()}. They are also generated when {@code stack()}, {@code + * ustack()}, or {@code jstack()} is used as a stand-alone action + * outside of an aggregation tuple. + * <p> + * The returned array is a copy and modifying it has no effect on + * this record. Elements of the returned array are not {@code + * null}. + * + * @return a non-null, possibly empty array of this record's + * human-readable stack frames, none of which are {@code null} + */ + public StackFrame[] getStackFrames(); + + /** + * Gets the native DTrace representation of this record's stack as + * an array of raw bytes. The raw bytes are needed to distinguish + * stacks that have the same string representation but are + * considered distinct by DTrace. Duplicate stacks (stacks with the + * same human-readable stack frames) can have distinct raw stack + * data when program text is relocated. + * <p> + * Implementations of this interface use raw stack data to compute + * {@link Object#equals(Object o) equals()} and {@link + * Object#hashCode() hashCode()}. If the stack belongs to a user + * process, the raw bytes include the process ID. + * + * @return the native DTrace library's internal representation of + * this record's stack as a non-null array of bytes + */ + public byte[] getRawStackData(); + + /** + * Gets the raw bytes used to represent this record's stack value in + * the native DTrace library. + * + * @return {@link #getRawStackData()} + */ + public Object getValue(); + + /** + * Gets a read-only {@code List} view of this record's stack frames. + * The returned list implements {@link java.util.RandomAccess}. It + * is empty if {@link #getStackFrames()} returns an empty array. + * + * @return non-null, unmodifiable {@code List} view of this record's + * stack frames + */ + public List <StackFrame> asList(); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/SumValue.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/SumValue.java new file mode 100644 index 0000000000..256b1c76e0 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/SumValue.java @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.beans.*; + +/** + * A {@code long} value aggregated by the DTrace {@code sum()} action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @see Aggregation + * @author Tom Erickson + */ +public final class SumValue extends AbstractAggregationValue { + static final long serialVersionUID = 4929338907817617943L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(SumValue.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"value"}); + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** + * Creates a value aggregated by the DTrace {@code sum()} action. + * Supports XML persistence. + * + * @param v sum total of the aggregated values + */ + public + SumValue(long v) + { + super(v); + } + + // Needed to support XML persistence since XMLDecoder cannot find + // the public method of the non-public superclass. + + /** + * Gets the sum total of the aggregated values. + * + * @return the sum total of the aggregated values + */ + public Long + getValue() + { + return (Long)super.getValue(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Tuple.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Tuple.java new file mode 100644 index 0000000000..65743e657f --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Tuple.java @@ -0,0 +1,370 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.*; +import java.util.*; +import java.beans.*; +import java.util.*; + +/** + * Multi-element key to a value in an {@link Aggregation}. + * <p> + * Tuple equality is based on the length of each tuple and the equality + * of each corresponding element. The natural ordering of tuples is + * based on a lenient comparison designed not to throw exceptions when + * corresponding elements are not mutually comparable or the number of + * tuple elements differs. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class Tuple implements Serializable, Comparable <Tuple>, + Iterable<ValueRecord> +{ + static final long serialVersionUID = 5192674716869462720L; + + /** + * The empty tuple has zero elements and may be used to obtain the + * singleton {@link AggregationRecord} of a non-keyed {@link + * Aggregation}, such as the one derived from the D statement + * <code>@a = count()</code>. (In D, an aggregation without + * square brackets aggregates a single value.) + */ + public static final Tuple EMPTY = new Tuple(); + + static { + try { + BeanInfo info = Introspector.getBeanInfo(Tuple.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"elements"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + /** @serial */ + private java.util.List <ValueRecord> elements; + + private + Tuple() + { + // + // expected to be a short list (usually one to three elements) + // + elements = new ArrayList <ValueRecord> (4); + } + + /** + * Creates a tuple with the given elements in the given order. + * + * @param tupleElements ordered series of tuple elements + * @throws NullPointerException if the given array or any of its + * elements is {@code null} + */ + public + Tuple(ValueRecord ... tupleElements) + { + this(); + if (tupleElements == null) { + throw new NullPointerException("null array"); + } + for (ValueRecord r : tupleElements) { + if (r == null) { + throw new NullPointerException("null element"); + } + elements.add(r); + } + } + + /** + * Creates a tuple with the given element list in the given list + * order. + * + * @param tupleElements ordered list of tuple elements + * @throws NullPointerException if the given list or any of its + * elements is {@code null} + */ + public + Tuple(List <ValueRecord> tupleElements) + { + this(); + if (tupleElements == null) { + throw new NullPointerException("element list is null"); + } + for (ValueRecord r : tupleElements) { + if (r == null) { + throw new NullPointerException("null element"); + } + elements.add(r); + } + } + + /** + * Called by native code. + * + * @throws NullPointerException if element is null + * @throws IllegalArgumentException if element is neither a {@link + * ValueRecord} nor one of the DTrace primitive types returned by + * {@link ScalarRecord#getValue()} + */ + private void + addElement(Object element) + { + if (element == null) { + throw new NullPointerException("tuple element is null at " + + "index " + elements.size()); + } + if (element instanceof ValueRecord) { + elements.add(ValueRecord.class.cast(element)); + } else { + elements.add(new ScalarRecord(element)); + } + } + + /** + * Gets a modifiable list of this tuple's elements in the same order + * as their corresponding variables in the original D program tuple. + * Modifying the returned list has no effect on this tuple. + * Supports XML persistence. + * + * @return a modifiable list of this tuple's elements in the same order + * as their corresponding variables in the original D program tuple + */ + public List <ValueRecord> + getElements() + { + return new ArrayList <ValueRecord> (elements); + } + + /** + * Gets a read-only {@code List} view of this tuple. + * + * @return a read-only {@code List} view of this tuple + */ + public List <ValueRecord> + asList() + { + return Collections.unmodifiableList(elements); + } + + /** + * Gets the number of elements in this tuple. + * + * @return non-negative element count + */ + public int + size() + { + return elements.size(); + } + + /** + * Returns {@code true} if this tuple has no elements. + * + * @return {@code true} if this tuple has no elements, {@code false} + * otherwise + * @see Tuple#EMPTY + */ + public boolean + isEmpty() + { + return elements.isEmpty(); + } + + /** + * Gets the element at the given tuple index (starting at zero). + * + * @return non-null tuple element at the given zero-based index + */ + public ValueRecord + get(int index) + { + return elements.get(index); + } + + /** + * Gets an iterator over the elements of this tuple. + * + * @return an iterator over the elements of this tuple + */ + public Iterator<ValueRecord> + iterator() + { + return elements.iterator(); + } + + /** + * Compares the specified object with this {@code Tuple} instance + * for equality. Defines equality as having the same elements in + * the same order. + * + * @return {@code true} if and only if the specified object is of + * type {@code Tuple} and both instances have the same size and + * equal elements at corresponding tuple indexes + */ + public boolean + equals(Object o) + { + if (o instanceof Tuple) { + Tuple t = (Tuple)o; + return elements.equals(t.elements); + } + return false; + } + + /** + * Overridden to ensure that equals instances have equal hash codes. + */ + public int + hashCode() + { + return elements.hashCode(); + } + + // lenient sort does not throw exceptions + @SuppressWarnings("unchecked") + private int + compareObjects(Object o1, Object o2) + { + int cmp; + Class c1 = o1.getClass(); + Class c2 = o2.getClass(); + if (c1.isAssignableFrom(c2) && (o1 instanceof Comparable)) { + Comparable c = Comparable.class.cast(o1); + cmp = c.compareTo(c1.cast(o2)); + } else { + // Compare string values. If matching, compare object class. + String s1 = o1.toString(); + String s2 = o2.toString(); + cmp = s1.compareTo(s2); + if (cmp == 0) { + cmp = c1.getName().compareTo(c2.getName()); + } + } + return cmp; + } + + /** + * Defines the natural ordering of tuples. Uses a lenient algorithm + * designed not to throw exceptions. Sorts tuples by the natural + * ordering of corresponding elements, starting with the first pair + * of corresponding elements and comparing subsequent pairs only + * when all previous pairs are equal (as a tie breaker). If + * corresponding elements are not mutually comparable, it compares + * the string values of those elements, then if the string values + * are equal, it compares the class names of those elements' types. + * If all corresponding elements are equal, then the tuple with more + * elements sorts higher than the tuple with fewer elements. + * + * @return a negative integer, zero, or a postive integer as this + * tuple is less than, equal to, or greater than the given tuple + */ + public int + compareTo(Tuple t) + { + int cmp = 0; + int len = size(); + int tlen = t.size(); + ValueRecord rec; + ValueRecord trec; + Object val; + Object tval; + for (int i = 0; (cmp == 0) && (i < len) && (i < tlen); ++i) { + rec = get(i); + trec = t.get(i); + if (rec instanceof ScalarRecord) { + val = rec.getValue(); + } else { + val = rec; + } + if (trec instanceof ScalarRecord) { + tval = trec.getValue(); + } else { + tval = trec; + } + cmp = compareObjects(val, tval); + } + if (cmp == 0) { + cmp = (len < tlen ? -1 : (len > tlen ? 1 : 0)); + } + return cmp; + } + + private void + readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // Make a defensive copy of elements + if (elements == null) { + throw new InvalidObjectException("element list is null"); + } + List <ValueRecord> copy = new ArrayList <ValueRecord> + (elements.size()); + copy.addAll(elements); + elements = copy; + // check class invariants + for (ValueRecord e : elements) { + if (e == null) { + throw new InvalidObjectException("null element"); + } + } + } + + /** + * Gets a string representation of this tuple's elements in the same + * format as that returned by {@link AbstractCollection#toString()}. + * The representation, although specified, is subject to change. + */ + public String + toString() + { + return elements.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/UserStackRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/UserStackRecord.java new file mode 100644 index 0000000000..7c6e64872b --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/UserStackRecord.java @@ -0,0 +1,327 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.util.*; +import java.io.*; +import java.beans.*; + +/** + * A value generated by the DTrace {@code ustack()} or {@code jstack()} + * action. + * <p> + * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. + * + * @author Tom Erickson + */ +public final class UserStackRecord implements StackValueRecord, + Serializable, Comparable <UserStackRecord> +{ + static final long serialVersionUID = -4195269026915862308L; + + static { + try { + BeanInfo info = Introspector.getBeanInfo(UserStackRecord.class); + PersistenceDelegate persistenceDelegate = + new DefaultPersistenceDelegate( + new String[] {"processID", "stackFrames", "rawStackData"}) + { + /* + * Need to prevent DefaultPersistenceDelegate from using + * overridden equals() method, resulting in a + * StackOverFlowError. Revert to PersistenceDelegate + * implementation. See + * http://forum.java.sun.com/thread.jspa?threadID= + * 477019&tstart=135 + */ + protected boolean + mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null && oldInstance != null && + oldInstance.getClass() == newInstance.getClass()); + } + }; + BeanDescriptor d = info.getBeanDescriptor(); + d.setValue("persistenceDelegate", persistenceDelegate); + } catch (IntrospectionException e) { + System.out.println(e); + } + } + + private transient KernelStackRecord stackRecord; + /** @serial */ + private final int processID; + + /** + * Called by native code. + */ + private + UserStackRecord(int pid, byte[] rawBytes) + { + stackRecord = new KernelStackRecord(rawBytes); + processID = pid; + validate(); + } + + /** + * Creates a {@code UserStackRecord} with the given stack frames, + * user process ID, and raw stack data. Supports XML persistence. + * + * @param frames array of human-readable stack frames, copied so + * that later modifying the given frames array will not affect this + * {@code UserStackRecord}; may be {@code null} or empty to indicate + * that the raw stack data was not converted to human-readable stack + * frames (see {@link StackValueRecord#getStackFrames()}) + * @param pid non-negative user process ID + * @param rawBytes array of raw bytes used to represent this stack + * value in the native DTrace library, needed to distinguish stacks + * that have the same display value but are considered distinct by + * DTrace; copied so that later modifying the given array will not + * affect this {@code UserStackRecord} + * @throws NullPointerException if the given array of raw bytes is + * {@code null} or if any element of the {@code frames} array is + * {@code null} + * @throws IllegalArgumentException if the given process ID is + * negative + */ + public + UserStackRecord(int pid, StackFrame[] frames, byte[] rawBytes) + { + stackRecord = new KernelStackRecord(frames, rawBytes); + processID = pid; + validate(); + } + + private void + validate() + { + if (processID < 0) { + throw new IllegalArgumentException("process ID is negative"); + } + } + + public StackFrame[] + getStackFrames() + { + return stackRecord.getStackFrames(); + } + + void + setStackFrames(StackFrame[] frames) + { + stackRecord.setStackFrames(frames); + } + + /** + * Gets the native DTrace representation of this record's stack as + * an array of raw bytes. The raw bytes include the process ID and + * are used in {@link #equals(Object o) equals()} and {@link + * #compareTo(UserStackRecord r) compareTo()} to test equality and + * to determine the natural ordering of user stack records. + * + * @return the native DTrace library's internal representation of + * this record's stack as a non-null array of bytes + */ + public byte[] + getRawStackData() + { + return stackRecord.getRawStackData(); + } + + /** + * Gets the raw bytes used to represent this record's stack value in + * the native DTrace library. To get a human-readable + * representation, call {@link #toString()}. + * + * @return {@link #getRawStackData()} + */ + public Object + getValue() + { + return stackRecord.getValue(); + } + + /** + * Gets the process ID associated with this record's user stack. + * + * @return non-negative pid + */ + public int + getProcessID() + { + return processID; + } + + public List <StackFrame> + asList() + { + return stackRecord.asList(); + } + + /** + * Gets a {@code KernelStackRecord} view of this record. + * + * @return non-null {@code KernelStackRecord} view of this record + */ + public KernelStackRecord + asKernelStackRecord() + { + return stackRecord; + } + + /** + * Compares the specified object with this {@code UserStackRecord} + * for equality. Returns {@code true} if and only if the specified + * object is also a {@code UserStackRecord} and both stacks have the + * same raw stack data (including process ID). + * <p> + * This implementation first checks if the specified object is this + * {@code UserStackRecord}. If so, it returns {@code true}. + * + * @return {@code true} if and only if the specified object is also + * a {@code UserStackRecord} and both stacks have the same raw stack + * data (including process ID) + */ + @Override + public boolean + equals(Object o) + { + if (o == this) { + return true; + } + if (o instanceof UserStackRecord) { + UserStackRecord r = (UserStackRecord)o; + // raw stack data includes the process ID, but the process + // ID passed to the constructor is not validated against the + // raw stack data; probably best for this class to test all + // of its state without making assumptions + return ((processID == r.processID) && + stackRecord.equals(r.stackRecord)); + } + return false; + } + + /** + * Overridden to ensure that equal instances have equal hash codes. + */ + @Override + public int + hashCode() + { + return (stackRecord.hashCode() + processID); + } + + /** + * Compares this record with the given {@code UserStackRecord}. + * Compares process ID first, then if those are equal, compares the + * views returned by {@link #asKernelStackRecord()}. The {@code + * compareTo()} method is compatible with {@link #equals(Object o) + * equals()}. + * <p> + * This implementation first checks if the specified object is this + * {@code UserStackRecord}. If so, it returns {@code 0}. + * + * @return -1, 0, or 1 as this record is less than, equal to, or + * greater than the given record + */ + public int + compareTo(UserStackRecord r) + { + if (r == this) { + return 0; + } + + int cmp = 0; + cmp = ((processID < r.processID) ? -1 : + ((processID > r.processID) ? 1 : 0)); + if (cmp == 0) { + cmp = stackRecord.compareTo(r.stackRecord); + } + return cmp; + } + + /** + * Serialize this {@code UserStackRecord} instance. + * + * @serialData Serialized fields are emitted, followed first by this + * record's stack frames as an array of type {@link String}, then by + * this record's raw stack data as an array of bytes. + */ + private void + writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeObject(stackRecord.getStackFrames()); + s.writeObject(stackRecord.getRawStackData()); + } + + private void + readObject(ObjectInputStream s) throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + try { + StackFrame[] frames = (StackFrame[])s.readObject(); + byte[] rawBytes = (byte[])s.readObject(); + // defensively copies stack frames and raw bytes + stackRecord = new KernelStackRecord(frames, rawBytes); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + // check class invariants + try { + validate(); + } catch (Exception e) { + throw new InvalidObjectException(e.getMessage()); + } + } + + /** + * Gets the {@link KernelStackRecord#toString() string + * representation} of the view returned by {@link + * #asKernelStackRecord()} preceded by the user process ID on its + * own line. The process ID is in the format {@code process ID: + * pid} (where <i>pid</i> is a decimal integer) and is indented by + * the same number of spaces as the stack frames. The exact format + * is subject to change. + */ + public String + toString() + { + StringBuffer buf = new StringBuffer(); + final int stackindent = KernelStackRecord.STACK_INDENT; + int i; + buf.append('\n'); + for (i = 0; i < KernelStackRecord.STACK_INDENT; ++i) { + buf.append(' '); + } + buf.append("process ID: "); + buf.append(processID); + buf.append(stackRecord.toString()); // starts with newline + return buf.toString(); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Utility.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Utility.java new file mode 100644 index 0000000000..5cd9c035d8 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Utility.java @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +import java.io.File; + +/** + * Common functionality for all {@code org.opensolaris.os} subpackages. + */ +class Utility +{ + private static void + loadLibrary(String paths[], String name, boolean debug) + { + File file; + for (String p : paths) { + file = new File(p); + // Allows LD_LIBRARY_PATH to include relative paths + p = file.getAbsolutePath(); + try { + System.load(p + "/" + name); + if (debug) { + System.out.println("loaded " + name + " from " + p); + } + return; + } catch (UnsatisfiedLinkError e) { + } + } + throw (new UnsatisfiedLinkError("Unable to find " + name)); + } + + /** + * Loads a library. + */ + public static void + loadLibrary(String name, boolean debug) + { + String path = System.getProperty("java.library.path"); + path = path + ":/usr/lib/64"; /* Java bug 6254947 */ + String[] paths = path.split(":"); + + if (debug) { + String root = System.getenv("ROOT"); + if (root != null && root.length() > 0) { + System.out.println("Prepending $ROOT to library path."); + String[] npaths = new String[paths.length * 2]; + for (int i = 0; i < paths.length; i++) { + npaths[i] = root + "/" + paths[i]; + npaths[i + paths.length] = paths[i]; + } + paths = npaths; + } + } + + loadLibrary(paths, name, debug); + } +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ValueRecord.java b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ValueRecord.java new file mode 100644 index 0000000000..e0ea54e450 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ValueRecord.java @@ -0,0 +1,42 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * ident "%Z%%M% %I% %E% SMI" + */ +package org.opensolaris.os.dtrace; + +/** + * A data record generated by DTrace that contains a single value. + * + * @author Tom Erickson + */ +public interface ValueRecord extends Record { + /** + * Gets a traced value. + * + * @return non-null record value + */ + public abstract Object getValue(); +} diff --git a/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/package.html b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/package.html new file mode 100644 index 0000000000..92689cf5f2 --- /dev/null +++ b/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/package.html @@ -0,0 +1,62 @@ +<!-- + Copyright 2006 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + ident "%Z%%M% %I% %E% SMI" + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head/> +<body bgcolor="white"> + + Interface to the native DTrace library. + +<!-- commented out ... + +<h2>Package Specification</h2> + +##### FILL IN ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT ##### + +<ul> +<li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a> +</ul> + +end commented-out block --> + +<h2>Related Documentation</h2> + +For overviews, tutorials, examples, guides, and tool documentation, please see: + +<br><br> +<a href="http://docs.sun.com/db/doc/817-6223"> + <i>Solaris Dynamic Tracing Guide</i></a><br> +<a href="http://www.opensolaris.org/os/community/dtrace"> + OpenSolaris DTrace Website</a><br> +<a href="../../../../../html/JavaDTraceAPI.html" target="parent"> + API Diagram</a><br> +<a href="../../../../../html/fast.html" target="parent"> + Quick Start Guide</a><br> + +<!-- Put @see and @since tags down here. --> + +</body> +</html> |
