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; 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; 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; 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.. œSpyLogReaderImpl.mesa last modified by: John Maxwell on March 23, 1983 10:03 am procedures listed in top down order ********************************************* building a call tree from the trace log ********************************************* statistics IF UserInput.userAbort THEN EXIT; IF logging THEN SpyLog.Open[TRUE]; IF UserInput.userAbort THEN ERROR SpyOps.UserAborted; INVARIANT: exactly n proc.calls or proc.count should be incremented. add the first frame to the appropriate process list [level: 7, n: 0] is the code for an overflow are we at the end of the stack? add the current frame to the current node proc.marked _ TRUE; add in the leaf counts should we add it to the beginning of the list? is this the process we want? should the process follow this one? INVARIANT: the lists remain sorted INVARIANT: exactly one LIST OF Call.calls will be incremented should we add it to the beginning of the list? is this the procedure we want? should the procedure follow this one? INVARIANT: the lists remain sorted INVARIANT: exactly one LIST OF Call.calls will be decremented IF s.first.calls > 0 THEN RETURN[list]; s.first.proc.refs _ s.first.proc.refs - 1; IF s = list THEN RETURN[list.rest]; prior.rest _ s.rest; INVARIANT: the lists remain sorted INVARIANT: exactly one ref will be incremented should we add it to the beginning of the list? is this the procedure we want? should the procedure follow this one? INVARIANT: the lists remain sorted should we add it to the beginning of the list? is this the module we want? should the module follow this one? heuristics to see if we are at the end of the entry vector determine the entry and exit PCs. ********************************************* determining the precedence relationships ********************************************* set the root containers chase down the trees catch all of the ones we missed . . . add self to son's parent list determine the intersection of the procedures that references proc ********************************************* some printing ********************************************* ********************************************* debugging facilities ********************************************* Ê_˜Jšœ™Jšœ9™9J˜šÏk ˜ Jšœ œ˜&Jšœœœ˜'Jšœœ˜šœœ˜ Jšœ œ1˜?Jšœ&œœ˜7—šœ œ˜J˜N—Jšœœ ˜Jšœœ ˜Jšœœœ˜(Jšœ œ&˜8Jšœ œ˜*Jšœ œ!˜0Jšœœ˜1Jšœœ*˜6šœœ˜J˜:J˜6—Jšœœ0˜J˜&—Jšœ ˜—Jšœœ ˜J˜Jšœœœ˜J˜Jšœœœ˜J˜JšÏc ˜ J˜Jš œ œœœœ˜+Jš œ œœœ œ˜)Jšœœœ˜J˜J˜Jšœ#™#J˜šÏn œœ œ˜ š Ÿœœœœœ˜Jšœ˜Jš˜Jšœ"™"Jšœ.™.J˜Jšœœœ˜J˜Jšœ œ˜J˜Jšœ.™.šœœœ'œ˜=J˜%Jšœ œœœ˜&J˜Jšœœ˜,J˜Jšœ ˜—šœœ˜J˜Jšœ™šœœ˜#J˜$Jšœ ˜—Jšœ%™%šœ œœ,œ˜GJ˜%Jšœ œœœ˜%J˜Jšœ œ˜/J˜Jšœ ˜—J˜Jšœ˜—Jšœ˜Jšœ˜J˜—šŸ œ œ˜-Jšœ˜Jš˜Jšœ"™"Jšœœœ ˜Jšœ œ˜ J˜Jšœ.™.šœœœœ˜,J˜Jšœ œ˜Jšœ ˜—šœœ˜J˜Jšœ™Jšœœœ ˜(Jšœ"™"šœ œœœ˜6J˜Jšœ œ˜$Jšœ ˜—J˜Jšœ˜—Jšœ˜Jšœ˜J˜—šŸ œ œ˜-Jšœ˜Jš˜Jšœ œ˜J˜J˜šœœ˜J˜JJ˜2Jšœ˜$—Jšœ˜J˜—šŸ œ œœ˜9Jšœœ˜%Jš˜Jšœœ˜ Jšœœ˜Jšœœ-˜CJšœ œ˜J˜J˜J˜J˜šœœœ ˜*šœœ˜J˜J˜J˜Jšœ˜—šœœ˜'J˜J˜J˜J˜Jšœ˜—Jšœ˜—šœœœœ˜'Jšœœœœ˜2J˜#Jšœœ˜'—J˜@Jšœœ˜Jšœ˜J˜—šŸœ œœ˜9Jšœœ˜ Jš˜Jšœ œ˜J˜ Jšœœœœ˜(J˜ J˜J˜Jšœœ ˜šœœœ˜1Jšœ:™:Jšœ œœ˜,Jšœ2œœ˜>Jšœœœ˜EJšœ œœ˜Jšœ!™!J˜!Jšœœœ˜7Jšœ œœ˜4Jšœ˜—J˜Jšœ˜—J˜Jšž˜J˜Jšœ œœ˜#šœœœ˜Jšœœ˜Jšœœ˜šœœœœ˜/Jšœœ˜Jšœœ˜J˜——šŸœ œ(œ˜MJ˜GJ˜—šŸ œ œœœ˜9Jšœ˜ Jšœœœœ˜&š œœ œœ˜3Jšœ7œœœ˜KJšœ˜—Jšœœ˜Jšœ˜J˜—šŸœ œ"˜1Jš˜J˜Jšœœœ˜J˜(š œœ œœ˜3J˜J˜TJ˜ Jšœœœ˜3Jšœ˜—Jšœ˜Jšœ˜—J˜Jšœ-™-Jšœ)™)Jšœ-™-J˜šŸœ œ˜Jš˜š œœœœœ˜;š œœœœœ˜;Jšœœ˜Jšœ˜—Jšœ˜—Jšœ˜J˜—šŸœ œ˜Jš˜J˜Jšœ™š œœœ œœ˜>š œœœœœ˜;J˜&Jšœ˜—Jšœ˜—Jšœ™š œœœ œœ˜>š œœœœœ˜;J˜!Jšœ˜—Jšœ˜—Jšœ™š œœœœœ˜;š œœœœœ˜;Jšœœœœ˜*J˜1Jšœœœ'˜KJšœ˜—Jšœ˜—Jšœ˜J˜—šŸ œœ˜J˜Jšœœ˜š œœœœœ˜;š œœœœœ˜;Jšœž˜0š œœœœœ˜8Jšœœ˜Jšœœœ˜š œœœ*œ˜HJšœœ œœ˜,Jšœ˜—Jšœ#™#Jšœœœ˜GJšœ˜—Jšœ˜—Jšœ˜—Jšœ˜J˜—šŸ œ œœœ˜.Jš˜šœœœ˜%Jšœœœœ˜Jšœœœœ˜*J˜1Jšœœœœ˜*J˜!Jšœ˜—Jšœ˜J˜—šŸ œ œ˜&Jšœœ˜Jš˜JšœA™AJšœœ˜š œœœ"œœ˜@Jšœœœž˜4Jšœœœ(˜GJšœœœœ˜&Jšœœœœ˜$Jšœ˜Jš œœœœœ ž˜DJšœ˜—Jšœœ˜Jšœ˜J˜—šŸ œ œœ ˜=Jš˜šœœœœ˜šœ˜$Jšœœœ˜"Jšœœœ˜Jšœ˜—šœ˜$Jšœœœ˜"Jšœœœ˜Jšœ˜—Jš œœœœœ˜7J˜J˜Jšœ˜—Jšœ˜J˜—š Ÿœ œœœ œœ˜JJšœ˜ šœœœ˜*Jšœœœœ˜Jšœœœœ˜$Jšœ˜—Jšœœ˜Jšœ˜—J˜Jšœ-™-Jšœ™Jšœ-™-J˜šŸœ œ œœ˜0Jš˜J˜Jšœ?œ˜DJšœœ˜˜ Jšœœ˜"J˜ —J˜RJšœœ˜˜ Jšœœ˜"J˜ —J˜RJšœœ˜˜ Jšœœ˜"J˜ —J˜RJšœœ˜˜ Jšœœ˜"J˜ —J˜RJšœ œœž˜9˜ Jšœœ˜&—J˜:˜ J˜H—Jšœ˜šœœ˜&J˜1—Jšœ ˜šœœ˜#J˜6—Jšœ˜J˜VJ˜NJ˜Nšœœœ ˜@Jšœ œœ ˜1—J˜MJšœœ˜Jšœ˜J˜—š Ÿœ œœœœ˜0Jšœ˜ J˜*J˜#Jšœ˜J˜—š Ÿ œ œ œœ œœ˜?Jš˜Jšœœœ˜ J˜J˜J˜J˜J˜J˜.Jšœ#œ˜(Jšœ˜—J˜Jšœ-™-Jšœ™Jšœ-™-J˜šŸœ œœœ ˜0Jš˜Jšœœœ˜"šœœœ˜*Jšœœœœ˜Jšœ œœ ˜7Jšœœœ˜šœœœ˜-Jšœœœœ˜Jšœ%œœ˜AJšœ˜—Jšœ˜—Jšœœ˜ Jšœ˜J˜—š Ÿ œœœœœœ˜UJš˜J˜"šœœœ˜-Jšœœœœ˜J˜šœœœ˜-Jšœœœœ˜J˜J˜šœœœ"˜2Jšœœœœ˜J˜Jšœ˜—Jšœ˜—Jšœ˜—šœœœ ˜0Jšœœœœ˜J˜šœœœ˜-Jšœœœœ˜J˜Jšœ˜—Jšœ˜—šœœ˜#Jšœœœ ˜1—Jšœ˜—J˜Jšœ˜J˜J˜—…—R„y