Quite ofter we’d like to know what happens to a particular process on a system, and if we’re using DTrace for our investigation, we’d probably use the $target variable (I’ve already spoken about it in the past) or simply specify the process identifier (pid) in some predicates of our probes, thus pointing to DTrace that we’re after only a certain process. $target variable still can and should be used when creating our probes with pid provider.
For DTrace beginners it’s quite a common and good practice. But there is a better way: using the pid provider.
pid provider in DTrace
As it’s explained in the official DTrace guide, pid provider isn’t just a provider, but in fact is a whole class of providers. And, as the name suggests, it provides us with probes related to a certain process.
For instance, pid737 provider allows us to trace everything happening within a process with pid of 737. What’s really cool about this provide – it’s not only easier to scope our interest, but it also gets much easier to see how where certain probes originate from (what libraries provide certain functions used by our process, read on – I’ll explain it a bit later).
Such a way of looking at a certain process, in contrast to using predicates like /pid == $target/, is more correct. Instead of enabling all the specified probes by default, and then using predicates for each probe firing for each process on the system only to see whether we’re interested in a given process, DTrace won’t even look at other processes – and pid737 will make sure that all the probes specified will be enabled and possibly fired only for the specified process.
How DTrace pid provider works
We already know how to get the list of all the probes known to DTrace: use dtrace -l command. The thing you need to remember about the pid provider is this: after you’ve used pid provider in some probes for a process, dtrace -l will start reporting you these probes at the end of all the available probes. They’ve been dynamically allocated just for you. If the process in question dies or finishes, all the dynamically allocated probes for its pid provider will disappear.
One more thing I really like about pid provider – modules used in probes are in fact libraries used for the execution of the process you’re looking at. This gives you a wonderful opportunity of observing only what’s been done in your process by functions of a particular library. Very useful.
Right, enough of theory – here’s an example. First, we create a simple bit of “Hello, world!” code in C:
void main() {
printf("Hello, world!n");
}
After you compile it, let’s get going with out DTrace script:
#!/usr/sbin/dtrace -s #pragma D option quiet BEGIN { printf("Target pid: %dn", $target); }
pid$target:::entry { printf("%s:%s:%s:%sn", probeprov, probemod, probefunc, probename); }
Save the script, make it executable, and start it, specifying our hello as a command to start:
solaris# ./pid-global.d -c ./hello | more Target pid: 1059 Hello, world! pid1059:LM1`ld.so.1:call_array:entry pid1059:LM1`ld.so.1:call_init:entry pid1059:LM1`ld.so.1:leave:entry pid1059:LM1`ld.so.1:fmap_setup:entry pid1059:LM1`ld.so.1:munmap:entry pid1059:LM1`ld.so.1:rt_bind_clear:entry pid1059:LM1`ld.so.1:_rt_bind_clear:entry pid1059:LM1`ld.so.1:rt_mutex_unlock:entry pid1059:LM1`ld.so.1:_rt_null:entry pid1059:LM1`ld.so.1:rt_bind_clear:entry pid1059:LM1`ld.so.1:_rt_bind_clear:entry pid1059:libc.so.1:libc_init:entry pid1059:libc.so.1:__amd64id:entry pid1059:libc.so.1:atexit:entry pid1059:libc.so.1:lmalloc:entry pid1059:libc.so.1:getbucketnum:entry ...
You see this list of probes? I won’t give you the full list – it’s going to be almost a 1000 lines, but I hope you get the idea. It’s also a perfect opportunity to see how easy it’s going to be for you to track probes of a particular library.
Back to our example, it’s obvious what I’m going to do now: I’d like to get our printf caught with a probe.
#!/usr/sbin/dtrace -s #pragma D option quiet BEGIN { printf("Target pid: %dn", $target); }
pid$target::printf:entry { printf("%s:%s:%s:%sn", probeprov, probemod, probefunc, probename); printf("Argument: %sn", copyinstr(arg0)); }
If we start tihs script, here’s what you’ll get:
solaris# ./pid-printf.d -c ./hello
Target pid: 1201
Hello, world!
pid1201:libc.so.1:printf:entry
Argument: Hello, world!
Pas Argenio says
Nice. At least your example is complete and works, unlike Sun’s (Example 33-1 in Solaris Dynamic Tracing Guide).
One wonders how to apply this to an already running program. What magic sets the $target variable?
Gleb Reys says
Thanks for your comment, Pas! Great to have helped!
sabbahillel says
You said
“We already know how to get the list of all the probes known to DTrace. I’ve explained it a while ago.”
This link is no longer available.
sabbahillel says
You said:
This link is no longer available.
Gleb Reys says
Thank you for pointing the broken links out, just fixed them!
Arnoud Buzing says
Is it possible to have multiple pid probes like so (if you want to monitor two or more processes):
#!/usr/bin/dtrace -s
#pragma D option quiet
pid$1:::entry {
printf(“%s:%s:%s:%s\n”, probeprov, probemod, probefunc, probename);
}
pid$2:::entry {
printf(“%s:%s:%s:%s\n”, probeprov, probemod, probefunc, probename);
}