1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
|
<!--
Copyright 2007 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
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(8)</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://dtrace.org/guide/chp-sec.html>
<b>Security</b></a> chapter of the <i>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://dtrace.org/guide/chp-script.html#chp-script-3><b>
Macro Arguments</b></a> section of the <b>Scripting</b> chapter of the
<i>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://dtrace.org/guide/chp-opt.html>
<b>Options and Tunables</b></a> chapter of the <i>Dynamic
Tracing Guide</i>. See also the <a
href=http://dtrace.org/guide/chp-aggs.html#chp-aggs-4>
<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://dtrace.org/guide/chp-script.html#chp-script-4>
<b>Target Process ID</b></a> section of the <b>Scripting</b> chapter of
the <i>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://dtrace.org/">
DTrace page</a> has links to resources to help you learn
DTrace. In particular, you should read the <a
href="http://dtrace.org/guide/"><i>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>
|