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 Oracle Linux
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()
system call:
ssize_t write(intfd, const void *buf, size_tnbytes);
The following D program illustrates an incorrect attempt to print
the contents of a string passed to the write()
system call:
syscall::write:entry
{
printf("%s", stringof(arg1)); /* incorrect use of arg1 */
}If you try to run this script, DTrace produces 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() 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()
system call might refer to binary data rather than ASCII strings
or to ASCII strings which do not include a terminating null byte.
To print only as much of the string as the caller intended, use
the two parameter version of the copyinstr
subroutine which includes the size of the targeted string buffer:
syscall::write:entry { printf("%s", copyinstr(arg1, arg2)); }
Alternatively, use the copyin subroutine which
takes an address and size:
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 with
copyinstr because it always returns type
string.
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
1 8 open:entry /dev/sr0
1 8 open:entry /var/run/utmp
1 8 open:entry /dev/sr0
dtrace: error on enabled probe ID 2 (ID 8: syscall::open:entry): \
invalid address (0x9af1b) in action #1 at DIF offset 52
In the 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 dtrace: description 'syscall::open:return' matched 1 probe CPU ID FUNCTION:NAME 0 9 open:return /dev/sr0 1 9 open:return /usr/lib64/gconv/gconv-modules.cache 0 9 open:return /dev/sr0 0 9 open:return public/pickup 1 9 open:return maildrop 1 9 open:return /dev/sr0 1 9 open:return /dev/sr0 1 9 open:return /var/run/utmp ...