The D keyword struct
, short for
structure, is used to introduce a new type
that is composed of a group of other types. The new
struct
type can be used as the type for D
variables and arrays, enabling you to define groups of related
variables under a single name. D structs are the same as the
corresponding construct in C and C++. If you have programmed in
the Java programming language previously, think of a D struct as
a class that contains only data members and no methods.
Suppose you want to create a more sophisticated system call
tracing program in D that records a number of things about each
read()
and write()
system
call that is executed by your shell, for example, the elapsed
time, number of calls, and the largest byte count passed as an
argument.
You could write a D clause to record these properties in three separate associative arrays, as shown in the following example:
int maxbytes[string]; /* declare maxbytes */ syscall::read:entry, syscall::write:entry /pid == 12345/ { ts[probefunc] = timestamp; calls[probefunc]++; maxbytes[probefunc] = arg2 > maxbytes[probefunc] ? arg2 : maxbytes[probefunc]; }
This clause, however, is inefficient because DTrace must create
three separate associative arrays and store separate copies of
the identical tuple values corresponding to
probefunc
for each one. Instead, you can
conserve space and make your program easier to read and maintain
by using a struct.
First, declare a new struct
type at the top
of the D program source file:
struct callinfo { uint64_t ts; /* timestamp of last syscall entry */ uint64_t elapsed; /* total elapsed time in nanoseconds */ uint64_t calls; /* number of calls made */ size_t maxbytes; /* maximum byte count argument */ };
The struct
keyword is followed by an optional
identifier that is used to refer back to the new type, which is
now known as struct callinfo
. The struct
members are then enclosed in a set of braces
{}
and the entire declaration is terminated
by a semicolon (;
). Each struct member is
defined by using the same syntax as a D variable declaration,
with the type of the member listed first followed by an
identifier naming the member and another semicolon
(;
).
The struct
declaration simply defines the new
type. It does not create any variables or allocate any storage
in DTrace. When declared, you can use struct
callinfo
as a type throughout the remainder of your D
program. Each variable of type struct
callinfo
stores a copy of the four variables that are
described by our structure template. The members are arranged in
memory in order, according to the member list, with padding
space introduced between members, as required for data object
alignment purposes.
You can use the member identifier names to access the individual
member values using the “.
” operator by
writing an expression of the following form:
variable-name
.member-name
The following example is an improved program that uses the new
structure type. In a text editor, type the following D program
and save it in a file named rwinfo.d
:
struct callinfo { uint64_t ts; /* timestamp of last syscall entry */ uint64_t elapsed; /* total elapsed time in nanoseconds */ uint64_t calls; /* number of calls made */ size_t maxbytes; /* maximum byte count argument */ }; struct callinfo i[string]; /* declare i as an associative array */ syscall::read:entry, syscall::write:entry /pid == $1/ { i[probefunc].ts = timestamp; i[probefunc].calls++; i[probefunc].maxbytes = arg2 > i[probefunc].maxbytes ? arg2 : i[probefunc].maxbytes; } syscall::read:return, syscall::write:return /i[probefunc].ts != 0 && pid == $1/ { i[probefunc].elapsed += timestamp - i[probefunc].ts; } END { printf(" calls max bytes elapsed nsecs\n"); printf("------ ----- --------- -------------\n"); printf(" read %5d %9d %d\n", i["read"].calls, i["read"].maxbytes, i["read"].elapsed); printf(" write %5d %9d %d\n", i["write"].calls, i["write"].maxbytes, i["write"].elapsed); }
When you have typed the program, run the dtrace -q -s
rwinfo.d command, specifying one of your shell
processes. Then, type a few commands in your shell. When you
have finished typing the shell commands, type
Ctrl-C to fire the END
probe and print the results:
#dtrace -q -s rwinfo.d `pgrep -n bash`
^C
calls max bytes elapsed nsecs ------ ----- --------- ------------- read 25 1024 8775036488 write 33 22 1859173