Appendix: Sample Programs

This appendix includes some sample data-capture and parsing programs:

These programs could be a useful starting point if you want to write your own event data collection program, especially if you want to do any on-the-fly data collection/parsing for system robustness control.

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:

Note: This program needs to run as root.

#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_device_memory(0, 4*sizeof(tracebuf_t), PROT_READ|PROT_WRITE, 0, paddr );
   if( MAP_FAILED == kbufs )
   {
      perror("mmap");
      return 1;
   }

   chid = ChannelCreate(0);
   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;
            }
         }
      }
      else
      {
         printf ("Unexpected rcvid: %d\n", rcvid);
         return 1;
      }
   }
}

Parser

This program reads the events file or output generated by the data-capture program and does some very simple parsing of just the expected events in that data set.

#include <sys/trace.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

// The events for the _NTO_TRACE_THREAD and _NTO_TRACE_VTHREAD classes
// (for the most part) correspond to the thread states defined in <sys/states.h>:
//
//      STATE_DEAD,		/* 0	0x00 */
//	STATE_RUNNING,		/* 1	0x01 */
//	STATE_READY,		/* 2	0x02 */
//	STATE_STOPPED,		/* 3	0x03 */
//
//	STATE_SEND,		/* 4	0x04 */
//	STATE_RECEIVE,		/* 5	0x05 */
//	STATE_REPLY,		/* 6	0x06 */
//
//	STATE_STACK,		/* 7	0x07 */
//	STATE_WAITTHREAD,	/* 8	0x08 */
//	STATE_WAITPAGE,		/* 9	0x09 */
//
//	STATE_SIGSUSPEND,	/* 10	0x0a */
//	STATE_SIGWAITINFO,	/* 11	0x0b */
//	STATE_NANOSLEEP,	/* 12	0x0c */
//	STATE_MUTEX,		/* 13	0x0d */
//	STATE_CONDVAR,		/* 14	0x0e */
//	STATE_JOIN,		/* 15	0x0f */
//	STATE_INTR,		/* 16	0x10 */
//	STATE_SEM,		/* 17	0x11 */
//	STATE_WAITCTX,		/* 18	0x12 */
//
//	STATE_NET_SEND,		/* 19	0x13 */
//	STATE_NET_REPLY,	/* 20	0x14 */
//
//	STATE_MAX = 24
//
//  There are two additional events:
//	enum _TRACE_THREAD_STATE {
//	_TRACE_THREAD_CREATE = STATE_MAX,
//	_TRACE_THREAD_DESTROY,
//	_TRACE_MAX_TH_STATE_NUM
//      };

char *thread_events[26] =
{
 "DEAD",      // 0
 "RUNNING",
 "READY",
 "STOPPED",
 "SEND"   ,
 "RECEIVE", // 5
 "REPLY",
 "STACK",
 "WAITTHREAD",
 "WAITPAGE",
 "SIGSUSPEND",  //10
 "SIGWAITINFO",
 "NANOSLEEP",
 "MUTEX",
 "CONDVAR",
 "JOIN",   // 15
 "INTR",
 "SEM",
 "WAITCTX",
 "NET_SEND",
 "NET_REPLY", // 20
 "INVAL",
 "INVAL",
 "INVAL", // 23
 "CREATE", // 24
 "DESTROY" // 25
};

void internal_to_external (unsigned int_class, unsigned int_event,
                           unsigned *ext_class, unsigned *ext_event)
{
   int event_64 = 0;

   *ext_class = -1;
   *ext_event = -1;

   switch (int_class)
   {
     case _TRACE_COMM_C:
        *ext_class = _NTO_TRACE_COMM;
        *ext_event = int_event;
        break;

     case _TRACE_CONTROL_C:
        *ext_class = _NTO_TRACE_CONTROL;
        *ext_event = int_event;
        break;

     case _TRACE_INT_C:
        *ext_event = -1;
        switch (int_event)
        {
           case _TRACE_INT_ENTRY:
              *ext_class = _NTO_TRACE_INTENTER;
              break;

           case _TRACE_INT_EXIT:
              *ext_class = _NTO_TRACE_INTEXIT;
              break;

           case _TRACE_INT_HANDLER_ENTRY:
              *ext_class = _NTO_TRACE_INT_HANDLER_ENTER;
              break;

           case _TRACE_INT_HANDLER_EXIT:
              *ext_class = _NTO_TRACE_INT_HANDLER_EXIT;
              break;

           default:
              printf ("Unknown Interrupt event: %d\n", int_event);
        }
        break;

     case _TRACE_KER_CALL_C:

        /* Remove _NTO_TRACE_KERCALL64 if it's set. */
        if (int_event & _NTO_TRACE_KERCALL64)
        {
           event_64 = 1;
           int_event = int_event & ~_NTO_TRACE_KERCALL64;
        }

        /* Determine the class and event. */
        if (int_event < _TRACE_MAX_KER_CALL_NUM)
        {
           *ext_class = _NTO_TRACE_KERCALLENTER;
           *ext_event = int_event;
        }
        else if (int_event < 2 * _TRACE_MAX_KER_CALL_NUM)
        {
           *ext_class = _NTO_TRACE_KERCALLEXIT;
           *ext_event = int_event - _TRACE_MAX_KER_CALL_NUM;
        }
        else if (int_event < 3 * _TRACE_MAX_KER_CALL_NUM)
        {
           *ext_class = _NTO_TRACE_KERCALLINT;
           *ext_event = int_event - 2 * _TRACE_MAX_KER_CALL_NUM;
        }
        else
        {
           printf ("Unknown kernel event: %d\n", int_event);
        }

        /* Add _NTO_TRACE_KERCALL64 to the external event if it was set for the internal event. */
        if (event_64)
        {
           *ext_event = *ext_event | _NTO_TRACE_KERCALL64;
        }

        break;

     case _TRACE_PR_TH_C:
        *ext_event = -1;
        if (int_event >= (2 * _TRACE_MAX_TH_STATE_NUM))
        {
           *ext_class = _NTO_TRACE_PROCESS;
           *ext_event = 1 << ((int_event >> 6) -1);
        }
        else if (int_event >= _TRACE_MAX_TH_STATE_NUM)
        {
           *ext_class = _NTO_TRACE_VTHREAD;
           *ext_event = 1 << (int_event - _TRACE_MAX_TH_STATE_NUM);
        }
        else
        {
           *ext_class = _NTO_TRACE_THREAD;
           *ext_event = 1 << int_event;
        }
        break;

     case _TRACE_SYSTEM_C:
        *ext_class = _NTO_TRACE_SYSTEM;
        *ext_event = int_event;
        break;

     case _TRACE_USER_C:
        *ext_class = _NTO_TRACE_USER;
        *ext_event = int_event;
        break;

     default:
        printf ("Unknown class: %d\n", int_class);
   }
}
   
int main()
{
   struct traceevent *tv;
   int i;
   char buf[64 * sizeof *tv ];

   int nb;

   unsigned internal_class, internal_event, cpu;
   unsigned external_class, external_event, event_type;
   char name_buff[1024];
   int name_index;

   while(1)
   {
      nb = read( 0, buf, sizeof(buf));
      if( nb <= 0 )
      {
         return 0;
      }
      for( i = 0; i < nb/sizeof *tv; i++)
      {
         tv = (struct traceevent *)&buf[i*sizeof *tv];

         /* The header includes the internal class and event numbers, the CPU index, and
            the type of event (simple or combine). */
         internal_class = _NTO_TRACE_GETEVENT_C(tv->header);
         internal_event = _NTO_TRACE_GETEVENT(tv->header);
         cpu =  _NTO_TRACE_GETCPU(tv->header);
         event_type = _TRACE_GET_STRUCT(tv->header);
         switch (event_type)
         {
            case _TRACE_STRUCT_S:
               printf("S  ");
               break;
            case _TRACE_STRUCT_CB:
               printf("CB ");
               break;
            case _TRACE_STRUCT_CC:
               printf("CC ");
               break;
            case _TRACE_STRUCT_CE:
               printf("CE ");
               break;
            default:
               printf("?  ");
         }

         /* Convert the internal class and event numbers into external ones. */
         internal_to_external (internal_class, internal_event, &external_class, &external_event);

         if( _NTO_TRACE_PROCESS == external_class )
         {
            switch (external_event)
            {
               case _NTO_TRACE_PROCCREATE:
                  printf("_NTO_TRACE_PROCESS, _NTO_TRACE_PROCCREATE, cpu: %d, timestamp: %8x, ppid:%d, pid:%d\n",
                         cpu, tv->data[0], tv->data[1], tv->data[2]);
                  break;
               case _NTO_TRACE_PROCCREATE_NAME:
                  if ((event_type == _TRACE_STRUCT_CB) || (event_type == _TRACE_STRUCT_S))
                  {
                     /* The first combine event includes the parent's pid and the pid of the new process. */
                     printf("_NTO_TRACE_PROCESS, _NTO_TRACE_PROCCREATE_NAME, cpu: %d, timestamp: %8x, ppid:%d, pid:%d\n",
                            cpu, tv->data[0], tv->data[1], tv->data[2]);
                     memset (name_buff, 0, sizeof(name_buff));
                     name_index = 0;
                  }
                  else
                  {
                     /* Subsequent combine events include parts of the name. */
                     memcpy (name_buff + name_index, &(tv->data[1]), sizeof(unsigned int));
                     memcpy (name_buff + name_index + sizeof(unsigned int), &(tv->data[2]), sizeof(unsigned int));
                     name_index += 2 * sizeof(unsigned int);

                     if (event_type == _TRACE_STRUCT_CE)
                     {
                        /* This is the last of the combine events. */
                        printf("_NTO_TRACE_PROCESS, _NTO_TRACE_PROCCREATE_NAME, cpu: %d, timestamp: %8x, %s\n",
                               cpu, tv->data[0], name_buff);
                     }
                  }
                  break;
               case _NTO_TRACE_PROCDESTROY:
                  printf("_NTO_TRACE_PROCESS, _NTO_TRACE_PROCDESTROY, cpu: %d, timestamp: %8x, ppid:%d, pid:%d\n",
                         cpu, tv->data[0], tv->data[1], tv->data[2]);
                  break;
               case _NTO_TRACE_PROCDESTROY_NAME: // Not used.
                  printf("_NTO_TRACE_PROCESS, _NTO_TRACE_PROCDESTROY_NAME, cpu: %d, timestamp: %8x, ppid:%d, pid:%d\n",
                         cpu, tv->data[0], tv->data[1], tv->data[2]);
                  break;
               case _NTO_TRACE_PROCTHREAD_NAME:
                  if ((event_type == _TRACE_STRUCT_CB) || (event_type == _TRACE_STRUCT_S))
                  {
                     /* The first combine event includes the pid and tid. */
                     printf("_NTO_TRACE_PROCESS, _NTO_TRACE_PROCTHREAD_NAME, cpu: %d, timestamp: %8x, pid:%d, tid:%d\n",
                            cpu, tv->data[0], tv->data[1], tv->data[2]);
                     memset (name_buff, 0, sizeof(name_buff));
                     name_index = 0;
                  }
                  else
                  {
                     /* Subsequent combine events include parts of the name. */
                     memcpy (name_buff + name_index, &(tv->data[1]), sizeof(unsigned int));
                     memcpy (name_buff + name_index + sizeof(unsigned int), &(tv->data[2]), sizeof(unsigned int));
                     name_index += 2 * sizeof(unsigned int);

                     if (event_type == _TRACE_STRUCT_CE)
                     {
                        /* This is the last of the combine events. */
                        printf("_NTO_TRACE_PROCESS, _NTO_TRACE_PROCTHREAD_NAME, cpu: %d, timestamp: %8x, %s\n",
                               cpu, tv->data[0], name_buff);
                     }
                  }
                  break;
               default:
                  printf("_NTO_TRACE_PROCESS, Unknown event: %d, cpu: %d, timestamp: %8x, pid:%d, tid:%d\n",
                         external_event, cpu, tv->data[0], tv->data[1], tv->data[2]);
            }
         }
         else if (_NTO_TRACE_THREAD == external_class)
         {
            /* The data for all the THREAD events includes the process and thread IDs.
               The internal event corresponds to the thread state. */

            printf("_NTO_TRACE_THREAD, _NTO_TRACE_TH%s, cpu: %d, timestamp: %8x, pid:%d, tid:%d\n",
                   thread_events[internal_event], cpu, tv->data[0], tv->data[1],
                   tv->data[2]);
         }
         else if (_NTO_TRACE_VTHREAD == external_class)
         {
            /* The data for all the VTHREAD events includes the process and thread IDs.
               The internal event corresponds to the thread state. */

            printf("_NTO_TRACE_VTHREAD, _NTO_TRACE_VTH%s, cpu: %d, timestamp: %8x, pid:%d, tid:%d\n",
                   thread_events[internal_event], cpu, tv->data[0], tv->data[1],
                   tv->data[2]);
         }
         else if ( _NTO_TRACE_CONTROL == external_class )
         {
            switch (external_event)
            {
               case _NTO_TRACE_CONTROLBUFFER:
                  /* The data includes the sequence number of the buffer and the number of event slots in it. */
                  printf("_NTO_TRACE_CONTROL,  _NTO_TRACE_CONTROLBUFFER, cpu: %d, timestamp: %8x, sequence:%d, number of events:%d\n",
                         cpu, tv->data[0], tv->data[1], tv->data[2]);
                  break;
               case _NTO_TRACE_CONTROLTIME:
                  /* The data includes the full time stamp. */
                  printf("_NTO_TRACE_CONTROL, _NTO_TRACE_CONTROLTIME, cpu: %d, timestamp: %8x, msb:%x, lsb:%x\n",
                         cpu, tv->data[0], tv->data[1], tv->data[2]);
                  break;
               default:
                  printf("_NTO_TRACE_CONTROL, Unknown event: %d, cpu: %d, timestamp: %8x, d1:%x, d2:%x\n",
                         external_event, cpu, tv->data[0], tv->data[1], tv->data[2]);
            }
         }
         else
         {
            printf("Unhandled class: %d, event: %d, cpu: %d, timestamp: %8x, d1:%x, d2:%x\n",
                   external_class, external_event, cpu, tv->data[0], tv->data[1], tv->data[2]);
         }

      }

   }
}