Data-capture program

This program creates the kernel trace buffers, captures the data, and writes it to standard output. You'd normally redirect the output to a file.

The basic steps in this program are as follows:

  1. Get memory for the buffers.
  2. Configure the kernel buffers.
  3. Indicate which events to collect (thread events and control events).
  4. Attach a handler.
  5. Start tracing:
    • In the handler, copy the data and schedule a thread.
    • In the thread, write the data to stdout.
Note: This program needs to run with the PROCMGR_AID_INTERRUPT, PROCMGR_AID_MEM_PHYS, and PROCMGR_AID_TRACE abilities enabled.
#include <sys/trace.h>
#include <sys/neutrino.h>
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

struct my_buf
{
   unsigned nbytes;
   char data[16*1024];
} my_bufs[4];

int cur_my_buf;

tracebuf_t *kbufs;
paddr_t paddr; // A pointer to physical kernel memory
int hookid;

struct sigevent notify_ev;

const struct sigevent *got_buf( int info )
{
   int ind;
   tracebuf_t *tbuf;

   ind = _TRACE_GET_BUFFNUM(info);
   tbuf = &kbufs[ind];

   my_bufs[ind].nbytes = tbuf->h.num_events * sizeof(struct traceevent);
   memcpy( my_bufs[ind].data, tbuf->data, my_bufs[ind].nbytes );

   notify_ev.sigev_value.sival_int = ind;
   return &notify_ev;
}

void sig_cleanup(int signo )
{
   TraceEvent( _NTO_TRACE_STOP );
   TraceEvent(_NTO_TRACE_DEALLOCBUFFER);
   InterruptDetach( hookid );
}

int main()
{
   int ret;
   int coid;
   int chid;
   struct _pulse pmsg;
   int rcvid;

   signal( SIGINT, sig_cleanup );

   ret = ThreadCtl(_NTO_TCTL_IO, 0);
   if (-1 == ret )
   {
      perror( "ThreadCtl");
      return 1;
   }

   // This may be dangerous: if anyone else is tracing, this may break them.  But
   // it should allow us to run again if killed without cleaning up.
   TraceEvent(_NTO_TRACE_DEALLOCBUFFER);
   TraceEvent( _NTO_TRACE_STOP );


   // Ask the kernel for four buffers.
   ret = TraceEvent(_NTO_TRACE_ALLOCBUFFER, 4, &paddr);
   if( -1 == ret )
   {
      perror( "TraceEvent Alloc Bufs");
      return 1;
   }

   // Get a vaddr for this memory.
   kbufs = mmap( 0, 4 * sizeof(tracebuf_t), PROT_READ | PROT_WRITE,
                 MAP_PHYS | MAP_SHARED, NOFD, paddr );
   if( MAP_FAILED == kbufs )
   {
      perror("mmap");
      return 1;
   }

   chid = ChannelCreate(_NTO_CHF_PRIVATE);
   coid = ConnectAttach( 0, 0, chid, _NTO_SIDE_CHANNEL, 0 );

   SIGEV_PULSE_INIT( &notify_ev, coid, 15, 1, 0 );

   // Attach the notification handler for the _NTO_HOOK_TRACE synthetic interrupt
   // that the kernel uses to tell us about a full buffer.
   hookid = InterruptHookTrace( got_buf, 0 );

   // Indicate which events we want.

   // Turn off all filters, putting us in default (nothing) state.
   TraceEvent(_NTO_TRACE_DELALLCLASSES);
   TraceEvent(_NTO_TRACE_CLRCLASSPID, _NTO_TRACE_KERCALL);
   TraceEvent(_NTO_TRACE_CLRCLASSTID, _NTO_TRACE_KERCALL);
   TraceEvent(_NTO_TRACE_CLRCLASSPID, _NTO_TRACE_THREAD);
   TraceEvent(_NTO_TRACE_CLRCLASSTID, _NTO_TRACE_THREAD);

   // Ask for thread, vthread, and process events.
   TraceEvent(_NTO_TRACE_ADDCLASS, _NTO_TRACE_THREAD);
   TraceEvent(_NTO_TRACE_ADDCLASS, _NTO_TRACE_VTHREAD);
   TraceEvent(_NTO_TRACE_ADDCLASS, _NTO_TRACE_PROCESS);

   // Ask for control events.
   TraceEvent(_NTO_TRACE_ADDCLASS, _NTO_TRACE_CONTROL );

   // Set fast mode for all classes.
   TraceEvent( _NTO_TRACE_SETALLCLASSESFAST );

   // Make sure we are in linear (not ring) mode; as buffers fill,
   // we will be notified
   TraceEvent(_NTO_TRACE_SETLINEARMODE);

   // Start tracing.
   TraceEvent( _NTO_TRACE_START );

   while(1)
   {
      rcvid = MsgReceive( chid, &pmsg, sizeof(pmsg), 0 );
      if( -1 == rcvid )
      {
         perror("MsgReceive");
         return 1;
      }
      if( 0 == rcvid )
      {
         if (pmsg.code == 1 )
         {
            ret = write( 1, my_bufs[pmsg.value.sival_int].data,
                         my_bufs[pmsg.value.sival_int].nbytes );
            if( -1 == ret )
            {
               perror("write");
               return 1;
            }
         }
      }
      // We do not need a message error case, since it is a private channel,
      // so nobody should be able to attach to it or send me messages.
   }
}