| Current File : //usr/share/lib/java/javadoc/dtrace/html/fast.html |
<!--
Copyright 2007 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
ident "%Z%%M% %I% %E% SMI"
-->
<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>
javac -cp /usr/share/lib/java/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>
<pre><tt>
java -cp .:/usr/share/lib/java/dtrace.jar TestAPI hello.d
</tt></pre>
<br>
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, such as
"java":<br>
<pre><tt>
java -cp .:/usr/share/lib/java/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 a read-consistent
snapshot of 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 /usr/share/lib/java/dtrace.jar TestAPI2.java
</tt></pre>
<pre><tt>
java -cp .:/usr/share/lib/java/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 elements. 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> 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, you can 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 /usr/share/lib/java/dtrace.jar TestTarget.java
</tt></pre>
<pre><tt>
java -cp .:/usr/share/lib/java/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/app/docs/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 data 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>