home *** CD-ROM | disk | FTP | other *** search
/ Carousel / CAROUSEL.cdr / mactosh / lang / lsc30p4u.sit / profile.c < prev    next >
Text File  |  1989-01-15  |  11KB  |  390 lines

  1. /*
  2.  
  3.   Profile and tracing package for LightspeedC¬.
  4.   
  5.   (C) Copyright 1986 THINK Technologies, Inc.  All rights reserved.
  6.  
  7.  */
  8.  
  9.  
  10. #include        "stdio.h"
  11. #include         "DeviceMgr.h"
  12.  
  13.  
  14. typedef unsigned char   Bool;                   /* for size                  */
  15.  
  16. typedef void (*address)();                        /* address type                  */
  17. typedef unsigned char   byte;
  18. typedef unsigned long   time;
  19.  
  20. typedef struct {
  21.         char             *name;                  /* function name             */
  22.         time            average;                /* average time              */
  23.         time            maximum;                /* largest time              */
  24.         time            minimum;                /* smallest time             */
  25.         time            total;                  /* total time                */
  26.         unsigned int    entries;                /* times entered             */
  27.         } PROFILE;
  28.  
  29. typedef struct {
  30.         address         user;                   /* user return address       */
  31.         int             function;               /* index into profiles       */
  32.         time            correction;             /* nested call time          */
  33.         time            start;                  /* entry time                */
  34.         } STACK;
  35.  
  36. #define LOCAL           static                  /* local to profiler         */
  37. #define MAX_PROFILES       200                  /* routines to follow        */
  38. #define MAX_STACK          200                  /* max nested calls          */
  39. #define MAX_TIME        500000L                 /* max time                  */
  40.  
  41. /*
  42.  
  43.         Select execution/compilation options.
  44.  
  45.         SINGLE                  - each routine contains only its time
  46.         TESTING                 - test profile package without LightspeedC PROFILE option
  47.  
  48.  */
  49.  
  50. #define SINGLE                                  /* single routine only       */
  51. /* #define TESTING                              standalone testing   */
  52.  
  53.  
  54. #ifdef  TESTING
  55. #undef  LOCAL                                   /* make items visible        */
  56. #define LOCAL
  57. #endif
  58.  
  59.  
  60.  
  61. LOCAL int               depth                   = -1;
  62. LOCAL PROFILE           profiles[MAX_PROFILES];
  63. LOCAL int               profile_count           =  0;
  64. LOCAL STACK             stack[MAX_STACK];
  65. LOCAL int               stack_pointer           = -1;
  66. LOCAL time              start_time;
  67.  
  68. int                     _profile                =  1; 
  69. int                     _trace                  =  0;
  70.  
  71. void    DumpProfile();
  72. extern    _profile_exit_();
  73. extern  char *CtoPstr();
  74. extern  char *PtoCstr();
  75.  
  76. #define _VIATIMER_    /* if this symbol is defined, the VIA timer now is used
  77.                         instead of TickCount -- it provides faster ticks,
  78.                         however there is a false precision to the numbers
  79.                         and there is a range of + or - 500 ticks since
  80.                         the VIAtimer is so fast that interrupts and such
  81.                         play a more significant time role.
  82.                         
  83.                         NOTE THAT THIS PROFILE OPTION CANNOT BE USED WITH
  84.                         ANY PROGRAM THAT MANIPULATES THE #1 VIA timer */
  85.                         
  86.  
  87. #ifdef    _VIATIMER_
  88.  
  89. /* TickCount() - redefine TickCount trap to be a procedure for determining
  90.                  the timing according to the VIA timer */
  91.  
  92. #define    VIAbase    (*((unsigned char**)0x01D4))
  93. #define    VIAt1lo    (*(VIAbase+512*4))
  94. #define    VIAt1hi    (*(VIAbase+512*5))
  95. #define    VIAEnable (*((unsigned char *) ((long)VIA + 0x1C00)))
  96. #define    VIAmaxtime    ~0
  97. #define ProcAddr(x) (*(ProcPtr *)&((short *)(x))[1])
  98.  
  99. time timer=0;
  100. ProcPtr savePtr;
  101. unsigned char saveEnable;
  102.  
  103. pascal void Rollover()
  104. {
  105.     SetUpA5();
  106.     timer += 65536;
  107.     VIAt1hi = VIAmaxtime;
  108.     VIAt1lo = VIAmaxtime;
  109.     RestoreA5();
  110. }
  111.  
  112. void Remove_Rollover()
  113. {
  114.     if ((saveEnable&0x40)==0) VIAEnable = 0x40;
  115.     Lvl1DT[6] = savePtr;
  116. }
  117.  
  118. time TickCount()
  119. {
  120.  
  121.     if (timer > 0)
  122.     {
  123.         /* Use VIA timer #1 (sound driver) and to implement a fine
  124.             resolution counter */
  125.         
  126.         /* Find delta of VIA #1 */
  127.         
  128.         timer += VIAmaxtime - (((unsigned short)VIAt1hi<<8)+VIAt1lo);
  129.     }
  130.     else {
  131.         /* initialize profiling interrupt: */
  132.         savePtr = Lvl1DT[6];
  133.         Lvl1DT[6] = ProcAddr(Rollover);
  134.         saveEnable = VIAEnable;
  135.         VIAEnable |= 0x40;
  136.  
  137.         _onexit( Remove_Rollover );
  138.         timer = 1;
  139.     }
  140.     
  141.     /* Reset VIA counter */
  142.     
  143.     VIAt1hi = VIAmaxtime;
  144.     VIAt1lo = VIAmaxtime;
  145.     
  146.     return (timer);
  147. }
  148.  
  149. #endif _VIATIMER_
  150.  
  151.  
  152. /*
  153.  
  154.   Compare two Pascal strings for equality.
  155.  
  156.  */
  157.  
  158. LOCAL Bool pstreq( s1, s2 )
  159. register unsigned char    *s1, *s2;
  160. {
  161.   register int            n = *s1;
  162.   
  163.   if ((n = *s1++) != *s2++)
  164.     return( false );
  165.   while (n-- > 0)
  166.     if (*s1++ != *s2++)
  167.       return( false );
  168.   return( true );
  169. }
  170.  
  171.  
  172. /*
  173.  
  174.   Return the time difference caused by the overhead of function entry and
  175.   timer call.
  176.  
  177.  */
  178.  
  179. LOCAL time delta( original )
  180. time            original;
  181. {
  182. /*    return( (time) TickCount() - original ); */
  183.     return(0L);
  184. }
  185.  
  186.  
  187. /*
  188.  
  189.   Lookup a name in the active profile table.  For this system, "names" are
  190.   the address of the routine.  If the name is found in the table, then the
  191.   index to the table entry is returned.  If the name is not found and the
  192.   insert flag is on, then the item is inserted, and the new table index
  193.   is returned (if insert is false, then -1 is returned).  If the profile
  194.   table is full, then -1 is returned.
  195.  
  196.  */
  197.  
  198. LOCAL int lookup( name, insert )
  199. char             *name;
  200. Bool            insert;
  201. {
  202.   register int  i;
  203.   
  204.      insert = _profile;                /* only insert if profiling is on */
  205.          
  206.   for (i = 0; i < profile_count; i++)
  207.     if (pstreq( profiles[i].name, name ))
  208.       return( i );
  209.       
  210.   if ( insert && (profile_count < MAX_PROFILES))
  211.     { /* place in table  - i points to correct entry */
  212.     if (++profile_count == 1)
  213.       { /* first time in */
  214.       start_time = (time) TickCount();
  215.       onexit( DumpProfile );
  216.       }
  217.     profiles[i].name            = name;
  218.     profiles[i].minimum         = MAX_TIME;
  219.     profiles[i].maximum         =
  220.     profiles[i].total           = NULL;
  221.     profiles[i].entries         = 0;
  222.     return( i );
  223.     }
  224.   return( -1 );
  225. }
  226.  
  227.  
  228. /*
  229.  
  230.   Skip over to column 32 from position 'n'.
  231.  
  232.  */
  233.  
  234. LOCAL void Skip( n )
  235. register int    n;
  236. {
  237.   for (; n < 32; n++)
  238.     putchar( ' ' );
  239. }
  240.  
  241.  
  242. /*
  243.  
  244.   Print the profile table on demand.  Output is written to stdout and may
  245.   be redirected with the Unix '>' mechanism to a file.  The user should
  246.   call this routine whenever a report is desired.  Each time the routine is
  247.   called, the program opens the map file which corresponds to the program
  248.   running.  The name of the map file is built by appending ".map" to the name
  249.   of the running program.  If the map file cannot be found then the routine
  250.   returns.
  251.  
  252.  */
  253.  
  254. void DumpProfile()
  255. {
  256.   Boolean    flag;                                /* save state of _profile */
  257.   int           j, k;                           /* generic loop variables    */
  258.   register int  i;                              /* primary loop variable     */
  259.   register time duration        = 0L;           /* time in test regions      */
  260.  
  261.     flag = _profile;
  262.     
  263.   _profile = 0;                                 /* turn off profiling here   */
  264.  
  265.   for (i = 0; i < profile_count; i++)
  266.     duration += profiles[i].total;              /* compute total time        */
  267.  
  268.   printf( "\n\n" );
  269.   printf( "\t\t\t\tRoutine Profiles\n\n" );
  270.   printf( "Routine Address" );
  271.   Skip( 15 );
  272.   printf( "  Minimum   Maximum   Average    %% Entries\n" );
  273.  
  274.   if (duration <= 0.0)
  275.     duration++;
  276.  
  277.   for (i = 0; i < profile_count; i++)
  278.     { /* for all entries in the profile table */
  279.     if (profiles[i].minimum == MAX_TIME)
  280.       continue;                                 /* routine entry, no exit    */
  281.     printf( "%s", PtoCstr( profiles[i].name ) );
  282.     CtoPstr( profiles[i].name );
  283.     Skip( profiles[i].name[0] );
  284.     printf( "%9lu %9lu %9lu  %3.0f %7u\n",
  285.             profiles[i].minimum, profiles[i].maximum,
  286.             profiles[i].average,
  287.             ((float) profiles[i].total * 100.0) / (float) duration + 0.05,
  288.             profiles[i].entries );
  289.     }
  290.     
  291.     _profile = flag;                 /* restore state of _profile */
  292. }
  293.  
  294.  
  295. /*
  296.  
  297.   Called by assembler exit routine.  Compute usage statistics for routine on
  298.   top of profiling stack.
  299.  
  300.  */
  301.  
  302. address __profile_exit()
  303. {
  304.   int           i;
  305.   time          exit_time       = (time) TickCount();
  306.  
  307.   depth--;
  308.   /* end timing for a function */
  309.  
  310. /* Debugger(); */
  311.   i = stack[stack_pointer].function;
  312. #ifdef  SINGLE
  313.     exit_time = exit_time - delta( exit_time ) -
  314.                 stack[stack_pointer].start - stack[stack_pointer--].correction;
  315.     if (exit_time > 0x7FFFFFFF)
  316.       exit_time = 0L;                             /* handle clock rollover     */
  317.     profiles[i].total += exit_time;
  318.     if (stack_pointer >= 0)
  319.       stack[stack_pointer].correction += exit_time + stack[stack_pointer + 1].correction;
  320. #else
  321.     exit_time = exit_time - delta( exit_time ) - stack[stack_pointer--].start;
  322.     if (exit_time > 0x7FFFFFFF)
  323.       exit_time = 0L;                            /* handle clock rollover     */
  324.     profiles[i].total += exit_time;
  325. #endif
  326.   if (exit_time > profiles[i].maximum)
  327.     profiles[i].maximum         = exit_time;
  328.   if (exit_time < profiles[i].minimum)
  329.     profiles[i].minimum         = exit_time;
  330.   if (profiles[i].entries)
  331.     profiles[i].average         = profiles[i].total / profiles[i].entries;
  332.   return( stack[stack_pointer + 1].user );
  333. }
  334.  
  335.  
  336. /*
  337.  
  338.  Handle the routine entry profiling.  Setup a stack frame and a profile 
  339.  table entry if needed.
  340.  
  341.  */
  342.  
  343. void __profile( unused, ret, name )
  344. unsigned long    *ret;
  345. address            unused;
  346. char            *name;
  347.  
  348. {
  349.   register int  function,                       /* index of routine          */
  350.                 i;
  351.   register time entry_time = (time) TickCount();
  352.   Boolean         flag;                        /* save state of _profile */
  353.   depth++;
  354.   if (_trace)
  355.     {
  356.     flag = _profile;            /* save _profile */
  357.     _profile = 0;
  358.  
  359.     for (i = 0; i < depth; i++)
  360.       putchar( ' ' );
  361.     printf( "%s\n", PtoCstr( name ) );
  362.     CtoPstr( name );
  363.     _profile = flag;
  364.     }
  365.   if (++stack_pointer < MAX_STACK)
  366.     { /* setup for return to PROFILE_EXIT on user exit */
  367.     stack[stack_pointer].user           = (address) *ret;
  368.     *ret                                = (unsigned long) _profile_exit_;
  369.     stack[stack_pointer].correction     = NULL;
  370.     }
  371.   else
  372.     {
  373.     depth--;
  374.     return;
  375.     }
  376.  
  377.   if (_profile && ((function = lookup( name, true )) >= 0))
  378.     { /* process function entry */
  379.     profiles[function].entries++;
  380.     stack[stack_pointer].function     = function;
  381.     entry_time                        = TickCount();
  382.     stack[stack_pointer].start        = entry_time - delta( entry_time );
  383.     }
  384.   else
  385.     { /* remove entry from stack */
  386.     *ret                                = (unsigned long) stack[stack_pointer--].user;
  387.     depth--;
  388.     }
  389. }
  390.