Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

libdtrace buffered output

I'm trying to use dtrace through libdtrace (on Snow Leopard; 10.6.4). I want to catch the printed output of my dtrace script within my own program. One way to do so would be to have the output go to a temporary file and read it from there. However, libdtrace supports a callback function to catch the output directly which I'd prefer.

I assumed the callback would just pass me formatted strings that I could consume, but this does not seem to be the case. For example, in the below test program, I'd expect the printout to be "process pid = 86138". However, it always prints out "process pid = 1" (the dtrace script works fine, when ran with 'dtrace -n').

What am I doing wrong ? How should I be consuming the data passed to the buffer handler ? (Specifically, data from printf and tracemem actions are what I'm interested in).

#include <dtrace.h>
#include <stdio.h>
#include <stdlib.h>
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <mach-o/dyld.h>
#include <mach-o/fat.h>
#include <sys/sysctl.h>
#include <signal.h>

static const char *g_prog =
  "pid86138::write:entry"
  "{"
  "  printf(\"process pid = %d\\n\", pid);"
  "}";

static int dcmdbuffered(const dtrace_bufdata_t *bufdata, void *arg) {
  if((bufdata->dtbda_recdesc != NULL) && (bufdata->dtbda_recdesc->dtrd_action == DTRACEACT_PRINTF))
    printf("BUF: %s\n", bufdata->dtbda_buffered);

  return DTRACE_HANDLE_OK;
}

static int chew(const dtrace_probedata_t *data, void *arg) {
  return DTRACE_CONSUME_THIS;
}

static int chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) {
  if(rec == NULL)
    return (DTRACE_CONSUME_NEXT);
  return (DTRACE_CONSUME_THIS);
}

int main(int argc, char **argv) {
  int err, done = 0;
  dtrace_proginfo_t info;

  dtrace_hdl_t *g_dtp = dtrace_open(DTRACE_VERSION, DTRACE_O_ILP32, &err);
  dtrace_prog_t *prog = dtrace_program_strcompile(g_dtp, g_prog, DTRACE_PROBESPEC_NAME, 0, 0, NULL);
  dtrace_handle_buffered(g_dtp, dcmdbuffered, NULL);
  dtrace_program_exec(g_dtp, prog, &info);
  dtrace_setopt(g_dtp, "strsize", "4096");
  dtrace_setopt(g_dtp, "bufsize", "4m");
  dtrace_setopt(g_dtp, "arch", "x86_64");
  dtrace_go(g_dtp);

  while(dtrace_work(g_dtp, NULL, chew, chewrec, NULL) == DTRACE_WORKSTATUS_OKAY)
    dtrace_sleep(g_dtp);

  dtrace_stop(g_dtp);
  dtrace_close(g_dtp);
  return 0;
}
like image 949
Sami Avatar asked Nov 15 '22 07:11

Sami


1 Answers

Buffered output seems to be broken on OSX. The actions seem to be somehow executed in the consumer's context in some twisted way. ustack() doesn't work at all, for example. copyinstr() on the other hand seems to function properly.

You can circumvent output buffering yet still get mostly the same result by using pipe:

int fds [2];

if (pipe (fds) != 0)
    assert (0);

int flags = fcntl (fds [0], F_GETFL, 0);
assert (flags != -1);
fcntl (fds [0], F_SETFL, flags | O_NONBLOCK);

FILE *faux_stdout = fdopen (fds [1], "a");
assert (faux_stdout);

while(dtrace_work(g_dtp, faux_stdout, chew, chewrec, NULL) == DTRACE_WORKSTATUS_OKAY) {
    char buf [1024];
    for (;;) {
        ssize_t num_read = read (fds [0], buf, sizeof (buf));
        if (num_read <= 0)
            break;
        /* process your buffer here */
        fwrite (buf, 1, num_read, stdout);
    }
    dtrace_sleep(g_dtp);
}

Error handling is left as an exercise to the reader.

like image 75
Mark Probst Avatar answered Dec 19 '22 11:12

Mark Probst