2015-01-18 3 views
8

Я хотел бы написать программу C на FreeBSD 10.1, которая реализует DTrace-потребитель с использованием libdtrace.Написание потребителя DTrace в C

Я знаю, что мне нужно начать с звонка dtrace_open() - например. Я нашел this старую презентацию, но я не могу даже начать, так как нет dtrace.h (только в исходном дереве системы).

Общая библиотека установлена, например. инструмент /usr/sbin/dtrace, входящий в состав FreeBSD, может выступать в качестве потребителя DTrace, и этот инструмент ссылается на /lib/libdtrace.so.2 (на который также указывается символьная ссылка от /usr/lib/libdtrace.so).

Любой базовый пример, включая инструкции по сборке (FreeBSD 10.1/clang), очень поможет мне.


Фактическая цель написания пользовательского потребителя является создание на основе обертку CFFI годный к употреблению в Python и PyPy. Средство: выше программы C только для начала, изучения, а затем продолжить.

CFFI - рекомендуемый, современный высокопроизводительный способ взаимодействия PyPy с общими библиотеками.

CFFI может использоваться на уровне ABI и API. Последнее требует, чтобы заголовочный файл включался, первый должен объявить материал, используемый в lib.


Адаптированный ответ Адама, вот полный пример, который работает на 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; 
} 

Для запуска:

[[email protected] ~/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 
+0

Не уверен, что это помогает, но вы можете попробовать https://wiki.freebsd.org/DTrace. Там есть примеры кода. – user590028

+1

Спасибо, я прочитал их, но он не раскрывает, как писать пользовательского потребителя в C. – oberstet

+0

Извините, что так педантичен, но вы также прочитали ссылку с этого сайта на https://wiki.freebsd.org/DTrace/KernelSupport. Есть инструкции для дополнительных 'make buildworld', которые, я думаю, будут тянуть файлы поддержки, которые вы ищете. – user590028

ответ

7

API libdtrace API не обязательно предназначен для стабильных потребителей, но для начала довольно легко изучить некоторые существующие потребители. Самый простой и современный - plockstat, утилита подсветки для статистики блокировки пользователей.

Вот основной поток простого потребителя программы 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 и lockstat. Для кухонной раковины проверьте код утилиты командной строки dtrace (1M).

+0

Удивительный! У меня есть что-то, работающее над FreeBSD (добавлено в тело вопроса). Похоже, 'plockstat' имеет основной цикл блокировки в' dtrace_sleep', тогда как 'intrstat' устанавливает высокочастотный таймер и блокирует в' sigsuspend'. Оба блокируют вызывающий поток. Любые намеки на какой подход я должен придерживаться? – oberstet

+0

Ах, и еще одна вещь: в конечном счете, это будет работать в программе, которая имеет асинхронный цикл реактора/события (Python Twisted/asyncio). В этом контексте нельзя блокировать (основной) поток (поскольку петля реактора хочет блокировать себя в вызове 'select()', 'poll()' или такое ожидание готовности FDs). Поэтому мне нужно, чтобы потребительский цикл dtrace (который блокирует) работал на фоновом потоке. Любые проблемы с этим? – oberstet

3

DTrace считается система внутреннего интерфейса и написания пользовательского потребителя это не то, что поддерживается.

Лучшее, что вы можете сделать, это проверить копию исходного дерева FreeBSD и создать код оттуда. На самом деле это не очень сложно. Очевидно, что dtrace(1) является каноническим потребителем dtrace, поэтому вы можете посмотреть его реализацию на примерах использования libdtrace. Кроме того, dtrace.h содержит огромное количество комментариев, объясняющих структуры данных и внутренние элементы.

Построение файлов в контексте исходного дерева довольно просто; макет исходного дерева FreeBSD прост и писать Makefiles для создания двоичных файлов в автономном режиме, в основном предоставляется вам «бесплатно».

Соответствующие пункты:

  • Clone the dtrace(1) Makefile в новый каталог в дереве исходных проверки.Измените Makefile таким образом, чтобы .PATH был прав, и установите PROG и SRCS, чтобы включить набор источников, содержащих пользовательский потребитель.

  • Клон dtrace.c в исходный каталог (независимо от того, указали ли вы .PATH) и внесите необходимые изменения. Хотя источник составляет около 2000 строк, большая часть из них - код поддержки. Если вы просто хотите клонировать подмножество функций, вы обнаружите, что большинство вариантов двоичного кода реализованы в автономных функциях, и поэтому довольно легко обрезать dtrace.c до минимума форма.

Не зная, что конкретно вам нужно делать с вашим обычным потребителем, трудно рассказать вам, что еще вам нужно, чтобы позвонить. Я предполагаю, что вам, вероятно, понадобится compile_file, а также exec_prog. Конечно, ваши потребности могут отличаться.

dtrace.h you will be using имеет несколько комментариев о различных предоставляемых интерфейсах.

Надеюсь, этого достаточно, чтобы вы начали; к сожалению, нет способа сделать то, что вы хотите от установки ванили. Разумеется, вы могли бы взломать соответствующие файлы Makefile для установки необходимых файлов заголовков и создания собственного внутреннего дистрибутива. Но это кажется больнее, чем того стоит.

+0

Спасибо за подробный ответ. Пожалуйста, дайте мне немного времени, чтобы попробовать, а также из-за https://twitter.com/ahl/status/557981334615126016 - Я обновил вопрос о том, что мои фактические цели с пользовательским потребителем – oberstet

+1

Добро пожаловать! Я рад, что AHL расширит ответ/предоставит больше информации о том, что нужно сделать для предоставления приемлемого ответа. (Привет, Адам!) – dho

Смежные вопросы