home *** CD-ROM | disk | FTP | other *** search
- /*
-
- Profile and tracing package for LightspeedC¬.
-
- (C) Copyright 1986 THINK Technologies, Inc. All rights reserved.
-
- */
-
-
- #include "stdio.h"
- #include "DeviceMgr.h"
-
-
- typedef unsigned char Bool; /* for size */
-
- typedef void (*address)(); /* address type */
- typedef unsigned char byte;
- typedef unsigned long time;
-
- typedef struct {
- char *name; /* function name */
- time average; /* average time */
- time maximum; /* largest time */
- time minimum; /* smallest time */
- time total; /* total time */
- unsigned int entries; /* times entered */
- } PROFILE;
-
- typedef struct {
- address user; /* user return address */
- int function; /* index into profiles */
- time correction; /* nested call time */
- time start; /* entry time */
- } STACK;
-
- #define LOCAL static /* local to profiler */
- #define MAX_PROFILES 200 /* routines to follow */
- #define MAX_STACK 200 /* max nested calls */
- #define MAX_TIME 500000L /* max time */
-
- /*
-
- Select execution/compilation options.
-
- SINGLE - each routine contains only its time
- TESTING - test profile package without LightspeedC PROFILE option
-
- */
-
- #define SINGLE /* single routine only */
- /* #define TESTING standalone testing */
-
-
- #ifdef TESTING
- #undef LOCAL /* make items visible */
- #define LOCAL
- #endif
-
-
-
- LOCAL int depth = -1;
- LOCAL PROFILE profiles[MAX_PROFILES];
- LOCAL int profile_count = 0;
- LOCAL STACK stack[MAX_STACK];
- LOCAL int stack_pointer = -1;
- LOCAL time start_time;
-
- int _profile = 1;
- int _trace = 0;
-
- void DumpProfile();
- extern _profile_exit_();
- extern char *CtoPstr();
- extern char *PtoCstr();
-
- #define _VIATIMER_ /* if this symbol is defined, the VIA timer now is used
- instead of TickCount -- it provides faster ticks,
- however there is a false precision to the numbers
- and there is a range of + or - 500 ticks since
- the VIAtimer is so fast that interrupts and such
- play a more significant time role.
-
- NOTE THAT THIS PROFILE OPTION CANNOT BE USED WITH
- ANY PROGRAM THAT MANIPULATES THE #1 VIA timer */
-
-
- #ifdef _VIATIMER_
-
- /* TickCount() - redefine TickCount trap to be a procedure for determining
- the timing according to the VIA timer */
-
- #define VIAbase (*((unsigned char**)0x01D4))
- #define VIAt1lo (*(VIAbase+512*4))
- #define VIAt1hi (*(VIAbase+512*5))
- #define VIAEnable (*((unsigned char *) ((long)VIA + 0x1C00)))
- #define VIAmaxtime ~0
- #define ProcAddr(x) (*(ProcPtr *)&((short *)(x))[1])
-
- time timer=0;
- ProcPtr savePtr;
- unsigned char saveEnable;
-
- pascal void Rollover()
- {
- SetUpA5();
- timer += 65536;
- VIAt1hi = VIAmaxtime;
- VIAt1lo = VIAmaxtime;
- RestoreA5();
- }
-
- void Remove_Rollover()
- {
- if ((saveEnable&0x40)==0) VIAEnable = 0x40;
- Lvl1DT[6] = savePtr;
- }
-
- time TickCount()
- {
-
- if (timer > 0)
- {
- /* Use VIA timer #1 (sound driver) and to implement a fine
- resolution counter */
-
- /* Find delta of VIA #1 */
-
- timer += VIAmaxtime - (((unsigned short)VIAt1hi<<8)+VIAt1lo);
- }
- else {
- /* initialize profiling interrupt: */
- savePtr = Lvl1DT[6];
- Lvl1DT[6] = ProcAddr(Rollover);
- saveEnable = VIAEnable;
- VIAEnable |= 0x40;
-
- _onexit( Remove_Rollover );
- timer = 1;
- }
-
- /* Reset VIA counter */
-
- VIAt1hi = VIAmaxtime;
- VIAt1lo = VIAmaxtime;
-
- return (timer);
- }
-
- #endif _VIATIMER_
-
-
- /*
-
- Compare two Pascal strings for equality.
-
- */
-
- LOCAL Bool pstreq( s1, s2 )
- register unsigned char *s1, *s2;
- {
- register int n = *s1;
-
- if ((n = *s1++) != *s2++)
- return( false );
- while (n-- > 0)
- if (*s1++ != *s2++)
- return( false );
- return( true );
- }
-
-
- /*
-
- Return the time difference caused by the overhead of function entry and
- timer call.
-
- */
-
- LOCAL time delta( original )
- time original;
- {
- /* return( (time) TickCount() - original ); */
- return(0L);
- }
-
-
- /*
-
- Lookup a name in the active profile table. For this system, "names" are
- the address of the routine. If the name is found in the table, then the
- index to the table entry is returned. If the name is not found and the
- insert flag is on, then the item is inserted, and the new table index
- is returned (if insert is false, then -1 is returned). If the profile
- table is full, then -1 is returned.
-
- */
-
- LOCAL int lookup( name, insert )
- char *name;
- Bool insert;
- {
- register int i;
-
- insert = _profile; /* only insert if profiling is on */
-
- for (i = 0; i < profile_count; i++)
- if (pstreq( profiles[i].name, name ))
- return( i );
-
- if ( insert && (profile_count < MAX_PROFILES))
- { /* place in table - i points to correct entry */
- if (++profile_count == 1)
- { /* first time in */
- start_time = (time) TickCount();
- onexit( DumpProfile );
- }
- profiles[i].name = name;
- profiles[i].minimum = MAX_TIME;
- profiles[i].maximum =
- profiles[i].total = NULL;
- profiles[i].entries = 0;
- return( i );
- }
- return( -1 );
- }
-
-
- /*
-
- Skip over to column 32 from position 'n'.
-
- */
-
- LOCAL void Skip( n )
- register int n;
- {
- for (; n < 32; n++)
- putchar( ' ' );
- }
-
-
- /*
-
- Print the profile table on demand. Output is written to stdout and may
- be redirected with the Unix '>' mechanism to a file. The user should
- call this routine whenever a report is desired. Each time the routine is
- called, the program opens the map file which corresponds to the program
- running. The name of the map file is built by appending ".map" to the name
- of the running program. If the map file cannot be found then the routine
- returns.
-
- */
-
- void DumpProfile()
- {
- Boolean flag; /* save state of _profile */
- int j, k; /* generic loop variables */
- register int i; /* primary loop variable */
- register time duration = 0L; /* time in test regions */
-
- flag = _profile;
-
- _profile = 0; /* turn off profiling here */
-
- for (i = 0; i < profile_count; i++)
- duration += profiles[i].total; /* compute total time */
-
- printf( "\n\n" );
- printf( "\t\t\t\tRoutine Profiles\n\n" );
- printf( "Routine Address" );
- Skip( 15 );
- printf( " Minimum Maximum Average %% Entries\n" );
-
- if (duration <= 0.0)
- duration++;
-
- for (i = 0; i < profile_count; i++)
- { /* for all entries in the profile table */
- if (profiles[i].minimum == MAX_TIME)
- continue; /* routine entry, no exit */
- printf( "%s", PtoCstr( profiles[i].name ) );
- CtoPstr( profiles[i].name );
- Skip( profiles[i].name[0] );
- printf( "%9lu %9lu %9lu %3.0f %7u\n",
- profiles[i].minimum, profiles[i].maximum,
- profiles[i].average,
- ((float) profiles[i].total * 100.0) / (float) duration + 0.05,
- profiles[i].entries );
- }
-
- _profile = flag; /* restore state of _profile */
- }
-
-
- /*
-
- Called by assembler exit routine. Compute usage statistics for routine on
- top of profiling stack.
-
- */
-
- address __profile_exit()
- {
- int i;
- time exit_time = (time) TickCount();
-
- depth--;
- /* end timing for a function */
-
- /* Debugger(); */
- i = stack[stack_pointer].function;
- #ifdef SINGLE
- exit_time = exit_time - delta( exit_time ) -
- stack[stack_pointer].start - stack[stack_pointer--].correction;
- if (exit_time > 0x7FFFFFFF)
- exit_time = 0L; /* handle clock rollover */
- profiles[i].total += exit_time;
- if (stack_pointer >= 0)
- stack[stack_pointer].correction += exit_time + stack[stack_pointer + 1].correction;
- #else
- exit_time = exit_time - delta( exit_time ) - stack[stack_pointer--].start;
- if (exit_time > 0x7FFFFFFF)
- exit_time = 0L; /* handle clock rollover */
- profiles[i].total += exit_time;
- #endif
- if (exit_time > profiles[i].maximum)
- profiles[i].maximum = exit_time;
- if (exit_time < profiles[i].minimum)
- profiles[i].minimum = exit_time;
- if (profiles[i].entries)
- profiles[i].average = profiles[i].total / profiles[i].entries;
- return( stack[stack_pointer + 1].user );
- }
-
-
- /*
-
- Handle the routine entry profiling. Setup a stack frame and a profile
- table entry if needed.
-
- */
-
- void __profile( unused, ret, name )
- unsigned long *ret;
- address unused;
- char *name;
-
- {
- register int function, /* index of routine */
- i;
- register time entry_time = (time) TickCount();
- Boolean flag; /* save state of _profile */
- depth++;
- if (_trace)
- {
- flag = _profile; /* save _profile */
- _profile = 0;
-
- for (i = 0; i < depth; i++)
- putchar( ' ' );
- printf( "%s\n", PtoCstr( name ) );
- CtoPstr( name );
- _profile = flag;
- }
- if (++stack_pointer < MAX_STACK)
- { /* setup for return to PROFILE_EXIT on user exit */
- stack[stack_pointer].user = (address) *ret;
- *ret = (unsigned long) _profile_exit_;
- stack[stack_pointer].correction = NULL;
- }
- else
- {
- depth--;
- return;
- }
-
- if (_profile && ((function = lookup( name, true )) >= 0))
- { /* process function entry */
- profiles[function].entries++;
- stack[stack_pointer].function = function;
- entry_time = TickCount();
- stack[stack_pointer].start = entry_time - delta( entry_time );
- }
- else
- { /* remove entry from stack */
- *ret = (unsigned long) stack[stack_pointer--].user;
- depth--;
- }
- }
-