Exercise 1
This exercise is intended to learn some features of dynamic tracing languages that was discussed in modules 1 and 2. First of all we need to pick probes that we will use in our tracing script. They would be parts of syscall
provider/tapset. As you can remember from stap command options, probe parameters can be checked with -L
option:
# stap -L 'syscall.open' syscall.open name:string filename:string flags:long mode:long argstr:string $filename:long int $flags:long int $mode:long int # stap -L 'syscall.open.return' syscall.open.return name:string retstr:string $return:long int $filename:long int $flags:long int $mode:long intSame can be done for dtrace with
-l
option:
# dtrace -l -f openat\* -v ID PROVIDER MODULE FUNCTION NAME 14167 syscall openat entry ...
Return value (which would represent file descriptor number) will be saved into $return
variable in SystemTap and arg1
argument in DTrace. We will also need flags values: arg2
in DTrace (because they are going third in openat()
prototype). In SystemTap you can use either DWARF variable $flags
or tapset variable flags
. Latter is more stable.
Similarly, path to opened file will be passed as second openat()
argument and will be available in DTrace as arg1
or $filename
/filename
in SystemTap. At the moment of system call, however, file path will be a string which is located in user address space, so to get it in tracing script, you will need to copy it by using copyinstr()
in DTrace or one of user_string*()
functions. Tapset variable already uses user_string_quoted()
to access variable, so we will use it in our scripts.
Note that data that we want gather is available in two different probes: flags and file path are provided by entry probe, while file descriptor number can only be collected in return probe (SystemTap can provide flags and file path in return probe, but as we mentioned, it depends on compiler optimizations). Since both probes will be executed in the same context, we can use thread-local variables.
Finally, stringifying flags will require usage of ternary operator ?:
in DTrace or if/else
construct in SystemTap. To concatenate strings, use strjoin
from DTrace or string concatenation operator .
in SystemTap.
Here are resulting DTrace script which implements required functionality:
/* These constants are already defined in /usr/lib/dtrace/io.d inline int O_WRONLY = 1; inline int O_RDWR = 2; inline int O_APPEND = 8; inline int O_CREAT = 256; */ this string flag_str; syscall::openat*:entry { self->path = copyinstr(arg1); self->flags = arg2; } syscall::openat*:return { this->flags_str = strjoin( self->flags & O_WRONLY ? "O_WRONLY" : self->flags & O_RDWR ? "O_RDWR" : "O_RDONLY", strjoin( self->flags & O_APPEND ? "|O_APPEND" : "", self->flags & O_CREAT ? "|O_CREAT" : "")); printf("%s[%d(%d:%d)] open(\"%s\", %s) = %d\n", execname, pid, uid, gid, self->path, this->flags_str, arg1); }
sprintf()
to concatenate strings in SystemTap version of a script:
global O_WRONLY = 1; global O_RDWR = 2; global O_APPEND = 1024; global O_CREAT = 64; global t_path, t_flags; probe syscall.open { t_path[tid()] = filename; t_flags[tid()] = flags; } probe syscall.open.return { flags = t_flags[tid()]; if(flags & O_RDWR) { flags_str = "O_RDWR"; } else if(flags & O_WRONLY) { flags_str = "O_WRONLY"; } else { flags_str = "O_RDONLY"; } if(flags & O_APPEND) { flags_str = sprintf("%s|%s", flags_str, "O_APPEND"); } if(flags & O_CREAT) { flags_str = sprintf("%s|%s", flags_str, "O_CREAT"); } printf("%s[%d(%d:%d)] open(%s, %s) = %d\n", execname(), pid(), uid(), gid(), t_path[tid()], flags_str, $return); }
/etc
in DTrace by using strstr()
subroutine and comparing it with 0
and SystemTap's ininstr()
function.