Variables and Arithmetic Expressions
Our next example program makes use of the DTrace profile provider to implement
a simple time-based counter. The profile provider is able to create new probes
based on the descriptions found in your D program. If you create a
probe named profile:::tick-nsec for some integer n, the profile provider will create a
probe that fires every n seconds. Type the following source code and save
it in a file named counter.d:
/*
* Count off and report the number of seconds elapsed
*/
dtrace:::BEGIN
{
i = 0;
}
profile:::tick-1sec
{
i = i + 1;
trace(i);
}
dtrace:::END
{
trace(i);
}
When executed, the program counts off the number of elapsed seconds until you
press Control-C, and then prints the total at the end:
# dtrace -s counter.d
dtrace: script 'counter.d' matched 3 probes
CPU ID FUNCTION:NAME
0 25499 :tick-1sec 1
0 25499 :tick-1sec 2
0 25499 :tick-1sec 3
0 25499 :tick-1sec 4
0 25499 :tick-1sec 5
0 25499 :tick-1sec 6
^C
0 2 :END 6
#
The first three lines of the program are a comment to explain
what the program does. Similar to C, C++, and the Java programming language,
the D compiler ignores any characters between the /* and */ symbols. Comments
can be used anywhere in a D program, including both inside and outside
your probe clauses.
The BEGIN probe clause defines a new variable named i and assigns
it the integer value zero using the statement:
i = 0;
Unlike C, C++, and the Java programming language, D variables can be created
by simply using them in a program statement; explicit variable declarations are not
required. When a variable is used for the first time in a
program, the type of the variable is set based on the type of
its first assignment. Each variable has only one type over the lifetime of
the program, so subsequent references must conform to the same type as the
initial assignment. In counter.d, the variable i is first assigned the integer constant
zero, so its type is set to int. D provides the same basic
integer data types as C, including:
char |
Character or single byte integer |
int |
Default integer |
short |
Short integer |
long |
Long
integer |
long long |
Extended long integer |
The sizes of these types are dependent on the operating system kernel's data
model, described in Chapter 2, Types, Operators, and Expressions. D also provides built-in friendly names for signed and
unsigned integer types of various fixed sizes, as well as thousands of other
types that are defined by the operating system.
The central part of counter.d is the probe clause that increments the counter
i:
profile:::tick-1sec
{
i = i + 1;
trace(i);
}
This clause names the probe profile:::tick-1sec, which tells the profile provider to create
a new probe which fires once per second on an available processor. The
clause contains two statements, the first assigning i to the previous value plus one,
and the second tracing the new value of i. All the usual C
arithmetic operators are available in D; the complete list is found in Chapter 2, Types, Operators, and Expressions.
Also as in C, the ++ operator can be used as shorthand for
incrementing the corresponding variable by one. The trace() function takes any D expression
as its argument, so you could write counter.d more concisely as follows:
profile:::tick-1sec
{
trace(++i);
}
If you want to explicitly control the type of the variable i, you
can surround the desired type in parentheses when you assign it in
order to cast the integer zero to a specific type. For example, if you
wanted to determine the maximum size of a char in D, you could
change the BEGIN clause as follows:
dtrace:::BEGIN
{
i = (char)0;
}
After running counter.d for a while, you should see the traced value grow
and then wrap around back to zero. If you grow impatient waiting for
the value to wrap, try changing the profile probe name to profile:::tick-100msec to
make a counter that increments once every 100 milliseconds, or 10 times per
second.