Adam Leventhal
在github
上建立了一个dtrace-example
项目,介绍如何使用DTrace
的API
做一个单独的trace
工具。在这篇文章里,我就对这个项目的源码做一个简单的分析。
dtrace-example.c
包含了4
个函数,其中最主要的是main
函数,代码如下:
int
main(int argc, char **argv)
{
int err;
if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) {
die("failed to initialize: %s\n",
dtrace_errmsg(NULL, err));
}
if (dtrace_setopt(g_dtp, "bufsize", "1k") == -1)
ddie("failed to set 'bufsize'");
dtrace_prog_t *prog;
dtrace_proginfo_t info;
if ((prog = dtrace_program_strcompile(g_dtp, g_script,
DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) {
die("failed to compile\n");
}
if (dtrace_program_exec(g_dtp, prog, &info) == -1)
die("failed to enable probes\n");
if (dtrace_go(g_dtp) != 0)
ddie("failed to start");
int done = 0;
do {
if (!g_exit && !done)
dtrace_sleep(g_dtp);
if (g_exit || done) {
done = 1;
if (dtrace_stop(g_dtp) == -1)
ddie("failed to stop");
}
switch (dtrace_work(g_dtp, stdout, NULL, chewrec, NULL)) {
case DTRACE_WORKSTATUS_DONE:
done = 1;
break;
case DTRACE_WORKSTATUS_OKAY:
break;
default:
die("processing aborted");
}
} while (!done);
dtrace_close(g_dtp);
return (0);
}
(1)程序首先调用dtrace_open
函数,返回一个dtrace handle
。第一个参数是DTrace
的版本;第二个参数是一些flags
(例如DTRACE_O_NODEV
,表示不打开dtrace
设备);第三个参数则是在出错时存放错误值。
(2)得到dtrace handle
后,调用dtrace_setopt
函数设置dtrace
的缓冲区大小为1k
。
(3)调用dtrace_program_strcompile
函数编译g_script
所指向的字符串(内容是一个简单的DTrace
脚本)。
(4)编译成功后,调用dtrace_program_exec
开始执行编译好的程序,也即使probe
生效。
(5)dtrace_go
函数开始trace
的工作。
(6)在接下的循环中包含两个动作:dtrace_sleep
和dtrace_work
。dtrace_sleep
会阻塞程序等待事件的发生,但不会超过dtrace handle
所设定的时间。而dtrace_work
则注册了chewrec
这个callback
函数,用来消化得到的记录。
(7)最后dtrace_close
优雅地关闭dtrace handle
。
接下来看一下chewrec
这个函数:
static int
chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg)
{
if (rec == NULL)
return (DTRACE_CONSUME_NEXT);
switch (rec->dtrd_action) {
case DTRACEACT_DIFEXPR:
(void) printf("%s\n", data->dtpda_data);
return (DTRACE_CONSUME_NEXT);
case DTRACEACT_EXIT:
g_exit = 1;
return (DTRACE_CONSUME_NEXT);
default:
(void) printf("%d\n", rec->dtrd_action);
return (DTRACE_CONSUME_NEXT);
}
return (DTRACE_CONSUME_THIS);
}
可以看到这个函数根据dtrace action
的不同,或者打印原始的数据(printf("%s\n", data->dtpda_data)
),或者准备退出(g_exit = 1
)。
另外两个函数(die
和ddie
)都是打印log
而已,就不细说了。
以下是编译和执行这个程序得到的结果:
root# make
cc -o dtrace-example dtrace-example.c -ldtrace
root# ./dtrace-example
Hello, DTrace API!