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