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( 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( ¬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. } }