home *** CD-ROM | disk | FTP | other *** search
- /*
- 93/11/24 aih
- - added maximum stack size calculation
-
- 93/10/31 aih
- - Added some useful features, like printing total elapsed time and only
- printing routines that used at least 1% of the time. Added ResetProfile.
- Also made numerous performance enhancements, such as adding a hash
- table and removing some unnecessary statements. The only changes
- to the external interface to this are the addition of ResetProfile
- (though this file could be used without calling it), and the output
- produced by DumpProfile; also, DumpProfile is no longer called
- automatically at exit. */
-
- /* Copyright (c) 1991 Symantec Corporation. All rights reserved. */
-
- #pragma options(!profile)
-
- #include <stdio.h>
- #include <limits.h>
- #include <stdlib.h>
- #include <string.h>
- #include <profile.h>
-
- Ptr CurStackBase : 0x908;
-
- /*** clock definitions ***/
-
- #ifdef VIATIMER
-
- /* VIA timer doesn't return increasing values, i.e.,
- VIA_ticks() called at time t0 may be greater
- than VIA_ticks() called at time t1. This makes
- the timer unuseable. */
-
- #define Ticks VIA_ticks()
- #define TICKSEC (783330.72)
- #define TICKMSEC (783.33072)
- #define TICKUSEC (0.78333072)
-
- #else /* VIATIMER */
-
- /* Use time manager. The resolution of 10 msec could be improved on
- a faster machine on which a better resolution didn't slow it
- down too much. */
-
- #define Ticks (clock.count)
- #define TICKSEC (100.0)
- #define TICKMSEC (0.1)
- #define TICKUSEC (0.0001)
- #define PRIMETIME (0x0A)
-
- /* information for timer */
- typedef struct {
- TMTask tm; /* time manager task */
- unsigned long count; /* incremented for each execution */
- Boolean installed; /* true if installed the timer */
- } timer_t;
-
- static timer_t clock;
-
- static pascal void ClockTask(void)
- {
- asm {
- addq.l #1, timer_t.count(a1)
- movea.l a1, a0
- move.l #PRIMETIME, d0
- _PrimeTime
- }
- }
-
- static void ClockReset(void)
- {
- clock.count = 0;
- }
-
- static void ClockRemove(void)
- {
- if (clock.installed) {
- RmvTime((QElemPtr) &clock.tm);
- clock.installed = false;
- }
- }
-
- static void ClockInstall(void)
- {
- if (! clock.installed) {
- memset(&clock.tm, 0, sizeof(TMTask));
- _atexit(ClockRemove);
- clock.tm.tmAddr = (ProcPtr) ClockTask;
- InsXTime((QElemPtr) &clock.tm);
- PrimeTime((QElemPtr) &clock.tm, PRIMETIME);
- clock.installed = true;
- }
- }
-
- #endif /* VIATIMER */
-
- /*** type definitions ***/
-
- /* Time data type. Using microseconds as the unit of time, the maximum
- time is 2^31 * 1.0*10^-6, or 35.8 minutes. This means you can't profile any
- operation taking longer than this maximum. */
- typedef long ticks_t;
-
- /* index into a table */
- typedef short index_t; /* change in ExceptionLib.{c,h} also! */
-
- /* symbol table entry */
- typedef struct sym {
- unsigned char *fname; /* pointer to function's name */
- long count; /* number of times function's been called */
- ticks_t total; /* total time spent in function */
- Boolean hash; /* true if installed in hash table */
- } sym_t;
-
- /* stack entry */
- typedef struct stack {
- sym_t *sym; /* symbol table entry */
- void **ret; /* function's return address */
- ticks_t start; /* start time */
- ticks_t overhead; /* overhead time */
- } stack_t;
-
- /*** globals ***/
-
- #define HASHSIZE (1031) /* size of hash table (prime number) */
-
- static struct { /* hash table */
- struct sym table[HASHSIZE]; /* symbols in hash table */
- index_t nsyms; /* number of symbols in hash table */
- } hash;
-
- static struct { /* binary search overflow table */
- sym_t *syms; /* symbol table */
- index_t nsyms; /* number of symbols */
- index_t max_nsyms; /* max number of symbols */
- } binary;
-
- static struct { /* call stack */
- stack_t *base; /* base of stack */
- index_t max_depth; /* max stack depth */
- } stack;
-
- int _profile, _trace; /* profile and trace flags */
- index_t _profile_depth; /* stack depth; external for popping on exception */
- static ticks_t tstart; /* execution time including profile overhead */
- static size_t maxstksz; /* maximum size of stack */
-
- /*** prototypes ***/
-
- void _profile_(unsigned char *fname);
-
- /*** external interface to profiler ***/
-
- /* reset profiler */
- void ResetProfile(void)
- {
- index_t i;
- for (i = 0; i < binary.nsyms; i++)
- binary.syms[i].count = binary.syms[i].total = binary.syms[i].hash = 0;
- for (i = 0; i < HASHSIZE; i++)
- hash.table[i].count = hash.table[i].total = hash.table[i].hash = 0;
- ClockInstall();
- ClockReset();
- tstart = Ticks;
- maxstksz = 0;
- }
-
- /* This must be called once to initialize the profiler. The arguments
- specify the maximum number of functions to be profiled and the
- maximum number of nested function calls. */
- void InitProfile(unsigned nsyms, unsigned depth)
- {
- if (binary.syms = calloc(nsyms, sizeof(sym_t)))
- binary.max_nsyms = nsyms;
- if (stack.base = calloc(depth, sizeof(stack_t)))
- stack.max_depth = depth;
- ResetProfile();
- }
-
- /* print the symbol */
- static void printsym(sym_t *p, ticks_t total)
- {
- if (p->fname && total && 100.0 * p->total / total >= 1) {
- printf("%#-32s %8.3f %8.3f %8lu %12.3e\n",
- /* name */ p->fname,
- /* %time */ total ? (100.0 * p->total / total) : 0,
- /* cumsecs */ (float) p->total / TICKSEC,
- /* #call */ p->count,
- /* sec/call */ (float) p->total / p->count / TICKSEC
- );
- }
- }
-
- /* print a report */
- void DumpProfile(void)
- {
- register index_t n;
- register sym_t *p;
- register ticks_t total = 0;
- ticks_t totalreal = Ticks - tstart;
- int oldprofile;
-
- oldprofile = _profile;
- _profile = false;
- for (p = hash.table, n = HASHSIZE; n--; p++) total += p->total;
- for (p = binary.syms, n = binary.nsyms; n--; p++) total += p->total;
- printf("Total time %.3f seconds\n", total / TICKSEC);
- printf("Total time, including profiler overhead, %.3f seconds\n", totalreal / TICKSEC);
- printf("hash.nsyms = %u, binary.nsyms (collisions) = %u\n", hash.nsyms, binary.nsyms);
- printf("Maximum size of stack = %lu\n", maxstksz);
- printf("\n%-32s%10s%10s%10s%14s\n\n",
- "Function", "%time", "cumsecs", "#call", "sec/call");
- for (p = hash.table, n = HASHSIZE; n--; p++) printsym(p, total);
- for (p = binary.syms, n = binary.nsyms; n--; p++) printsym(p, total);
- _profile = oldprofile;
- }
-
- /*** symbol table ***/
-
- /* get the symbol table entry for the named function */
- static sym_t *lookup(unsigned char *fname)
- {
- register index_t i, j, n;
- register sym_t *p;
- register stack_t *q;
-
- /* hash search */
- p = hash.table + (unsigned long) fname % HASHSIZE;
- if (p->fname == fname)
- return(p); /* found symbol */
- else if (! p->fname) {
- /* new symbol */
- hash.nsyms++;
- p->hash = true;
- p->fname = fname;
- p->count = p->total = 0;
- return(p);
- }
-
- /* not found in hash table, or there's a collision, so
- use binary search overflow table */
- i = 0;
- n = binary.nsyms;
- while (i < n) {
- j = (i + n - 1) >> 1;
- p = binary.syms + j;
- if (p->fname == fname)
- return(p);
- if (p->fname > fname)
- n = j;
- else
- i = j + 1;
- }
-
- /* insert new symbol into table */
- if (binary.nsyms == binary.max_nsyms)
- return(NULL);
- p = binary.syms + i;
- memmove(p + 1, p, (binary.nsyms - i) * sizeof(sym_t));
- p->fname = fname;
- p->count = p->total = 0;
- ++binary.nsyms;
-
- /* adjust pointer to moved symbols */
- for (q = stack.base, n = _profile_depth; n--; q++) {
- if (! q->sym->hash && q->sym >= p)
- ++q->sym;
- }
- return(p);
- }
-
- /*** pre- and post- function routines ***/
-
- /* function called at end of each profiled function */
- static void *profile_exit(void)
- {
- register stack_t *q = stack.base + --_profile_depth;
- register ticks_t gross = Ticks - q->start;
- register ticks_t net = gross - q->overhead;
- register long stksz;
-
- asm {
- move.l CurStackBase, stksz
- sub.l sp, stksz
- }
- if (stksz > maxstksz)
- maxstksz = stksz;
- q->sym->total += net;
- if (_profile_depth)
- q[-1].overhead += gross;
- return(q->ret);
- }
-
- /* Each function compiled with the "Profile" option begins with a call
- to _profile_("\pfuncname"). */
- void _profile_(unsigned char *fname)
- {
- register sym_t *p;
- register stack_t *q;
- register ticks_t start;
- register ticks_t net;
- register unsigned long result;
-
- if (_profile && _profile_depth < stack.max_depth) {
- start = Ticks;
- if (p = lookup(fname)) {
- if (_trace)
- printf("%*s%#s\n", _profile_depth, "", fname);
- ++p->count;
- q = stack.base + _profile_depth++;
- q->sym = p;
- asm {
- movea.l (a6),a0
- move.l 4(a0),q->ret
- lea @exit,a1
- move.l a1,4(a0)
- }
- q->start = start;
- q->overhead = Ticks - start;
- }
- }
- return;
-
- /* the profiled function will return here */
-
- exit:
-
- #define ORIGINAL (1)
- #if ORIGINAL
- asm {
- move.l d0,-(sp) ; preserve result
- jsr profile_exit
- movea.l d0,a0 ; real return address
- move.l (sp)+,d0 ; restore result
- jmp (a0) ; return
- }
- #else /* ORIGINAL */
-
- /* this needs a bit more work to get address offsets to work correctly */
-
- /* register assignments */
- #define retaddr a0
- #define p a0
- #define q a1
- #define gross d0
- #define net d1
- asm {
- move.l d0, -(sp) ; save result
- subq.w #1, depth ; --depth;
- move.w depth, d0 ; q = stack + depth;
- muls.w #sizeof(stack_t), d0 ;
- movea.l stack, q ;
- adda.l d0, q ;
- jsr VIA_ticks ; gross = Ticks - q->start;
- sub.l stack_t.start(q), gross ;
- move.l gross, net ; net = gross - q->overhead;
- sub.l stack_t.overhead(q), net ;
- ble.s @1 ; if (net > 0)
- movea.l stack_t.sym(q), p ; q->sym->total += net;
- add.l net, sym_t.total(p) ;
- movea.l stack_t.ret(q),retaddr ; get real return address
- @1:
- tst.w depth ; if (depth)
- beq.s @2 ; q[-1].overhead += gross
- suba.l #sizeof(stack_t), q ;
- add.l gross, stack_t.overhead(q) ;
- @2:
- move.l (sp)+,d0 ; restore result
- jmp (retaddr) ; return
- }
- #endif /* ORIGINAL */
- }
-
-