/* (C) Copyright 1988 Diomidis D. Spinellis. All rights reserved. This profiler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing. Refer to the profiler General Public License for full details. Everyone is granted permission to copy, modify and redistribute the profiler, but only under the conditions described in the profiler General Public License. A copy of this license is supposed to have been given to you along with the profiler so you can know your rights and responsibilities. It should be in a file named COPYING. Among other things, the copyright notice and this notice must be preserved on all copies. In other words, go ahead and share the profiler, but don't try to stop anyone else from sharing it farther. Help stamp out software hoarding! */ /* * A C program profiler. * * (C) Copyright 1988 Diomidis D. Spinellis. All rights reserved. * * $Header: PROF.C^v 1.1 88/11/20 17:33:16 dds Rel $ * * $Log: PROF.C^v $ * Revision 1.1 88/11/20 17:33:16 dds * Initial revision * * */ #include #include #include #include #include /* Linker output maximum line length */ #define LINELEN 129 /* Linker output maximum symbol length */ #define STRLEN 65 /* Entries can be absolute or relocatable */ enum relocation { absolute, relocatable } ; /* Function prototypes */ static void add_proc( char * name, unsigned long addr, enum relocation rel ) ; static void adjust_proc( long main_offset ) ; static void install_int( void ) ; static int prof_end( void ) ; static void * xmalloc( size_t size ) ; static void * xrealloc( void * buffer, size_t size ) ; static char * strsave( char * string ) ; static void interrupt far timer_handler(int es,int ds,int di,int si,int bp,int sp,int bx,int dx,int cx,int ax,int ip,int cs,int flags) ; void main( int argc, char *argv[] ) ; #ifdef DEBUG static void dump_table( void ) ; static void test_search( void ) ; static void disp(long n) ; #endif static char rcsid[] = "$Header: PROF.C^v 1.1 88/11/20 17:33:16 dds Rel $" ; void prof_start( char * argv0 ) { FILE *f ; static char fname[65] ; static char line[LINELEN] ; static char str1[STRLEN], str2[STRLEN], str3[STRLEN], str4[STRLEN], str5[STRLEN] ; enum {searchsegs, scansegs, searchprocs, scanprocs } state ; static char *state_errs[] = { "start of segment definitions", "ENDCODE segment definition", "the ``Publics by Value'' line", "address greater than ENDCODE was found" } ; unsigned int seg, off ; unsigned long endcode ; int linenum = 0 ; unsigned long main_addr, addr; long main_offset = -1 ; void far * main_p ; /* Find the address of main to adjust everything else */ main_p = (void far * )main ; main_addr = ( (unsigned long)FP_SEG( main_p ) << 4 ) + (unsigned long)FP_OFF( main_p ) ; #ifdef DEBUG printf("main=%08lx\n", main_addr ) ; #endif add_proc("DOS", 0l, absolute ) ; strcpy(fname, argv0 ) ; strcpy( strrchr( fname, '.' ), ".MAP" ) ; if( ( f = fopen( fname, "r" ) ) == NULL ){ perror( fname ) ; exit( 1 ) ; } state = searchsegs ; while( fgets(line, LINELEN, f ) ){ linenum++ ; switch( state ){ case searchsegs : if( sscanf(line, " %s %s %s %s %s ", str1, str2, str3, str4, str5 ) == 5 && strcmp(str1, "Start" ) == 0 && strcmp( str2, "Stop" ) == 0 && strcmp( str3, "Length" ) == 0 && strcmp( str4, "Name" ) == 0 && strcmp( str5, "Class" ) == 0 ) state = scansegs ; break ; case scansegs : if( sscanf(line, " %lxH %*lxH %*lxH %*s %s ", &endcode, str1 ) != 2 ){ fprintf(stderr, "%s(%d) : Unable to parse line : %s\n", fname, linenum, line ) ; exit( 1 ) ; } if( strcmp( str1, "ENDCODE" ) == 0 ) state = searchprocs ; case searchprocs : if( sscanf(line, " %s %s %s %s ", str1, str2, str3, str4) == 4 && strcmp(str1, "Address" ) == 0 && strcmp( str2, "Publics" ) == 0 && strcmp( str3, "by" ) == 0 && strcmp( str4, "Value" ) == 0 ) state = scanprocs ; break ; case scanprocs : if( *line == '\n' || sscanf(line, " %x:%x Abs %s ", &seg, &off, str1 ) == 3 ) break ; if( sscanf(line, " %x:%x %s ", &seg, &off, str1 ) != 3 ){ fprintf(stderr, "%s(%d) : Unable to parse line : %s\n", fname, linenum, line ) ; exit( 1 ) ; } addr = ((unsigned long)seg << 4 ) + (unsigned long)off ; if( strcmp( str1, "_main" ) == 0 ) main_offset = addr ; add_proc( str1, addr + main_addr, relocatable ) ; if( addr > endcode ){ /* * Add here in ascending order any important * memory bounds. One idea would be to partition * the BIOS in tasks e.g. printer, screen etc. */ add_proc( "UNKOWN", addr + main_addr + 1, relocatable ) ; add_proc( "EGA_BIOS", 0xc0000l, absolute ) ; add_proc( "FDISK_BIOS", 0xc8000l, absolute ) ; add_proc( "SYSTEM_ROM", 0xf0000l, absolute ) ; add_proc( "SYSTEM_BIOS", 0xfe000l, absolute ) ; add_proc( "OUTER_SPACE", (unsigned long)-1l, absolute ) ; fclose( f ) ; if( main_offset == -1 ){ fputs("_main address not found\n", stderr ) ; exit(1) ; } adjust_proc( main_offset ) ; if( onexit( prof_end ) == NULL ){ fputs("onexit failed\n", stderr ) ; exit( 1 ) ; } #ifdef DEBUG dump_table() ; test_search() ; #endif install_int() ; return ; } } } /* Something went wrong */ fprintf(stderr, "%s(%d) EOF reached before %s\n", fname, linenum, state_errs[state] ) ; exit( 1 ) ; } /* The structure where procedures are kept */ static struct proc_data { unsigned long addr ; /* Procedure start address */ unsigned long count ; /* Hit count set by interrupt */ char *name ; /* Procedure name */ enum relocation rel ; /* Relocation type */ } *procs ; /* Number of procedures and allocated memory */ static int procnum, procalloc ; /* Size of memory allocation chunk */ #define BLK 30 /* Define a procedure */ static void add_proc( char * name, unsigned long addr, enum relocation rel ) { if( procs == NULL ){ procs = xmalloc( sizeof( struct proc_data ) * BLK ) ; procalloc = BLK ; } procs[procnum].addr = addr ; procs[procnum].count = 0l ; procs[procnum].name = strsave( name ) ; procs[procnum].rel = rel ; procnum++ ; if( procnum >= procalloc ){ procalloc += BLK ; procs = xrealloc( procs, sizeof( struct proc_data ) * procalloc ) ; } } /* * Adjust downwards the memory allocated for procedure data storage * and subtract main_offset. */ static void adjust_proc( long main_offset ) { struct proc_data *pp ; xrealloc( procs, sizeof( struct proc_data ) * procnum ) ; for( pp = procs ; pp < &procs[procnum] ; pp++ ) if( pp->rel == relocatable ) pp->addr -= main_offset ; } /* Timer interrupt (Do not use 0x1c since it is always called from BIOS) */ #define TIMER_INT 8 /* Old timer handler to chain to */ static void (interrupt far * old_timer_handler)( void ) ; /* Disable timer handler and print the profiling results */ static int prof_end( void ) { register i ; FILE *f ; _dos_setvect( TIMER_INT, old_timer_handler ) ; if( ( f = fopen( "prof.out", "w" ) ) == NULL ){ perror( "prof.out" ) ; return 1 ; } for( i = 0 ; i < procnum ; i++ ) fprintf(f, "%s %ld\n", procs[i].name, procs[i].count ) ; fclose( f ) ; return 0 ; } /* Allocate memory with error checking. */ static void * xmalloc( size_t size ) { void * p ; if( ( p = malloc( size ) ) == NULL ){ fputs("Profiler : Out of memory\n", stderr ) ; exit( 1 ) ; } return p ; } /* Reallocate memory with error checking. */ static void * xrealloc( void * buffer, size_t size ) { void * p ; if( ( p = realloc( buffer, size ) ) == NULL ){ fputs("Profiler : Out of memory\n", stderr ) ; exit( 1 ) ; } return p ; } /* Save a string in allocated memory */ static char * strsave( char * string ) { return strcpy( xmalloc( strlen( string ) + 1 ), string ) ; } /* The timer interrupt handler */ static void interrupt far timer_handler(es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,cs,flags) { long addr ; int lower, upper, middle ; addr = ( (unsigned long)cs << 4 ) + (unsigned long)ip ; #ifdef DEBUG disp( addr ) ; #endif /* * Precondition : * { a[k] < a[k+1] & 0 <= k <= procnum & a[0] <= addr < a[procnum] } */ lower = 0 ; upper = procnum - 2 ; /* * Invariant : * { a[l] <= addr < a[u] } * Variant : * { u - l } */ while( upper - lower > 1 ){ middle = ( lower + upper ) / 2 ; /* * m = l + ( u - l ) / 2 = ( u + l ) / 2 * m = l + ( u - l ) / 2 & u - l > 1 implies l < m < u as: * m = ( u + l ) / 2 < ( u + u ) / 2 = u implies m < u * m = l + ( u - l ) / 2 >= l + 1 implies m > l */ if( procs[middle].addr <= addr ) lower = middle ; else upper = middle ; } /* * Postcondition : * { a[f] <= addr < a[f + 1] } which can be expressed as: * { a[l] <= addr < a[u] & u = l + 1 } */ procs[lower].count++ ; (*old_timer_handler)() ; /* Silence warnings */ (void)(es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,cs,flags) ; } /* Install the interrupt driver */ static void install_int( void ) { old_timer_handler = _dos_getvect( TIMER_INT ) ; _dos_setvect( TIMER_INT, timer_handler ) ; } #ifdef DEBUG #ifdef MDA #define REGEN_BASE 0xb0000000 #else /* CGA */ #define REGEN_BASE 0xb8000000 #endif /* Very fast display of a number on the screen. (Define MDA for mono adapter) */ static void disp(long n) { register i ; char far * sb = (char far *)(REGEN_BASE + 20 ) ; for( i = 0 ; i < 8 ; i++ ){ *sb = "0123456789abcdef"[n % 16] ; n /= 16 ; sb -= 2 ; } } /* Test the binary search algorithm */ static void pr_name( long addr ) { int lower, upper, middle ; /* * Precondition : * { a[k] < a[k+1] & 0 <= k <= procnum & a[0] <= addr < a[procnum] } */ lower = 0 ; upper = procnum - 2 ; /* * Invariant : * { a[l] <= addr < a[u] } * Variant : * { u - l } */ while( upper - lower > 1 ){ middle = ( lower + upper ) / 2 ; /* * m = l + ( u - l ) / 2 = ( u + l ) / 2 * m = l + ( u - l ) / 2 & u - l > 1 implies l < m < u as: * m = ( u + l ) / 2 < ( u + u ) / 2 = u implies m < u * m = l + ( u - l ) / 2 >= l + 1 implies m > l */ if( procs[middle].addr <= addr ) lower = middle ; else upper = middle ; printf("%5d %5d %5d\n", lower, middle, upper ) ; } /* * Postcondition : * { a[f] <= addr < a[f + 1] } which can be expressed as: * { a[l] <= addr < a[u] & u = l + 1 } */ puts( procs[lower].name ) ; } /* Interact with the user testing the search algorithm */ static void test_search() { char buff[80] ; long addr ; puts("Enter -1 to finish") ; do{ gets(buff) ; sscanf(buff, " %lx ", &addr ) ; pr_name( addr ) ; } while( addr != -1l ) ; } /* Dump the procedure table */ static void dump_table() { struct proc_data *pd ; for( pd = procs ; pd < &procs[procnum] ; pd++ ) printf("%08lx %s\n", pd->addr, pd->name ) ; } #endif