/* HEADER: CUG000.00; TITLE: Amiga Monitor source code file; DATE: 09/07/88; VERSION: 1.0; FILENAME: MONITOR.C; SEE-ALSO: MONITOR.H; AUTHORS: Steve Roggenkamp */ /* monitor.c -- monitor a process, demonstrating interprocess communications and other such things. Written for the Amiga running AmigaDOS. Steve Roggenkamp 5394 Winetavern Lane Dublin, OH 43017 (614)792-8236 9 / 6 / 88 */ #include #include "monitor.h" #ifdef DEBUG #define DBG( x ) #else #define DBG( x ) #endif #define SAMPLE_RATE 20000 /* sample rate in microseconds this rate is 50 samples per second */ #define STACKSIZE 1000L /* size of task stack */ #define MONITOR_TASK "monitor" /* monitor task name */ #define SIG_PORT (1 << reply->mp_SigBit) APTR AllocMem(); void DeleteTimer( ); struct timerequest *CreateTimer( ); struct Task *FindTask(); struct Task *thistask; /* monitored task */ struct Task *montask; /* monitoring task */ extern long mon_tabsize; /* size of mon_tbl in # of entries */ extern mon_table mon_tbl[]; /* table containing function information */ static ULONG mon_total; /* number of times the task is sampled */ static mon_table **mon_tptr; /* sorted array of pointers to mon_tbl */ long time_delay = SAMPLE_RATE; /* time delay in usec */ long mon_cmp_addr( x, y ) /* compare mon_table function addresses */ register mon_table *x, *y; /* entries in mon_table to compare */ { return ( (long) y->func - (long) x->func); } long mon_cmp_name( x, y ) /* compare mon_table function names */ register mon_table *x, *y; /* entries in mon_table to compare */ { return ( strcmp( y->name, x->name )); } long mon_cmp_count( x, y ) /* compare mon_table function names */ register mon_table *x, *y; /* entries in mon_table to compare */ { return ( x->count - y->count ); } struct timerequest * mon_create_timer( mon_tr ) /* create a timer */ struct timerequest **mon_tr; /* time request */ { LONG error; /* error */ ULONG sigmask; /* signal mask */ struct MsgPort *timerport; /* message port for the timer */ static struct timeval tv; /* time delay structure */ timerport = CreatePort( 0L, 0L ); /* get a message port for timer */ if ( timerport == NULL ) { return( NULL ); } *mon_tr = (struct timerequest *) CreateExtIO( timerport, sizeof( struct timerequest )); if ( *mon_tr == NULL ) { mon_delete_timer( *mon_tr ); return( NULL ); } error = OpenDevice( TIMERNAME, UNIT_VBLANK, *mon_tr, 0L ); if ( error != 0L ) { mon_delete_timer( *mon_tr ); return( NULL ); } tv.tv_secs = 0; /* set the time delay we want */ tv.tv_micro = time_delay; (*mon_tr)->tr_time = tv; (*mon_tr)->tr_node.io_Command = TR_ADDREQUEST; return( *mon_tr ); } void mon_delete_timer( tr ) /* delete the timer we created above */ struct timerequest *tr; /* timer to delete */ { struct MsgPort *tp; /* timer message port */ Forbid(); /* disable multi-tasking so we don't */ if ( tr != NULL ) /* mess up some system data structs */ { tp = tr->tr_node.io_Message.mn_ReplyPort; CloseDevice( tr ); /* close the timer */ DeleteExtIO( tr, sizeof( struct timerequest )); if ( tp != 0L ) { DeletePort( tp ); /* get rid of message port */ } } Permit(); /* enable multi-tasking again */ } mon_table * mon_get_func( pc ) /* return a pointer to the function pc is in */ register void (*pc)(); /* program counter */ { long lo, hi; /* low and high offsets into mon_table array */ register long mid; /* offset to the middle of the table */ register mon_table **mp; /* pointer to middle of the lo - hi range */ lo = 1; /* don't look at entry 0, it's the system calls */ hi = mon_tabsize-1; /* point to the last entry into the table */ /* first, check to see if the program counter is contained within the monitor function table. If it is not, then just return the pointer to the beginning of the table. */ if ( pc < mon_tptr[hi]->func && pc > mon_tptr[1]->func ) { while ( hi >= lo ) /* use a binary search for speed */ { mid = ( hi + lo ) / 2; mp = mon_tptr + mid; if ( pc < (*mp)->func ) { hi = mid-1; } else if ( pc > (*(mp+1))->func ) { lo = mid+1; } else { return ( *mp ); /* found a function */ break; } } } return ( *mon_tptr ); /* didn't find a func, return a pointer to */ } /* the "system" call entry */ long mon_init() /* initialize the monitor task */ { register long i; /* loop counter */ register long pri; /* old task priority */ /* * first, make up an array of pointers to the function table. This * new array will be used for sorting and searching. By using an * array of pointers, we don't have to copy much information and * things run much faster. */ mon_tptr = (mon_table **) AllocMem( mon_tabsize * sizeof( mon_table * ), MEMF_PUBLIC | MEMF_CLEAR ); for ( i = 0; i < mon_tabsize; i++ ) { *(mon_tptr + i) = mon_tbl + i; } mon_sort_table( mon_cmp_addr ); /* sort the table by function addr */ Forbid(); /* turn off multi-tasking to access */ thistask = FindTask( 0L ); /* system data structures */ if ( thistask == NULL ) { Permit(); exit( 1 ); } /* * task name, priority, task entry pt, task stack size */ CreateTask( MONITOR_TASK, 5L, mon_task, STACKSIZE ); Permit(); /* turn multi-tasking back on */ DBG( printf( "starting monitoring\n" ); ) return ( 0L ); } void mon_print() /* print monitor results */ { register long i; /* loop counter */ register long cnt; /* count for individual functions */ printf( "%-20s %6s %4s\n", "function", "count", "pct" ); for ( i = 0; i < mon_tabsize; i++ ) { cnt = (*(mon_tptr+i))->count; printf("%-20s %6ld %4ld\n", (*(mon_tptr+i))->name, cnt, 100*cnt/mon_total ); } printf( "\n%-20s %6ld\n\n", "total count", mon_total ); } void mon_sample() /* samples the monitored process */ { register mon_table *tp; /* pointer to function table */ register APTR pc; /* stack pointer */ Forbid(); /* turn off multi-tasking */ pc = (APTR) *(thistask->tc_SPReg); /* get the program counter */ Permit(); /* turn on multi-tasking */ tp = mon_get_func( (void (*)()) pc ); /* find the current function */ tp->count++; /* increment its count */ mon_total++; } void mon_sort_table( cmp ) /* sort the function table */ long (*cmp)(); /* pointer to comparison function */ { mon_table_qsort( mon_tptr+1, mon_tptr+mon_tabsize-1, cmp ); } void mon_table_qsort( lo, hi, cmp ) /* quicksort the function table */ mon_table **lo, **hi; /* table pointers */ long (*cmp)(); /* comparison function */ { register mon_table **i, **j; /* temporary pointers */ register mon_table *t; /* partition */ register mon_table *w; /* swap temporary */ i = lo; j = hi; t = *(lo + (hi-lo)/2); do { while ( (*cmp)( *i, t ) > 0 ) i++; while ( (*cmp)( *j, t ) < 0 ) j--; if ( i <= j ) { w = *i; *i = *j; *j = w; i++; j--; } } while ( i < j ); if ( lo < j ) mon_table_qsort( lo, j, cmp ); if ( i < hi ) mon_table_qsort( i, hi, cmp ); } void mon_task() /* the monitor task */ { struct timerequest *mon_tr; /* timer request struct pointer */ register ULONG signals; /* signals sent to task */ register struct MsgPort *reply; /* message reply port */ geta4(); /* MUST CALL 1st thing in a created task */ /* when using the AZTEC-C compiler */ if ( (mon_tr = mon_create_timer( &mon_tr )) != NULL ) { reply = mon_tr->tr_node.io_Message.mn_ReplyPort; do { SendIO( mon_tr ); /* start things off */ do /* wait until timer goes off or the */ /* monitor should be terminated */ signals = Wait( SIGBREAKF_CTRL_C | SIG_PORT ); while ( (signals & SIG_PORT ) != 0 && CheckIO( mon_tr ) == 0 ); if ( CheckIO( mon_tr ) == 0 ) /* if timer isn't done abort */ AbortIO( mon_tr ); if ( (signals & SIG_PORT) != 0 ) /* if timer went off, sample */ mon_sample(); } while ( (signals & SIGBREAKF_CTRL_C ) == 0 ); mon_delete_timer( mon_tr ); /* terminating, so delete timer */ } } void mon_terminate() /* terminate the monitor */ { register long i; /* loop counter */ struct Task *mt; /* monitor task */ register long pri; /* monitored task priority */ /* get the message signal and delete the exception */ do { Forbid(); /* disable multi-tasking */ mt = FindTask( MONITOR_TASK ); /* find monitor task */ if ( mt ) { Signal( mt, SIGBREAKF_CTRL_C ); /* blow it away */ Delay( 10 ); /* wait for things to happen */ } Permit(); /* enable multi-tasking again */ } while ( mt != NULL ); /* * sort the table first by count and print it, then sort by name * and print it. */ mon_table_qsort( mon_tptr, mon_tptr+mon_tabsize-1, mon_cmp_count ); mon_print(); mon_table_qsort( mon_tptr, mon_tptr+mon_tabsize-1, mon_cmp_name ); mon_print(); /* * free the space allocated for the function table */ FreeMem( mon_tptr, mon_tabsize * sizeof( mon_table * ) ); } #if 0 /* the following function is not used anywhere in the system; however, I used it in the beginning to see what was going on with the stack. It's included here for your personal enjoyment and edification. usage: a = b + c; <- in funca mon_trace(); . . . result: funca called by funcb funcb called by foo foo called by main main the main function! */ void mon_trace() /* print a trace of the stack */ { long *fp; /* frame pointer */ void (*pc)(); /* program counter */ mon_table *func; /* function name */ fp = (long *) &fp + STACKDIR; /* set frame pointer to current frame */ pc = (void (*)()) *(fp + STACKDIR); /* get the program counter */ while ( (func = mon_get_func( pc )) != *mon_tptr ) { printf( "%-16s %lx\n", func->name, pc ); if ( func->func == main ) break; fp = (int *) *fp; /* set the frame pointer to the next one */ pc = (void (*)()) *(fp + STACKDIR ); } } #endif #ifdef TEST_MONITOR #define NWORD 150 /* default # of words to dump */ void system(){} /* used when the pc is outside the program */ int funca(), funcb(), funcc(); mon_table mon_tbl[] = { MON_NAME( system ), MON_NAME( main ), MON_NAME( dumpstack ), MON_NAME( funca ), MON_NAME( funcc ), MON_NAME( funcb ), MON_NAME( mon_trace ) }; long mon_tabsize = sizeof( mon_tbl ) / sizeof( mon_table ); void main( argc, argv ) int argc; char **argv; { int i; if ( mon_init() != 0L ) { printf( "initialization problems\n" ); exit( 1 ); } dumpstack(); mon_terminate(); } void dumpstack() { static long loc = 0; /* location of i in original call */ long *ps; /* stack pointer */ long i; /* loop counter */ if ( !loc ) /* is this the first call to dumpstack */ { loc = (int) &i; /* save temp location in loc */ dumpstack(); } else { mon_trace( ); for ( i = 0; i < 1000; i++ ) funca(); ps = &i; for ( i = 0; i < nword; i++ ) { printf( "%lx: %lx\n", ps, *ps ); ps += STACKDIR; } } } funca() { funcb(); } funcb() { funcc(); } funcc() { static long i; i++; printf( "print: %d\n", i ); } #endif