1<!--
2   Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3   Use is subject to license terms.
4
5   CDDL HEADER START
6
7   The contents of this file are subject to the terms of the
8   Common Development and Distribution License (the "License").
9   You may not use this file except in compliance with the License.
10
11   You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
12   or http://www.opensolaris.org/os/licensing.
13   See the License for the specific language governing permissions
14   and limitations under the License.
15
16   When distributing Covered Code, include this CDDL HEADER in each
17   file and include the License file at usr/src/OPENSOLARIS.LICENSE.
18   If applicable, add the following below this CDDL HEADER, with the
19   fields enclosed by brackets "[]" replaced with your own identifying
20   information: Portions Copyright [yyyy] [name of copyright owner]
21
22   CDDL HEADER END
23-->
24<html>
25<head>
26  <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
27  <title>Quick Start Guide to the Java DTrace API</title>
28</head>
29<body>
30<h1><a name="Quick_Start_Guide_to_the_Java_DTrace_API_"></a>Quick Start
31Guide</h1>
32<h1><small><small>to the</small> Java DTrace API</small></h1>
33<hr style="width: 100%; height: 2px;">
34<h2>Contents</h2>
35<ul>
36  <li><a href="#Hello_World">"hello, world" Example</a></li>
37  <ul>
38    <li><a href="#Writing_a_Simple_Consumer">Writing a Simple Consumer</a></li>
39    <li><a href="#Running_hello.d_Script">Running the <tt>hello.d</tt>
40	Script</a></li>
41  </ul>
42  <li><a href="#Aggregations">Aggregations</a></li>
43  <li><a href="#Target_Process">Target Process ID</a></li>
44  <li><a href="#Closing_Consumers">Closing Consumers</a></li>
45  <li><a href="#Learning_DTrace">Learning More</a><br>
46  </li>
47</ul>
48<h2><a name="Hello_World"></a>"hello, world" Example</h2>
49To demonstrate how to use the Java DTrace API, let's write a simple Java
50program that runs a D script, in this case <tt>hello.d</tt> (prints
51"hello, world" and exits).  You will need root permission to use the
52Java DTrace API (just as you do to use the <tt>dtrace(8)</tt> command).
53You may want to eliminate this inconvenience by adding the following
54line to <tt>/etc/user_attr</tt>:
55<br>
56<br>
57<tt><i>user-name</i>::::defaultpriv=basic,dtrace_kernel,dtrace_proc</tt>
58<br>
59<br>
60<i>(Substitute your user name.)</i>  See the <a
61href=http://dtrace.org/guide/chp-sec.html>
62<b>Security</b></a> chapter of the <i>Dynamic Tracing Guide</i>
63for more information.
64<br>
65<h4><a name="Writing_a_Simple_Consumer"></a>Writing a Simple Consumer</h4>
66Creating a DTrace <a
67href="../api/org/opensolaris/os/dtrace/Consumer.html">consumer</a>
68is easy:
69<pre><tt>
70    Consumer consumer = new LocalConsumer();
71</tt></pre>
72<br>
73Before you can do anything with the consumer, you must first open it.
74Then you simply compile and enable one or more D programs and run it:
75<pre><tt>
76    consumer.open();
77    consumer.compile(program);
78    consumer.enable();
79    consumer.go();	// runs in a background thread
80</tt></pre>
81<br>
82To get the data generated by DTrace, you also need to add a <a
83href="../api/org/opensolaris/os/dtrace/ConsumerListener.html">listener</a>:
84<pre><tt>
85    consumer.addConsumerListener(new ConsumerAdapter() {
86	public void dataReceived(DataEvent e) {
87	    System.out.println(e.getProbeData());
88	}
89    });
90</tt></pre>
91<br>
92Here is a simple example that runs a given D script:<br>
93<br>
94<b>Java program (<a href="../examples/TestAPI.java">TestAPI.java</a>)</b>
95<pre><tt><font color=#aaaaaa>
96    import org.opensolaris.os.dtrace.*;
97    import java.io.File;
98
99    public class TestAPI {
100	public static void
101	main(String[] args)
102	{
103	    if (args.length &lt; 1) {
104		System.err.println("Usage: java TestAPI &lt;script&gt; [ macroargs... ]");
105		System.exit(2);
106	    }
107
108	    File file = new File(args[0]);
109	    String[] macroArgs = new String[args.length - 1];
110	    System.arraycopy(args, 1, macroArgs, 0, (args.length - 1));</font>
111
112	    Consumer consumer = new LocalConsumer();
113	    consumer.addConsumerListener(new ConsumerAdapter() {
114		public void dataReceived(DataEvent e) {
115		    System.out.println(e.getProbeData());
116		}
117	    });
118<font color=#aaaaaa>
119	    try {</font>
120		consumer.open();
121		consumer.compile(file, macroArgs);
122		consumer.enable();
123		consumer.go();<font color=#aaaaaa>
124	    } catch (Exception e) {
125		e.printStackTrace();
126		System.exit(1);
127	    }
128	}
129    }</font>
130</tt></pre>
131<br>
132Compile the test program as follows:
133<pre><tt>
134    javac -cp /usr/share/lib/java/dtrace.jar TestAPI.java
135</tt></pre>
136<br>
137<h4><a name="Running_hello.d_Script"></a>Running the <tt>hello.d</tt> Script</h4>
138Now we need a D script for the program to run.  The following is a
139simple example that prints "hello, world" and exits:<br>
140<b>D script (<a href="../examples/hello.d">hello.d</a>)</b>
141<pre><tt>
142    dtrace:::BEGIN
143    {
144	    trace("hello, world");
145	    exit(0);
146    }
147</tt></pre>
148<br>
149Run as follows:<br>
150<pre><tt>
151    java -cp .:/usr/share/lib/java/dtrace.jar TestAPI hello.d
152</tt></pre>
153<br>
154The output should look like this:
155<pre><tt>
156    org.opensolaris.os.dtrace.ProbeData[epid = 1, cpu = 1,
157    enabledProbeDescription = dtrace:::BEGIN, flow = null, records =
158    ["hello, world", 0]]
159</tt></pre>
160<br>
161There is one record in the <a
162href="../api/org/opensolaris/os/dtrace/ProbeData.html"><tt>ProbeData</tt></a>
163for each action in the D script.  The first record is generated by the
164<tt>trace()</tt> action.  The second is generated by the <tt>exit()</tt>
165action.  For prettier output, you could change the <tt>ConsumerAdapter <a
166	href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#dataReceived%28org.opensolaris.os.dtrace.DataEvent%29">dataReceived()</a></tt>
167implementation as follows:
168<pre><tt><font color=#aaaaaa>
169    consumer.addConsumerListener(new ConsumerAdapter() {
170	public void dataReceived(DataEvent e) {
171	    // System.out.println(e.getProbeData());</font>
172	    ProbeData data = e.getProbeData();
173	    java.util.List &lt; Record &gt; records = data.getRecords();
174	    for (Record r : records) {
175		if (r instanceof ExitRecord) {
176		} else {
177		    System.out.println(r);
178		}
179	    }<font color=#aaaaaa>
180	}
181    });</font>
182</tt></pre>
183<br>
184<h2><a name="Aggregations"></a>Aggregations</h2>
185The example Java program can just as easily run a more complex script,
186such as an aggregation:<br>
187<b>D script (<a href="../examples/syscall.d">syscall.d</a>)</b>
188<pre><tt>
189    syscall:::entry
190    / execname == $$1 /
191    {
192	    @[probefunc] = count();
193    }
194
195    profile:::tick-1sec
196    {
197	    printa(@);
198	    clear(@);
199    }
200</tt></pre>
201<br>
202The above script uses the <tt>$$1</tt> macro variable as a placeholder
203for whatever executable you'd like to trace.  See the <a
204href=http://dtrace.org/guide/chp-script.html#chp-script-3><b>
205Macro Arguments</b></a> section of the <b>Scripting</b> chapter of the
206<i>Dynamic Tracing Guide</i>.  Using two dollar signs (<tt>$$1</tt>)
207instead of one (<tt>$1</tt>) forces expansion of the macro variable to
208type string.<br>
209<br>
210To run the example Java program using the above D script, you need to
211specify an argument to the <tt>execname</tt> placeholder, such as
212"java":<br>
213<pre><tt>
214    java -cp .:/usr/share/lib/java/dtrace.jar TestAPI syscall.d java
215</tt></pre>
216<br>
217A data record generated by the <tt>printa()</tt> action is printed to
218the console once every second.  It contains counts of system calls by
219function name made by java.  No record is generated by the
220<tt>clear()</tt> action.<br>
221<br>
222If you omit the argument to the <tt>execname</tt> placeholder, the
223program fails to compile and the API throws the following exception:
224<pre><tt>
225    org.opensolaris.os.dtrace.DTraceException: failed to compile script
226    syscall.d: line 2: macro argument $$1 is not defined
227	at org.opensolaris.os.dtrace.LocalConsumer._compileFile(Native Method)
228	at org.opensolaris.os.dtrace.LocalConsumer.compile(LocalConsumer.java:342)
229	at TestAPI.main(TestAPI.java:26)
230</tt></pre>
231<br>
232A DTrace script may have more than one aggregation.  In that case, each
233aggregation needs a distinct name:<br>
234<b>D script (<a href="../examples/intrstat.d">intrstat.d</a>)</b>
235<pre><tt>
236    sdt:::interrupt-start
237    {
238	    self-&gt;ts = vtimestamp;
239    }
240
241    sdt:::interrupt-complete
242    / self-&gt;ts &amp;&amp; arg0 /
243    {
244	    this-&gt;devi = (struct dev_info *)arg0;
245	    @counts[stringof(`devnamesp[this-&gt;devi-&gt;devi_major].dn_name),
246		this-&gt;devi-&gt;devi_instance, cpu] = count();
247	    @times[stringof(`devnamesp[this-&gt;devi-&gt;devi_major].dn_name),
248		this-&gt;devi-&gt;devi_instance, cpu] = sum(vtimestamp - self-&gt;ts);
249	    self-&gt;ts = 0;
250    }
251</tt></pre>
252<br>
253The <tt>@counts</tt> and <tt>@times</tt> aggregations both accumulate
254values for each unique combination of device name, device instance, and
255CPU (a three-element tuple).  In this example we drop the <tt>tick</tt>
256probe to demonstrate a more convenient way to get aggregation data
257without the use of the <tt>printa()</tt> action.  The <a
258href="../api/org/opensolaris/os/dtrace/Consumer.html#getAggregate%28%29">
259<tt>getAggregate()</tt></a> method allows us to get a read-consistent
260snapshot of all aggregations at once on a programmatic interval.<br>
261<b>Java program (<a href="../examples/TestAPI2.java">TestAPI2.java</a>)</b>
262<pre><tt><font color=#aaaaaa>
263    ...
264
265    try {
266	consumer.open();
267	consumer.compile(file, macroArgs);
268	consumer.enable();
269	consumer.go();</font>
270
271	Aggregate a;
272	do {
273	    Thread.sleep(1000); // 1 second
274	    a = consumer.getAggregate();
275	    if (!a.asMap().isEmpty()) {
276		System.out.println(a);
277	    }
278	} while (consumer.isRunning());<font color=#aaaaaa>
279    } catch (Exception e) {
280	e.printStackTrace();
281	System.exit(1);
282    }
283
284    ...</font>
285</tt></pre>
286<br>
287Compile and run:
288<pre><tt>
289    javac -cp /usr/share/lib/java/dtrace.jar TestAPI2.java
290</tt></pre>
291<pre><tt>
292    java -cp .:/usr/share/lib/java/dtrace.jar TestAPI2 intrstat.d
293</tt></pre>
294<br>
295Try removing the <tt>tick</tt> probe from the <tt>syscall.d</tt> example
296and running it again with the above modification (<tt>TestAPI2</tt>).<br>
297<br>
298By default, the requested aggregate includes every aggregation and
299accumulates running totals.  To display values per time interval
300(instead of running totals), clear the aggregations each time you call
301<tt>getAggregate()</tt>.  Clearing an aggregation resets all counts to
302zero without removing any elements.  The following modification to the
303example above clears all aggregations:
304<pre><tt><font color=#aaaaaa>
305	    // a = consumer.getAggregate();</font>
306	    a = consumer.getAggregate(null, null); // included, cleared
307</tt></pre>
308<br>
309Each <tt>Set</tt> of aggregation names, <tt>included</tt> and
310<tt>cleared</tt>, specifies <i>all</i> aggregations if <tt>null</tt> and
311no aggregations if empty.  Any subset is possible.  However, if an
312aggregation has ever been used in the <tt>printa()</tt> action, it is no
313longer available to the <tt>getAggregate()</tt> method.<br>
314<br>
315Be aware that you cannot call <tt>getAggregate()</tt> on an interval
316faster that the <tt>aggrate</tt> setting.  See the <a
317href=http://dtrace.org/guide/chp-opt.html>
318<b>Options and Tunables</b></a> chapter of the <i>Dynamic
319Tracing Guide</i>.  See also the <a
320href=http://dtrace.org/guide/chp-aggs.html#chp-aggs-4>
321<b>Minimizing Drops</b></a> section of the <b>Aggregations</b> chapter
322for specific information about the <tt>aggrate</tt> option.  The default
323<tt>aggrate</tt> is once per second.  Here's an example of how you might
324double the <tt>aggrate</tt> to minimize drops:
325<pre><tt>
326    consumer.setOption(Option.aggrate, Option.millis(500)); // every half second
327</tt></pre>
328<br>
329Even a single drop terminates the consumer unless you override the <a
330href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#dataDropped%28org.opensolaris.os.dtrace.DropEvent%29">
331<tt>dataDropped()</tt></a> method of <tt>ConsumerAdapter</tt> to handle
332drops differently.  To avoid drops, it is probably better to increase
333the <tt>aggsize</tt> option, since increasing the <tt>aggrate</tt> makes
334the consumer work harder.  In most cases, the <tt>aggrate</tt> should
335only be increased when you need to update a display of aggregation data
336more frequently than once per second.  Many runtime options, including
337<tt>aggrate</tt>, can be changed dynamically while the consumer is
338running.<br>
339<br>
340It's also worth mentioning that a D aggregation may omit square
341brackets and aggregate only a single value:
342<pre><tt>
343    @total = count();
344</tt></pre>
345<br>
346The resulting singleton <a
347href="../api/org/opensolaris/os/dtrace/Aggregation.html">
348<tt>Aggregation</tt></a> has one record that may be obtained as follows:
349<pre><tt>
350    Aggregate a = consumer.getAggregate();
351    Aggregation total = a.getAggregation("total");
352    AggregationRecord totalRecord = total.getRecord(Tuple.EMPTY);
353</tt></pre>
354<br>
355<h2><a name="Target_Process"></a>Target Process ID</h2>
356In addition to supporting macro arguments (see the <tt>syscall.d</tt>
357aggregation example above), the Java DTrace API also supports the
358<tt>$target</tt> macro variable.  (See the <a
359href=http://dtrace.org/guide/chp-script.html#chp-script-4>
360<b>Target Process ID</b></a> section of the <b>Scripting</b> chapter of
361the <i>Dynamic Tracing Guide</i>.)  This allows you to trace a
362process from the very beginning of its execution, rather than sometime
363after you manually obtain its process ID.  The API does this by creating
364a process that is initially suspended and allowed to start only after <a
365href="../api/org/opensolaris/os/dtrace/Consumer.html#go%28%29">
366<tt>go()</tt></a> has initiated tracing.  For example, you can aggregate
367all the system calls from start to finish made by the <tt>date</tt>
368command:<br>
369<b>D script (<a href="../examples/target.d">target.d</a>)</b>
370<pre><tt>
371    syscall:::entry
372    / pid == $target /
373    {
374	    @[probefunc] = count();
375    }
376</tt></pre>
377<br>
378A modified version of the <tt>TestAPI.java</tt> program adds the <a
379href="../api/org/opensolaris/os/dtrace/Consumer.html#createProcess%28java.lang.String%29">
380<tt>createProcess()</tt></a> call to execute the given command but
381prevent it from starting until the consumer is running:<br>
382<b>Java program (<a href="../examples/TestTarget.java">TestTarget.java</a>)</b>
383<pre><tt><font color=#aaaaaa>
384    ...
385    consumer.open();</font>
386    consumer.createProcess(command);<font color=#aaaaaa>
387    consumer.compile(file);
388    consumer.enable();
389    consumer.go();
390    ...</font>
391</tt></pre>
392<br>
393It also overrides the <a
394href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#processStateChanged%28org.opensolaris.os.dtrace.ProcessEvent%29">
395<tt>processStateChanged()</tt></a> method of the
396<tt>ConsumerAdapter</tt> to print a notification when the process has
397ended:
398<pre><tt><font color=#aaaaaa>
399    ...
400    consumer.addConsumerListener(new ConsumerAdapter() {
401	public void dataReceived(DataEvent e) {
402	    System.out.println(e.getProbeData());
403	}
404	public void consumerStopped(ConsumerEvent e) {
405	    try {
406		Aggregate a = consumer.getAggregate();
407		for (Aggregation agg : a.asMap().values()) {
408		    for (AggregationRecord rec : agg.asMap().values()) {
409			System.out.println(rec.getTuple() + " " +
410				rec.getValue());
411		    }
412		}
413	    } catch (Exception x) {
414		x.printStackTrace();
415		System.exit(1);
416	    }
417	    consumer.close();
418	}</font>
419	public void processStateChanged(ProcessEvent e) {
420	    System.out.println(e.getProcessState());
421	}<font color=#aaaaaa>
422    });
423    ...</font>
424</tt></pre>
425<br>
426Compile and run:
427<pre><tt>
428    javac -cp /usr/share/lib/java/dtrace.jar TestTarget.java
429</tt></pre>
430<pre><tt>
431    java -cp .:/usr/share/lib/java/dtrace.jar TestTarget target.d date
432</tt></pre>
433<br>
434The consumer exits automatically when the target <tt>date</tt> process
435completes.<br>
436<h2><a name="Closing_Consumers"></a>Closing Consumers</h2>
437An application using the Java DTrace API may run multiple consumers
438simultaneously.  When a consumer stops running, the programmer is
439responsible for closing it in order to release the system resources it
440holds.  A consumer may stop running for any of the following reasons:
441<ul>
442  <li>It was stopped explicitly by a call to its <a
443href=../api/org/opensolaris/os/dtrace/Consumer.html#stop()>
444<tt>stop()</tt></a> method</li>
445  <li>It encountered the <tt>exit()</tt> action</li>
446  <li>Its <tt>$target</tt> process or processes (if any) all completed</li>
447  <li>It encountered an exception</li>
448</ul>
449By default, an exception prints a stack trace to <tt>stderr</tt> before
450notifying listeners that the consumer has stopped.  You can define
451different behavior by setting an <a
452href=../api/org/opensolaris/os/dtrace/ExceptionHandler.html>
453<tt>ExceptionHandler</tt></a>, but the consumer is still stopped.<br>
454<br>
455The same listener that receives probe data generated by DTrace is also
456notified when the consumer stops.  This is a good place to close the
457consumer:
458<pre><tt><font color=#aaaaaa>
459    consumer.addConsumerListener(new ConsumerAdapter() {
460	public void dataReceived(DataEvent e) {
461	    System.out.println(e.getProbeData());
462	}</font>
463	public void consumerStopped(ConsumerEvent e) {
464	    Consumer consumer = (Consumer)e.getSource();
465		consumer.close();
466	    }
467	}<font color=#aaaaaa>
468    });</font>
469</tt></pre>
470<br>
471This releases the resources held by the consumer in all cases, i.e.
472after it exits for <i>any</i> of the reasons listed above.<br>
473<br>
474You can request the last aggregate snapshot made by a stopped consumer,
475as long as it has not yet been closed:
476<pre><tt>
477    Aggregate a = consumer.getAggregate();
478</tt></pre>
479<br>
480Note however that any aggregation that has already appeared in a <a
481href=../api/org/opensolaris/os/dtrace/PrintaRecord.html>
482<tt>PrintaRecord</tt></a> as a result of the <tt>printa()</tt> action
483action will not be included in the requested aggregate.
484<h2><a name="Learning_DTrace"></a>Learning More</h2>
485<br>
486The <a href="http://dtrace.org/">
487DTrace page</a> has links to resources to help you learn
488DTrace.  In particular, you should read the <a
489href="http://dtrace.org/guide/"><i>Dynamic Tracing
490    Guide</i></a>.<br>
491<br>
492Try the example Java programs on this page with other D scripts.  You
493need not remove <tt>#!/usr/sbin/dtrace -s</tt> from the top of an
494executable script.  You may want to remove <tt>profile:::tick*</tt>
495clauses if you plan to use the <tt>Consumer</tt> <a
496href="../api/org/opensolaris/os/dtrace/Consumer.html#getAggregate%28%29">
497<tt>getAggregate()</tt></a> method and control the data interval
498programmatically.  If the script uses the pre-compiler, you will need to
499call the <tt>Consumer</tt> <a
500href="../api/org/opensolaris/os/dtrace/Consumer.html#setOption%28java.lang.String%29">
501<tt>setOption()</tt></a> method with the <a
502href="../api/org/opensolaris/os/dtrace/Option.html#cpp">
503<tt>Option.cpp</tt></a> argument.<br>
504<br>
505To quickly familiarize yourself with the Java DTrace API, take a look at
506the overview <a href="JavaDTraceAPI.html">diagram</a>.<br>
507<br>
508<a href="#Quick_Start_Guide_to_the_Java_DTrace_API_">Back to top</a><br>
509<br>
510</body>
511</html>
512