24 November, 2005

DTrace Translators

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.

1 comment:

Anonymous said...

I am having some issues trying to compile the following code. Can you provide some guidance here? Here is the code:
#!/usr/sbin/dtrace -s
typedef struct fc_frame_info {
int fc_r_ctl;
} fc_frame_info_t;

translator fc_frame_info_t < struct FC2_FRAME_HDR *T > {
fc_r_ctl = T->r_ctl;
};

when complied the following message is returned:
dtrace: failed to compile script ./fcpsnoop.d: line 21: operator -> cannot be applied to a forward declaration: no struct FC2_FRAME_HDR definition is available