Thursday, December 3, 2015

First DTrace hack

I was able to get a rough port of the DTrace Toolkit script "statsnoop" to run on FreeBSD. This largely involved removing references to Solaris syscalls that don't exist on FreeBSD, and commenting out some Solaris specific lookups for fstat().

The patch is below:

# diff -u statsnoop statsnoop.FreeBSD 
--- statsnoop   2015-11-12 05:11:04.000000000 -0500
+++ statsnoop.FreeBSD   2015-12-03 00:38:45.089471021 -0500
@@ -191,9 +191,9 @@
  /*
   * Print stat event
   */
- syscall::stat:entry, syscall::stat64:entry, syscall::xstat:entry,
- syscall::lstat:entry, syscall::lstat64:entry, syscall::lxstat:entry,
- syscall::fstat:entry, syscall::fstat64:entry, syscall::fxstat:entry
+ syscall::stat:entry,
+ syscall::lstat:entry,
+ syscall::fstat:entry
  {
        /* default is to trace unless filtering */
        self->ok = FILTER ? 0 : 1;
@@ -204,34 +204,29 @@
        (OPT_trace == 1 && TRACE == probefunc) ? self->ok = 1 : 1;
  }
 
- syscall::stat:entry, syscall::stat64:entry,
- syscall::lstat:entry, syscall::lstat64:entry, syscall::lxstat:entry
+ syscall::stat:entry,
+ syscall::lstat:entry
  /self->ok/
  {
        self->pathp = arg0;
  }
 
- syscall::xstat:entry
- /self->ok/
- {
-       self->pathp = arg1;
- }
-
- syscall::stat:return, syscall::stat64:return, syscall::xstat:return,
- syscall::lstat:return, syscall::lstat64:return, syscall::lxstat:return
+ syscall::stat:return,
+ syscall::lstat:return
  /self->ok/
  {
        self->path = copyinstr(self->pathp);
        self->pathp = 0;
  }
 
- syscall::fstat:return, syscall::fstat64:entry, syscall::fxstat:entry
+/*
+ syscall::fstat:return
  /self->ok/
  {
        self->filep = curthread->t_procp->p_user.u_finfo.fi_list[arg0].uf_file;
  }
 
- syscall::fstat:return, syscall::fstat64:return, syscall::fxstat:return
+ syscall::fstat:return
  /self->ok/
  {
         this->vnodep = self->filep != 0 ? self->filep->f_vnode : 0;
@@ -239,10 +234,11 @@
             cleanpath(this->vnodep->v_path) : "") : "";
        self->filep = 0;
  }
+*/
 
- syscall::stat:return, syscall::stat64:return, syscall::xstat:return,
- syscall::lstat:return, syscall::lstat64:return, syscall::lxstat:return,
- syscall::fstat:return, syscall::fstat64:return, syscall::fxstat:return
+ syscall::stat:return,
+ syscall::lstat:return,
+ syscall::fstat:return
  /self->ok && (! OPT_failonly || (int)arg0 < 0) && 
      ((OPT_file == 0) || (OPT_file == 1 && PATHNAME == copyinstr(self->pathp)))/
  {
@@ -275,9 +271,9 @@
  /* 
   * Cleanup 
   */
- syscall::stat:return, syscall::stat64:return, syscall::xstat:return,
- syscall::lstat:return, syscall::lstat64:return, syscall::lxstat:return,
- syscall::fstat:return, syscall::fstat64:return, syscall::fxstat:return
+ syscall::stat:return,
+ syscall::lstat:return,
+ syscall::fstat:return
  /self->ok/
  {
        self->path = 0;


And voila, I'm able to watch all the stat(2) calls in real time. Sample of the output, with warnings removed:
 
 
# ./statsnoop.FreeBSD 2>&1 | grep -v 'invalid address'
  UID    PID COMM          FD PATH                 
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
 1001   1373 konsole        0 /etc/nsswitch.conf   
    0   1074 sh            -1 /var/tmp/appcafe/dispatch-queue 
    0  10683 sleep          0 /etc                 
    0  10683 sleep          0 /etc/libmap.conf     
    0  10683 sleep          0 /usr                 
    0  10683 sleep          0 /usr/local           
    0  10683 sleep          0 /usr/local/etc       
    0  10683 sleep         -1 /usr/local/etc/libmap.d 

For some reason, konsole loves to make sure that /etc/nsswitch.conf has not changed, and calls stat() constantly. Sounds like a job for stated(8) to solve, someday..