用 C 编写 DTrace 使用者
Writing a DTrace consumer in C
我想在 FreeBSD 10.1 上编写一个 C 程序,使用 libdtrace
.
实现 DTrace 消费者
我知道我需要先调用 dtrace_open()
- 例如我找到了 this 旧的演示文稿,但我什至无法开始,因为没有安装 dtrace.h
(仅在系统源代码树中)。
共享库已安装,例如FreeBSD 附带的 /usr/sbin/dtrace
工具可以充当 DTrace 使用者,此工具 links 到 /lib/libdtrace.so.2
(也通过 [=18= 的符号 link 指向]).
任何基本示例,包括构建说明 (FreeBSD 10.1 / clang) 都会对我有很大帮助。
写自定义消费者的实际目的是创建一个CFFI based wrapper usable in Python and PyPy. 意思是:上面的C程序只是入门,学习再进行。
CFFI 是推荐的现代高性能 PyPy 与共享库接口方式。
CFFI 可用于 ABI 和 API 级别。后者需要一个头文件来包含,前者需要从一个库中声明使用的东西。
改编自 Adam 的回答,这是一个适用于 FreeBSD 10.1 的完整示例。
Makefile
:
all:
cc \
-I /usr/src/cddl/compat/opensolaris/include \
-I /usr/src/cddl/contrib/opensolaris/lib/libdtrace/common/ \
-I /usr/src/sys/cddl/compat/opensolaris \
-I /usr/src/sys/cddl/contrib/opensolaris/uts/common/ \
hello_dtrace.c \
-l dtrace -l proc -l ctf -l elf -l z -l rtld_db -l pthread -l util \
-o hello_dtrace
hello_dtrace.c
:
#include <dtrace.h>
#include <signal.h>
#include <stdio.h>
static dtrace_hdl_t* g_dtp;
static int chewrec (const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) {
printf("chewing dtrace record ..\n");
// A NULL rec indicates that we've processed the last record.
if (rec == NULL) {
return (DTRACE_CONSUME_NEXT);
}
return (DTRACE_CONSUME_THIS);
}
static const char* g_prog = "BEGIN { printf(\"hello from dtrace\n\"); }";
//static const char* g_prog = "syscall::open*:entry { printf(\"%s %s\n\", execname, copyinstr(arg0)); }";
static int g_intr;
static int g_exited;
static void intr (int signo) {
g_intr = 1;
}
int main (int argc, char** argv) {
int err;
if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) {
fprintf(stderr, "failed to initialize dtrace: %s\n", dtrace_errmsg(NULL, err));
return -1;
}
printf("Dtrace initialized\n");
(void) dtrace_setopt(g_dtp, "bufsize", "4m");
(void) dtrace_setopt(g_dtp, "aggsize", "4m");
printf("dtrace options set\n");
dtrace_prog_t* prog;
if ((prog = dtrace_program_strcompile(g_dtp, g_prog, DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) {
fprintf(stderr, "failed to compile dtrace program\n");
return -1;
} else {
printf("dtrace program compiled\n");
}
dtrace_proginfo_t info;
if (dtrace_program_exec(g_dtp, prog, &info) == -1) {
fprintf(stderr, "failed to enable dtrace probes\n");
return -1;
} else {
printf("dtrace probes enabled\n");
}
struct sigaction act;
(void) sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = intr;
(void) sigaction(SIGINT, &act, NULL);
(void) sigaction(SIGTERM, &act, NULL);
if (dtrace_go(g_dtp) != 0) {
fprintf(stderr, "could not start instrumentation\n");
return -1;
} else {
printf("instrumentation started ..\n");
}
int done = 0;
do {
if (!g_intr && !done) {
dtrace_sleep(g_dtp);
}
if (done || g_intr || g_exited) {
done = 1;
if (dtrace_stop(g_dtp) == -1) {
fprintf(stderr, "could not stop tracing\n");
return -1;
}
}
switch (dtrace_work(g_dtp, stdout, NULL, chewrec, NULL)) {
case DTRACE_WORKSTATUS_DONE:
done = 1;
break;
case DTRACE_WORKSTATUS_OKAY:
break;
default:
fprintf(stderr, "processing aborted");
return -1;
}
} while (!done);
printf("closing dtrace\n");
dtrace_close(g_dtp);
return 0;
}
至运行:
[oberstet@brummer2 ~/hello_dtrace]$ make; sudo ./hello_dtrace
cc -I /usr/src/cddl/contrib/opensolaris/lib/libdtrace/common/ -I /usr/src/sys/cddl/compat/opensolaris -I /usr/src/sys/cddl/contrib/opensolaris/uts/common/ hello_dtrace.c -l dtrace -l proc -l ctf -l elf -l rtld_db -l z -l pthread -l util -o hello_dtrace
Dtrace initialized
dtrace options set
dtrace program compiled
dtrace probes enabled
instrumentation started ..
chewing dtrace record ..
hello from dtrace
chewing dtrace record ..
^Cclosing dtrace
dtrace 被认为是系统内部接口,不支持编写自定义使用者。
您最好的办法是检查 FreeBSD 源代码树的副本并从那里构建您的代码。这实际上并不十分困难。显然,dtrace(1)
是规范的 dtrace 使用者,因此您可以查看其实现以了解如何使用 libdtrace 的示例。此外 dtrace.h
有大量解释数据结构和内部结构的评论。
在源代码树的上下文中构建文件非常容易; FreeBSD 源代码树布局很简单,编写 Makefile 以自包含的方式构建二进制文件基本上已经给了你 "for free".
相关要点:
将 the dtrace(1) Makefile 克隆到源树结帐中的新目录中。修改 Makefile 以使 .PATH
正确,并设置 PROG
和 SRCS
以包含包含自定义消费者的源集。
克隆 dtrace.c 到您的源目录(无论您将 .PATH
指向何处)并根据需要进行更改。尽管源代码将近 2,000 行,但其中大部分是支持代码。如果你只是想克隆功能的一个子集,你会发现二进制文件的大部分选项都是在独立的函数中实现的,因此应该很容易 trim dtrace.c
降至最低限度。
如果不知道您具体需要对自定义使用者做什么,就很难告诉您还需要调用什么。我假设您可能需要 compile_file in there as well as exec_prog。当然,您的需求可能会有所不同。
dtrace.h
you will be using 对提供的各种接口有很多评论。
希望这足以让您入门;不幸的是,没有办法从普通安装中做你想做的事。当然,您可以破解相关的 Makefile 以安装必要的头文件并创建您自己的内部分发版。但这似乎比它值得的更痛苦。
libdtrace
API 不一定适用于稳定的消费者,但从一些现有消费者那里学习是很容易上手的。最简单和最现代的是 plockstat
,用于用户空间锁定统计的 illumos 实用程序。
下面是一个简单的 DTrace 消费者程序的基本流程:
dtrace_open()
以获得 dtrace_hdl_t,其他 libdtrace
交互的句柄
dtrace_setopt()
配置选项(-x
标记为 dtrace(1M)
)
dtrace_strcompile()
编译你的 D 程序的字符串
dtrace_program_exec()
将该程序发送到内核
dtrace_go()
检测系统并开始记录数据
dtrace_close()
最后清理
对于主要数据收集循环(在 dtrace_go()
和 dtrace_close()
之间)执行以下操作:
dtrace_sleep()
根据 DTrace 选项暂停(switchrate 和 aggrate)
dtrace_work()
处理跟踪数据
dtrace_stop()
中止
有关更多信息,请参阅 plockstat.c
中的主循环。
对于其他简单的 DTrace 使用者,请查看 intrstat
and lockstat
。对于厨房水槽,查看 dtrace(1M) 命令行实用程序的代码。
我想在 FreeBSD 10.1 上编写一个 C 程序,使用 libdtrace
.
我知道我需要先调用 dtrace_open()
- 例如我找到了 this 旧的演示文稿,但我什至无法开始,因为没有安装 dtrace.h
(仅在系统源代码树中)。
共享库已安装,例如FreeBSD 附带的 /usr/sbin/dtrace
工具可以充当 DTrace 使用者,此工具 links 到 /lib/libdtrace.so.2
(也通过 [=18= 的符号 link 指向]).
任何基本示例,包括构建说明 (FreeBSD 10.1 / clang) 都会对我有很大帮助。
写自定义消费者的实际目的是创建一个CFFI based wrapper usable in Python and PyPy. 意思是:上面的C程序只是入门,学习再进行。
CFFI 是推荐的现代高性能 PyPy 与共享库接口方式。
CFFI 可用于 ABI 和 API 级别。后者需要一个头文件来包含,前者需要从一个库中声明使用的东西。
改编自 Adam 的回答,这是一个适用于 FreeBSD 10.1 的完整示例。
Makefile
:
all:
cc \
-I /usr/src/cddl/compat/opensolaris/include \
-I /usr/src/cddl/contrib/opensolaris/lib/libdtrace/common/ \
-I /usr/src/sys/cddl/compat/opensolaris \
-I /usr/src/sys/cddl/contrib/opensolaris/uts/common/ \
hello_dtrace.c \
-l dtrace -l proc -l ctf -l elf -l z -l rtld_db -l pthread -l util \
-o hello_dtrace
hello_dtrace.c
:
#include <dtrace.h>
#include <signal.h>
#include <stdio.h>
static dtrace_hdl_t* g_dtp;
static int chewrec (const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) {
printf("chewing dtrace record ..\n");
// A NULL rec indicates that we've processed the last record.
if (rec == NULL) {
return (DTRACE_CONSUME_NEXT);
}
return (DTRACE_CONSUME_THIS);
}
static const char* g_prog = "BEGIN { printf(\"hello from dtrace\n\"); }";
//static const char* g_prog = "syscall::open*:entry { printf(\"%s %s\n\", execname, copyinstr(arg0)); }";
static int g_intr;
static int g_exited;
static void intr (int signo) {
g_intr = 1;
}
int main (int argc, char** argv) {
int err;
if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) {
fprintf(stderr, "failed to initialize dtrace: %s\n", dtrace_errmsg(NULL, err));
return -1;
}
printf("Dtrace initialized\n");
(void) dtrace_setopt(g_dtp, "bufsize", "4m");
(void) dtrace_setopt(g_dtp, "aggsize", "4m");
printf("dtrace options set\n");
dtrace_prog_t* prog;
if ((prog = dtrace_program_strcompile(g_dtp, g_prog, DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) {
fprintf(stderr, "failed to compile dtrace program\n");
return -1;
} else {
printf("dtrace program compiled\n");
}
dtrace_proginfo_t info;
if (dtrace_program_exec(g_dtp, prog, &info) == -1) {
fprintf(stderr, "failed to enable dtrace probes\n");
return -1;
} else {
printf("dtrace probes enabled\n");
}
struct sigaction act;
(void) sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = intr;
(void) sigaction(SIGINT, &act, NULL);
(void) sigaction(SIGTERM, &act, NULL);
if (dtrace_go(g_dtp) != 0) {
fprintf(stderr, "could not start instrumentation\n");
return -1;
} else {
printf("instrumentation started ..\n");
}
int done = 0;
do {
if (!g_intr && !done) {
dtrace_sleep(g_dtp);
}
if (done || g_intr || g_exited) {
done = 1;
if (dtrace_stop(g_dtp) == -1) {
fprintf(stderr, "could not stop tracing\n");
return -1;
}
}
switch (dtrace_work(g_dtp, stdout, NULL, chewrec, NULL)) {
case DTRACE_WORKSTATUS_DONE:
done = 1;
break;
case DTRACE_WORKSTATUS_OKAY:
break;
default:
fprintf(stderr, "processing aborted");
return -1;
}
} while (!done);
printf("closing dtrace\n");
dtrace_close(g_dtp);
return 0;
}
至运行:
[oberstet@brummer2 ~/hello_dtrace]$ make; sudo ./hello_dtrace
cc -I /usr/src/cddl/contrib/opensolaris/lib/libdtrace/common/ -I /usr/src/sys/cddl/compat/opensolaris -I /usr/src/sys/cddl/contrib/opensolaris/uts/common/ hello_dtrace.c -l dtrace -l proc -l ctf -l elf -l rtld_db -l z -l pthread -l util -o hello_dtrace
Dtrace initialized
dtrace options set
dtrace program compiled
dtrace probes enabled
instrumentation started ..
chewing dtrace record ..
hello from dtrace
chewing dtrace record ..
^Cclosing dtrace
dtrace 被认为是系统内部接口,不支持编写自定义使用者。
您最好的办法是检查 FreeBSD 源代码树的副本并从那里构建您的代码。这实际上并不十分困难。显然,dtrace(1)
是规范的 dtrace 使用者,因此您可以查看其实现以了解如何使用 libdtrace 的示例。此外 dtrace.h
有大量解释数据结构和内部结构的评论。
在源代码树的上下文中构建文件非常容易; FreeBSD 源代码树布局很简单,编写 Makefile 以自包含的方式构建二进制文件基本上已经给了你 "for free".
相关要点:
将 the dtrace(1) Makefile 克隆到源树结帐中的新目录中。修改 Makefile 以使
.PATH
正确,并设置PROG
和SRCS
以包含包含自定义消费者的源集。克隆 dtrace.c 到您的源目录(无论您将
.PATH
指向何处)并根据需要进行更改。尽管源代码将近 2,000 行,但其中大部分是支持代码。如果你只是想克隆功能的一个子集,你会发现二进制文件的大部分选项都是在独立的函数中实现的,因此应该很容易 trimdtrace.c
降至最低限度。
如果不知道您具体需要对自定义使用者做什么,就很难告诉您还需要调用什么。我假设您可能需要 compile_file in there as well as exec_prog。当然,您的需求可能会有所不同。
dtrace.h
you will be using 对提供的各种接口有很多评论。
希望这足以让您入门;不幸的是,没有办法从普通安装中做你想做的事。当然,您可以破解相关的 Makefile 以安装必要的头文件并创建您自己的内部分发版。但这似乎比它值得的更痛苦。
libdtrace
API 不一定适用于稳定的消费者,但从一些现有消费者那里学习是很容易上手的。最简单和最现代的是 plockstat
,用于用户空间锁定统计的 illumos 实用程序。
下面是一个简单的 DTrace 消费者程序的基本流程:
dtrace_open()
以获得 dtrace_hdl_t,其他 libdtrace
交互的句柄
dtrace_setopt()
配置选项(-x
标记为 dtrace(1M)
)
dtrace_strcompile()
编译你的 D 程序的字符串
dtrace_program_exec()
将该程序发送到内核
dtrace_go()
检测系统并开始记录数据
dtrace_close()
最后清理
对于主要数据收集循环(在 dtrace_go()
和 dtrace_close()
之间)执行以下操作:
dtrace_sleep()
根据 DTrace 选项暂停(switchrate 和 aggrate)
dtrace_work()
处理跟踪数据
dtrace_stop()
中止
有关更多信息,请参阅 plockstat.c
中的主循环。
对于其他简单的 DTrace 使用者,请查看 intrstat
and lockstat
。对于厨房水槽,查看 dtrace(1M) 命令行实用程序的代码。