<> <> DIRECTORY AMBridge USING [TVForPointerReferent], AMTypes USING [TV, TVType, TypeToName], Convert USING [ValueToRope], IO USING [ card, char, CR, CreateOutputStreamToRope, CreateViewerStreams, GetOutputStreamRope, Put, PutF, rope, STREAM, TAB, tv], PrincOps USING [ BytePC, CSegPrefix, EntryVectorItem, GFTIndex, GlobalFrameHandle, LastAVSlot], Process USING [Priority], PSB USING [PsbIndex], Rope USING [Cat, FromChar, Equal, ROPE], RTSymbolDefs USING [CallableBodyIndex, SymbolTableBase], RTSymbolOps USING [AcquireRope, BodyName], RTSymbols USING [AcquireSTBFromGFH, ReleaseSTB], RTTypesPrivate USING [GetCBTI, GetEp, GFHToName], SpyLog USING [Close, Entry, logging, NextEntry, Open], SpyOps USING [ Call, Count, DataType, GFHFromGFI, Procedure, ProcessRef, ProcessRec, ProcRec, Stack, stackHeader, stackLength], System USING [GetClockPulses, Pulses, PulsesToMicroseconds]; SpyLogReaderImpl: PROGRAM IMPORTS AMBridge, AMTypes, Convert, IO, Rope, RTSymbolOps, RTSymbols, RTTypesPrivate, SpyLog, SpyOps, System EXPORTS SpyOps = BEGIN OPEN IO, SpyOps; ROPE: TYPE = Rope.ROPE; Error: SIGNAL = CODE; -- global variables and types -- processes: PUBLIC LIST OF ProcessRef _ NIL; modules: PUBLIC LIST OF Procedure _ NIL; sorted: BOOLEAN _ FALSE; current: SpyOps.DataType; <> DestroyLog: PUBLIC PROCEDURE = { NilLinks: PROC[proc: Procedure] RETURNS[BOOLEAN _ FALSE] = { proc.container _ NIL; proc.parents _ NIL; proc.sons _ NIL}; [] _ EnumerateProcs[NilLinks]; FOR modules _ modules, modules.rest WHILE modules # NIL DO modules.first.container _ NIL; modules.first.parents _ NIL; modules.first.sons _ NIL; ENDLOOP; FOR processes _ processes, processes.rest WHILE processes # NIL DO processes.first.sons _ NIL; ENDLOOP; processes _ NIL; modules _ NIL; SpyLog.Close[]}; ReadLog: PUBLIC PROCEDURE[typescript: IO.STREAM, datatype: SpyOps.DataType] = BEGIN IF typescript = NIL THEN typescript _ CreateViewerStreams["Spy log"].out; typescript.Put[rope["(Please wait-- processing log.)\n\n"]]; current _ datatype; ReadSpyLog[]; SetAllContainers[]; PrintStatistics[typescript]; END; <<*********************************************>> <> <<*********************************************>> <> total: Count _ 0; processed: Count _ 0; overflow: Count _ 0; -- # of samples with stacks >= SpyOps.stackLength error: Count _ 0; -- # of samples with stacks >= SpyOps.stackLength stackDepth: Count _ 0; -- sum of stack depths noCalls, noProcesses, noProcs, noModules: Count _ 0; out: IO.STREAM; spyTime: ARRAY [0..3] OF RECORD[ count: Count, time: System.Pulses]; readLog: System.Pulses; -- elapsed time to read log recordSpyTime: BOOLEAN _ FALSE; ReadSpyLog: PROCEDURE = BEGIN i: CARDINAL; data: BOOLEAN _ FALSE; dataSize: CARDINAL _ 0; first, last: System.Pulses _ [0]; t1, delta, dataTime: System.Pulses; entry: LONG POINTER TO SpyLog.Entry; logging: BOOLEAN _ SpyLog.logging; Initialize[]; SpyLog.Open[FALSE, TRUE]; -- open for reading readLog _ [0]; t1 _ System.GetClockPulses[]; WHILE TRUE DO <> entry _ SpyLog.NextEntry[]; WITH e: entry SELECT FROM endOfLog => EXIT; trace => IF recordSpyTime THEN { IF ~data THEN last _ e.timestamp; IF data THEN { delta _ [e.timestamp - last]; IF delta > 10000000 THEN {data _ FALSE; RETURN}; SELECT delta FROM <= 10 => i _ 0; <= 100 => i _ 1; <= 1000 => i _ 2; ENDCASE => i _ 3; spyTime[i].count _ spyTime[i].count + 1; spyTime[i].time _ [spyTime[i].time + delta]}; data _ FALSE} ELSE { IF first = 0 THEN first _ e.timestamp; IF last = 0 THEN delta _ [0] ELSE delta _ [e.timestamp - last]; PrintTrace[e.gfi, e.pc, [e.timestamp - first], delta]; last _ e.timestamp}; data => { IF e.rttype # SpyOps.Stack.CODE THEN { IF first = 0 THEN first _ e.timestamp; IF last = 0 THEN delta _ [0] ELSE delta _ [e.timestamp - last]; PrintEntry[e.rttype, e.size, @e.data, [e.timestamp - first], delta]; last _ e.timestamp; LOOP}; EnterStack[LOOPHOLE[@e.data], (e.size - SpyOps.stackHeader)/2]; dataSize _ e.size + 4; dataTime _ e.timestamp; data _ TRUE}; ENDCASE => SIGNAL Error; ENDLOOP; readLog _ [System.GetClockPulses[]-t1]; <> <> stack _ NIL; out _ NIL; END; PrintEntry: PROCEDURE[type, words: CARDINAL, data: LONG POINTER, time, delta: System.Pulses] = BEGIN value: AMTypes.TV; IF out = NIL THEN { out _ IO.CreateViewerStreams["auxilary spy log"].out; out.PutF["%l uSeconds delta data\n", char['f]]}; value _ AMBridge.TVForPointerReferent[data, LOOPHOLE[type]]; out.PutF["%9d%8d %g: %g\n", card[System.PulsesToMicroseconds[time]], card[System.PulsesToMicroseconds[delta]], rope[AMTypes.TypeToName[AMTypes.TVType[value]]], tv[value]]; END; PrintTrace: PROCEDURE[gfi: PrincOps.GFTIndex, pc: PrincOps.BytePC, time, delta: System.Pulses] = BEGIN name: Rope.ROPE; IF out = NIL THEN { out _ IO.CreateViewerStreams["auxilary spy log"].out; out.PutF["%l uSeconds delta data\n", char['f]]}; name _ GetName[gfi, pc]; out.PutF["%9d%8d %g (gfi: %g, pc: %g)\n", card[System.PulsesToMicroseconds[time]], card[System.PulsesToMicroseconds[delta]], rope[name], card[gfi], card[pc]]; END; GetName: PROC[gfi: PrincOps.GFTIndex, pc: PrincOps.BytePC] RETURNS[name: Rope.ROPE] = BEGIN proc: Rope.ROPE; gfh: PrincOps.GlobalFrameHandle; stb: RTSymbolDefs.SymbolTableBase _ [x[e: NIL]]; gfh _ SpyOps.GFHFromGFI[gfi]; name _ RTTypesPrivate.GFHToName[gfh]; BEGIN ENABLE ANY => {IF stb # [x[e: NIL]] THEN RTSymbols.ReleaseSTB[stb]; CONTINUE}; ep: CARDINAL; cbti: RTSymbolDefs.CallableBodyIndex; stb _ RTSymbols.AcquireSTBFromGFH[gfh, TRUE]; [ep,] _ RTTypesPrivate.GetEp[[pc], gfh, stb]; cbti _ RTTypesPrivate.GetCBTI[stb, ep]; proc _ RTSymbolOps.AcquireRope[stb, RTSymbolOps.BodyName[stb, cbti]]; RTSymbols.ReleaseSTB[stb]; END; RETURN[Rope.Cat[name, ".", proc]]; END; -- tree management -- Initialize: PROCEDURE = BEGIN sorted _ FALSE; processes _ NIL; modules _ NIL; stackDepth _ 0; processed _ overflow _ error _ 0; noProcesses _ noCalls _ noProcs _ noModules _ 0; spyTime _ ALL[[0, [0]]]; stack _ NIL; END; EnumerateProcs: PROCEDURE[userProc: PROC[Procedure] RETURNS[BOOLEAN]] RETURNS[last: Procedure] = BEGIN FOR m: LIST OF Procedure _ modules, m.rest WHILE m # NIL DO FOR p: LIST OF Call _ m.first.sons, p.rest WHILE p # NIL DO IF userProc[p.first.proc] THEN RETURN[p.first.proc]; ENDLOOP; ENDLOOP; RETURN[NIL]; END; -- adding a stack to the call tree -- stack: ProcStack; EnterStack: PROCEDURE[s: LONG POINTER TO SpyOps.Stack, n: CARDINAL] = BEGIN ENABLE Error => {error _ error + 1; CONTINUE}; <> process: ProcessRef _ NIL; proc, newSon: Procedure _ NIL; processed _ processed + 1; stackDepth _ stackDepth + n; <> process _ AddProcess[s.process]; process.calls _ process.calls + s.count; <<[level: 7, n: 0] is the code for an overflow>> IF s.level = 7 THEN {overflow _ overflow + s.count; RETURN}; IF process.level[0] # s.level THEN FOR i: CARDINAL IN [0..4] DO IF process.level[i] = s.level THEN EXIT; IF process.level[i] # 0 THEN LOOP; process.level[i] _ s.level; EXIT; ENDLOOP; IF n = 0 THEN RETURN ELSE n _ n - 1; [process.sons, proc] _ AddSon[process.sons, 0, s.frame[n].gfi, s.frame[n].pc, s.count]; IF stack = NIL THEN stack _ NEW[ProcStackRec _ []]; stack.process _ process; AddFrame[stack, proc, 0]; WHILE TRUE DO <> IF n = 0 THEN EXIT ELSE n _ n - 1; proc.calls _ proc.calls + s.count; <> [proc.sons, newSon] _ AddSon[proc.sons, s.frame[n+1].pc, s.frame[n].gfi, s.frame[n].pc, s.count]; proc _ newSon; AddFrame[stack, proc, s.frame[n+1].pc]; IF Recursive[stack] THEN { <> Undo[stack, s.count]}; ENDLOOP; <> proc.count _ proc.count + s.count; IF s.type # 0 THEN { -- treat as a call even tho it's a count [proc.sons, newSon] _ AddSon[proc.sons, s.frame[0].pc, 0, s.type, s.count]; newSon.count _ newSon.count + s.count}; stack.index _ 0; END; -- adding to the list of processes -- AddProcess: PROCEDURE[psb: PSB.PsbIndex] RETURNS[process: ProcessRef _ NIL] = BEGIN list: LIST OF ProcessRef _ processes; <> IF list = NIL OR psb < list.first.psb THEN { process _ NewProcess[psb]; processes _ CONS[process, processes]; RETURN[process]}; WHILE list # NIL DO process _ list.first; <> IF process.psb = psb THEN RETURN[process]; <> IF list.rest = NIL OR psb < list.rest.first.psb THEN { process _ NewProcess[psb]; list.rest _ CONS[process, list.rest]; RETURN[process]}; list _ list.rest; ENDLOOP; ERROR; END; NewProcess: PROCEDURE[psb: PSB.PsbIndex] RETURNS[process: ProcessRef] = INLINE BEGIN process _ NEW[ProcessRec _ []]; noProcesses _ noProcesses + 1; process.psb _ psb; END; -- adding/deleting procedures from a LIST OF Call -- AddSon: PROC[list: LIST OF Call, from: CARDINAL, gfi: PrincOps.GFTIndex, pc, count: Count] RETURNS[newList: LIST OF Call, procedure: Procedure] = BEGIN <> <> IF sorted THEN SIGNAL Error; -- sorted by count rather than pc. procedure _ NIL; newList _ list; <> IF list = NIL OR from < list.first.pc THEN { procedure _ AddProcedure[gfi, pc]; newList _ CONS[[from, count, procedure], list]; noCalls _ noCalls + 1; RETURN[newList, procedure]}; WHILE list # NIL DO procedure _ list.first.proc; <> IF list.first.pc = from AND Equal[gfi, pc, procedure] THEN { IF list.first.calls = 0 THEN list.first.proc.refs _ list.first.proc.refs + 1; list.first.calls _ list.first.calls + count; RETURN[newList, procedure]}; <> IF list.rest = NIL OR from < list.rest.first.pc THEN { procedure _ AddProcedure[gfi, pc]; list.rest _ CONS[[from, count, procedure], list.rest]; noCalls _ noCalls + 1; RETURN[newList, procedure]}; list _ list.rest; ENDLOOP; ERROR; END; RemoveSon: PROC[proc: Procedure, pc: CARDINAL, list: LIST OF Call, count: Count] RETURNS[newList: LIST OF Call] = BEGIN <> <> prior: LIST OF Call; FOR s: LIST OF Call _ list, s.rest DO IF s = NIL THEN SIGNAL Error; -- where is it? IF s.first.proc # proc OR s.first.pc # pc THEN {prior _ s; LOOP}; IF s.first.calls = 0 THEN RETURN[list]; s.first.calls _ s.first.calls - count; IF s.first.calls = 0 THEN s.first.proc.refs _ s.first.proc.refs - 1; < 0 THEN RETURN[list];>> <> <> <> RETURN[list]; ENDLOOP; END; GreaterThan: PROC[gfi: PrincOps.GFTIndex, pc: CARDINAL, proc: Procedure] RETURNS[BOOLEAN] = INLINE BEGIN IF gfi > proc.gfi THEN RETURN[TRUE]; IF gfi < proc.gfi THEN RETURN[FALSE]; IF pc > proc.exitPC THEN RETURN[TRUE]; RETURN[FALSE]; END; Equal: PROC[gfi: PrincOps.GFTIndex, pc: CARDINAL, proc: Procedure] RETURNS[BOOLEAN] = INLINE BEGIN IF proc = NIL THEN RETURN[TRUE]; -- source IF gfi # proc.gfi THEN RETURN[FALSE]; IF pc < proc.entryPC THEN RETURN[FALSE]; IF pc > proc.exitPC THEN RETURN[FALSE]; RETURN[TRUE]; END; -- adding modules and procedures to the modules list -- AddProcedure: PROCEDURE[gfi: PrincOps.GFTIndex, pc: CARDINAL] RETURNS[procedure: Procedure] = BEGIN <> <> module: Procedure; list: LIST OF Call; module _ AddModule[gfi]; procedure _ NIL; list _ module.sons; <> IF list = NIL OR GreaterThan[gfi, pc, list.first.proc] THEN { procedure _ NewProcedure[module, pc]; IF procedure = NIL THEN SIGNAL Error; procedure.refs _ 1; module.sons _ CONS[[0, 0, procedure], list]; noCalls _ noCalls + 1; RETURN[procedure]}; WHILE list # NIL DO procedure _ list.first.proc; <> IF Equal[gfi, pc, procedure] THEN { procedure.refs _ procedure.refs + 1; RETURN[procedure]}; <> IF list.rest = NIL OR GreaterThan[gfi, pc, list.rest.first.proc] THEN { procedure _ NewProcedure[module, pc]; IF procedure = NIL THEN SIGNAL Error; procedure.refs _ 1; list.rest _ CONS[[0, 0, procedure], list.rest]; noCalls _ noCalls + 1; RETURN[procedure]}; list _ list.rest; ENDLOOP; ERROR; END; AddModule: PROCEDURE[gfi: PrincOps.GFTIndex] RETURNS[module: Procedure] = BEGIN <> list: LIST OF Procedure; module _ NIL; list _ modules; <> IF list = NIL OR gfi > list.first.gfi THEN { module _ NewModule[gfi]; modules _ CONS[module, list]; RETURN[module]}; WHILE list # NIL DO module _ list.first; <> IF gfi = module.gfi THEN RETURN[module]; <> IF list.rest = NIL OR gfi > list.rest.first.gfi THEN { module _ NewModule[gfi]; list.rest _ CONS[module, list.rest]; RETURN[module]}; list _ list.rest; ENDLOOP; ERROR; END; NewModule: PROCEDURE[gfi: PrincOps.GFTIndex] RETURNS[module: Procedure] = BEGIN module _ NEW[ProcRec _ []]; noModules _ noModules + 1; module.gfi _ gfi; SELECT TRUE FROM gfi # 0 => module.name _ RTTypesPrivate.GFHToName[SpyOps.GFHFromGFI[gfi]]; current = pagefaults => module.name _ "pagefault"; ENDCASE => module.name _ "waiting"; END; NewProcedure: PROCEDURE[module: Procedure, pc: CARDINAL] RETURNS[procedure: Procedure _ NIL] = BEGIN name: ROPE; entryPC, exitPC: CARDINAL _ pc; IF module.gfi # 0 THEN [entryPC, exitPC] _ PcRange[module.gfi, pc]; procedure _ NEW[ProcRec _ []]; noProcs _ noProcs + 1; procedure.gfi _ module.gfi; procedure.entryPC _ entryPC; procedure.exitPC _ exitPC; IF module.gfi = 0 THEN SELECT current FROM pagefaults => SELECT pc FROM 1 => name _ "data"; 2 => name _ "code"; 3 => name _ "xfer"; ENDCASE; process, breakProcess => SELECT pc FROM 1 => name _ "pagefault"; 2 => name _ "ML"; 3 => name _ "CV"; 4 => name _ "preempted"; ENDCASE; ENDCASE; IF module.gfi = 0 AND name = NIL THEN { stream: IO.STREAM _ IO.CreateOutputStreamToRope[]; stream.Put[rope["type"], card[pc]]; name _ IO.GetOutputStreamRope[stream]}; procedure.name _ Rope.Cat[module.name, Rope.FromChar['.], name]; procedure.named _ name # NIL; END; PcRange: PROCEDURE[gfi: PrincOps.GFTIndex, pc: CARDINAL] RETURNS[entry, exit: CARDINAL] = BEGIN procPC, min: CARDINAL _ pc; gfh: PrincOps.GlobalFrameHandle; ev: LONG POINTER TO PrincOps.CSegPrefix; entry _ 0; exit _ min _ 177777B; gfh _ SpyOps.GFHFromGFI[gfi]; ev _ LOOPHOLE[gfh.code]; FOR i: CARDINAL IN [0..ev.header.info.ngfi*32) DO <> IF ev.entry[i].initialpc > 77777B THEN EXIT; IF ev.entry[i].info.framesize > PrincOps.LastAVSlot THEN EXIT; min _ MIN[min, ev.entry[i].initialpc/SIZE[PrincOps.EntryVectorItem]]; IF i+1 >= min THEN EXIT; <> procPC _ ev.entry[i].initialpc*2; IF entry < procPC AND procPC <= pc THEN entry _ procPC; IF pc < procPC AND procPC < exit THEN exit _ procPC; ENDLOOP; exit _ exit-1; END; -- handling marked stacks -- ProcStack: TYPE = REF ProcStackRec; ProcStackRec: TYPE = RECORD[ process: ProcessRef _ NIL, index: CARDINAL _ 0, frame: ARRAY [0..SpyOps.stackLength) OF RECORD[ proc: Procedure _ NIL, pc: CARDINAL _ 0]]; AddFrame: PROCEDURE[stack: ProcStack, proc: Procedure, pc: CARDINAL] = INLINE {stack.frame[stack.index] _ [proc, pc]; stack.index _ stack.index + 1}; Recursive: PROCEDURE[stack: ProcStack] RETURNS[BOOLEAN] = INLINE BEGIN IF stack.index = 0 THEN RETURN[FALSE]; FOR i: CARDINAL DECREASING IN [0..stack.index-1) DO IF stack.frame[i].proc = stack.frame[stack.index-1].proc THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; END; Undo: PROCEDURE[stack: ProcStack, count: Count] = BEGIN first, proc: Procedure; IF stack.index = 0 THEN RETURN; first _ stack.frame[stack.index-1].proc; FOR i: CARDINAL DECREASING IN [0..stack.index-1) DO proc _ stack.frame[i].proc; proc.sons _ RemoveSon[stack.frame[i+1].proc, stack.frame[i+1].pc, proc.sons, count]; proc.calls _ proc.calls - count; IF proc = first THEN {stack.index _ i + 1; RETURN}; ENDLOOP; ERROR; END; <<*********************************************>> <> <<*********************************************>> ClearContainers: PROCEDURE = BEGIN FOR m: LIST OF Procedure _ modules, m.rest WHILE m # NIL DO FOR p: LIST OF Call _ m.first.sons, p.rest WHILE p # NIL DO p.first.proc.container _ NIL; ENDLOOP; ENDLOOP; END; SetAllContainers: PROCEDURE = BEGIN SetAllParents[]; <> FOR a: LIST OF ProcessRef _ processes, a.rest WHILE a # NIL DO FOR s: LIST OF Call _ a.first.sons, s.rest WHILE s # NIL DO s.first.proc.container _ s.first.proc; ENDLOOP; ENDLOOP; <> FOR a: LIST OF ProcessRef _ processes, a.rest WHILE a # NIL DO FOR p: LIST OF Call _ a.first.sons, p.rest WHILE p # NIL DO SetContainers[p.first.proc.sons]; ENDLOOP; ENDLOOP; <> FOR m: LIST OF Procedure _ modules, m.rest WHILE m # NIL DO FOR p: LIST OF Call _ m.first.sons, p.rest WHILE p # NIL DO IF p.first.proc.container # NIL THEN LOOP; p.first.proc.container _ Container[p.first.proc]; IF p.first.proc.container = NIL THEN p.first.proc.container _ p.first.proc; ENDLOOP; ENDLOOP; END; SetAllParents: PROC = BEGIN proc: Procedure; found: BOOLEAN; FOR m: LIST OF Procedure _ modules, m.rest WHILE m # NIL DO FOR p: LIST OF Call _ m.first.sons, p.rest WHILE p # NIL DO proc _ p.first.proc; -- for each procedure . . . FOR s: LIST OF Call _ proc.sons, s.rest WHILE s # NIL DO found _ FALSE; IF s.first.calls = 0 THEN LOOP; FOR f: LIST OF Procedure _ s.first.proc.parents, f.rest WHILE f # NIL DO IF f.first = proc THEN {found _ TRUE; EXIT}; ENDLOOP; <<. . . add self to son's parent list>> IF ~found THEN s.first.proc.parents _ CONS[proc, s.first.proc.parents]; ENDLOOP; ENDLOOP; ENDLOOP; END; SetContainers: PROCEDURE[sons: LIST OF Call] = BEGIN FOR s: LIST OF Call _ sons, s.rest DO IF s = NIL THEN EXIT; IF s.first.proc.container # NIL THEN LOOP; s.first.proc.container _ Container[s.first.proc]; IF s.first.proc.container = NIL THEN LOOP; SetContainers[s.first.proc.sons]; ENDLOOP; END; Container: PROCEDURE[proc: Procedure] RETURNS[c: Procedure _ NIL] = BEGIN <> proc.marked _ TRUE; FOR f: LIST OF Procedure _ proc.parents, f.rest WHILE f # NIL DO IF f.first.marked THEN LOOP; -- already traversed it IF f.first.container = NIL THEN f.first.container _ Container[f.first]; IF f.first.container = NIL THEN LOOP; IF c = NIL THEN {c _ f.first; LOOP}; c _ Intersection[c, f.first]; IF c = NIL THEN {proc.marked _ FALSE; RETURN[proc]}; -- no container ENDLOOP; proc.marked _ FALSE; END; Intersection: PROCEDURE[a, b: Procedure] RETURNS[Procedure] = BEGIN DO IF a = b THEN RETURN[a]; FOR c: Procedure _ a, c.container DO IF c.container = b THEN RETURN[b]; IF c.container = c THEN EXIT; ENDLOOP; FOR c: Procedure _ b, c.container DO IF c.container = a THEN RETURN[a]; IF c.container = c THEN EXIT; ENDLOOP; IF a = a.container OR b = b.container THEN RETURN[NIL]; a _ a.container; b _ b.container; ENDLOOP; END; In: PROCEDURE[proc: Procedure, list: LIST OF Procedure] RETURNS[BOOLEAN] = INLINE BEGIN FOR l: LIST OF Procedure _ list, l.rest DO IF l = NIL THEN EXIT; IF l.first = proc THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; END; <<*********************************************>> <> <<*********************************************>> PrintStatistics: PROCEDURE[stream: IO.STREAM] = BEGIN allocated: Count; stream.Put[rope["Statistics on execution of Cedar Spy:"], char[CR]]; IF spyTime[0].count > 0 THEN { stream.Put[ char[TAB], card[spyTime[0].count], rope[" spy samples averaged "]]; PrintTime[stream, System.PulsesToMicroseconds[spyTime[0].time]/spyTime[0].count]}; IF spyTime[1].count > 0 THEN { stream.Put[ char[TAB], card[spyTime[1].count], rope[" spy samples averaged "]]; PrintTime[stream, System.PulsesToMicroseconds[spyTime[1].time]/spyTime[1].count]}; IF spyTime[2].count > 0 THEN { stream.Put[ char[TAB], card[spyTime[2].count], rope[" spy samples averaged "]]; PrintTime[stream, System.PulsesToMicroseconds[spyTime[2].time]/spyTime[2].count]}; IF spyTime[3].count > 0 THEN { stream.Put[ char[TAB], card[spyTime[3].count], rope[" spy samples averaged "]]; PrintTime[stream, System.PulsesToMicroseconds[spyTime[3].time]/spyTime[3].count]}; IF readLog IN (0..10000000] THEN {-- to avoid wrap around stream.Put[ char[TAB], rope["Processed log in "]]; PrintTime[stream, System.PulsesToMicroseconds[readLog]]}; stream.Put[ rope["\tTotal samples read from log = "], card[processed], rope[".\n"]]; IF overflow > 0 THEN stream.Put[char[TAB], card[overflow], rope[" SAMPLES WITH STACKS OF >= 75 FRAMES!\n"]]; IF error > 0 THEN stream.Put[char[TAB], card[error], rope[" SAMPLES WERE CUT SHORT BECAUSE OF ERRORS!\n"]]; IF processed > 0 THEN stream.Put[rope["\tAverage stack depth = "], card[stackDepth/processed], rope[".\n"]]; stream.Put[rope["\tNo. modules allocated = "], card[noModules], rope[".\n"]]; stream.Put[rope["\tNo. procedures allocated = "], card[noProcs], rope[".\n"]]; allocated _ noProcesses*SIZE[ProcessRec] + noCalls*SIZE[Call] + noModules*SIZE[ProcRec] + noProcs*SIZE[ProcRec]; stream.Put[rope["\tTotal words allocated = "], card[allocated], rope[".\n"]]; stream.Put[char[CR]]; END; Octal: PROCEDURE[n: CARDINAL] RETURNS[r: ROPE] = INLINE BEGIN r _ Convert.ValueToRope[[unsigned[n, 8]]]; r _ Rope.Cat[r, Rope.FromChar['B]]; END; PrintTime: PROCEDURE[stream: IO.STREAM, ticks: LONG CARDINAL] = BEGIN secs,mSecs,uSecs: LONG CARDINAL; uSecs _ ticks; secs _ uSecs/1000000; uSecs _ uSecs - secs*1000000; mSecs _ uSecs/1000; uSecs _ uSecs - mSecs*1000; stream.Put[card[secs], char['.], card[mSecs]]; stream.Put[rope[" seconds."], char[CR]]; END; <<*********************************************>> <> <<*********************************************>> Find: PROCEDURE[name: ROPE] RETURNS[Procedure] = BEGIN list: LIST OF Procedure _ modules; FOR m: LIST OF Procedure _ list, m.rest DO IF m = NIL THEN EXIT; IF Rope.Equal[name, m.first.name] THEN RETURN[m.first]; IF list # modules THEN LOOP; FOR p: LIST OF Call _ m.first.sons, p.rest DO IF p = NIL THEN EXIT; IF Rope.Equal[name, p.first.proc.name] THEN RETURN[p.first.proc]; ENDLOOP; ENDLOOP; RETURN[NIL]; END; CountStorage: PROC RETURNS[process, mods, procs, sons: INTEGER, total:LONG INTEGER] = BEGIN process _ mods _ procs _ sons _ 0; FOR m: LIST OF Procedure _ modules, m.rest DO IF m = NIL THEN EXIT; mods _ mods + 1; FOR p: LIST OF Call _ m.first.sons, p.rest DO IF p = NIL THEN EXIT; sons _ sons + 1; procs _ procs + 1; FOR s: LIST OF Call _ p.first.proc.sons, s.rest DO IF s = NIL THEN EXIT; sons _ sons + 1; ENDLOOP; ENDLOOP; ENDLOOP; FOR p: LIST OF ProcessRef _ processes, p.rest DO IF p = NIL THEN EXIT; process _ process + 1; FOR s: LIST OF Call _ p.first.sons, s.rest DO IF s = NIL THEN EXIT; sons _ sons + 1; ENDLOOP; ENDLOOP; total _ process*SIZE[ProcessRec] + (mods + procs)*SIZE[ProcRec] + sons*SIZE[Call]; END; END..