1<!-- 2 Copyright 2007 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 javac -cp /usr/share/lib/java/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> 152<pre><tt> 153 java -cp .:/usr/share/lib/java/dtrace.jar TestAPI hello.d 154</tt></pre> 155<br> 156The output should look like this: 157<pre><tt> 158 org.opensolaris.os.dtrace.ProbeData[epid = 1, cpu = 1, 159 enabledProbeDescription = dtrace:::BEGIN, flow = null, records = 160 ["hello, world", 0]] 161</tt></pre> 162<br> 163There is one record in the <a 164href="../api/org/opensolaris/os/dtrace/ProbeData.html"><tt>ProbeData</tt></a> 165for each action in the D script. The first record is generated by the 166<tt>trace()</tt> action. The second is generated by the <tt>exit()</tt> 167action. For prettier output, you could change the <tt>ConsumerAdapter <a 168 href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#dataReceived%28org.opensolaris.os.dtrace.DataEvent%29">dataReceived()</a></tt> 169implementation as follows: 170<pre><tt><font color=#aaaaaa> 171 consumer.addConsumerListener(new ConsumerAdapter() { 172 public void dataReceived(DataEvent e) { 173 // System.out.println(e.getProbeData());</font> 174 ProbeData data = e.getProbeData(); 175 java.util.List < Record > records = data.getRecords(); 176 for (Record r : records) { 177 if (r instanceof ExitRecord) { 178 } else { 179 System.out.println(r); 180 } 181 }<font color=#aaaaaa> 182 } 183 });</font> 184</tt></pre> 185<br> 186<h2><a name="Aggregations"></a>Aggregations</h2> 187The example Java program can just as easily run a more complex script, 188such as an aggregation:<br> 189<b>D script (<a href="../examples/syscall.d">syscall.d</a>)</b> 190<pre><tt> 191 syscall:::entry 192 / execname == $$1 / 193 { 194 @[probefunc] = count(); 195 } 196 197 profile:::tick-1sec 198 { 199 printa(@); 200 clear(@); 201 } 202</tt></pre> 203<br> 204The above script uses the <tt>$$1</tt> macro variable as a placeholder 205for whatever executable you'd like to trace. See the <a 206href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidliq?a=view><b> 207Macro Arguments</b></a> section of the <b>Scripting</b> chapter of the 208<i>Solaris Dynamic Tracing Guide</i>. Using two dollar signs (<tt>$$1</tt>) 209instead of one (<tt>$1</tt>) forces expansion of the macro variable to 210type string.<br> 211<br> 212To run the example Java program using the above D script, you need to 213specify an argument to the <tt>execname</tt> placeholder, such as 214"java":<br> 215<pre><tt> 216 java -cp .:/usr/share/lib/java/dtrace.jar TestAPI syscall.d java 217</tt></pre> 218<br> 219A data record generated by the <tt>printa()</tt> action is printed to 220the console once every second. It contains counts of system calls by 221function name made by java. No record is generated by the 222<tt>clear()</tt> action.<br> 223<br> 224If you omit the argument to the <tt>execname</tt> placeholder, the 225program fails to compile and the API throws the following exception: 226<pre><tt> 227 org.opensolaris.os.dtrace.DTraceException: failed to compile script 228 syscall.d: line 2: macro argument $$1 is not defined 229 at org.opensolaris.os.dtrace.LocalConsumer._compileFile(Native Method) 230 at org.opensolaris.os.dtrace.LocalConsumer.compile(LocalConsumer.java:342) 231 at TestAPI.main(TestAPI.java:26) 232</tt></pre> 233<br> 234A DTrace script may have more than one aggregation. In that case, each 235aggregation needs a distinct name:<br> 236<b>D script (<a href="../examples/intrstat.d">intrstat.d</a>)</b> 237<pre><tt> 238 sdt:::interrupt-start 239 { 240 self->ts = vtimestamp; 241 } 242 243 sdt:::interrupt-complete 244 / self->ts && arg0 / 245 { 246 this->devi = (struct dev_info *)arg0; 247 @counts[stringof(`devnamesp[this->devi->devi_major].dn_name), 248 this->devi->devi_instance, cpu] = count(); 249 @times[stringof(`devnamesp[this->devi->devi_major].dn_name), 250 this->devi->devi_instance, cpu] = sum(vtimestamp - self->ts); 251 self->ts = 0; 252 } 253</tt></pre> 254<br> 255The <tt>@counts</tt> and <tt>@times</tt> aggregations both accumulate 256values for each unique combination of device name, device instance, and 257CPU (a three-element tuple). In this example we drop the <tt>tick</tt> 258probe to demonstrate a more convenient way to get aggregation data 259without the use of the <tt>printa()</tt> action. The <a 260href="../api/org/opensolaris/os/dtrace/Consumer.html#getAggregate%28%29"> 261<tt>getAggregate()</tt></a> method allows us to get a read-consistent 262snapshot of all aggregations at once on a programmatic interval.<br> 263<b>Java program (<a href="../examples/TestAPI2.java">TestAPI2.java</a>)</b> 264<pre><tt><font color=#aaaaaa> 265 ... 266 267 try { 268 consumer.open(); 269 consumer.compile(file, macroArgs); 270 consumer.enable(); 271 consumer.go();</font> 272 273 Aggregate a; 274 do { 275 Thread.sleep(1000); // 1 second 276 a = consumer.getAggregate(); 277 if (!a.asMap().isEmpty()) { 278 System.out.println(a); 279 } 280 } while (consumer.isRunning());<font color=#aaaaaa> 281 } catch (Exception e) { 282 e.printStackTrace(); 283 System.exit(1); 284 } 285 286 ...</font> 287</tt></pre> 288<br> 289Compile and run: 290<pre><tt> 291 javac -cp /usr/share/lib/java/dtrace.jar TestAPI2.java 292</tt></pre> 293<pre><tt> 294 java -cp .:/usr/share/lib/java/dtrace.jar TestAPI2 intrstat.d 295</tt></pre> 296<br> 297Try removing the <tt>tick</tt> probe from the <tt>syscall.d</tt> example 298and running it again with the above modification (<tt>TestAPI2</tt>).<br> 299<br> 300By default, the requested aggregate includes every aggregation and 301accumulates running totals. To display values per time interval 302(instead of running totals), clear the aggregations each time you call 303<tt>getAggregate()</tt>. Clearing an aggregation resets all counts to 304zero without removing any elements. The following modification to the 305example above clears all aggregations: 306<pre><tt><font color=#aaaaaa> 307 // a = consumer.getAggregate();</font> 308 a = consumer.getAggregate(null, null); // included, cleared 309</tt></pre> 310<br> 311Each <tt>Set</tt> of aggregation names, <tt>included</tt> and 312<tt>cleared</tt>, specifies <i>all</i> aggregations if <tt>null</tt> and 313no aggregations if empty. Any subset is possible. However, if an 314aggregation has ever been used in the <tt>printa()</tt> action, it is no 315longer available to the <tt>getAggregate()</tt> method.<br> 316<br> 317Be aware that you cannot call <tt>getAggregate()</tt> on an interval 318faster that the <tt>aggrate</tt> setting. See the <a 319href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlis?a=view> 320<b>Options and Tunables</b></a> chapter of the <i>Solaris Dynamic 321Tracing Guide</i>. See also the <a 322href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlhf?a=view> 323<b>Minimizing Drops</b></a> section of the <b>Aggregations</b> chapter 324for specific information about the <tt>aggrate</tt> option. The default 325<tt>aggrate</tt> is once per second. Here's an example of how you might 326double the <tt>aggrate</tt> to minimize drops: 327<pre><tt> 328 consumer.setOption(Option.aggrate, Option.millis(500)); // every half second 329</tt></pre> 330<br> 331Even a single drop terminates the consumer unless you override the <a 332href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#dataDropped%28org.opensolaris.os.dtrace.DropEvent%29"> 333<tt>dataDropped()</tt></a> method of <tt>ConsumerAdapter</tt> to handle 334drops differently. To avoid drops, it is probably better to increase 335the <tt>aggsize</tt> option, since increasing the <tt>aggrate</tt> makes 336the consumer work harder. In most cases, the <tt>aggrate</tt> should 337only be increased when you need to update a display of aggregation data 338more frequently than once per second. Many runtime options, including 339<tt>aggrate</tt>, can be changed dynamically while the consumer is 340running.<br> 341<br> 342It's also worth mentioning that a D aggregation may omit square 343brackets and aggregate only a single value: 344<pre><tt> 345 @total = count(); 346</tt></pre> 347<br> 348The resulting singleton <a 349href="../api/org/opensolaris/os/dtrace/Aggregation.html"> 350<tt>Aggregation</tt></a> has one record that may be obtained as follows: 351<pre><tt> 352 Aggregate a = consumer.getAggregate(); 353 Aggregation total = a.getAggregation("total"); 354 AggregationRecord totalRecord = total.getRecord(Tuple.EMPTY); 355</tt></pre> 356<br> 357<h2><a name="Target_Process"></a>Target Process ID</h2> 358In addition to supporting macro arguments (see the <tt>syscall.d</tt> 359aggregation example above), the Java DTrace API also supports the 360<tt>$target</tt> macro variable. (See the <a 361href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlir?a=view> 362<b>Target Process ID</b></a> section of the <b>Scripting</b> chapter of 363the <i>Solaris Dynamic Tracing Guide</i>.) This allows you to trace a 364process from the very beginning of its execution, rather than sometime 365after you manually obtain its process ID. The API does this by creating 366a process that is initially suspended and allowed to start only after <a 367href="../api/org/opensolaris/os/dtrace/Consumer.html#go%28%29"> 368<tt>go()</tt></a> has initiated tracing. For example, you can aggregate 369all the system calls from start to finish made by the <tt>date</tt> 370command:<br> 371<b>D script (<a href="../examples/target.d">target.d</a>)</b> 372<pre><tt> 373 syscall:::entry 374 / pid == $target / 375 { 376 @[probefunc] = count(); 377 } 378</tt></pre> 379<br> 380A modified version of the <tt>TestAPI.java</tt> program adds the <a 381href="../api/org/opensolaris/os/dtrace/Consumer.html#createProcess%28java.lang.String%29"> 382<tt>createProcess()</tt></a> call to execute the given command but 383prevent it from starting until the consumer is running:<br> 384<b>Java program (<a href="../examples/TestTarget.java">TestTarget.java</a>)</b> 385<pre><tt><font color=#aaaaaa> 386 ... 387 consumer.open();</font> 388 consumer.createProcess(command);<font color=#aaaaaa> 389 consumer.compile(file); 390 consumer.enable(); 391 consumer.go(); 392 ...</font> 393</tt></pre> 394<br> 395It also overrides the <a 396href="../api/org/opensolaris/os/dtrace/ConsumerAdapter.html#processStateChanged%28org.opensolaris.os.dtrace.ProcessEvent%29"> 397<tt>processStateChanged()</tt></a> method of the 398<tt>ConsumerAdapter</tt> to print a notification when the process has 399ended: 400<pre><tt><font color=#aaaaaa> 401 ... 402 consumer.addConsumerListener(new ConsumerAdapter() { 403 public void dataReceived(DataEvent e) { 404 System.out.println(e.getProbeData()); 405 } 406 public void consumerStopped(ConsumerEvent e) { 407 try { 408 Aggregate a = consumer.getAggregate(); 409 for (Aggregation agg : a.asMap().values()) { 410 for (AggregationRecord rec : agg.asMap().values()) { 411 System.out.println(rec.getTuple() + " " + 412 rec.getValue()); 413 } 414 } 415 } catch (Exception x) { 416 x.printStackTrace(); 417 System.exit(1); 418 } 419 consumer.close(); 420 }</font> 421 public void processStateChanged(ProcessEvent e) { 422 System.out.println(e.getProcessState()); 423 }<font color=#aaaaaa> 424 }); 425 ...</font> 426</tt></pre> 427<br> 428Compile and run: 429<pre><tt> 430 javac -cp /usr/share/lib/java/dtrace.jar TestTarget.java 431</tt></pre> 432<pre><tt> 433 java -cp .:/usr/share/lib/java/dtrace.jar TestTarget target.d date 434</tt></pre> 435<br> 436The consumer exits automatically when the target <tt>date</tt> process 437completes.<br> 438<h2><a name="Closing_Consumers"></a>Closing Consumers</h2> 439An application using the Java DTrace API may run multiple consumers 440simultaneously. When a consumer stops running, the programmer is 441responsible for closing it in order to release the system resources it 442holds. A consumer may stop running for any of the following reasons: 443<ul> 444 <li>It was stopped explicitly by a call to its <a 445href=../api/org/opensolaris/os/dtrace/Consumer.html#stop()> 446<tt>stop()</tt></a> method</li> 447 <li>It encountered the <tt>exit()</tt> action</li> 448 <li>Its <tt>$target</tt> process or processes (if any) all completed</li> 449 <li>It encountered an exception</li> 450</ul> 451By default, an exception prints a stack trace to <tt>stderr</tt> before 452notifying listeners that the consumer has stopped. You can define 453different behavior by setting an <a 454href=../api/org/opensolaris/os/dtrace/ExceptionHandler.html> 455<tt>ExceptionHandler</tt></a>, but the consumer is still stopped.<br> 456<br> 457The same listener that receives probe data generated by DTrace is also 458notified when the consumer stops. This is a good place to close the 459consumer: 460<pre><tt><font color=#aaaaaa> 461 consumer.addConsumerListener(new ConsumerAdapter() { 462 public void dataReceived(DataEvent e) { 463 System.out.println(e.getProbeData()); 464 }</font> 465 public void consumerStopped(ConsumerEvent e) { 466 Consumer consumer = (Consumer)e.getSource(); 467 consumer.close(); 468 } 469 }<font color=#aaaaaa> 470 });</font> 471</tt></pre> 472<br> 473This releases the resources held by the consumer in all cases, i.e. 474after it exits for <i>any</i> of the reasons listed above.<br> 475<br> 476You can request the last aggregate snapshot made by a stopped consumer, 477as long as it has not yet been closed: 478<pre><tt> 479 Aggregate a = consumer.getAggregate(); 480</tt></pre> 481<br> 482Note however that any aggregation that has already appeared in a <a 483href=../api/org/opensolaris/os/dtrace/PrintaRecord.html> 484<tt>PrintaRecord</tt></a> as a result of the <tt>printa()</tt> action 485action will not be included in the requested aggregate. 486<h2><a name="Learning_DTrace"></a>Learning More</h2> 487<br> 488The <a href="http://www.opensolaris.org/os/community/dtrace/"> 489OpenSolaris DTrace page</a> has links to resources to help you learn 490DTrace. In particular, you should read the <a 491href="http://docs.sun.com/app/docs/doc/817-6223"><i>Solaris Dynamic Tracing 492 Guide</i></a>.<br> 493<br> 494Try the example Java programs on this page with other D scripts. You 495need not remove <tt>#!/usr/sbin/dtrace -s</tt> from the top of an 496executable script. You may want to remove <tt>profile:::tick*</tt> 497clauses if you plan to use the <tt>Consumer</tt> <a 498href="../api/org/opensolaris/os/dtrace/Consumer.html#getAggregate%28%29"> 499<tt>getAggregate()</tt></a> method and control the data interval 500programmatically. If the script uses the pre-compiler, you will need to 501call the <tt>Consumer</tt> <a 502href="../api/org/opensolaris/os/dtrace/Consumer.html#setOption%28java.lang.String%29"> 503<tt>setOption()</tt></a> method with the <a 504href="../api/org/opensolaris/os/dtrace/Option.html#cpp"> 505<tt>Option.cpp</tt></a> argument.<br> 506<br> 507To quickly familiarize yourself with the Java DTrace API, take a look at 508the overview <a href="JavaDTraceAPI.html">diagram</a>.<br> 509<br> 510<a href="#Quick_Start_Guide_to_the_Java_DTrace_API_">Back to top</a><br> 511<br> 512</body> 513</html> 514