copyin() and copyinstr() Subroutines
DTrace's interaction with processes is a little different than most traditional debuggers or observability
tools. Many such tools appear to execute within the scope of the process,
letting users dereference pointers to program variables directly. Rather than appearing to execute
within or as part of the process itself, DTrace probes execute in the
Solaris kernel. To access process data, a probe needs to use the copyin()
or copyinstr() subroutines to copy user process data into the address space of
the kernel.
For example, consider the following write(2) system call:
ssize_t write(int fd, const void *buf, size_t nbytes);
The following D program illustrates an incorrect attempt to print the contents of
a string passed to the write(2) system call:
syscall::write:entry
{
printf("%s", stringof(arg1)); /* incorrect use of arg1 */
}
If you try to run this script, DTrace will produce error messages
similar to the following example:
dtrace: error on enabled probe ID 1 (ID 37: syscall::write:entry): \
invalid address (0x10038a000) in action #1
The arg1 variable, containing the value of the buf parameter, is an
address that refers to memory in the process executing the system call. To
read the string at that address, use the copyinstr() subroutine and record
its result with the printf() action:
syscall::write:entry
{
printf("%s", copyinstr(arg1)); /* correct use of arg1 */
The output of this script shows all of the strings being passed
to the write(2) system call. Occasionally, however, you might see irregular output similar
to the following example:
0 37 write:entry mada���
The copyinstr() subroutine acts on an input argument that is the user address
of a null-terminated ASCII string. However, buffers passed to the write(2) system call might
refer to binary data rather than ASCII strings. To print only as much
of the string as the caller intended, use the copyin() subroutine, which takes
a size as its second argument:
syscall::write:entry
{
printf("%s", stringof(copyin(arg1, arg2)));
}
Notice that the stringof operator is necessary so that DTrace properly converts the
user data retrieved using copyin() to a string. The use of stringof is
not necessary when using copyinstr() because this function always returns type string.
Avoiding Errors
The copyin() and copyinstr() subroutines cannot read from user addresses which have not
yet been touched so even a valid address may cause an error if
the page containing that address has not yet been faulted in by being
accessed. Consider the following example:
# dtrace -n syscall::open:entry'{ trace(copyinstr(arg0)); }'
dtrace: description 'syscall::open:entry' matched 1 probe
CPU ID FUNCTION:NAME
dtrace: error on enabled probe ID 2 (ID 50: syscall::open:entry): invalid address
(0x9af1b) in action #1 at DIF offset 52
In the above example output, the application was functioning properly, and the address
in arg0 was valid, but it referred to a page that had not
yet been accessed by the corresponding process. To resolve this issue, wait for
kernel or application to use the data before tracing it. For example, you
might wait until the system call returns to apply copyinstr(), as shown
in the following example:
# dtrace -n syscall::open:entry'{ self->file = arg0; }' \
-n syscall::open:return'{ trace(copyinstr(self->file)); self->file = 0; }'
dtrace: description 'syscall::open:entry' matched 1 probe
CPU ID FUNCTION:NAME
2 51 open:return /dev/null