Parser

Updated: April 19, 2023

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_SEC_C:
        *ext_class = _NTO_TRACE_SEC;
        *ext_event = 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]);
         }
      }
   }
}