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:
#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(_NTO_CHF_PRIVATE);
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;
}
}
}
// 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.
}
}