home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Special / myprofile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-14  |  9.5 KB  |  376 lines  |  [TEXT/KAHL]

  1. /* 
  2.     93/11/24 aih
  3.     - added maximum stack size calculation
  4.     
  5.     93/10/31 aih
  6.     - Added some useful features, like printing total elapsed time and only
  7.     printing routines that used at least 1% of the time. Added ResetProfile.
  8.     Also made numerous performance enhancements, such as adding a hash
  9.     table and removing some unnecessary statements. The only changes
  10.     to the external interface to this are the addition of ResetProfile
  11.     (though this file could be used without calling it), and the output
  12.     produced by DumpProfile; also, DumpProfile is no longer called
  13.     automatically at exit. */
  14.  
  15. /* Copyright (c) 1991 Symantec Corporation.  All rights reserved. */
  16.  
  17. #pragma options(!profile)
  18.  
  19. #include <stdio.h>
  20. #include <limits.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <profile.h>
  24.  
  25. Ptr CurStackBase : 0x908;
  26.  
  27. /*** clock definitions ***/
  28.  
  29. #ifdef VIATIMER
  30.  
  31. /*    VIA timer doesn't return increasing values, i.e., 
  32.     VIA_ticks() called at time t0 may be greater
  33.     than VIA_ticks() called at time t1. This makes
  34.     the timer unuseable. */
  35.     
  36. #define Ticks        VIA_ticks()
  37. #define TICKSEC        (783330.72)
  38. #define TICKMSEC    (783.33072)
  39. #define TICKUSEC    (0.78333072)
  40.  
  41. #else /* VIATIMER */
  42.  
  43. /*    Use time manager. The resolution of 10 msec could be improved on
  44.     a faster machine on which a better resolution didn't slow it
  45.     down too much. */
  46.  
  47. #define Ticks        (clock.count)
  48. #define TICKSEC        (100.0)
  49. #define TICKMSEC    (0.1)
  50. #define TICKUSEC    (0.0001)
  51. #define PRIMETIME    (0x0A)
  52.  
  53. /* information for timer */
  54. typedef struct {
  55.     TMTask tm;                /* time manager task */
  56.     unsigned long count;    /* incremented for each execution */
  57.     Boolean installed;        /* true if installed the timer */
  58. } timer_t;
  59.  
  60. static timer_t clock;
  61.  
  62. static pascal void ClockTask(void)
  63. {
  64.     asm {
  65.         addq.l #1, timer_t.count(a1)
  66.         movea.l a1, a0
  67.         move.l #PRIMETIME, d0
  68.         _PrimeTime
  69.     }
  70. }
  71.  
  72. static void ClockReset(void)
  73. {
  74.     clock.count = 0;
  75. }
  76.  
  77. static void ClockRemove(void)
  78. {
  79.     if (clock.installed) {
  80.         RmvTime((QElemPtr) &clock.tm);
  81.         clock.installed = false;
  82.     }
  83. }
  84.  
  85. static void ClockInstall(void)
  86. {
  87.     if (! clock.installed) {
  88.         memset(&clock.tm, 0, sizeof(TMTask));
  89.         _atexit(ClockRemove);
  90.         clock.tm.tmAddr = (ProcPtr) ClockTask;
  91.         InsXTime((QElemPtr) &clock.tm);
  92.         PrimeTime((QElemPtr) &clock.tm, PRIMETIME);
  93.         clock.installed = true;
  94.     }
  95. }
  96.  
  97. #endif /* VIATIMER */
  98.  
  99. /*** type definitions ***/
  100.  
  101. /*    Time data type. Using microseconds as the unit of time, the maximum
  102.     time is 2^31 * 1.0*10^-6, or 35.8 minutes. This means you can't profile any
  103.     operation taking longer than this maximum.  */
  104. typedef long ticks_t;
  105.  
  106. /* index into a table */
  107. typedef short index_t;            /* change in ExceptionLib.{c,h} also! */
  108.  
  109. /*  symbol table entry  */
  110. typedef struct sym {
  111.     unsigned char    *fname;        /* pointer to function's name */
  112.     long    count;                /* number of times function's been called */
  113.     ticks_t    total;                /* total time spent in function */
  114.     Boolean    hash;                /* true if installed in hash table */
  115. } sym_t;
  116.  
  117. /*  stack entry  */
  118. typedef struct stack {
  119.     sym_t    *sym;                /* symbol table entry */
  120.     void    **ret;                /* function's return address */
  121.     ticks_t    start;                /* start time */
  122.     ticks_t    overhead;            /* overhead time */
  123. } stack_t;
  124.  
  125. /*** globals ***/
  126.  
  127. #define HASHSIZE (1031)            /* size of hash table (prime number) */
  128.  
  129. static struct {                    /* hash table */
  130.     struct sym table[HASHSIZE];    /* symbols in hash table */
  131.     index_t nsyms;                /* number of symbols in hash table */
  132. } hash;
  133.  
  134. static struct {                    /* binary search overflow table */
  135.     sym_t *syms;                /* symbol table */
  136.     index_t nsyms;                /* number of symbols */
  137.     index_t max_nsyms;            /* max number of symbols */
  138. } binary;
  139.  
  140. static struct {                    /* call stack */
  141.     stack_t *base;                /* base of stack */
  142.     index_t max_depth;            /* max stack depth */
  143. } stack;
  144.  
  145. int _profile, _trace;            /* profile and trace flags */
  146. index_t _profile_depth;            /* stack depth; external for popping on exception */
  147. static ticks_t tstart;            /* execution time including profile overhead */
  148. static size_t maxstksz;            /* maximum size of stack */
  149.  
  150. /*** prototypes ***/
  151.  
  152. void _profile_(unsigned char *fname);
  153.  
  154. /*** external interface to profiler ***/
  155.  
  156. /* reset profiler */
  157. void ResetProfile(void)
  158. {
  159.     index_t i;
  160.     for (i = 0; i < binary.nsyms; i++)
  161.         binary.syms[i].count = binary.syms[i].total = binary.syms[i].hash = 0;
  162.     for (i = 0; i < HASHSIZE; i++)
  163.         hash.table[i].count = hash.table[i].total = hash.table[i].hash = 0;
  164.     ClockInstall();
  165.     ClockReset();
  166.     tstart = Ticks;
  167.     maxstksz = 0;
  168. }
  169.  
  170. /*    This must be called once to initialize the profiler. The arguments
  171.     specify the maximum number of functions to be profiled and the
  172.     maximum number of nested function calls. */
  173. void InitProfile(unsigned nsyms, unsigned depth)
  174. {
  175.     if (binary.syms = calloc(nsyms, sizeof(sym_t)))
  176.         binary.max_nsyms = nsyms;
  177.     if (stack.base = calloc(depth, sizeof(stack_t)))
  178.         stack.max_depth = depth;
  179.     ResetProfile();
  180. }
  181.  
  182. /* print the symbol */
  183. static void printsym(sym_t *p, ticks_t total)
  184. {
  185.     if (p->fname && total && 100.0 * p->total / total >= 1) {
  186.         printf("%#-32s  %8.3f  %8.3f  %8lu  %12.3e\n",
  187.                 /* name */        p->fname,
  188.                 /* %time */        total ? (100.0 * p->total / total) : 0,
  189.                 /* cumsecs */    (float) p->total / TICKSEC,
  190.                 /* #call */        p->count,
  191.                 /* sec/call */    (float) p->total / p->count / TICKSEC
  192.                 );
  193.     }
  194. }
  195.  
  196. /* print a report */
  197. void DumpProfile(void)
  198. {
  199.     register index_t n;
  200.     register sym_t *p;
  201.     register ticks_t total = 0;
  202.     ticks_t totalreal = Ticks - tstart;
  203.     int oldprofile;
  204.     
  205.     oldprofile = _profile;
  206.     _profile = false;
  207.     for (p = hash.table, n = HASHSIZE; n--; p++) total += p->total;
  208.     for (p = binary.syms, n = binary.nsyms; n--; p++) total += p->total;
  209.     printf("Total time %.3f seconds\n", total / TICKSEC);
  210.     printf("Total time, including profiler overhead, %.3f seconds\n", totalreal / TICKSEC);
  211.     printf("hash.nsyms = %u, binary.nsyms (collisions) = %u\n", hash.nsyms, binary.nsyms);
  212.     printf("Maximum size of stack = %lu\n", maxstksz);
  213.     printf("\n%-32s%10s%10s%10s%14s\n\n",
  214.         "Function", "%time", "cumsecs", "#call", "sec/call");
  215.     for (p = hash.table, n = HASHSIZE; n--; p++) printsym(p, total);
  216.     for (p = binary.syms, n = binary.nsyms; n--; p++) printsym(p, total);
  217.     _profile = oldprofile;
  218. }
  219.  
  220. /*** symbol table ***/
  221.  
  222. /* get the symbol table entry for the named function */
  223. static sym_t *lookup(unsigned char *fname)
  224. {
  225.     register index_t i, j, n;
  226.     register sym_t *p;
  227.     register stack_t *q;
  228.     
  229.     /*  hash search  */
  230.     p = hash.table + (unsigned long) fname % HASHSIZE;
  231.     if (p->fname == fname)
  232.         return(p); /* found symbol */
  233.     else if (! p->fname) {
  234.         /* new symbol */
  235.         hash.nsyms++;
  236.         p->hash = true;
  237.         p->fname = fname;
  238.         p->count = p->total = 0;
  239.         return(p);
  240.     }
  241.     
  242.     /*  not found in hash table, or there's a collision, so
  243.         use binary search overflow table */
  244.     i = 0;
  245.     n = binary.nsyms;
  246.     while (i < n) {
  247.         j = (i + n - 1) >> 1;
  248.         p = binary.syms + j;
  249.         if (p->fname == fname)
  250.             return(p);
  251.         if (p->fname > fname)
  252.             n = j;
  253.         else
  254.             i = j + 1;
  255.     }
  256.     
  257.     /*  insert new symbol into table  */
  258.     if (binary.nsyms == binary.max_nsyms)
  259.         return(NULL);
  260.     p = binary.syms + i;
  261.     memmove(p + 1, p, (binary.nsyms - i) * sizeof(sym_t));
  262.     p->fname = fname;
  263.     p->count = p->total = 0;
  264.     ++binary.nsyms;
  265.     
  266.     /*  adjust pointer to moved symbols  */
  267.     for (q = stack.base, n = _profile_depth; n--; q++) {
  268.         if (! q->sym->hash && q->sym >= p)
  269.             ++q->sym;
  270.     }
  271.     return(p);
  272. }
  273.  
  274. /*** pre- and post- function routines ***/
  275.  
  276. /* function called at end of each profiled function */
  277. static void *profile_exit(void)
  278. {
  279.     register stack_t *q = stack.base + --_profile_depth;
  280.     register ticks_t gross = Ticks - q->start;
  281.     register ticks_t net = gross - q->overhead;
  282.     register long stksz;
  283.     
  284.     asm {
  285.         move.l CurStackBase, stksz
  286.         sub.l sp, stksz
  287.     }
  288.     if (stksz > maxstksz)
  289.         maxstksz = stksz;
  290.     q->sym->total += net;
  291.     if (_profile_depth)
  292.         q[-1].overhead += gross;
  293.     return(q->ret);
  294. }
  295.  
  296. /*    Each function compiled with the "Profile" option begins with a call
  297.     to _profile_("\pfuncname"). */
  298. void _profile_(unsigned char *fname)
  299. {
  300.     register sym_t *p;
  301.     register stack_t *q;
  302.     register ticks_t start;
  303.     register ticks_t net;
  304.     register unsigned long result;
  305.     
  306.     if (_profile && _profile_depth < stack.max_depth) {
  307.         start = Ticks;
  308.         if (p = lookup(fname)) {
  309.             if (_trace)
  310.                 printf("%*s%#s\n", _profile_depth, "", fname);
  311.             ++p->count;
  312.             q = stack.base + _profile_depth++;
  313.             q->sym = p;
  314.             asm {
  315.                 movea.l    (a6),a0
  316.                 move.l    4(a0),q->ret
  317.                 lea        @exit,a1
  318.                 move.l    a1,4(a0)
  319.             }
  320.             q->start = start;
  321.             q->overhead = Ticks - start;
  322.         }
  323.     }
  324.     return;
  325.  
  326.         /*  the profiled function will return here  */
  327.         
  328. exit:
  329.  
  330. #define ORIGINAL (1)
  331. #if ORIGINAL
  332.     asm {
  333.         move.l    d0,-(sp)        ;  preserve result
  334.         jsr        profile_exit
  335.         movea.l    d0,a0            ;  real return address
  336.         move.l    (sp)+,d0        ;  restore result
  337.         jmp        (a0)            ;  return
  338.     }
  339. #else /* ORIGINAL */
  340.  
  341.     /* this needs a bit more work to get address offsets to work correctly */
  342.     
  343.     /* register assignments */
  344.     #define retaddr    a0
  345.     #define p        a0
  346.     #define q        a1
  347.     #define gross    d0
  348.     #define net        d1
  349.     asm {
  350.         move.l d0, -(sp)                    ; save result
  351.         subq.w #1, depth                    ; --depth;
  352.         move.w depth, d0                    ; q = stack + depth;
  353.         muls.w #sizeof(stack_t), d0            ;
  354.         movea.l stack, q                    ;
  355.         adda.l d0, q                        ;
  356.         jsr VIA_ticks                        ; gross = Ticks - q->start; 
  357.         sub.l stack_t.start(q), gross        ;
  358.         move.l gross, net                    ; net = gross - q->overhead;
  359.         sub.l stack_t.overhead(q), net        ;
  360.         ble.s @1                            ; if (net > 0)
  361.         movea.l stack_t.sym(q), p            ;     q->sym->total += net;
  362.         add.l net, sym_t.total(p)            ;
  363.         movea.l    stack_t.ret(q),retaddr        ; get real return address
  364.     @1:
  365.         tst.w depth                            ; if (depth)
  366.         beq.s @2                            ;    q[-1].overhead += gross
  367.         suba.l #sizeof(stack_t), q            ;
  368.         add.l gross, stack_t.overhead(q)    ;
  369.     @2:
  370.         move.l    (sp)+,d0                    ; restore result
  371.         jmp        (retaddr)                    ; return
  372.     }
  373. #endif /* ORIGINAL */
  374. }
  375.  
  376.