-- 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.)