home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************/
- /* PROFILE.C */
- /* */
- /* Execution time profiler. Reads an executable and it's link */
- /* map and produces an output file with hit counts for all the */
- /* functions listed in the map file. Handles (by option selec- */
- /* tion) both MicroSoft LINK and Borland TLINK map files. */
- /* PROF only profiles '.EXE' files. */
- /****************************************************************/
- /* Command line: */
- /* */
- /* prof [-adin0$?] [-#<n>] [-m<mapfile>] [-o<outfile>] <cmd> */
- /* */
- /* -d Include DOS areas (DOS and BIOSes) in the profiling. */
- /* -a Sort output table by address, not by frequency. */
- /* -i Include intrinsic functions (name starting with '__, */
- /* or containing '@' or '$')' in the profiling. */
- /* -n Sort output table by name, not by frequency. */
- /* -0 List also functions that got no hits. */
- /* -#<n> */
- /* Execute the profiled command <n> times to increase */
- /* statistical confidence. */
- /* -? Report the address of the own PSP and main() function. */
- /* -$ Report the address of the top DOS aloccation. */
- /* -m<mapfile> */
- /* Read link map from file <mapfile> (default <cmd>.MAP). */
- /* -o<outfile> */
- /* Output profile table in file <outfile> (default */
- /* <cmd.PRF>). */
- /* <cmd> */
- /* The normal command line for the profiled command. */
- /****************************************************************/
- /* Exit codes delivered by prof: */
- /* */
- /* 0: No error. */
- /* 1: Illegal command line arguments. */
- /* 2: Cannot open command binary. */
- /* 3: Cannot open linker map file. */
- /* 4: Cannot open output file. */
- /* 5: Invalid map file format. */
- /* 6: Insufficient memory. */
- /* 7: Invalid EXE file format. */
- /* 8: EXEC error */
- /* 9: Program too fast - no hits. */
- /****************************************************************/
- /* Revised: */
- /* 1.02: Doesn't show functions that were not hit, thus */
- /* reducing the amount of output data: 890909 */
- /* 1.01: Attempts to make load address more certain: 890906 */
- /* 1.00: Functional: 890904 */
- /****************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <io.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <process.h>
- #include <dos.h>
- #include <errno.h>
- #if TRC_2_0
- #include <alloc.h>
- #endif
- #if MSC_5_1
- #include <malloc.h>
- #include <signal.h>
- #endif
-
- /****************************************************************/
- /* Adjuct constant for file load addresses. */
- /* THIS IS COMPILER SPECIFIC AND MUST BE FOUND EMPIRICALLY! */
- /****************************************************************/
-
- #if TRC_2_0
- #define ADJ_CONST 0L
- #endif
- #if MSC_5_1
- #define ADJ_CONST 0x160L
- #endif
-
- /* Compiler-specific #defines for DOS access functions */
-
- #if TRC_2_0
- #define ENABLE() enable()
- #define DISABLE() disable()
- #define ALLMEM(s,p,r) (r = allocmem(0xffff,p))
- #define ALLMEMERR(s,p) (allocmem(s,p) != -1)
- #define FREEMEM(p) freemem(p)
- #define CTRLBREAK(f) ctrlbrk(f)
- #define SETVECT(i,f) setvect(i,f)
- #define GETVECT(i) getvect(i)
- #endif
- #if MSC_5_1
- #define ENABLE() _enable()
- #define DISABLE() _disable()
- #define ALLMEM(s,p,r) (_dos_allocmem(0xffff,&r))
- #define ALLMEMERR(s,p) (_dos_allocmem(s,p) != 0)
- #define FREEMEM(p) _dos_freemem(p)
- #define CTRLBREAK(f) signal(SIGINT,f)
- #define SETVECT(i,f) _dos_setvect(i,f)
- #define GETVECT(i) _dos_getvect(i)
- #endif
-
- #define TMRINT 8 /* Timer hardware interrupt */
- #define MAXFUNC 2000 /* Max 2000 functions... */
- #define CMDSIZ 130 /* Cmd's command line size */
- #define FNSIZ 130 /* Max file name size */
- #define LNSIZ 130 /* Max map file line length */
- #define STSIZ 35 /* Size of small strings */
-
- #define ABSTYP 1 /* Table entry is absolute */
- #define USRTYP 2 /* Table entry is USR func */
- #define INRTYP 4 /* Table entry is INR type */
- #define DOSTYP 8 /* Table entry is DOS type */
-
- typedef struct /* Entry in funcion table */
- {
- unsigned long addr; /* Function start address */
- unsigned long hits; /* Hit count */
- char *name; /* Function name */
- char typ; /* Function properties */
- char dummy; /* For word-aligning */
- } fdesc; /* Function descriptor */
-
- static char exename[FNSIZ] = {0}; /* Prof'ed command's binary */
- static char mapname[FNSIZ] = {0}; /* Prof'ed command's map */
- static char outname[FNSIZ] = {0}; /* Prof output table file */
-
- static FILE *file = NULL; /* For read/write of files */
- static struct stat statinfo; /* Stat() buffer */
- static fdesc *descs; /* Start of desc table */
- static int exe_count = 1; /* # times to exec command */
- static int nfuncs; /* Number of functions */
- static char name_ordr = 0; /* If sorting address-wise */
- static char addr_ordr = 0; /* If sorting adress.-wise */
- static char tell_psp = 0; /* Tell load PSP address */
- static char list_nulls = 0; /* IF listing no-hit funcs */
- static char wrt_msk = USRTYP; /* Default only user funcs */
- static double tot_hits = 0.0; /* Total function hits */
- static double dsp_hits = 0.0; /* Total displayed hits */
- static double usr_hits = 0.0; /* Total user pgm hits */
- static char **cmdline; /* Command line array ptr */
- static unsigned freemem_pg = 0; /* At beg of free memory */
- static unsigned load_psp = 0; /* Paragr of load area */
-
- static void (interrupt *old_tmr_handler)() = NULL; /* Original handler */
-
- static char *msgs[] =
- {
- "profile: ", /* 0 */
- "Illegal command line option: `-%s'\n", /* 1 */
- "No command specified for profiling\n", /* 2 */
- "Cannot find executable `%s'\n", /* 3 */
- "`%s' is a directory\n", /* 4 */
- "Cannot find linker map file `%s'\n", /* 5 */
- "Output file `%s' is write-protected\n", /* 6 */
- "Invalid map file format in `%s'", /* 7 */
- "Insufficient memory for function tables\n", /* 8 */
- "Not enough function table slots\n", /* 9 */
- "Cannot open output file `%s'\n", /* 10 */
- "Insufficient memory to execute command", /* 11 */
- "Error executing command %s\n", /* 12 */
- "Invalid EXE file format in `%s'\n", /* 13 */
- "@(#)profile.c v.1.02 - 890909", /* 14 */
- "Program ran too fast - no hits!\n" /* 15 */
- } ; /* msgs */
-
- static void get_cmdline(); /* Interpret command line */
- static void set_filenames(); /* Fix definitive filenames */
- static void check_files(); /* Check files are OK */
- static void read_map(); /* Read map, build tables */
- static void add_func(); /* add function to table */
- static int blankline(); /* Check if line is blank */
- static void adjust(); /* Set correct func addrs */
- static void profile(); /* Do the actual profiling */
- static void write_result(); /* Output result */
- static int fadr_comp(); /* Compare func addresses */
- static int fhit_comp(); /* Compare func hitcounts */
- static int fnam_comp(); /* Compare func names */
- static void usage(); /* Help routine */
- static void error(); /* Error/Abort routine */
-
- static int brk_handler(); /* SIGINT handler */
- static void interrupt tmr_handler(); /* Timer interrupt handler */
-
- /****************************************************************/
-
- void main(narg, args)
- int narg;
- char *args[];
- {
- CTRLBREAK(brk_handler);
- get_cmdline(narg, args);
- set_filenames();
- check_files();
- read_map();
- adjust();
- profile();
- write_result();
- error(0,"");
- } /* main */
-
- /****************************************************************/
- /* Get_cmdline() extracts all switches etc, gets the name of */
- /* the command to be profiled, and sets up file names. It also */
- /* builds the command line for the command to be profiled. */
- /****************************************************************/
-
- static void get_cmdline(narg, args)
- int narg;
- char *args[];
- {
- int i = 1;
- char *p;
- unsigned long m;
-
- while ((i < narg) && (*args[i] == '-')) /* Extract switches */
- {
- args[i]++;
- while (*args[i]) /* Get switches */
- {
- switch (*args[i])
- {
- case '#': if (sscanf(args[i]+1, "%d", &exe_count) != 1)
- error(1, msgs[1], args[i]);
- args[i] = " ";
- break;
- case '?': printf("Actual PSP is at absolute address 0x%05x0\n",
- _psp);
- p = (char *) main; /* Trick for MSC */
- m = FP_SEG(p);
- m <<= 4;
- m += FP_OFF(p);
- printf("Main() fnc is at absolute address 0x%06lx\n",
- m);
- error(0,"");
- case '$': tell_psp = 1; /* Tell DOS alloc address */
- break;
- case 'a': name_ordr = 0; /* Sort table by freq */
- addr_ordr = 1;
- break;
- case 'd': wrt_msk |= DOSTYP; /* Include DOS areas */
- break;
- case 'i': wrt_msk |= INRTYP; /* Include intrinsic funcs */
- break;
- case 'n': name_ordr = 1; /* Sort table by name */
- addr_ordr = 0;
- break;
- case '0': list_nulls = 1; /* List no-hit funcs */
- break;
- case 'm': strcpy(mapname, args[i]+1); /* Map file name */
- args[i] = " ";
- break;
- case 'o': strcpy(outname, args[i]+1); /* Output table file name */
- args[i] = " ";
- break;
- default: error(1, msgs[1], args[i]);
- } /* switch */
- args[i]++;
- } /* while */
- i++;
- } /* while */
-
- if (i >= narg) /* Check there is a command */
- error(1, msgs[2]);
- strcpy(exename,args[i]);
- cmdline = args + i;
- } /* get_cmdline */
-
- /****************************************************************/
- /* Set_filenames() adjust names of needed file names based on */
- /* defaults and options. */
- /****************************************************************/
-
- static void set_filenames()
- {
- if (!mapname[0]) /* Set default mapfile name */
- {
- strcpy(mapname, exename);
- strcat(mapname, ".map");
- } /* if */
- if (!outname[0]) /* Set default outfile name */
- {
- strcpy(outname, exename);
- strcat(outname, ".prf");
- } /* if */
- strcat(exename,".exe"); /* It's an 'EXE' file */
- } /* set_filenames */
-
- /****************************************************************/
- /* Check_files() checks that all files are available, readable */
- /* and writeable as required. */
- /****************************************************************/
-
- static void check_files()
- {
- if (stat(exename, &statinfo)) /* Check executable exists */
- error(2, msgs[3], exename);
- if (statinfo.st_mode & S_IFDIR)
- error(2, msgs[4], exename);
- if (stat(mapname, &statinfo)) /* Check mapfile exists */
- error(3, msgs[5], mapname);
- if (statinfo.st_mode & S_IFDIR)
- error(3, msgs[4], mapname);
- if (stat(outname, &statinfo)) /* Check outfile writeable */
- return;
- if (statinfo.st_mode & S_IFDIR)
- error(4, msgs[4], outname);
- if (!(statinfo.st_mode & S_IWRITE))
- error(4, msgs[6], outname);
- } /* check_files */
-
- /****************************************************************/
- /* Read_map() reads the map file into memory and builds the */
- /* linked list of entries. */
- /****************************************************************/
-
- static void read_map()
- {
- char line[LNSIZ+1];
- char str1[STSIZ],str2[STSIZ],str3[STSIZ],str4[STSIZ];
- fdesc *p;
- long ofs, seg;
-
- if ((p = descs = calloc(MAXFUNC,sizeof(fdesc))) == NULL)
- error(6, msgs[8]);
-
- if ((file = fopen(mapname,"r")) == NULL) /* 'Impossible' */
- error(3, msgs[5], mapname);
-
- while (fgets(line, LNSIZ, file) != NULL) /* Find 'Add Pub by Val' */
- {
- if (
- (sscanf(line, " %s %s %s %s ", str1, str2, str3, str4) == 4)
- &&
- (stricmp(str1, "Address") == 0)
- &&
- (stricmp(str2, "Publics") == 0)
- &&
- (strcmp(str3, "by") == 0)
- &&
- (strcmp(str4, "Value") == 0)
- )
- break;
- } /* while */
- if (feof(file))
- error(5, msgs[7], mapname);
-
- while (fgets(line, LNSIZ, file) != NULL) /* Find Non-blank line */
- if (!blankline(line))
- break;
- if (feof(file))
- error(5, msgs[7], mapname);
-
- add_func(p++, "Low Mem", 0l, ABSTYP|DOSTYP); /* Make entry for low mem */
- add_func(p++, "DOS", 0x400l, ABSTYP|DOSTYP); /* Make entry for low mem */
- seg = _psp; /* Get profiler's psp */
- add_func(p++,"Profiler",seg<<4,ABSTYP|DOSTYP);/* Make entry for prof */
-
- nfuncs = 2;
- do /* Process and read another */
- {
- if (blankline(line)) /* Blank line end of data */
- break;
- if (sscanf(line, " %lx:%lx Abs %s ", &seg, &ofs, str1) != 3)
- if (sscanf(line, " %lx:%lx %s ", &seg, &ofs, str1) != 3)
- error(5, msgs[7], mapname);
- if (str1[0] == '_') /* Play with '_' for */
- str1[0] = 1; /* alpha sorting */
- if (str1[1] == '_') /* This is converted back */
- str1[1] = 0x7f; /* on output */
-
- if ( /* Intrinsic function */
- ((str1[0] == 1) && (str1[1] == 0x7f)) /* with '__' */
- ||
- (strchr(str1,'@') != NULL) /* or with '@' */
- ||
- (strchr(str1,'$') != NULL) /* or with '$' */
- )
- add_func(p++, str1, (seg << 4) + ofs, INRTYP);/* Make entry */
- else /* User function */
- add_func(p++, str1, (seg << 4) + ofs, USRTYP);/* Make entry */
- nfuncs++;
- if (nfuncs > (MAXFUNC - 10))
- error(6, msgs[9]);
- }
- while (fgets(line, LNSIZ, file) != NULL);
-
- add_func(p++,"EGA BIOS", 0xc0000l, ABSTYP|DOSTYP);
- add_func(p++,"Fixed Disk BIOS", 0xc8000l, ABSTYP|DOSTYP);
- add_func(p++,"System ROM", 0xf0000l, ABSTYP|DOSTYP);
- add_func(p++,"System BIOS", 0xfe000l, ABSTYP|DOSTYP);
- nfuncs += 4;
-
- fclose(file);
- file = (FILE *) NULL;
- } /* read_map */
-
- /****************************************************************/
- /* Add_func() adds a function to the function table. */
- /****************************************************************/
-
- static void add_func(p, nam, addr, typ)
- fdesc *p;
- char *nam;
- long addr;
- char typ;
- {
- p->addr = addr;
- p->hits = 0l;
- if ((p->name = calloc(1,strlen(nam)+1)) == NULL)
- error(6, msgs[8]);
- strcpy(p->name, nam);
- p->typ = typ;
- } /* add_func */
-
- /****************************************************************/
- /* Blankline() returns 1 if the passed line is entirely blank. */
- /****************************************************************/
-
- static int blankline(s)
- char *s;
- {
- while (*s)
- {
- if (!isspace(*s))
- return(0);
- s++;
- } /* while */
- return(1);
- } /* blankline */
-
- /****************************************************************/
- /* Adjust() finds out where in memory the executable will be */
- /* loaded, and adjust function addresses accordingly. Does this */
- /* By allocating first a hole, then sys memory 1, and then sys */
- /* memory 2. Sys memory 2 indicates first free address, and is */
- /* immediately re-freed. The hole is also freed to function as */
- /* work space for functions that will run before the actual */
- /* execution of the profiled program. THIS IS TRICKY AND COM- */
- /* PILER DEPENDENT... */
- /****************************************************************/
-
- static void adjust()
- {
- char *hole;
- long adj;
- int i;
- int maxsz;
-
- if ((hole = malloc(0x800)) == NULL) /* Fix workspace for others */
- error(6, msgs[11]);
- if (ALLMEMERR(0x20, &freemem_pg)) /* Grab small mem over it */
- error(6, msgs[11]);
- ALLMEM(0xffff, &load_psp, maxsz); /* See what max space is */
- if (ALLMEMERR(maxsz, &load_psp)) /* Grab it to know address */
- error(6, msgs[11]);
- free (hole); /* Make workspace available */
-
- adj = load_psp;
- adj <<= 4;
- adj += 0x100 + ADJ_CONST;
-
- if (tell_psp) /* If display free start */
- printf("Expecting PSP at absolute address 0x%06lx\n", adj-0x100L);
-
- for (i = 0; i < nfuncs; i++) /* Add adj to func addr:s */
- if (!((descs + i)->typ & ABSTYP)) /* Only relocatable ones */
- (descs + i)->addr += adj;
-
- qsort(descs,nfuncs,sizeof(fdesc),fadr_comp); /* Sort in address order */
- } /* adjust */
-
- /****************************************************************/
- /* Profile() does the profiling. It finds out where the pro- */
- /* filed command will be loaded, adjusts function addresses */
- /* accordingly, starts timer interrupts, and executes the com- */
- /* mand as a subshell. */
- /****************************************************************/
-
- static void profile()
- {
- int i = 0;
-
- old_tmr_handler = GETVECT(TMRINT); /* Save old int vector */
- SETVECT(TMRINT,tmr_handler); /* Start profiling */
- DISABLE();
- FREEMEM(load_psp); /* Free the load area */
- load_psp = 0; /* To not free it at exit */
- ENABLE();
- while(i++ < exe_count)
- {
- fprintf(stderr, "%-3d ----- Executing %s\n", i, *cmdline);
- if (spawnv(P_WAIT, *cmdline, cmdline) == -1)
- {
- switch(errno)
- {
- case ENOEXEC: error(7,msgs[13], exename);
- break;
- case ENOMEM: error(6,msgs[11]);
- break;
- default: error(8,msgs[12]);
- } /* switch */
- } /* if */
- } /* switch */
-
- SETVECT(TMRINT,old_tmr_handler);
- (char *) old_tmr_handler = NULL;
-
- DISABLE();
- FREEMEM(freemem_pg);
- freemem_pg = 0;
- ENABLE();
- fprintf(stderr, "--------- Executing completed\n");
- } /* profile */
-
- /****************************************************************/
- /* Write_result() sorts the data, computes profiling percen- */
- /* tages, and write out the resulting list. */
- /****************************************************************/
-
- static void write_result()
- {
- int i;
-
- if (name_ordr) /* Sort table by name? */
- qsort(descs,nfuncs,sizeof(fdesc),fnam_comp);/* Sort in name order */
- else
- if (!addr_ordr)
- qsort(descs,nfuncs,sizeof(fdesc),fhit_comp);/* Sort in freq order */
-
- if ((file = fopen(outname,"w")) == NULL) /* Possible if command did */
- error(4, msgs[10], outname); /* something like chmod -w */
-
- for (i = 0; i < nfuncs; i++) /* Add up total hit counts */
- {
- tot_hits += (double) ((descs + i)->hits); /* Add upp total hits */
- if ((descs + i)->typ & wrt_msk)
- dsp_hits += (double) ((descs + i)->hits); /* Add upp displayed hits */
- if ((descs + i)->typ & USRTYP)
- usr_hits += (double) ((descs + i)->hits); /* Add up user hits */
- } /* for */
- if (tot_hits == 0.0) /* Avoid div by 0.0 */
- tot_hits = 1.0;
- if (dsp_hits == 0.0) /* Avoid div by 0.0 */
- {
- if (!tell_psp)
- error(9,msgs[15]);
- else
- dsp_hits = 1.0;
- } /* if */
- if (usr_hits == 0.0) /* Avoid div by 0.0 */
- usr_hits = 1.0;
-
- fprintf(file, "Function name Addr Total Disp User\n\n");
- for (i = 0; i < nfuncs; i++)
- {
- if (((descs + i)->hits == 0) && /* Don't show 0 hit funcs */
- !(list_nulls || tell_psp))
- continue;
- if (wrt_msk & (descs +i)->typ)
- {
- if ((descs + i)->name[0] == 1) /* Reconvert fixes done */
- (descs + i)->name[0] = '_'; /* when reading the map */
- if ((descs + i)->name[1] == 0x7f)
- (descs + i)->name[1] = '_';
- fprintf(file, "%-20s %05lx %6.2f %6.2f",
- (descs + i)->name, (descs + i)->addr,
- 100.0 * ((descs + i)->hits / tot_hits),
- 100.0 * ((descs + i)->hits / dsp_hits));
- if ((descs + i)->typ & USRTYP) /* Only usrfuncs get col 3 */
- fprintf(file," %6.2f", 100.0 * ((descs + i)->hits / usr_hits));
- fprintf(file,"\n");
- } /* if */
- } /* for */
- fprintf(file, "\nStatistics based on %6.0f hits\n", tot_hits);
- fclose(file);
- file = (FILE *) NULL;
- } /* write_result */
-
- /****************************************************************/
- /* Fadr_comp() compares addresses of two functions. If address */
- /* values are the same, name decides. */
- /****************************************************************/
-
- static int fadr_comp(f1, f2)
- fdesc *f1, *f2;
- {
- if (f1->addr > f2->addr)
- return(1);
- if (f1->addr < f2->addr)
- return(-1);
- return(fnam_comp(f1,f2));
- } /* fadr_comp */
-
- /****************************************************************/
- /* Fhit_comp() compares hit counts of two function table */
- /* entries. If counts are the same, name decides. */
- /****************************************************************/
-
- static int fhit_comp(f1, f2)
- fdesc *f1, *f2;
- {
- if (f1->hits > f2->hits)
- return(-1);
- if (f1->hits < f2->hits)
- return(1);
- return(fnam_comp(f1,f2));
- } /* fhit_comp */
-
- /****************************************************************/
- /* Fnam_comp() compares names of two function table entries. */
- /****************************************************************/
-
- static int fnam_comp(f1, f2)
- fdesc *f1, *f2;
- {
- return (stricmp(f1->name, f2->name));
- } /* fnam_comp */
-
- /****************************************************************/
- /* Usage() displays a usage menu. */
- /****************************************************************/
-
- static void usage()
- {
- fprintf(stderr,
- "Usage: profile [-adin0] [-#<n>] [-m<map>] [-o<out>] <cmd> [<args>...]\n");
- fprintf(stderr,
- " -a Sort output by adress, not by frequency\n");
- fprintf(stderr,
- " -d Include DOS and BIOS areas in profiling\n");
- fprintf(stderr,
- " -i Include intrinsic functions (starting with `__', or\n");
- fprintf(stderr,
- " containing the characters `@' or '$') in profiling\n");
- fprintf(stderr,
- " -n Sort output by name, not by frequency\n");
- fprintf(stderr,
- " -0 List also functions that had zero frequency\n");
- fprintf(stderr,
- " -#<n> Execute profiled command <n> times (default 1)\n");
- fprintf(stderr,
- " -m<map> Use file <map> as linker map info (default <cmd>.MAP)\n");
- fprintf(stderr,
- " -o<out> Put output result in file <out> (default <cmd>.PRF)\n");
- fprintf(stderr,
- " <cmd> Name of command to be profiled (including path)\n");
- fprintf(stderr,
- " <args> Optional arguments to <cmd>\n");
- } /* usage */
-
- /****************************************************************/
- /* Error() */
- /* */
- /* Print an error diagnosis and exit. */
- /****************************************************************/
- /*VARARGS*/
- static void error(ercode, ermsg, s1, s2, s3, s4)
- int ercode;
- char *ermsg;
- char *s1, *s2, *s3, *s4;
- {
- if ((char *) old_tmr_handler != NULL)
- SETVECT(TMRINT,old_tmr_handler);
- if (freemem_pg)
- FREEMEM(freemem_pg);
- if (load_psp)
- FREEMEM(load_psp);
- if (ercode != 0)
- {
- fprintf(stderr, msgs[0]);
- fprintf(stderr, ermsg, s1, s2, s3, s4);
- } /* if */
- if (ercode == 1)
- usage();
- if (file != (FILE *) NULL)
- fclose(file);
- exit(ercode);
- } /* error */
-
- /****************************************************************/
- /* Brk_handler() catches CTRL-C interrupts. */
- /****************************************************************/
-
- static int brk_handler()
- {
- CTRLBREAK(brk_handler);
- error(255,"User abort\n");
- return(0); /* Actually not executed */
- } /* brk_handler */
-
- /****************************************************************/
- /* Tmr_handler() is the interrupt handler that updates hit */
- /* counts. It must be fast. */
- /****************************************************************/
-
- #if TRC_2_0
- static void interrupt tmr_handler(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs)
- unsigned bp,di,si,ds,es,dx,cx,bx,ax,ip,cs;
- #endif
- #if MSC_5_1
- static void interrupt tmr_handler(es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,cs)
- unsigned es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,cs;
- #endif
- {
- long addr;
- int lower, upper, middle;
-
- addr = ((unsigned long)cs << 4) + (unsigned long) ip;
- lower = 0;
- upper = nfuncs - 1;
-
- while (upper - lower > 1)
- {
- middle = (lower + upper) / 2;
- if ((descs + middle)->addr <= addr)
- lower = middle;
- else
- upper = middle;
- } /* while */
- (descs + lower)->hits++;
- (*old_tmr_handler)();
- } /* tmr_handler */
-