Solaris Dynamic Tracing Guide

Chapter 15 Scripting

You can use the dtrace(1M) utility to create interpreter files out of D programs similar to shell scripts that you can install as reusable interactive DTrace tools. The D compiler and dtrace command provide a set of macro variables that are expanded by the D compiler that make it easy to create DTrace scripts. This chapter provides a reference for the macro variable facility and tips for creating persistent scripts.

Interpreter Files

Similar to your shell and utilities such as awk(1) and perl(1), dtrace(1M) can be used to create executable interpreter files. An interpreter file begins with a line of the form:

#! pathname arg

where pathname is the path of the interpreter and arg is a single optional argument. When an interpreter file is executed, the system invokes the specified interpreter. If arg was specified in the interpreter file, it is passed as an argument to the interpreter. The path to the interpreter file itself and any additional arguments specified when it was executed are then appended to the interpreter argument list. Therefore, you will always need to create DTrace interpreter files with at least these arguments:

#!/usr/sbin/dtrace -s

When your interpreter file is executed, the argument to the -s option will therefore be the pathname of the interpreter file itself. dtrace will then read, compile, and execute this file as if you had typed the following command in your shell:


# dtrace -s interpreter-file

The following example shows how to create and execute a dtrace interpreter file. Type the following D source code and save it in a file named interp.d:

#!/usr/sbin/dtrace -s
BEGIN
{
	trace("hello");
	exit(0);
}

Mark the interp.d file as executable and execute it as follows:


# chmod a+rx interp.d
# ./interp.d
dtrace: script './interp.d' matched 1 probe
CPU     ID                    FUNCTION:NAME
  1      1                           :BEGIN   hello
#

Remember that the #! directive must comprise the first two characters of your file with no intervening or preceding whitespace. The D compiler knows to automatically ignore this line when it processes the interpreter file.

dtrace uses getopt(3C) to process command-line options, so you can combine multiple options in your single interpreter argument. For example, to add the -q option to the preceding example you could change the interpreter directive to:

#!/usr/sbin/dtrace -qs

If you specify multiple option letters, the -s option must always end the list of boolean options so that the next argument (the interpreter file name) is processed as the argument corresponding to the -s option.

If you need to specify more than one option that requires an argument in your interpreter file, you will not be able to fit all your options and arguments into the single interpreter argument. Instead, use the #pragma D option directive syntax to set your options. All of the dtrace command-line options have #pragma equivalents that you can use, as shown in Chapter 16, Options and Tunables.

Macro Variables

The D compiler defines a set of built-in macro variables that you can use when writing D programs or interpreter files. Macro variables are identifiers that are prefixed with a dollar sign ($) and are expanded once by the D compiler when processing your input file. The D compiler provides the following macro variables:

Table 15–1 D Macro Variables

Name 

Description 

Reference 

$[0-9]+

macro arguments 

See Macro Arguments

$egid

effective group-ID 

getegid(2)

$euid

effective user-ID 

geteuid(2)

$gid

real group-ID 

getgid(2)

$pid

process ID 

getpid(2)

$pgid

process group ID 

getpgid(2)

$ppid

parent process ID 

getppid(2)

$projid

project ID 

getprojid(2)

$sid

session ID 

getsid(2)

$target

target process ID 

See Target Process ID

$taskid

task ID 

gettaskid(2)

$uid

real user-ID 

getuid(2)

Except for the $[0-9]+ macro arguments and the $target macro variable, the macro variables all expand to integers corresponding to system attributes such as the process ID and user ID. The variables expand to the attribute value associated with the current dtrace process itself, or whatever process is running the D compiler.

Using macro variables in interpreter files enables you to create persistent D programs that do not need to be edited each time you want to use them. For example, to count all system calls except those executed by the dtrace command, you can use the following D program clause containing $pid:

syscall:::entry
/pid != $pid/
{
	@calls = count();
}

This clause always produces the desired result, even though each invocation of the dtrace command will have a different process ID.

Macro variables can be used anywhere an integer, identifier, or string can be used in a D program. Macro variables are expanded only once (that is, not recursively) when the input file is parsed. Each macro variable is expanded to form a separate input token, and cannot be concatenated with other text to yield a single token. For example, if $pid expands to the value 456, the D code:

123$pid

would expand to the two adjacent tokens 123 and 456, resulting in a syntax error, rather than the single integer token 123456.

Macro variables are expanded and concatenated with adjacent text inside of D probe descriptions at the start of your program clauses. For example, the following clause uses the DTrace pid provider to instrument the dtrace command:

pid$pid:libc.so:printf:entry
{
	...
}

Macro variables are only expanded once within each probe description field; they may not contain probe description delimiters (:).

Macro Arguments

The D compiler also provides a set of macro variables corresponding to any additional argument operands specified as part of the dtrace command invocation. These macro arguments are accessed using the built-in names $0 for name of the D program file or dtrace command, $1 for the first additional operand, $2 for the second operand, and so on. If you use the dtrace -s option, $0 expands to the value of the name of the input file used with this option. For D programs specified on the command-line, $0 expands to the value of argv[0] used to exec dtrace itself.

Macro arguments can expand to integers, identifiers, or strings, depending on the form of the corresponding text. As with all macro variables, macro arguments can be used anywhere integer, identifier, and string tokens can be used in a D program. All of the following examples could form valid D expressions assuming appropriate macro argument values:

execname == $1    /* with a string macro argument */
x += $1           /* with an integer macro argument */
trace(x->$1)      /* with an identifier macro argument */

Macro arguments can be used to create dtrace interpreter files that act like real Solaris commands and use information specified by a user or by another tool to modify their behavior. For example, the following D interpreter file traces write(2) system calls executed by a particular process ID:

#!/usr/sbin/dtrace -s

syscall::write:entry
/pid == $1/
{
}

If you make this interpreter file executable, you can specify the value of $1 using an additional command-line argument to your interpreter file:


# chmod a+rx ./tracewrite
# ./tracewrite 12345

The resulting command invocation counts each write(2) system call executed by process ID 12345.

If your D program references a macro argument that is not provided on the command-line, an appropriate error message will be printed and your program will fail to compile:


# ./tracewrite
dtrace: failed to compile script ./tracewrite: line 4:
  macro argument $1 is not defined

D programs can reference unspecified macro arguments if the defaultargs option is set. If defaultargs is set, unspecified arguments will have the value 0. See Chapter 16, Options and Tunables for more information about D compiler options. The D compiler will also produce an error message if additional arguments are specified on the command line that are not referenced by your D program.

The macro argument values must match the form of an integer, identifier, or string. If the argument does not match any of these forms, the D compiler will report an appropriate error message. When specifying string macro arguments to a DTrace interpreter file, surround the argument in an extra pair of single quotes to avoid interpretation of the double quotes and string contents by your shell:


# ./foo '"a string argument"'

If you want your D macro arguments to be interpreted as string tokens even if they match the form of an integer or identifier, prefix the macro variable or argument name with two leading dollar signs (for example, $$1) to force the D compiler to interpret the argument value as if it were a string surrounded by double quotes. All the usual D string escape sequences (see Table 2–5) are expanded inside of any string macro arguments, regardless of whether they are referenced using the $arg or $$arg form of the macro. If the defaultargs option is set, unspecified arguments that are referenced with the $$arg form have the value of the empty string ("").

Target Process ID

Use the $target macro variable to create scripts that can be applied to a particular user process of interest that is selected on the dtrace command line using the -p option or created using the -c option. The D programs specified on the command line or using the -s option are compiled after processes are created or grabbed and the $target variable expands to the integer process-ID of the first such process. For example, the following D script could be used to determine the distribution of system calls executed by a particular subject process:

syscall:::entry
/pid == $target/
{
	@[probefunc] = count();
}

To determine the number of system calls executed by the date(1) command, save the script in the file syscall.d and execute the following command:


# dtrace -s syscall.d -c date
dtrace: script 'syscall.d' matched 227 probes
Fri Jul 30 13:46:06 PDT 2004
dtrace: pid 109058 has exited

  gtime                                                             1
  getpid                                                            1
  getrlimit                                                         1
  rexit                                                             1
  ioctl                                                             1
  resolvepath                                                       1
  read                                                              1
  stat                                                              1
  write                                                             1
  munmap                                                            1
  close                                                             2
  fstat64                                                           2
  setcontext                                                        2
  mmap                                                              2
  open                                                              2
  brk                                                               4