2N/A Copyright 2007 Sun Microsystems, Inc. All rights reserved. 2N/A Use is subject to license terms. 2N/A ident "%Z%%M% %I% %E% SMI" 2N/A The contents of this file are subject to the terms of the 2N/A Common Development and Distribution License (the "License"). 2N/A You may not use this file except in compliance with the License. 2N/A See the License for the specific language governing permissions 2N/A and limitations under the License. 2N/A When distributing Covered Code, include this CDDL HEADER in each 2N/A If applicable, add the following below this CDDL HEADER, with the 2N/A fields enclosed by brackets "[]" replaced with your own identifying 2N/A information: Portions Copyright [yyyy] [name of copyright owner] 2N/A <
meta content="text/html; charset=ISO-8859-1" http-
equiv="content-type">
2N/A <
title>Quick Start Guide to the Java DTrace API</
title>
2N/A<
h1><
a name="Quick_Start_Guide_to_the_Java_DTrace_API_"></
a>Quick Start
2N/A<
h1><
small><
small>to the</
small> Java DTrace API</
small></
h1>
2N/A<
hr style="width: 100%; height: 2px;">
2N/A <
li><
a href="#Hello_World">"hello, world" Example</
a></
li>
2N/A <
li><
a href="#Writing_a_Simple_Consumer">Writing a Simple Consumer</
a></
li>
2N/A <
li><
a href="#Aggregations">Aggregations</
a></
li>
2N/A <
li><
a href="#Target_Process">Target Process ID</
a></
li>
2N/A <
li><
a href="#Closing_Consumers">Closing Consumers</
a></
li>
2N/A <
li><
a href="#Learning_DTrace">Learning More</
a><
br>
2N/A<
h2><
a name="Hello_World"></
a>"hello, world" Example</
h2>
2N/ATo demonstrate how to use the Java DTrace API, let's write a simple Java
2N/Aprogram that runs a D script, in this case <
tt>
hello.d</
tt> (prints
2N/A"hello, world" and exits). You will need root permission to use the
2N/AJava DTrace API (just as you do to use the <
tt>dtrace(1M)</
tt> command).
2N/AYou may want to eliminate this inconvenience by adding the following
2N/A<
tt><
i>user-name</
i>::::defaultpriv=basic,dtrace_kernel,dtrace_proc</
tt>
2N/A<
i>(Substitute your user name.)</
i> See the <
a 2N/A<
b>Security</
b></
a> chapter of the <
i>Solaris Dynamic Tracing Guide</
i>
2N/Afor more information.
2N/A<
h4><
a name="Writing_a_Simple_Consumer"></
a>Writing a Simple Consumer</
h4>
2N/A Consumer consumer = new LocalConsumer();
2N/ABefore you can do anything with the consumer, you must first open it.
2N/AThen you simply compile and enable one or more D programs and run it:
2N/ATo get the data generated by DTrace, you also need to add a <
a 2N/A public void dataReceived(DataEvent e) {
2N/AHere is a simple example that runs a given D script:<
br>
2N/A<
pre><
tt><
font color=#
aaaaaa>
2N/A public class TestAPI {
2N/A File file = new File(args[0]);
2N/A Consumer consumer = new LocalConsumer();
2N/A public void dataReceived(DataEvent e) {
2N/A } catch (Exception e) {
2N/ACompile the test program as follows:
2N/ANow we need a D script for the program to run. The following is a
2N/Asimple example that prints "hello, world" and exits:<
br>
2N/A trace("hello, world");
2N/AThe output should look like this:
2N/A enabledProbeDescription = dtrace:::BEGIN, flow = null, records =
2N/A ["hello, world", 0]]
2N/AThere is one record in the <
a 2N/Afor each action in the D script. The first record is generated by the
2N/A<
tt>trace()</
tt> action. The second is generated by the <
tt>exit()</
tt>
2N/Aaction. For prettier output, you could change the <
tt>ConsumerAdapter <
a 2N/Aimplementation as follows:
2N/A<
pre><
tt><
font color=#
aaaaaa>
2N/A public void dataReceived(DataEvent e) {
2N/A for (Record r : records) {
2N/A if (r instanceof ExitRecord) {
2N/A }<
font color=#
aaaaaa>
2N/A<
h2><
a name="Aggregations"></
a>Aggregations</
h2>
2N/AThe example Java program can just as easily run a more complex script,
2N/Asuch as an aggregation:<
br>
2N/A @[probefunc] = count();
2N/AThe above script uses the <
tt>$$1</
tt> macro variable as a placeholder
2N/Afor whatever executable you'd like to trace. See the <
a 2N/AMacro Arguments</
b></
a> section of the <
b>Scripting</
b> chapter of the
2N/A<
i>Solaris Dynamic Tracing Guide</
i>. Using two dollar signs (<
tt>$$1</
tt>)
2N/Ainstead of one (<
tt>$1</
tt>) forces expansion of the macro variable to
2N/ATo run the example Java program using the above D script, you need to
2N/Aspecify an argument to the <
tt>execname</
tt> placeholder, such as
2N/AA data record generated by the <
tt>printa()</
tt> action is printed to
2N/Athe console once every second. It contains counts of system calls by
2N/Afunction name made by java. No record is generated by the
2N/A<
tt>clear()</
tt> action.<
br>
2N/AIf you omit the argument to the <
tt>execname</
tt> placeholder, the
2N/Aprogram fails to compile and the API throws the following exception:
2N/AA DTrace script may have more than one aggregation. In that case, each
2N/Aaggregation needs a distinct name:<
br>
2N/A sdt:::interrupt-start
2N/A self->ts = vtimestamp;
2N/A sdt:::interrupt-complete
2N/A / self->ts && arg0 /
2N/A this->devi = (struct dev_info *)arg0;
2N/A @counts[stringof(`devnamesp[this->devi->devi_major].dn_name),
2N/A this->devi->devi_instance, cpu] = count();
2N/A @times[stringof(`devnamesp[this->devi->devi_major].dn_name),
2N/A this->devi->devi_instance, cpu] = sum(vtimestamp - self->ts);
2N/AThe <
tt>@counts</
tt> and <
tt>@times</
tt> aggregations both accumulate
2N/Avalues for each unique combination of device name, device instance, and
2N/ACPU (a three-element tuple). In this example we drop the <
tt>tick</
tt>
2N/Aprobe to demonstrate a more convenient way to get aggregation data
2N/Awithout the use of the <
tt>printa()</
tt> action. The <
a 2N/A<
tt>getAggregate()</
tt></
a> method allows us to get a read-consistent
2N/Asnapshot of all aggregations at once on a programmatic interval.<
br>
2N/A<
pre><
tt><
font color=#
aaaaaa>
2N/A } catch (Exception e) {
2N/ATry removing the <
tt>tick</
tt> probe from the <
tt>
syscall.d</
tt> example
2N/Aand running it again with the above modification (<
tt>TestAPI2</
tt>).<
br>
2N/ABy default, the requested aggregate includes every aggregation and
2N/Aaccumulates running totals. To display values per time interval
2N/A(instead of running totals), clear the aggregations each time you call
2N/A<
tt>getAggregate()</
tt>. Clearing an aggregation resets all counts to
2N/Azero without removing any elements. The following modification to the
2N/Aexample above clears all aggregations:
2N/A<
pre><
tt><
font color=#
aaaaaa>
2N/AEach <
tt>Set</
tt> of aggregation names, <
tt>included</
tt> and
2N/A<
tt>cleared</
tt>, specifies <
i>all</
i> aggregations if <
tt>null</
tt> and
2N/Ano aggregations if empty. Any subset is possible. However, if an
2N/Aaggregation has ever been used in the <
tt>printa()</
tt> action, it is no
2N/Alonger available to the <
tt>getAggregate()</
tt> method.<
br>
2N/ABe aware that you cannot call <
tt>getAggregate()</
tt> on an interval
2N/Afaster that the <
tt>aggrate</
tt> setting. See the <
a 2N/A<
b>Options and Tunables</
b></
a> chapter of the <
i>Solaris Dynamic
2N/ATracing Guide</
i>. See also the <
a 2N/A<
b>Minimizing Drops</
b></
a> section of the <
b>Aggregations</
b> chapter
2N/Afor specific information about the <
tt>aggrate</
tt> option. The default
2N/A<
tt>aggrate</
tt> is once per second. Here's an example of how you might
2N/Adouble the <
tt>aggrate</
tt> to minimize drops:
2N/AEven a single drop terminates the consumer unless you override the <
a 2N/A<
tt>dataDropped()</
tt></
a> method of <
tt>ConsumerAdapter</
tt> to handle
2N/Adrops differently. To avoid drops, it is probably better to increase
2N/Athe <
tt>aggsize</
tt> option, since increasing the <
tt>aggrate</
tt> makes
2N/Athe consumer work harder. In most cases, the <
tt>aggrate</
tt> should
2N/Aonly be increased when you need to update a display of aggregation data
2N/Amore frequently than once per second. Many runtime options, including
2N/A<
tt>aggrate</
tt>, can be changed dynamically while the consumer is
2N/AIt's also worth mentioning that a D aggregation may omit square
2N/Abrackets and aggregate only a single value:
2N/AThe resulting singleton <
a 2N/A<
tt>Aggregation</
tt></
a> has one record that may be obtained as follows:
2N/A<
h2><
a name="Target_Process"></
a>Target Process ID</
h2>
2N/AIn addition to supporting macro arguments (see the <
tt>
syscall.d</
tt>
2N/Aaggregation example above), the Java DTrace API also supports the
2N/A<
tt>$target</
tt> macro variable. (See the <
a 2N/A<
b>Target Process ID</
b></
a> section of the <
b>Scripting</
b> chapter of
2N/Athe <
i>Solaris Dynamic Tracing Guide</
i>.) This allows you to trace a
2N/Aprocess from the very beginning of its execution, rather than sometime
2N/Aafter you manually obtain its process ID. The API does this by creating
2N/Aa process that is initially suspended and allowed to start only after <
a 2N/A<
tt>go()</
tt></
a> has initiated tracing. For example, you can aggregate
2N/Aall the system calls from start to finish made by the <
tt>date</
tt>
2N/A @[probefunc] = count();
2N/A<
tt>createProcess()</
tt></
a> call to execute the given command but
2N/Aprevent it from starting until the consumer is running:<
br>
2N/A<
pre><
tt><
font color=#
aaaaaa>
2N/AIt also overrides the <
a 2N/A<
tt>processStateChanged()</
tt></
a> method of the
2N/A<
tt>ConsumerAdapter</
tt> to print a notification when the process has
2N/A<
pre><
tt><
font color=#
aaaaaa>
2N/A public void dataReceived(DataEvent e) {
2N/A public void consumerStopped(ConsumerEvent e) {
2N/A } catch (Exception x) {
2N/A public void processStateChanged(ProcessEvent e) {
2N/A }<
font color=#
aaaaaa>
2N/AThe consumer exits automatically when the target <
tt>date</
tt> process
2N/A<
h2><
a name="Closing_Consumers"></
a>Closing Consumers</
h2>
2N/AAn application using the Java DTrace API may run multiple consumers
2N/Asimultaneously. When a consumer stops running, the programmer is
2N/Aresponsible for closing it in order to release the system resources it
2N/Aholds. A consumer may stop running for any of the following reasons:
2N/A <
li>It was stopped explicitly by a call to its <
a 2N/A<
tt>stop()</
tt></
a> method</
li>
2N/A <
li>It encountered the <
tt>exit()</
tt> action</
li>
2N/A <
li>Its <
tt>$target</
tt> process or processes (if any) all completed</
li>
2N/A <
li>It encountered an exception</
li>
2N/ABy default, an exception prints a stack trace to <
tt>stderr</
tt> before
2N/Anotifying listeners that the consumer has stopped. You can define
2N/Adifferent behavior by setting an <
a 2N/A<
tt>ExceptionHandler</
tt></
a>, but the consumer is still stopped.<
br>
2N/AThe same listener that receives probe data generated by DTrace is also
2N/Anotified when the consumer stops. This is a good place to close the
2N/A<
pre><
tt><
font color=#
aaaaaa>
2N/A public void dataReceived(DataEvent e) {
2N/A public void consumerStopped(ConsumerEvent e) {
2N/A }<
font color=#
aaaaaa>
2N/AThis releases the resources held by the consumer in all cases,
i.e. 2N/Aafter it exits for <
i>any</
i> of the reasons listed above.<
br>
2N/AYou can request the last aggregate snapshot made by a stopped consumer,
2N/Aas long as it has not yet been closed:
2N/ANote however that any aggregation that has already appeared in a <
a 2N/A<
tt>PrintaRecord</
tt></
a> as a result of the <
tt>printa()</
tt> action
2N/Aaction will not be included in the requested aggregate.
2N/A<
h2><
a name="Learning_DTrace"></
a>Learning More</
h2>
2N/AOpenSolaris DTrace page</
a> has links to resources to help you learn
2N/ADTrace. In particular, you should read the <
a 2N/ATry the example Java programs on this page with other D scripts. You
2N/Aexecutable script. You may want to remove <
tt>profile:::tick*</
tt>
2N/Aclauses if you plan to use the <
tt>Consumer</
tt> <
a 2N/A<
tt>getAggregate()</
tt></
a> method and control the data interval
2N/Aprogrammatically. If the script uses the pre-compiler, you will need to
2N/Acall the <
tt>Consumer</
tt> <
a 2N/A<
tt>setOption()</
tt></
a> method with the <
a 2N/ATo quickly familiarize yourself with the Java DTrace API, take a look at
2N/A<
a href="#Quick_Start_Guide_to_the_Java_DTrace_API_">Back to top</
a><
br>