You don’t have conditional statements like if..then..else in DTrace, instead of them predicates are used.
Predicates are slash (/) wrapped conditions which are put right after the lines of probe definitions, defining whether DTrace should or should not executre the code of such probes.
Here’s an example. The following script will print the list of all system calls, excluding write, for all the processes currently running on our system. Since DTrace tracks events on a thread-level, you can see in this example that some PIDs are the same. This is because these lines were printed by probes fired for different threads of the same process:
#!/usr/sbin/dtrace -s #pragma D option quietsyscall::: /probefunc != "write"/ { printf("probefunc: %s, pid: %d, execname: %sn", probefunc, pid, execname); }
When you execute this script, you’ll get something similar to this:
probefunc: ioctl, pid: 28603, execname: dtrace probefunc: ioctl, pid: 28603, execname: dtrace probefunc: ioctl, pid: 28603, execname: dtrace probefunc: ioctl, pid: 28603, execname: dtrace probefunc: ioctl, pid: 28603, execname: dtrace probefunc: sysconfig, pid: 28603, execname: dtrace probefunc: sysconfig, pid: 28603, execname: dtrace probefunc: sysconfig, pid: 28603, execname: dtrace probefunc: sysconfig, pid: 28603, execname: dtrace probefunc: sigaction, pid: 28603, execname: dtrace probefunc: sigaction, pid: 28603, execname: dtrace probefunc: sigaction, pid: 28603, execname: dtrace probefunc: sigaction, pid: 28603, execname: dtrace ...
As you can see, our script even took a note of all the dtrace syscalls which was used to run the script.
To exclude dtrace from our data, we should alter the predicate to include one more condition:
/probefunc != "write" && execname != "dtrace"/
this will result in our output including all the processes but dtrace.
In my next example, we can see a couple of my rxvt terminals and java:
probefunc: pollsys, pid: 28411, execname: rxvt probefunc: ioctl, pid: 28411, execname: rxvt probefunc: ioctl, pid: 28411, execname: rxvt probefunc: pollsys, pid: 28411, execname: rxvt probefunc: pollsys, pid: 28406, execname: rxvt probefunc: ioctl, pid: 28406, execname: rxvt probefunc: ioctl, pid: 28406, execname: rxvt probefunc: pollsys, pid: 28406, execname: rxvt probefunc: lwp_cond_wait, pid: 27870, execname: java_vm probefunc: lwp_cond_signal, pid: 27870, execname: java_vm probefunc: lwp_cond_signal, pid: 27870, execname: java_vm probefunc: lwp_cond_wait, pid: 27870, execname: java_vm ...
Predicates allow you to use any constants and variables defined earlier in the code, or standard ones, just like the ones shows in my examples. And if you keep in mind, that local variables are unique on thread-level, you can build up pretty complex constructions.
Thread-specific local variables are defined with the help of selfindicator and a special -> operator, for instance:
self->flag = 1;
My next example will print only one message for each write syscall of every thread. As you can see in the code, every time we note a thread returning from writesyscall, we mark this thread with a local variable, so that we know the thread has been processed:
self->processed = 1;
Next time a writeprobe fires for this thread, the predicate will take care of it and skip the probe code execution if needed:
#!/usr/sbin/dtrace -s #pragma D option quiet
syscall::write:return / self->processed == 0/ { printf("tid: %d, pid: %d, execname: %sn", tid, pid, execname); self->processed = 1; }
As it’s clearly seen, every thread has been registered only once:
tid: 1, pid: 21583, execname: soffice.bin tid: 4, pid: 21750, execname: thunderbird-bin tid: 4, pid: 27406, execname: mozilla-bin tid: 1, pid: 27406, execname: mozilla-bin tid: 1, pid: 21450, execname: icewm tid: 4, pid: 21583, execname: soffice.bin tid: 1, pid: 21750, execname: thunderbird-bin tid: 1, pid: 28636, execname: dtrace
It’s impossibly to even try to cover all different ways of using DTrace predicates in only one entry, so I think I’ll call it a day. Next time I’ll show a few more ways of making your life easier.
Leave a Reply