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.
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:
![]() |
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 ¬ify_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( ¬ify_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;
}
}
}
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]);
}
}
}
}