用 C 编写一个 DTrace 消费者

Posted

技术标签:

【中文标题】用 C 编写一个 DTrace 消费者【英文标题】:Writing a DTrace consumer in C 【发布时间】:2015-01-18 15:38:40 【问题描述】:

我想在 FreeBSD 10.1 上编写一个使用 libdtrace 实现 DTrace 消费者 的 C 程序。

我知道我需要先致电dtrace_open() - 例如。我找到了 this 旧演示文稿,但我什至无法开始,因为没有安装 dtrace.h(仅在系统源代码树中)。

共享库已安装,例如FreeBSD 附带的/usr/sbin/dtrace 工具可以充当DTrace 使用者,并且此工具链接到/lib/libdtrace.so.2(也通过/usr/lib/libdtrace.so 的符号链接指向)。

任何基本示例,包括构建说明 (FreeBSD 10.1 / clang) 都会对我有很大帮助。


编写自定义消费者的实际目标是创建一个可在 Python 和 PyPy 中使用的基于 CFFI 的包装器。 意思是:上面的 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

【问题讨论】:

不确定这是否有帮助,但您可以尝试wiki.freebsd.org/DTrace。那里有代码示例。 谢谢,我已经阅读了这些内容,但没有透露如何用 C 编写自定义使用者。 很抱歉这么迂腐,但您是否也阅读了该网站上的链接 wiki.freebsd.org/DTrace/KernelSupport。有额外的make buildworld 的说明,我认为这些说明会提取您正在寻找的支持文件。 “截至 2012 年 5 月,在 GENERIC 中默认启用 DTrace 支持,不需要以下步骤” - 我正在运行 10.1 版本(默认情况下内置 DTrace) @oberstet:我认为您可以尝试从 DTrace 邮件列表中提供帮助:http://dtrace.org/blogs/mailing-list/。 【参考方案1】:

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 选项(切换速率和聚合)暂停dtrace_work() 处理跟踪数据dtrace_stop() 中止

更多信息请参见plockstat.c 中的主循环。

对于其他简单的 DTrace 使用者,请查看 intrstatlockstat。对于厨房水槽,请查看 dtrace(1M) 命令行实用程序的代码。

【讨论】:

太棒了!我在 FreeBSD 上做了一些工作(添加到问题正文中)。看起来,plockstatdtrace_sleep 中有主循环阻塞,而intrstat 设置了一个高频定时器并在sigsuspend 中阻塞。两者都阻塞调用线程。关于我应该遵循哪种方法的任何提示? 啊,还有一件事:最终,这将在一个具有异步反应器/事件循环(Python Twisted/asyncio)的程序中运行。在这种情况下,绝不能阻塞(主)线程(因为反应器循环希望在调用 select()poll() 或等待 FD 准备就绪时阻塞自己)。所以我需要让 dtrace 消费者循环(阻塞)在后台线程上运行。有什么问题吗?【参考方案2】:

dtrace 被认为是系统内部接口,不支持编写自定义消费者。

您能做的最好的事情就是查看 FreeBSD 源代码树的副本并从那里构建您的代码。这实际上并不是非常困难。显然,dtrace(1) 是规范的 dtrace 使用者,因此您可以查看其实现以了解如何使用 libdtrace 的示例。此外,dtrace.h 有大量的 cmets 解释数据结构和内部结构。

在源代码树的上下文中构建文件非常容易; FreeBSD 源代码树的布局很简单,编写 Makefile 以自包含方式构建二进制文件基本上是“免费”提供给您的。

相关点:

将 the dtrace(1) Makefile 克隆到源代码树检出的新目录中。修改 Makefile 以使 .PATH 正确,并设置 PROGSRCS 以包含构成您的自定义使用者的源集。

将dtrace.c 克隆到您的源目录(无论您将.PATH 指向的哪个位置)并根据需要进行更改。虽然源代码将近 2,000 行,但其中大部分是支持代码。如果您只是想克隆功能的子集,您会发现二进制文件的大多数选项都是在自包含的函数中实现的,因此应该很容易将dtrace.c 修剪为裸露的- 最小形式。

如果不知道您具体需要对自定义使用者做什么,就很难告诉您还需要调用什么。我假设你可能会想要compile_file 和exec_prog。当然,您的需求可能会有所不同。

dtrace.h you will be using 有许多关于所提供的各种接口的 cmets。

希望这足以让您入门;不幸的是,没有办法从香草安装中做你想做的事。当然,您可以破解相关的 Makefile 以安装必要的头文件并创建您自己的内部发行版。但这似乎比它的价值更痛苦。

【讨论】:

非常感谢您的详细回答。请给我一些时间来尝试,也因为twitter.com/ahl/status/557981334615126016 - 我已经更新了我的实际目标是自定义消费者的问题 不客气!我很高兴 ahl 扩展了答案/提供了更多关于在提供可接受的答案时需要做什么的见解。 (嗨,亚当!)

以上是关于用 C 编写一个 DTrace 消费者的主要内容,如果未能解决你的问题,请参考以下文章

用c语言或c++编写编程实现生产者消费者或读写者的同步问题

生产者-消费者”问题 用C语言编写

如何用c++builder 编写多线程

使用 dtrace 分析 C 代码

简单的“生产者-消费者”问题 请用C语言编写

如何使用互斥锁在 C 中执行生产者-消费者程序