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(1M)</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 < 1) { 104 System.err.println("Usage: java TestAPI <script> [ 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 < Record > 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->ts = vtimestamp; 239 } 240 241 sdt:::interrupt-complete 242 / self->ts && arg0 / 243 { 244 this->devi = (struct dev_info *)arg0; 245 @counts[stringof(`devnamesp[this->devi->devi_major].dn_name), 246 this->devi->devi_instance, cpu] = count(); 247 @times[stringof(`devnamesp[this->devi->devi_major].dn_name), 248 this->devi->devi_instance, cpu] = sum(vtimestamp - self->ts); 249 self->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