DIRECTORY Atom, BasicTime, CodeTimerConcrete, Commander, IO, CodeTimer, Process, RefTab, Rope; CodeTimerImpl: CEDAR MONITOR IMPORTS Atom, BasicTime, Commander, IO, Process, RefTab EXPORTS CodeTimer = BEGIN ProcessPair: TYPE = CodeTimerConcrete.ProcessPair; ProcessPairObj: TYPE = CodeTimerConcrete.ProcessPairObj; Table: TYPE = REF TableObj; TableObj: PUBLIC TYPE = CodeTimerConcrete.TableObj; IntervalInContext: TYPE = REF IntervalInContextObj; IntervalInContextObj: PUBLIC TYPE = CodeTimerConcrete.IntervalInContextObj; gTablesRef: RefTab.Ref; intervalPoolMax: CARD32 = 25; intervalPool: ARRAY [0..intervalPoolMax) OF IntervalInContext; intervalPoolIndex: INT32 _ -1; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = CODE; CreateIntervalInContext: PROC [intervalName: ATOM] RETURNS [intervalInContext: IntervalInContext] = { intervalInContext _ NEW[IntervalInContextObj]; intervalInContext.name _ intervalName; }; AllocateIntervalInternal: INTERNAL PROC [intervalName: ATOM] RETURNS [interval: IntervalInContext] = { IF intervalPoolIndex >= 0 THEN { interval _ intervalPool[intervalPoolIndex]; ZeroInterval[interval]; interval.children _ NIL; interval.name _ intervalName; intervalPoolIndex _ intervalPoolIndex - 1; } ELSE interval _ CreateIntervalInContext[intervalName]; }; FreeIntervalInternal: INTERNAL PROC [interval: IntervalInContext] = { IF intervalPoolIndex < intervalPoolMax -1 THEN { intervalPoolIndex _ intervalPoolIndex + 1; intervalPool[intervalPoolIndex] _ interval; }; interval.children _ NIL; interval.parent _ NIL; }; CreateTable: PUBLIC PROC [name: ATOM _ NIL] RETURNS [table: Table] = { oldTable: Table _ GetTable[name]; IF oldTable = NIL THEN { table _ NEW[TableObj _ [name, NIL]]; IF name # NIL THEN AddTableToTables[table]; } ELSE { table _ oldTable; ResetTable[table]; }; }; GetTable: PUBLIC PROC [name: ATOM] RETURNS [table: Table] = { val: RefTab.Val; found: BOOL _ FALSE; [found, val] _ RefTab.Fetch[gTablesRef, name]; IF found THEN table _ NARROW[val] ELSE table _ NIL; }; AddTableToTables: PROC [table: Table] = { new: BOOL _ RefTab.Store[gTablesRef, table.name, table]; IF NOT new THEN ERROR; }; ResetTable: PUBLIC ENTRY PROC [table: Table] = { FOR lp: LIST OF ProcessPair _ table.processes, lp.rest UNTIL lp = NIL DO pair: ProcessPair _ lp.first; FOR list: LIST OF IntervalInContext _ pair.outer.children, list.rest UNTIL list = NIL DO FreeIntervalAndChildren[list.first]; ENDLOOP; pair.outer.children _ NIL; pair.current _ pair.outer; ENDLOOP; table.processes _ NIL; }; FreeIntervalAndChildren: INTERNAL PROC [interval: IntervalInContext] = { FOR list: LIST OF IntervalInContext _ interval.children, list.rest UNTIL list = NIL DO FreeIntervalAndChildren[list.first]; ENDLOOP; FreeIntervalInternal[interval]; }; ResetInterval: PUBLIC ENTRY PROC [intervalName: ATOM, table: Table] = { FOR lp: LIST OF ProcessPair _ table.processes, lp.rest UNTIL lp = NIL DO pair: ProcessPair _ lp.first; ZeroNamedIntervalInTree[intervalName, pair.outer]; ENDLOOP; }; ZeroNamedIntervalInTree: INTERNAL PROC [intervalName: ATOM, interval: IntervalInContext] = { IF interval.name = intervalName THEN ZeroInterval[interval]; FOR list: LIST OF IntervalInContext _ interval.children, list.rest UNTIL list = NIL DO ZeroNamedIntervalInTree[intervalName, list.first]; ENDLOOP; }; ZeroInterval: INTERNAL PROC [interval: IntervalInContext] = { interval.starts _ 0; interval.stops _ 0; interval.prematureStops _ 0; interval.prematureStopName _ NIL; interval.startTime _ 0; interval.totalTime _ 0; interval.maxTime _ 0; interval.minTime _ LAST[LONG CARDINAL] }; codeTimerOn: BOOL _ FALSE; noteThreads: BOOL _ FALSE; FindChildIntervalInContext: PROC [intervalName: ATOM, context: IntervalInContext] RETURNS [interval: IntervalInContext _ NIL] = { FOR list: LIST OF IntervalInContext _ context.children, list.rest UNTIL list = NIL DO IF list.first.name = intervalName THEN RETURN[list.first]; ENDLOOP; }; defaultProcess: PROCESS; GetPairForProcess: INTERNAL PROC [table: Table] RETURNS [pp: ProcessPair] = { IF noteThreads THEN { process: PROCESS _ Process.GetCurrent[]; FOR list: LIST OF ProcessPair _ table.processes, list.rest UNTIL list = NIL DO pair: ProcessPair _ list.first; IF pair.process = process THEN RETURN[pair]; ENDLOOP; pp _ AddProcessPair[table, process]; } ELSE { IF table.processes = NIL THEN pp _ AddProcessPair[table, defaultProcess] ELSE { pp _ table.processes.first; }; }; }; AddProcessPair: INTERNAL PROC [table: Table, process: PROCESS] RETURNS [pair: ProcessPair] = { pair _ NEW[ProcessPairObj]; pair.current _ pair.outer _ AllocateIntervalInternal[$Outer]; pair.process _ process; table.processes _ CONS[pair, table.processes]; }; StartInterval: PUBLIC ENTRY PROC [intervalName: ATOM, table: Table] = { ENABLE UNWIND => NULL; interval: IntervalInContext; current: IntervalInContext; pair: ProcessPair; IF NOT codeTimerOn THEN RETURN; pair _ GetPairForProcess[table]; current _ pair.current; interval _ FindChildIntervalInContext[intervalName, current]; IF interval = NIL THEN { interval _ AllocateIntervalInternal[intervalName]; current.children _ CONS[interval, current.children]; interval.parent _ current; }; interval.starts _ interval.starts + 1; interval.startTime _ BasicTime.GetClockPulses[]; pair.current _ interval; }; StartInt: PUBLIC PROC [intervalName: ATOM, tableName: ATOM] = { table: Table; IF NOT codeTimerOn THEN RETURN; table _ GetTable[tableName]; IF table = NIL THEN table _ CreateTable[tableName]; StartInterval[intervalName, table]; }; StopInterval: PUBLIC ENTRY PROC [intervalName: ATOM, table: Table] = { ENABLE UNWIND => NULL; interval: IntervalInContext; pair: ProcessPair; stopTime, elapsedTime: BasicTime.Pulses; IF NOT codeTimerOn THEN RETURN; stopTime _ BasicTime.GetClockPulses[]; pair _ GetPairForProcess[table]; interval _ pair.current; IF interval.name = intervalName THEN { -- normal case interval.stops _ interval.stops + 1; elapsedTime _ stopTime - interval.startTime; interval.totalTime _ interval.totalTime + elapsedTime; interval.minTime _ IF elapsedTime < interval.minTime THEN elapsedTime ELSE interval.minTime; IF elapsedTime > interval.maxTime THEN { interval.maxTime _ elapsedTime; interval.maxIndex _ interval.starts; }; IF interval.parent # NIL THEN pair.current _ interval.parent; } ELSE { -- encountered a stop while a different interval is active interval.prematureStops _ interval.prematureStops + 1; IF interval.prematureStops = 1 THEN interval.prematureStopName _ intervalName; }; }; StopInt: PUBLIC PROC [intervalName: ATOM, tableName: ATOM] = { table: Table; IF NOT codeTimerOn THEN RETURN; table _ GetTable[tableName]; IF table = NIL THEN table _ CreateTable[tableName]; StopInterval[intervalName, table]; }; SetIntMilliseconds: PUBLIC ENTRY PROC [intervalName: ATOM, startTime: CARD32, stopTime: CARD32, tableName: ATOM] = { period: INT32; table: Table; interval: IntervalInContext; current: IntervalInContext; pair: ProcessPair; InnerProc: INTERNAL PROC = { -- workaround for 4.1 C optimizer bug JKF 2/21/90 IF NOT codeTimerOn THEN RETURN; period _ Period[startTime, stopTime]; table _ GetTable[tableName]; IF table = NIL THEN table _ CreateTable[tableName]; pair _ GetPairForProcess[table]; current _ pair.current; interval _ FindChildIntervalInContext[intervalName, current]; IF interval = NIL THEN { interval _ AllocateIntervalInternal[intervalName]; current.children _ CONS[interval, current.children]; interval.parent _ current; }; IF period > 0 THEN { posPer: CARD32 _ period; pulses: CARD32 _ BasicTime.MicrosecondsToPulses[posPer*1000]; interval.starts _ interval.starts + 1; interval.stops _ interval.stops + 1; interval.totalTime _ interval.totalTime + pulses; interval.minTime _ IF pulses < interval.minTime THEN pulses ELSE interval.minTime; IF pulses > interval.maxTime THEN { interval.maxTime _ pulses; interval.maxIndex _ interval.stops; }; } ELSE { interval.prematureStops _ interval.prematureStops + 1; interval.prematureStopName _ $NegativeTimePeriod }; }; -- InnerProc InnerProc[]; }; Period: PUBLIC PROC [from, to: CARD32] RETURNS [INT32] ~ { -- in milliseconds RETURN [LOOPHOLE[to-from]] }; ForEachTable: PUBLIC PROC [proc: ForEachTableProc] RETURNS [aborted: BOOL _ FALSE] = { DoForEachTable: RefTab.EachPairAction = { tableName: ATOM _ NARROW[key]; table: Table _ NARROW[val]; quit _ proc[tableName, table]; }; aborted _ RefTab.Pairs[gTablesRef, DoForEachTable]; }; ForEachTableProc: TYPE = CodeTimer.ForEachTableProc; ForEachIntervalInContext: PUBLIC PROC [table: Table, proc: ForEachIntervalInContextProc] RETURNS [aborted: BOOL _ FALSE] = { processCount: CARD _ 0; IF table = NIL THEN RETURN; FOR lp: LIST OF ProcessPair _ table.processes, lp.rest UNTIL lp = NIL DO pair: ProcessPair _ lp.first; FOR list: LIST OF IntervalInContext _ pair.outer.children, list.rest UNTIL list = NIL DO aborted _ WalkInterval[processCount, list.first, proc, 0]; IF aborted THEN RETURN; ENDLOOP; processCount _ processCount + 1; ENDLOOP; }; ForEachIntervalInContextProc: TYPE = CodeTimer.ForEachIntervalInContextProc; WalkInterval: PROC [process: CARD, interval: IntervalInContext, proc: ForEachIntervalInContextProc, level: CARD] RETURNS [aborted: BOOL _ FALSE] = { totalMsec, minMsec, maxMsec: CARD; totalMsec _ BasicTime.PulsesToMicroseconds[interval.totalTime]/1000; minMsec _ BasicTime.PulsesToMicroseconds[interval.minTime]/1000; maxMsec _ BasicTime.PulsesToMicroseconds[interval.maxTime]/1000; aborted _ proc[interval.name, process, interval.starts, totalMsec, minMsec, maxMsec, interval.maxIndex, interval.prematureStops, level]; IF aborted THEN RETURN; FOR list: LIST OF IntervalInContext _ interval.children, list.rest UNTIL list = NIL DO aborted _ WalkInterval[process, list.first, proc, level+1]; IF aborted THEN RETURN; ENDLOOP; }; PrintTable: PUBLIC PROC [f: IO.STREAM, table: Table] = { DoPrintIntervalInContext: ForEachIntervalInContextProc = { IF starts # 0 THEN { avgMsec: CARD; FOR i: NAT IN [1..level] DO f.PutRope[" "]; ENDLOOP; -- indent f.PutF["%g.%g. n: %g. total: %g. ", [cardinal[process]], [atom[intervalName]], [integer[starts]], [integer[totalMsec]]]; avgMsec _ totalMsec/starts; IF startsWithoutStops > 0 THEN f.PutF["avg: %g. range: [%g..%g], worst: %g, errs: %g\n", [integer[avgMsec]], [integer[minMsec]], [integer[maxMsec]], [integer[maxIndex]], [integer[startsWithoutStops]]] ELSE f.PutF["avg: %g. range: [%g..%g], worst: %g\n", [integer[avgMsec]], [integer[minMsec]], [integer[maxMsec]], [integer[maxIndex]]]; }; }; IF table = NIL THEN RETURN; [] _ ForEachIntervalInContext[table, DoPrintIntervalInContext]; }; PrintIntervalInContext: PROC [f: IO.STREAM, process: CARD, interval: IntervalInContext, nestingLevel: NAT _ 0, children: BOOL _ FALSE] = { name: Rope.ROPE; totalTime, avgTime, minTime, maxTime: LONG CARDINAL; IF interval.starts # 0 THEN { FOR i: NAT IN [1..nestingLevel] DO f.PutRope[" "]; ENDLOOP; -- indent name _ Atom.GetPName[interval.name]; totalTime _ BasicTime.PulsesToMicroseconds[interval.totalTime]/1000; f.PutF["%g.%g. n: %g. total: %g. ", [cardinal[process]], [rope[name]], [integer[interval.starts]], [integer[totalTime]]]; avgTime _ totalTime/interval.starts; minTime _ BasicTime.PulsesToMicroseconds[interval.minTime]/1000; maxTime _ BasicTime.PulsesToMicroseconds[interval.maxTime]/1000; IF interval.prematureStops > 0 THEN f.PutF["avg: %g. range: [%g..%g], worst: %g, errs: %g", [integer[avgTime]], [integer[minTime]], [integer[maxTime]], [integer[interval.maxIndex]], [integer[interval.prematureStops]]] ELSE f.PutF["avg: %g. range: [%g..%g], worst: %g", [integer[avgTime]], [integer[minTime]], [integer[maxTime]], [integer[interval.maxIndex]]]; IF interval.starts > interval.stops THEN f.PutF[", %g extra starts", [integer[interval.starts - interval.stops]] ]; f.PutRope["\n"]; IF children THEN { FOR list: LIST OF IntervalInContext _ interval.children, list.rest UNTIL list = NIL DO PrintIntervalInContext[f, process, list.first, nestingLevel+1, children]; ENDLOOP; }; }; }; PrintInterval: PUBLIC PROC [f: IO.STREAM, intervalName: ATOM, table: Table, nestingLevel: NAT _ 0] = { IF table # NIL THEN { processCount: CARD _ 0; FOR lp: LIST OF ProcessPair _ table.processes, lp.rest UNTIL lp = NIL DO pair: ProcessPair _ lp.first; FOR list: LIST OF IntervalInContext _ pair.outer.children, list.rest UNTIL list = NIL DO PrintPartsContaining[f, processCount, intervalName, list.first, nestingLevel]; ENDLOOP; processCount _ processCount + 1; ENDLOOP; }; }; PrintInt: PUBLIC PROC [f: IO.STREAM, intervalName: ATOM, tableName: ATOM, nestingLevel: NAT _ 0] = { table: Table _ GetTable[tableName]; IF table # NIL THEN PrintInterval[f, intervalName, table, nestingLevel]; }; PrintPartsContaining: PROC [f: IO.STREAM, process: CARD, intervalName: ATOM, interval: IntervalInContext, nestingLevel: NAT _ 0] = { IF interval.name = intervalName THEN PrintIntervalInContext[f, process, interval, nestingLevel, TRUE] ELSE IF ContainsNamedInterval[intervalName, interval] THEN { PrintIntervalInContext[f, process, interval, nestingLevel, FALSE]; FOR list: LIST OF IntervalInContext _ interval.children, list.rest UNTIL list = NIL DO PrintPartsContaining[f, process, intervalName, list.first, nestingLevel + 1]; ENDLOOP; }; }; ContainsNamedInterval: PROC [intervalName: ATOM, tree: IntervalInContext] RETURNS [BOOL] = { IF tree.name = intervalName THEN RETURN[TRUE]; FOR list: LIST OF IntervalInContext _ tree.children, list.rest UNTIL list = NIL DO IF ContainsNamedInterval[intervalName, list.first] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; IntervalStatistics: TYPE = REF IntervalStatisticsObj; IntervalStatisticsObj: TYPE = CodeTimer.IntervalStatisticsObj; GetIntervalTotals: PUBLIC PROC [intervalName: ATOM, table: Table] RETURNS [starts, totalMsec, averageMsec, minMsec, maxMsec, maxIndex, startsWithoutStops: CARD _ 0] = { totalPulse, minPulse, maxPulse: CARD; starts1, totalPulse1, minPulse1, maxPulse1, maxIndex1, prematureStops1: CARD; IF table = NIL THEN RETURN; totalPulse _ 0; minPulse _ LAST[CARD]; maxPulse _ 0; FOR list: LIST OF ProcessPair _ table.processes, list.rest UNTIL list = NIL DO pair: ProcessPair _ list.first; [starts1, totalPulse1, minPulse1, maxPulse1, maxIndex1, prematureStops1] _ NamedIntervalTotals[intervalName, pair.outer]; IF starts1 > 0 THEN { starts _ starts + starts1; totalPulse _ totalPulse + totalPulse1; IF minPulse1 < minPulse THEN minPulse _ minPulse1; IF maxPulse1 > maxPulse THEN { maxPulse _ maxPulse1; maxIndex _ maxIndex1; }; startsWithoutStops _ startsWithoutStops + prematureStops1; }; ENDLOOP; IF starts = 0 THEN RETURN; -- to avoid divide by 0 below totalMsec _ BasicTime.PulsesToMicroseconds[totalPulse]/1000; averageMsec _ totalMsec/starts; minMsec _ BasicTime.PulsesToMicroseconds[minPulse]/1000; maxMsec _ BasicTime.PulsesToMicroseconds[maxPulse]/1000; }; GetIntervalStats: PUBLIC PROC [intervalName: ATOM, table: Table] RETURNS [stats: LIST OF IntervalStatistics] = { IF table = NIL THEN RETURN; FOR lp: LIST OF ProcessPair _ table.processes, lp.rest UNTIL lp = NIL DO pair: ProcessPair _ lp.first; theseStats: LIST OF IntervalStatistics _ NamedIntervalStats[intervalName, pair.outer]; stats _ AppendStatList[theseStats, stats]; ENDLOOP; }; NamedIntervalTotals: PROC [intervalName: ATOM, interval: IntervalInContext] RETURNS [starts, totalPulse, minPulse, maxPulse, maxIndex, prematureStops: CARD] = { starts1, totalPulse1, minPulse1, maxPulse1, maxIndex1, prematureStops1: CARD; starts _ totalPulse _ maxPulse _ maxIndex _ prematureStops _ 0; minPulse _ LAST[CARD]; FOR list: LIST OF IntervalInContext _ interval.children, list.rest UNTIL list = NIL DO [starts1, totalPulse1, minPulse1, maxPulse1, maxIndex1, prematureStops1] _ NamedIntervalTotals[intervalName, list.first]; IF starts1 > 0 THEN { starts _ starts + starts1; totalPulse _ totalPulse + totalPulse1; IF minPulse1 < minPulse THEN minPulse _ minPulse1; IF maxPulse1 > maxPulse THEN { maxPulse _ maxPulse1; maxIndex _ maxIndex1; }; prematureStops _ prematureStops + prematureStops1; }; ENDLOOP; IF interval.name = intervalName THEN { IF interval.starts > 0 THEN { starts _ starts + interval.starts; totalPulse _ totalPulse + interval.totalTime; IF interval.minTime < minPulse THEN minPulse _ interval.minTime; IF interval.maxTime > maxPulse THEN { maxPulse _ interval.maxTime; maxIndex _ interval.maxIndex; }; prematureStops _ prematureStops + interval.prematureStops; }; }; }; AddAtom: PROC [entity: ATOM, entityList, ptr: LIST OF ATOM] RETURNS [newList, newPtr: LIST OF ATOM] = { IF ptr = NIL THEN { IF NOT entityList = NIL THEN ERROR; newPtr _ newList _ CONS[entity, NIL]; RETURN; } ELSE { newList _ entityList; ptr.rest _ CONS[entity, NIL]; newPtr _ ptr.rest; }; }; AddStats: PROC [entity: IntervalStatistics, entityList, ptr: LIST OF IntervalStatistics] RETURNS [newList, newPtr: LIST OF IntervalStatistics] = { IF ptr = NIL THEN { IF NOT entityList = NIL THEN ERROR; newPtr _ newList _ CONS[entity, NIL]; RETURN; } ELSE { newList _ entityList; ptr.rest _ CONS[entity, NIL]; newPtr _ ptr.rest; }; }; AppendStatList: PROC [list1, list2: LIST OF IntervalStatistics] RETURNS [result: LIST OF IntervalStatistics] = { pos: LIST OF IntervalStatistics; newCell: LIST OF IntervalStatistics; IF list1 = NIL THEN RETURN[list2]; result _ CONS[list1.first, NIL]; pos _ result; FOR l: LIST OF IntervalStatistics _ list1.rest, l.rest UNTIL l = NIL DO newCell _ CONS[l.first, NIL]; pos.rest _ newCell; pos _ newCell; ENDLOOP; pos.rest _ list2; }; NamedIntervalStats: PROC [intervalName: ATOM, interval: IntervalInContext] RETURNS [stats: LIST OF IntervalStatistics] = { statTail, childStats: LIST OF IntervalStatistics; IF interval.name = intervalName THEN { IF interval.starts > 0 THEN { tail: LIST OF ATOM; parent: IntervalInContext; theseStats: IntervalStatistics; theseStats _ NEW[CodeTimer.IntervalStatisticsObj]; theseStats.process _ 0; -- for now parent _ interval.parent; UNTIL parent.parent = NIL DO [theseStats.context, tail] _ AddAtom[parent.name, theseStats.context, tail]; parent _ parent.parent; ENDLOOP; theseStats.starts _ interval.starts; theseStats.totalMsec _ BasicTime.PulsesToMicroseconds[interval.totalTime]/1000; theseStats.minMsec _ BasicTime.PulsesToMicroseconds[interval.minTime]/1000; theseStats.maxMsec _ BasicTime.PulsesToMicroseconds[interval.maxTime]/1000; theseStats.maxIndex _ interval.maxIndex; theseStats.startsWithoutStops _ interval.prematureStops; [stats, statTail] _ AddStats[theseStats, stats, statTail]; }; }; FOR list: LIST OF IntervalInContext _ interval.children, list.rest UNTIL list = NIL DO childStats _ NamedIntervalStats[intervalName, list.first]; stats _ AppendStatList[stats, childStats]; ENDLOOP; }; CodeTimerNoteThreadsOn: Commander.CommandProc = { noteThreads _ TRUE; }; CodeTimerNoteThreadsOff: Commander.CommandProc = { noteThreads _ FALSE; }; CodeTimerOn: Commander.CommandProc = { codeTimerOn _ TRUE; }; CodeTimerOff: Commander.CommandProc = { codeTimerOn _ FALSE; }; TimerOn: PUBLIC PROC = { codeTimerOn _ TRUE; }; TimerOff: PUBLIC PROC = { codeTimerOn _ FALSE; }; PrintCodeTimes: Commander.CommandProc = { nameStream: IO.STREAM _ IO.RIS[cmd.commandLine]; name: Rope.ROPE; atom: ATOM; table: Table; IF cmd.commandLine = "" THEN RETURN[$Failure, "Please specify a table name"]; [] _ IO.SkipWhitespace[nameStream]; name _ IO.GetLineRope[nameStream]; atom _ Atom.MakeAtom[name]; table _ GetTable[atom]; IF table # NIL THEN PrintTable[cmd.out, table] ELSE cmd.out.PutF["No such table.\n"]; }; ResetCodeTimes: Commander.CommandProc = { nameStream: IO.STREAM _ IO.RIS[cmd.commandLine]; name: Rope.ROPE; atom: ATOM; table: Table; IF cmd.commandLine = "" THEN RETURN[$Failure, "Please specify a table name"]; [] _ IO.SkipWhitespace[nameStream]; name _ IO.GetLineRope[nameStream]; atom _ Atom.MakeAtom[name]; table _ GetTable[atom]; IF table # NIL THEN ResetTable[table] ELSE cmd.out.PutF["I don't (yet) know of a table named %g.\n", [atom[atom]]]; }; PrintIntervalTimes: Commander.CommandProc = { nameStream: IO.STREAM _ IO.RIS[cmd.commandLine]; name: Rope.ROPE; tableName, intervalName: ATOM; table: Table; IF cmd.commandLine = "" THEN RETURN[$Failure, "Please specify a table name"]; [] _ IO.SkipWhitespace[nameStream]; name _ IO.GetTokenRope[nameStream].token; intervalName _ Atom.MakeAtom[name]; name _ IO.GetTokenRope[nameStream].token; tableName _ Atom.MakeAtom[name]; table _ GetTable[tableName]; IF table # NIL THEN PrintInterval[cmd.out, intervalName, table] ELSE cmd.out.PutF["I don't (yet) know of a table named %g.\n", [atom[tableName]]]; }; PrintIntervalTotals: Commander.CommandProc = { nameStream: IO.STREAM _ IO.RIS[cmd.commandLine]; tableRope, intervalRope: Rope.ROPE; tableName, intervalName: ATOM; table: Table; IF cmd.commandLine = "" THEN RETURN[$Failure, "Please specify a table name"]; [] _ IO.SkipWhitespace[nameStream]; intervalRope _ IO.GetTokenRope[nameStream].token; intervalName _ Atom.MakeAtom[intervalRope]; tableRope _ IO.GetTokenRope[nameStream].token; tableName _ Atom.MakeAtom[tableRope]; table _ GetTable[tableName]; IF table # NIL THEN { starts, totalMsec, averageMsec, minMsec, maxMsec, maxIndex, prematureStops: CARD; [starts, totalMsec, averageMsec, minMsec, maxMsec, maxIndex, prematureStops] _ GetIntervalTotals[intervalName, table]; cmd.out.PutF["%g. n: %g. total: %g. ", [rope[intervalRope]], [integer[starts]], [integer[totalMsec]]]; IF prematureStops > 0 THEN cmd.out.PutF["avg: %g. range: [%g..%g], worst: %g, errs: %g\n", [integer[averageMsec]], [integer[minMsec]], [integer[maxMsec]], [integer[maxIndex]], [integer[prematureStops]]] ELSE cmd.out.PutF["avg: %g. range: [%g..%g], worst: %g\n", [integer[averageMsec]], [integer[minMsec]], [integer[maxMsec]], [integer[maxIndex]]]; } ELSE cmd.out.PutF["I don't (yet) know of a table named %g.\n", [atom[tableName]]]; }; ResetIntervalTimes: Commander.CommandProc = { nameStream: IO.STREAM _ IO.RIS[cmd.commandLine]; name: Rope.ROPE; tableName, intervalName: ATOM; table: Table; IF cmd.commandLine = "" THEN RETURN[$Failure, "Please specify a table name"]; [] _ IO.SkipWhitespace[nameStream]; name _ IO.GetTokenRope[nameStream].token; intervalName _ Atom.MakeAtom[name]; name _ IO.GetTokenRope[nameStream].token; tableName _ Atom.MakeAtom[name]; table _ GetTable[tableName]; IF table # NIL THEN ResetInterval[intervalName, table] ELSE cmd.out.PutF["I don't (yet) know of a table named %g.\n", [atom[tableName]]]; }; PrintCodeTimeTables: Commander.CommandProc = { PrintTableName: CodeTimer.ForEachTableProc = { cmd.out.PutF["\n %g", [atom[tableName]] ]; }; [] _ ForEachTable[PrintTableName]; cmd.out.PutRope["\n"]; }; Init: PROC [] = { gTablesRef _ RefTab.Create[7]; defaultProcess _ Process.GetCurrent[]; Commander.Register["PrintCodeTimes", PrintCodeTimes, "PrintCodeTimes -- prints out the minimum, maximum, and average times taken to execute the marked code blocks in the table named in the first argument"]; Commander.Register["ResetCodeTimes", ResetCodeTimes, "ResetCodeTimes -- zeros the code times for all of the intervals in this table"]; Commander.Register["PrintCodeTimeTables", PrintCodeTimeTables, "Prints out all code time tables that CodeTimer currently knows about"]; Commander.Register["PrintIntervalTimes", PrintIntervalTimes, "PrintIntervalTime -- prints out the minimum, maximum, and average times taken to execute the named interval (in each context in which in appears) and its children"]; Commander.Register["PrintIntervalTotals", PrintIntervalTotals, "PrintIntervalTotals -- prints out the minimum, maximum, and average times taken to execute the named interval (totaled over all contexts in which it appears)"]; Commander.Register["ResetIntervalTimes", ResetIntervalTimes, "ResetIntervalTimes -- zeros the code times for the named interval in the named table"]; Commander.Register["codeTimerOn", CodeTimerOn, "Causes CodeTimer to begin timing code intervals that have been marked with StartInt and StopInt calls (use CodeTimerTool to view the results)"]; Commander.Register["codeTimerOff", CodeTimerOff, "Causes CodeTimer to stop timing code intervals that have been marked with StartInt and StopInt calls (use CodeTimerTool to view the results)"]; Commander.Register["codeTimerNoteThreadsOn", CodeTimerNoteThreadsOn, "Causes CodeTimer to distinguish calls to the same interval by different threads"]; Commander.Register["codeTimerNoteThreadsOff", CodeTimerNoteThreadsOff, "Causes CodeTimer to treat calls to an interval identically regardless of which thread makes the call (this is the default)"]; }; Test: PROC [] = { FOR i: NAT IN [1..9] DO StartInt[$LoopOnI, $CodeTimer]; Process.PauseMsec[100]; StartInt[$InnerBlock, $CodeTimer]; Process.PauseMsec[53]; StartInt[$InnermostBlock, $CodeTimer]; StopInt[$InnermostBlock, $CodeTimer]; StopInt[$InnerBlock, $CodeTimer]; StartInt[$AnotherBlock, $CodeTimer]; StartInt[$InnermostBlock, $CodeTimer]; Process.PauseMsec[20]; StopInt[$InnermostBlock, $CodeTimer]; StopInt[$AnotherBlock, $CodeTimer]; StopInt[$LoopOnI, $CodeTimer]; ENDLOOP; }; Init[]; END. ¦ CodeTimerImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Bier, February 3, 1992 6:04 pm PST JKF, February 21, 1990 8:26:15 am PST Contents: Routines for maintaining a table of average times for user-specified operations. Note: the current implementation does not tabulate statistics well when several processes are executed code that affects the same table. A more advanced version may keep per-process statistics. Getting ready to test performance. AllocateInterval: ENTRY PROC [intervalName: ATOM] RETURNS [interval: IntervalInContext] = { IF intervalPoolIndex >= 0 THEN { interval _ intervalPool[intervalPoolIndex]; ZeroInterval[interval]; interval.children _ NIL; interval.name _ intervalName; intervalPoolIndex _ intervalPoolIndex - 1; } ELSE interval _ CreateIntervalInContext[intervalName]; }; FreeInterval: ENTRY PROC [interval: IntervalInContext] = { IF intervalPoolIndex < intervalPoolMax -1 THEN { intervalPoolIndex _ intervalPoolIndex + 1; intervalPool[intervalPoolIndex] _ interval; }; interval.children _ NIL; interval.parent _ NIL; }; outer: IntervalInContext _ AllocateInterval[$Outer]; ResetTable: PUBLIC PROC [table: Table] = { The table is a tree of nested intervals. Return all of these intervals to the interval pool except for the top level interval; ResetProcess: RefTab.EachPairAction = { DoResetProcess: RefTab.UpdateAction = { PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation _ none, new: Val _ NIL] IF found THEN { IF val = NIL THEN RETURN[none, NIL]; pair: ProcessPair _ NARROW[val]; FOR list: LIST OF IntervalInContext _ pair.outer.children, list.rest UNTIL list = NIL DO FreeIntervalAndChildren[list.first]; ENDLOOP; pair.outer.children _ NIL; pair.current _ pair.outer; op _ none; new _ pair; }; }; RefTab.Update[table.processes, key, DoResetProcess]; }; RefTab.Pairs[table.processes, ResetProcess]; }; The table is a tree of nested intervals. Return all of these intervals to the interval pool except for the top level interval; ResetInterval: PUBLIC PROC [intervalName: ATOM, table: Table] = { Wherever this interval occurs in the tree, zero it and its children. ZeroIntervalInProcess: RefTab.EachPairAction = { DoZeroIntervalInProcess: RefTab.UpdateAction = { PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation _ none, new: Val _ NIL] IF found THEN { IF val = NIL THEN RETURN[none, NIL]; pair: ProcessPair _ NARROW[val]; ZeroNamedIntervalInTree[intervalName, pair.outer]; op _ none; new _ pair; }; }; RefTab.Update[table.processes, key, DoZeroIntervalInProcess]; }; RefTab.Pairs[table.processes, ZeroIntervalInProcess]; }; Wherever this interval occurs in the tree, zero it and its children. Testing performance. We have encountered the beginning of a new interval. Add a representation for it as a child of the most recently entered active interval. The current interval has come to an end. This call is equivalent to a StartInt, followed stopTime-startTime later by a StopInt. Printing results. Enumerates all of the CodeTimer tables in the current virtual address space. If the proc ever returns done=TRUE, then and only then aborted will be TRUE. PROC [key: Key, val: Val] RETURNS [quit: BOOL _ FALSE]; ForEachTableProc: TYPE = PROC [table: Table] RETURNS [done: BOOL _ FALSE]; Walks the tree of intervals. The first level of this tree consists of those intervals that were called when no other intervals were active. The children of each interval are the intervals that were called while that interval was active. Thus, each named code interval may appear several times in the tree, once for each interval that called it. Each appearance is called an interval-in-context ForEachIntervalInContextProc: TYPE = PROC [intervalName: ATOM, process: CARD, starts, totalMsec, minMsec, maxMsec, maxIndex, startsWithoutStops: CARD, level: NAT _ 0] RETURNS [done: BOOL _ FALSE]; PROC [intervalName: ATOM, process: CARD, starts, totalMsec, minMsec, maxMsec, maxIndex, startsWithoutStops: CARD, level: NAT _ 0] RETURNS [done: BOOL _ FALSE]; Like PrintTable, but prints only that subset of the tree that contains the interval in question. Like PrintInterval but the table is specified by name. Returns the total statistics for an interval (totaled over all of the contexts in which it was encountered). Returns the statistics individually for each context in which the interval was encountered. Non-destructive (copies the first list). PROC [tableName: ATOM, table: Table] RETURNS [done: BOOL _ FALSE]; Command Registration Κρ– "cedar" style•NewlineDelimiter ™codešœ™Kšœ Οmœ1™KšœŸœ˜K˜Kš  œŸœŸœ ŸœŸœ˜/K˜K™"K™š œŸœŸœŸœ+˜eKšœŸœ˜.Kšœ&˜&K˜K˜—š  œŸœŸœŸœŸœ"™[šŸœŸœ™ Kšœ+™+Kšœ™KšœŸœ™Kšœ™Kšœ*™*K™—KšŸœ2™6K™K™—š  œŸœŸœŸœŸœ"˜fšŸœŸœ˜ Kšœ+˜+Kšœ˜KšœŸœ˜Kšœ˜Kšœ*˜*K˜—KšŸœ2˜6K˜K˜—š  œŸœŸœ"™:šŸœ(Ÿœ™0Kšœ*™*Kšœ+™+K™—KšœŸœ™KšœŸœ™K™K™—š œŸœŸœ"˜EšŸœ(Ÿœ˜0Kšœ*˜*Kšœ+˜+K˜—KšœŸœ˜KšœŸœ˜K˜K˜—š   œŸœŸœŸ œŸœ˜FKšœ!˜!šŸœ ŸœŸœ˜Kšœ4™4KšœŸœŸœ˜$KšŸœŸœŸœ˜+K˜—šŸœ˜Kšœ˜Kšœ˜K˜—K˜K˜—š  œŸœŸœŸœŸœ˜=Kšœ˜KšœŸœŸœ˜Kšœ.˜.Kš ŸœŸœ ŸœŸœ Ÿœ˜3K˜K˜—š œŸœ˜)KšœŸœ/˜8KšŸœŸœŸœŸœ˜K˜K˜—š  œŸœŸœ™*K™š  œ™'š œ™'KšŸœ Ÿœ Ÿœ)Ÿœ™QšŸœŸœ™Kš ŸœŸœŸœŸœŸœ™$KšœŸœ™ š ŸœŸœŸœ4ŸœŸœŸ™XKšœ$™$KšŸœ™—KšœŸœ™Kšœ™Kšœ™K™—K™—Kšœ4™4K™—Kšœ,™,K™K™—š  œŸœŸ œ˜0K™š ŸœŸœŸœ(ŸœŸœŸ˜HKšœ˜š ŸœŸœŸœ4ŸœŸœŸ˜XKšœ$˜$KšŸœ˜—KšœŸœ˜Kšœ˜JšŸœ˜—JšœŸœ˜K˜K˜—š œŸœŸœ"˜Hš ŸœŸœŸœ2ŸœŸœŸ˜VKšœ$˜$KšŸœ˜—Kšœ˜K˜K˜—š  œŸœŸœŸœ™AK™Dš œ™0š œ™0KšŸœ Ÿœ Ÿœ)Ÿœ™QšŸœŸœ™Kš ŸœŸœŸœŸœŸœ™$KšœŸœ™ Kšž2™2Kšœ™K™—K™—Kšœ=™=K™—Kšœ5™5K™K™—š   œŸœŸœŸœŸœ˜GK™Dš ŸœŸœŸœ(ŸœŸœŸ˜HKšœ˜Kšž2˜2JšŸœ˜—K˜K˜—š œŸ œŸœ"˜\KšŸœŸœ˜<š ŸœŸœŸœ2ŸœŸœŸ˜VKšœ2˜2KšŸœ˜—K˜K˜—š  œŸ œ"˜=Kšœ˜Kšœ˜Kšœ˜KšœŸœ˜!Kšœ˜Kšœ˜Kšœ˜KšœŸœŸœŸœ˜&K˜K˜—K™K™K˜Kšœ ŸœŸœ˜Kšœ ŸœŸœ˜K˜š  œŸœŸœŸœ Ÿœ˜š ŸœŸœŸœ1ŸœŸœŸ˜UJšŸœ ŸœŸœ ˜:JšŸœ˜—K˜K˜—KšœŸœ˜š œŸœŸœŸœ˜MšŸœ Ÿœ˜Kšœ Ÿœ˜(š ŸœŸœŸœ*ŸœŸœŸ˜NJšœ˜JšŸœŸœŸœ˜,JšŸœ˜—Jšœ$˜$J˜—šŸœ˜JšŸœŸœŸœ+˜HšŸœ˜Jšœ˜J˜—J˜—K˜K˜—š œŸ œŸœŸœ˜^KšœŸœ˜Kšœ=˜=Kšœ˜KšœŸœ˜.K˜K˜—š  œŸœŸ œŸœ˜GK™ŠKšŸœŸœŸœ˜Kšœ˜Kšœ˜K˜K˜KšŸœŸœ ŸœŸœ˜Kšœ ˜ Kšœ˜Kšœ=˜=šŸœ ŸœŸœ˜Kšœ2˜2KšœŸœ˜4Kšœ˜K˜—Kšœ&˜&Kšœ0˜0Kšœ˜K˜K˜—š  œŸœŸœŸœ Ÿœ˜?K˜ K˜KšŸœŸœ ŸœŸœ˜Kšœ˜KšŸœ ŸœŸœ ˜3Kšœ#˜#K˜K˜—š  œŸœŸ œŸœ˜FKšŸœŸœŸœ˜Kšœ˜K˜Kšœ(˜(K˜KšŸœŸœ ŸœŸœ˜Kšœ&˜&Kšœ ˜ Kšœ˜šŸœŸœΟc˜5K™(Kšœ$˜$Kšœ,˜,Kšœ6˜6KšœŸœ Ÿœ Ÿœ˜\šŸœ Ÿœ˜(Kšœ˜Kšœ$˜$K˜—KšŸœŸœŸœ ˜=K˜—šŸœ‘:˜AKšœ6˜6KšŸœŸœ+˜NK˜—K˜K˜—š  œŸœŸœŸœ Ÿœ˜>K˜ K˜KšŸœŸœ ŸœŸœ˜Kšœ˜KšŸœ ŸœŸœ ˜3Kšœ"˜"K˜K˜—š œŸœŸ œŸœ Ÿœ Ÿœ Ÿœ˜tKšœV™VKšœŸœ˜Kšœ ˜ Kšœ˜Kšœ˜K˜K˜š  œŸœŸœ‘1˜NKšŸœŸœ ŸœŸœ˜Kšœ%˜%Kšœ˜KšŸœ ŸœŸœ ˜3K˜Kšœ ˜ Kšœ˜Kšœ=˜=šŸœ ŸœŸœ˜Kšœ2˜2KšœŸœ˜4Kšœ˜K˜—šŸœ Ÿœ˜KšœŸœ ˜KšœŸœ/˜=Kšœ&˜&Kšœ$˜$Kšœ1˜1KšœŸœŸœŸœ˜RšŸœŸœ˜#Kšœ˜Kšœ#˜#K˜—K˜—šŸœ˜Kšœ6˜6Kšœ0˜0K˜—Jšœ‘ ˜—J˜ K˜K˜—š  œŸœŸœ ŸœŸœŸœ‘˜MKšŸœŸœ ˜Kšœ˜K˜—K™K™K˜š   œŸœŸœŸœ ŸœŸœ˜VKšœš™šš œ˜)KšŸœŸœŸœŸœ™7Kšœ ŸœŸœ˜KšœŸœ˜Kšœ˜K˜—Kšœ3˜3K˜—KšœŸœ˜4Kš œŸœŸœŸœŸœŸœ™JK˜š  œŸœŸœ4Ÿœ ŸœŸœ˜|K™KšœŸœ˜KšŸœ ŸœŸœŸœ˜š ŸœŸœŸœ(ŸœŸœŸ˜HJšœ˜š ŸœŸœŸœ4ŸœŸœŸ˜XJšœ:˜:JšŸœ ŸœŸœ˜JšŸœ˜Jšœ ˜ —JšŸœ˜—K˜—KšœŸœ*˜LšœŸœŸœŸœ ŸœEŸœ ŸœŸœŸœŸœ™ΔK™—š  œŸœ ŸœJŸœŸœ ŸœŸœ˜”KšœŸœ˜"KšœD˜DKšœ@˜@Kšœ@˜@Kšœˆ˜ˆKšŸœ ŸœŸœ˜š ŸœŸœŸœ2ŸœŸœŸ˜VJšœ;˜;JšŸœ ŸœŸœ˜JšŸœ˜—K˜K˜—š   œŸœŸœŸœŸœ˜8Kš œ"˜:šŸœŸœ ŸœEŸœ ŸœŸœŸœŸœ™ŸšŸœ Ÿœ˜Kšœ Ÿœ˜Kš ŸœŸœŸœ ŸœŸœ‘ ˜?Kšœ{˜{Kšœ˜šŸœŸ˜Kšœͺ˜ͺ—šŸ˜Kšœ‚˜‚—K˜—K˜—KšŸœ ŸœŸœŸœ˜Kšœ?˜?K˜K˜—š œŸœŸœŸœ Ÿœ-ŸœŸœŸœ˜ŠKšœ Ÿœ˜Kšœ&ŸœŸœ˜4šŸœŸœ˜Kš ŸœŸœŸœŸœŸœ‘ ˜FKšœ$˜$KšœD˜DKšœ|˜|Kšœ$˜$Kšœ@˜@Kšœ@˜@šŸœŸ˜#KšœΆ˜Ά—šŸ˜Kšœ‰˜‰—KšŸœ"ŸœK˜sK˜šŸœ Ÿœ˜š ŸœŸœŸœ2ŸœŸœŸ˜VKšœI˜IKšŸœ˜—K˜—K˜—K˜K˜—š  œŸœŸœŸœŸœŸœŸœ ˜fKšœ`™`šŸœ ŸœŸœ˜KšœŸœ˜š ŸœŸœŸœ(ŸœŸœŸ˜HKšœ˜š ŸœŸœŸœ4ŸœŸœŸ˜XJšœN˜NJšŸœ˜—Jšœ ˜ JšŸœ˜—K˜—K˜K˜—š œŸœŸœŸœŸœŸœ ŸœŸœ ˜dKšœ6™6Kšœ#˜#KšŸœ ŸœŸœ5˜HK˜K˜—š œŸœŸœŸœ ŸœŸœ-Ÿœ ˜„KšŸœŸœ<Ÿœ˜ešŸœŸœ/Ÿœ˜K˜š  œŸœŸœŸœŸœRŸœ ˜¨K™lKšœ Ÿœ˜%KšœHŸœ˜MKšŸœ ŸœŸœŸœ˜Kšœ˜Kšœ ŸœŸœ˜Kšœ ˜ š ŸœŸœŸœ*ŸœŸœŸ˜NK˜Kšœy˜yšŸœ Ÿœ˜Jšœ˜Jšœ&˜&JšŸœŸœ˜2šŸœŸœ˜Jšœ˜Jšœ˜J˜—Jšœ:˜:J˜—JšŸœ˜—KšŸœ ŸœŸœ‘˜8Kšœ<˜