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-
n
sec
for some integer n
, the profile
provider creates 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 Ctrl-C
, and then prints
the total at the end:
#dtrace -s counter.d
dtrace: script 'counter.d' matched 3 probes CPU ID FUNCTION:NAME 1 638 :tick-1sec 1 1 638 :tick-1sec 2 1 638 :tick-1sec 3 1 638 :tick-1sec 4 1 638 :tick-1sec 5 1 638 :tick-1sec 6 1 638 :tick-1sec 7^C
1 638 :tick-1sec 8 0 2 :END 8
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 those in the following
table.
Data Type | Description |
---|---|
| Character or single byte integer |
| Default integer |
| Short integer |
| Long integer |
| Extended long integer |
The sizes of these types are dependent on the operating system kernel's data model, described in Section 2.8, “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 that
fires once per second on an available processor. The clause
contains two statements, the first incrementing
i
, and the second tracing (printing) the new
value of i
. All the usual C arithmetic
operators are available in D. For the complete list, see
Section 2.8, “Types, Operators, and Expressions”. 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.