I originally posted this at http://bdgregg.blogspot.com/2005/11/dtrace-translators-while-teaching.html.
While teaching a DTrace class in Sydney, I've been asked about translators. They are quite useful, so I've prepared the following as a quick demo.
This is a DTrace program to trace the time() syscall, print the process, it's parent, it's grand-parent, and so on.
#!/usr/sbin/dtrace -s /* Declare Translator */ typedef struct ancestory { string me; /* my cmd */ string p; /* parent cmd */ string gp; /* grand-parent cmd */ string ggp; /* great-grand-parent cmd */ string gggp; /* great-great-grand-parent cmd */ } ancestory_t; translator ancestory_t < struct _kthread *T > { /* fetch my details */ me = T->t_procp->p_user.u_comm; /* fetch anscestor details if they exist */ p = T->t_procp->p_parent != NULL ? T->t_procp->p_parent->p_user.u_comm : "<none>"; gp = T->t_procp->p_parent != NULL ? T->t_procp->p_parent->p_parent != NULL ? T->t_procp->p_parent->p_parent->p_user.u_comm : "<none>" : "<none>"; ggp = T->t_procp->p_parent != NULL ? T->t_procp->p_parent->p_parent != NULL ? T->t_procp->p_parent->p_parent->p_parent != NULL ? T->t_procp->p_parent->p_parent->p_parent->p_user.u_comm : "<none>" : "<none>" : "<none>"; gggp = T->t_procp->p_parent != NULL ? T->t_procp->p_parent->p_parent != NULL ? T->t_procp->p_parent->p_parent->p_parent != NULL ? T->t_procp->p_parent->p_parent->p_parent->p_parent != NULL ? T->t_procp->p_parent->p_parent->p_parent->p_parent->p_user.u_comm : "<none>" : "<none>" : "<none>" : "<none>"; }; inline ancestory_t *ancestors = xlate <ancestory_t *> (curthread); /* Main Program */ syscall::gtime:entry { printf("%s, %s, %s, %s, %s", ancestors->me, ancestors->p, ancestors->gp, ancestors->ggp, ancestors->gggp); }
The main program at the end is quite consise, it prints the details from "ancestors". The translator has walked the p_parent pointers carefully, returning "<none>" if the pointer is NULL. ("ancestors->me" is unnecessary since we have "execname", I've included it as a simple demonstration).
The output is:
# ./transdemo.d dtrace: script './transdemo.d' matched 1 probe CPU ID FUNCTION:NAME 0 6615 gtime:entry bash, sh, bash, sshd, sshd 0 6615 gtime:entry date, bash, sh, bash, sshd 0 6615 gtime:entry bash, sh, bash, sshd, sshd 0 6615 gtime:entry nscd, init, sched, <none>, <none> 0 6615 gtime:entry nscd, init, sched, <none>, <none> 0 6615 gtime:entry nscd, init, sched, <none>, <none> 0 6615 gtime:entry nscd, init, sched, <none>, <none> [...]
Without our careful pointer tests, the NULL parent pointers would have caused DTrace to print errors rather than our "<none>" keywords.
The "Declare Translator" section can be cut-n-pasted into a new .d file in /usr/lib/dtrace (eg, /usr/lib/dtrace/anscestors.d) where it will be automatically imported by every future DTrace script.
Take a look under /usr/lib/dtrace at the existing translator scripts, they are quite fascinating.