-- File MyPerfStatsImpl.mesa -- Last edited by: -- MBrown on 16-Feb-82 8:57:46 -- Kolling on September 8, 1982 4:55 pm DIRECTORY IO, PerfStats, Rope, System; MyPerfStatsImpl: CEDAR PROGRAM IMPORTS IO, Rope, System EXPORTS PerfStats SHARES PerfStats = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; -- Exported type Timer: TYPE = REF TimerObject; TimerObject: PUBLIC TYPE = RECORD[ pName: ROPE, TimerWasStarted: BOOLEAN, TimeWhenStarted: LONG CARDINAL _ NULL, TotalElapsedTime: LONG CARDINAL _ NULL, MaxTime: LONG CARDINAL _ NULL, MinTime: LONG CARDINAL _ NULL, NStopTimerCalls: LONG CARDINAL _ NULL, next: Timer]; Counter: TYPE = PerfStats.Counter; CounterObject: TYPE = PerfStats.CounterObject; -- Module state counterList: Counter _ NIL; timerList: Timer _ NIL; nGlitches: LONG CARDINAL _ 0; -- Counts number of improperly-matched Start - Stop calls. -- Procedures (also see inlines in PerfStats). CreateCounter: PUBLIC PROC[name: ROPE] RETURNS[Counter] = { TestForDuplicate: PROC [e: Counter] = { IF name.Equal[e.pName] THEN ERROR DuplicateName}; EnumerateCounters[TestForDuplicate]; {e: Counter _ NEW[CounterObject _ [pName: name, next: counterList]]; InitializeCounter[e]; RETURN[counterList _ e] }}; DuplicateName: PUBLIC ERROR = CODE; InitializeCounter: PUBLIC PROC[event: Counter] = { event.counter _ 0 }; DestroyCounter: PUBLIC PROC[event: Counter] = { Remove: PROC [e: Counter] = { IF e.next = event THEN e.next _ event.next }; IF event = counterList THEN counterList _ event.next ELSE EnumerateCounters[Remove]; event.pName _ NIL; event.next _ NIL }; EnumerateCounters: PROC[procToApply: PROC[Counter]] = { FOR p: Counter _ counterList, p.next UNTIL p=NIL DO procToApply[p] ENDLOOP }; CreateTimer: PUBLIC PROC[name: ROPE] RETURNS[Timer] = { TestForDuplicate: PROC [e: Timer] = { IF name.Equal[e.pName] THEN ERROR DuplicateName}; EnumerateTimers[TestForDuplicate]; {e: Timer _ NEW[TimerObject _ [pName: name, TimerWasStarted: FALSE, next: timerList]]; InitializeTimer[e]; RETURN[timerList _ e] }}; InitializeTimer: PUBLIC PROC[event: Timer] = { -- Don't reset a running timer. event.TotalElapsedTime _ 0; event.MaxTime _ 0; event.MinTime _ LAST[LONG CARDINAL]; event.NStopTimerCalls _ 0 }; DestroyTimer: PUBLIC PROC[event: Timer] = { Remove: PROC[e: Timer] = { IF e.next = event THEN e.next _ event.next }; IF event = timerList THEN timerList _ event.next ELSE EnumerateTimers[Remove]; event.pName _ NIL; event.next _ NIL }; EnumerateTimers: PROC[procToApply: PROC[Timer]] = { FOR p: Timer _ timerList, p.next UNTIL p=NIL DO procToApply[p] ENDLOOP }; Initialize: PUBLIC PROC[] = { EnumerateCounters[InitializeCounter]; EnumerateTimers[InitializeTimer] }; Start: PUBLIC PROC[event: Timer] = TRUSTED{ IF event.TimerWasStarted THEN nGlitches _ nGlitches + 1; event.TimerWasStarted _ TRUE; event.TimeWhenStarted _ System.GetClockPulses[] }; Stop: PUBLIC PROC[event: Timer] = TRUSTED{ IF ~event.TimerWasStarted THEN { nGlitches _ nGlitches + 1; RETURN }; event.TimerWasStarted _ FALSE; event.NStopTimerCalls _ event.NStopTimerCalls + 1; -- What is done next is a function of how elaborate the stats need to be. It might --even be a function of event. For now, do something simple. {elapsedTime: LONG CARDINAL _ System.GetClockPulses[] - event.TimeWhenStarted; event.TotalElapsedTime _ event.TotalElapsedTime + elapsedTime; event.MaxTime _ MAX[elapsedTime, event.MaxTime]; event.MinTime _ MIN[elapsedTime, event.MinTime] } }; Print: PUBLIC PROC [heading: ROPE, oStream: STREAM, verbose: BOOLEAN] = { PrintHighResolutionTime: PROC[time: LONG CARDINAL] RETURNS[BOOLEAN] = TRUSTED{ -- Returns TRUE iff time is less than 2 ms. time _ System.PulsesToMicroseconds[[time]]/100; -- convert to 1/10 ms units {ms: LONG CARDINAL _ time/10; tenthsecs: LONG CARDINAL _ time/1000; secs: LONG CARDINAL _ tenthsecs/10; decimal: LONG CARDINAL _ tenthsecs MOD 10; oStream.PutF["%g", IO.card[ms]]; RETURN[ms <= 1]}; }; PrintCounter: PROC[e: Counter] = { IF e.counter > 0 THEN { oStream.PutF["%22g: %g events*n", IO.rope[e.pName], IO.card[e.counter]] } ELSE IF verbose THEN { oStream.PutF["%22g: no events*n", IO.rope[e.pName]] } }; PrintTimer: PROC[e: Timer] = TRUSTED{ IF e.NStopTimerCalls # 1 THEN ERROR; IF e.NStopTimerCalls > 0 THEN { timeIsSmall: BOOLEAN; timeIsSmall _ PrintHighResolutionTime[e.TotalElapsedTime/e.NStopTimerCalls]; IF timeIsSmall THEN { avgTime: System.Microseconds _ System.PulsesToMicroseconds[[e.TotalElapsedTime/e.NStopTimerCalls]]; oStream.PutF[" ms. (%g us)", IO.card[avgTime]] } ELSE { oStream.PutRope[" ms."] }; } ELSE IF verbose THEN { oStream.PutF["%g: no events*n", IO.rope[e.pName]] }; }; IF heading # NIL AND heading.Size[] # 0 THEN oStream.PutF["%g", IO.rope[heading]]; EnumerateCounters[PrintCounter]; EnumerateTimers[PrintTimer]; IF nGlitches > 0 THEN oStream.PutF["?%g out of order calls to Starting or Stopping!*n", IO.card[nGlitches]]; nGlitches _ 0; oStream.PutF["*n"]; oStream.Flush[]; }; END.--PerfStatsImpl CHANGE LOG Created by MBrown on November 4, 1980 4:23 PM -- By editing DBStatsImpl. Changed by MBrown on November 6, 1980 3:30 PM -- Uses DBLogStream instead of its own internal stream, to allow output --to go to same file as DBStatsImpl. Changed by MBrown on November 7, 1980 9:31 AM -- Make InitializeCounterEvent and InitializeTimerEvent public. Changed by MBrown on November 7, 1980 4:26 PM -- Fix Print to make output take fewer lines. Changed by MBrown on November 10, 1980 1:11 PM -- Make ReadClock for Alto I faster (marginal improvement). When average time is small, print it --in microseconds. Changed by MBrown on December 8, 1980 6:21 PM -- Don't reset running timers in InitializeTimerEvent. Changed by MBrown on January 10, 1981 9:29 PM -- Created Pilot/collectible storage version. Print now takes putChar and cleanup procs as parms. --Renamed to PerfStatsImpl. Changed by MBrown on January 11, 1981 5:02 PM -- Added DuplicateName ERROR. Added verbose parm to Print. Changed by MBrown on 18-Aug-81 18:46:34 -- CedarString -> Rope (ugly since CWF does not know about Rope.) Changed by MBrown on 7-Dec-81 16:02:11 -- Convert to use IO. Changed by MBrown on 16-Feb-82 8:58:33 -- Remove LOOPHOLEs in dealing with times (compiler bug fixed.)