<> <> <> <> <> <> <> <> DIRECTORY AMBridge USING [TVForPointerReferent], AMTypes USING [TV, TVType, TypeToName], Convert USING [RopeFromCard], BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds, PulsesToSeconds], IO USING [ROS, RopeFromROS, Put, PutChar, PutF, PutFR, STREAM], PrincOps USING [BytePC, CSegPrefix, EntryVectorItem, GFT, GlobalFrameHandle, LastAVSlot, NullPsbHandle, PsbIndex, PsbHandle], PrincOpsUtils USING [PsbHandleToIndex], PrintTV USING [Print], Process USING [Priority], RealOps USING [RoundLI], Rope USING [Cat, FromChar, Equal, ROPE], RTSymbolDefs USING [CallableBodyIndex, SymbolTableBase], RTSymbolOps USING [AcquireRope, BodyName], RTSymbols USING [AcquireSTBFromGFH, ReleaseSTB], RTTypesPrivate USING [GetCBTI, GetEp, GFHToName], RuntimeError USING [UNCAUGHT], SpyLog USING [active, Close, Entry, NextEntry, OpenForRead], SpyOps USING [Call, Count, DataType, freqDivisor, LevelData, pageFaults, Procedure, ProcessRef, ProcessRec, ProcRec, runningTime, Stack, stackHeader, stackLength, StackType, wordsAllocated, wordsReclaimed], ViewerIO USING [CreateViewerStreams]; SpyLogReaderImpl: PROGRAM IMPORTS AMBridge, AMTypes, BasicTime, Convert, IO, PrincOpsUtils, PrintTV, RealOps, Rope, RTSymbolOps, RTSymbols, RTTypesPrivate, RuntimeError, SpyLog, SpyOps, ViewerIO EXPORTS SpyOps = { OPEN SpyOps; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Error: SIGNAL = CODE; <> processes: PUBLIC LIST OF ProcessRef _ NIL; modules: PUBLIC LIST OF Procedure _ NIL; current: SpyOps.DataType; <> DestroyLog: PUBLIC PROC = { NilLinks: PROC [proc: Procedure] RETURNS [BOOL _ 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 PROC [typescript: STREAM, datatype: SpyOps.DataType, spyOnSpyLog: BOOL] = { ignoreBefore: BasicTime.Pulses _ 0; IF typescript = NIL THEN typescript _ ViewerIO.CreateViewerStreams["Spy log"].out; typescript.Put[[rope["(Please wait-- processing log.)\n\n"]]]; current _ datatype; typeName _ ALL[NIL]; SELECT current FROM CPU => className _ NIL; process, breakProcess => { className _ "waiting"; typeName[0] _ "executing"; typeName[1] _ "pagefault"; typeName[2] _ "ML"; typeName[3] _ "CV"; typeName[4] _ "preempted"}; pagefaults => { className _ "pagefault"; typeName[1] _ "data"; typeName[2] _ "code"; typeName[3] _ "xfer"}; allocations, wordsAllocated => { className _ "allocation"; typeName[0] _ "safe"; typeName[1] _ "permanent"; typeName[2] _ "unsafe"}; userDefined => className _ "userBreaks"; ENDCASE => ERROR; typeData _ ALL[0]; IF useTrigger THEN { ignoreBefore _ FindTrigger[]; IF ignoreBefore = LOOPHOLE[0] THEN { typescript.Put[[rope["No trigger encountered.\n\n"]]]; RETURN}; IF triggerExtent > ignoreBefore THEN ignoreBefore _ 0 ELSE ignoreBefore _ ignoreBefore - triggerExtent}; ReadSpyLog[ignoreBefore, spyOnSpyLog]; SetAllContainers[]; PrintStatistics[typescript]; PrintInstructions[typescript]; PrintLogStatistics[typescript]; }; <<*********************************************>> <> <<*********************************************>> useTrigger: BOOL _ FALSE; triggerExtent: BasicTime.Pulses; whichTrigger: NAT _ 1; SetTrigger: PROC [extent: BasicTime.Pulses, which: NAT _ 1, enable: BOOL _ TRUE] = { useTrigger _ enable; triggerExtent _ extent; whichTrigger _ which; }; FindTrigger: PROC RETURNS [trigger: BasicTime.Pulses] = { count: NAT _ 0; entry: LONG POINTER TO SpyLog.Entry; SpyLog.OpenForRead[]; -- open for reading WHILE TRUE DO entry _ SpyLog.NextEntry[]; IF entry = NIL THEN EXIT; WITH e: entry SELECT FROM endOfLog => EXIT; data => { stack: LONG POINTER TO SpyOps.Stack _ LOOPHOLE[@e.data]; IF stack.type # LAST[SpyOps.StackType] THEN LOOP; count _ count + 1; IF count < whichTrigger THEN LOOP; RETURN [e.timestamp]}; ENDCASE; ENDLOOP; RETURN [0]; }; <<*********************************************>> <> <<*********************************************>> out: STREAM; <> wakeups: PUBLIC Count _ 0; -- Number of times that Spy counted something notScheduled: PUBLIC SpyOps.Count _ 0; -- no interesting process sceduled skips: PUBLIC SpyOps.Count _ 0; -- waiting on disk levelData: PUBLIC SpyOps.LevelData _ ALL[0]; typeName: ARRAY StackType OF Rope.ROPE; typeData: ARRAY StackType OF Count; className: Rope.ROPE; <> processed: Count _ 0; -- Number of stacks in the log 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; readLog: BasicTime.Pulses; -- elapsed time to read log recordSpyTime: BOOL _ FALSE; spyTime: ARRAY [0..3] OF RECORD[ count: Count, time: BasicTime.Pulses]; ReadSpyLog: PROC [ignoreBefore: BasicTime.Pulses, spyOnSpyLog: BOOL] = { i: CARDINAL; data: BOOL _ FALSE; dataSize: CARDINAL _ 0; first, last: BasicTime.Pulses _ 0; active: BOOL _ SpyLog.active; t1, delta, dataTime: BasicTime.Pulses; entry: LONG POINTER TO SpyLog.Entry; process: PrincOps.PsbHandle _ PrincOps.NullPsbHandle; Initialize[]; SpyLog.OpenForRead[]; -- open for reading readLog _ 0; t1 _ BasicTime.GetClockPulses[]; WHILE TRUE DO <> entry _ SpyLog.NextEntry[]; IF entry = NIL THEN EXIT; WITH e: entry SELECT FROM endOfLog => EXIT; nullEntry => LOOP; 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; IF out = NIL THEN { out _ ViewerIO.CreateViewerStreams["auxilary spy log"].out; out.PutF["%l uSeconds delta data\n", [character['f]]]}; IF e.process # process THEN { out.PutF[" Switch to process: %b\n", [cardinal[PrincOpsUtils.PsbHandleToIndex[e.process]]]]; process _ e.process}; PrintTrace[e.gfh, e.pc, e.timestamp - first, delta]; last _ e.timestamp}; data => { stack: LONG POINTER TO SpyOps.Stack; IF useTrigger AND e.timestamp < ignoreBefore THEN LOOP; 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}; stack _ LOOPHOLE[@e.data]; IF useTrigger AND stack.type = LAST[SpyOps.StackType] THEN EXIT; EnterStack[LOOPHOLE[@e.data], (e.size - SpyOps.stackHeader)/2]; dataSize _ e.size + 4; dataTime _ e.timestamp; data _ TRUE}; ENDCASE => SIGNAL Error; ENDLOOP; readLog _ BasicTime.GetClockPulses[]-t1; <> <> stack _ NIL; out _ NIL; }; PrintEntry: PROC [type, words: CARDINAL, data: LONG POINTER, time, delta: BasicTime.Pulses] = { value: AMTypes.TV; IF out = NIL THEN { out _ ViewerIO.CreateViewerStreams["auxilary spy log"].out; out.PutF["%l uSeconds delta data\n", [character['f]]]}; value _ AMBridge.TVForPointerReferent[data, LOOPHOLE[type]]; out.PutF["%9d%9d %g: \n", [cardinal[BasicTime.PulsesToMicroseconds[time]]], [cardinal[BasicTime.PulsesToMicroseconds[delta]]], [rope[AMTypes.TypeToName[AMTypes.TVType[value]]]]]; PrintTV.Print[value, out]; out.PutChar['\n]; }; PrintTrace: PROC [gfh: PrincOps.GlobalFrameHandle, pc: PrincOps.BytePC, time, delta: BasicTime.Pulses] = { name: Rope.ROPE _ GetName[gfh, pc]; out.PutF["%9d%9d %g (gfh: %g, pc: %g)\n", [cardinal[BasicTime.PulsesToMicroseconds[time]]], [cardinal[BasicTime.PulsesToMicroseconds[delta]]], [rope[name]], [cardinal[LOOPHOLE[gfh, CARDINAL]]], [cardinal[pc]]]; }; GetName: PROC [gfh: PrincOps.GlobalFrameHandle, pc: PrincOps.BytePC] RETURNS [name: Rope.ROPE _ NIL] = { proc: Rope.ROPE; stb: RTSymbolDefs.SymbolTableBase _ [x[e: NIL]]; name _ RTTypesPrivate.GFHToName[gfh ! RuntimeError.UNCAUGHT => GO TO badNews]; { ENABLE { RuntimeError.UNCAUGHT => { IF stb # [x[e: NIL]] THEN {RTSymbols.ReleaseSTB[stb]; stb _ [x[e: NIL]]}; CONTINUE}; UNWIND => { IF stb # [x[e: NIL]] THEN {RTSymbols.ReleaseSTB[stb]; stb _ [x[e: NIL]]}; CONTINUE}; }; ep: CARDINAL; cbti: RTSymbolDefs.CallableBodyIndex; stb _ RTSymbols.AcquireSTBFromGFH[gfh]; [ep,] _ RTTypesPrivate.GetEp[[pc], gfh, stb]; cbti _ RTTypesPrivate.GetCBTI[stb, ep]; proc _ RTSymbolOps.AcquireRope[stb, RTSymbolOps.BodyName[stb, cbti]]; RTSymbols.ReleaseSTB[stb]; }; name _ Rope.Cat[name, ".", proc]; EXITS badNews => name _ IO.PutFR["%bB.%bB", [cardinal[LOOPHOLE[gfh, CARDINAL]]], [cardinal[pc]] ]; }; <> Initialize: PROC = { processes _ NIL; modules _ NIL; stackDepth _ 0; processed _ overflow _ error _ 0; wakeups _ notScheduled _ skips _ 0; noProcesses _ noCalls _ noProcs _ noModules _ 0; spyTime _ ALL[[0, 0]]; levelData _ ALL[0]; stack _ NIL; }; EnumerateProcs: PROC [userProc: PROC [Procedure] RETURNS [BOOL]] RETURNS [last: Procedure] = { 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]; }; <> stack: ProcStack; EnterStack: PROC [s: LONG POINTER TO SpyOps.Stack, n: CARDINAL] = { ENABLE Error => {error _ error + 1; CONTINUE}; <> process: ProcessRef _ NIL; proc, newSon: Procedure _ NIL; processed _ processed + 1; wakeups _ wakeups + s.count; IF s.level = 0 AND n = 0 THEN {SELECT s.type FROM 0 => notScheduled _ notScheduled + s.count; 1 => skips _ skips + s.count; ENDCASE => error _ error + s.count; RETURN}; <> process _ AddProcess[s.process]; process.calls _ process.calls + s.count; stackDepth _ stackDepth + n; 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; levelData[s.level] _ levelData[s.level] + s.count; <<[level: 7, n: 0] is the code for an overflow>> IF s.level = 7 THEN {overflow _ overflow + s.count; RETURN}; IF n = 0 THEN RETURN ELSE n _ n - 1; [process.sons, proc] _ AddSon[process.sons, 0, s.frame[n].gfh, s.frame[n].pc, s.count]; IF proc = NIL THEN {stack _ NIL; RETURN}; 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].gfh, s.frame[n].pc, s.count]; proc _ newSon; IF proc = NIL THEN EXIT; AddFrame[stack, proc, s.frame[n+1].pc]; IF Recursive[stack] THEN Undo[stack, s.count]; ENDLOOP; IF proc # NIL THEN { <> proc.count _ proc.count + s.count; proc.countLocs _ AddCountLocs[list: proc.countLocs, pc: s.frame[0].pc, count: s.count]; IF s.type # 0 THEN { <> [proc.sons, newSon] _ AddSon[proc.sons, s.frame[0].pc, NIL, s.type, s.count]; IF newSon # NIL THEN newSon.count _ newSon.count + s.count; }; }; typeData[s.type] _ typeData[s.type] + s.count; stack.index _ 0; }; <> AddProcess: PROC [psb: PrincOps.PsbIndex] RETURNS [process: ProcessRef _ NIL] = { 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; }; NewProcess: PROC [psb: PrincOps.PsbIndex] RETURNS [process: ProcessRef] = { process _ NEW[ProcessRec _ []]; noProcesses _ noProcesses + 1; process.psb _ psb; }; <> AddSon: PROC [list: LIST OF Call, from: CARDINAL, gfh: PrincOps.GlobalFrameHandle, pc, count: Count] RETURNS [newList: LIST OF Call, procedure: Procedure] = { <> <> procedure _ NIL; newList _ list; <> IF list = NIL OR from < list.first.pc THEN { procedure _ AddProcedure[gfh, pc]; IF procedure = NIL THEN RETURN [NIL, NIL]; 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[gfh, 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[gfh, pc]; IF procedure = NIL THEN RETURN [NIL, NIL]; list.rest _ CONS[[from, count, procedure], list.rest]; noCalls _ noCalls + 1; RETURN [newList, procedure]}; list _ list.rest; ENDLOOP; ERROR; }; RemoveSon: PROC [proc: Procedure, pc: CARDINAL, list: LIST OF Call, count: Count] RETURNS [newList: LIST OF Call] = { <> <> 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; }; GreaterThan: PROC [gfh: PrincOps.GlobalFrameHandle, pc: CARDINAL, proc: Procedure] RETURNS [BOOL] = INLINE { IF gfh # proc.gfh THEN RETURN [LOOPHOLE[gfh, CARDINAL] > LOOPHOLE[proc.gfh, CARDINAL]]; RETURN [pc > proc.exitPC]; }; Equal: PROC [gfh: PrincOps.GlobalFrameHandle, pc: INT, proc: Procedure] RETURNS [BOOL] = INLINE { entryPC, exitPC: INT; IF proc = NIL THEN RETURN [TRUE]; -- source IF gfh # proc.gfh THEN RETURN [FALSE]; IF pc < (entryPC _ proc.entryPC) THEN RETURN [FALSE]; IF pc > (exitPC _ proc.exitPC) THEN RETURN [FALSE]; RETURN [TRUE]; }; AddCountLocs: PROC [list: LIST OF Call, pc: CARDINAL, count: Count] RETURNS [new: LIST OF Call] ~ { Insert: PROC [list: LIST OF Call] ~ { SELECT TRUE FROM list.rest=NIL OR pc list.rest _ CONS[call, list.rest]; pc=list.rest.first.pc => list.rest.first.calls _ list.rest.first.calls+count; ENDCASE => Insert[list.rest]; }; call: Call ~ [pc: pc, calls: count]; IF list=NIL OR pc> AddProcedure: PROC [gfh: PrincOps.GlobalFrameHandle, pc: CARDINAL] RETURNS [procedure: Procedure _ NIL] = { <> <> module: Procedure; list: LIST OF Call; module _ AddModule[gfh]; IF module = NIL THEN RETURN; list _ module.sons; <> IF list = NIL OR GreaterThan[gfh, 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[gfh, pc, procedure] THEN { procedure.refs _ procedure.refs + 1; RETURN [procedure]}; <> IF list.rest = NIL OR GreaterThan[gfh, 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; }; AddModule: PROC [gfh: PrincOps.GlobalFrameHandle] RETURNS [module: Procedure _ NIL] = { <> list: LIST OF Procedure _ modules; ValidateGFH[gfh ! RuntimeError.UNCAUGHT => GO TO badGuy]; <> IF list = NIL OR LOOPHOLE[gfh, CARDINAL] > LOOPHOLE[list.first.gfh, CARDINAL] THEN { module _ NewModule[gfh]; modules _ CONS[module, list]; RETURN [module]}; WHILE list # NIL DO module _ list.first; <> IF gfh = module.gfh THEN RETURN [module]; <> IF list.rest = NIL OR LOOPHOLE[gfh, CARDINAL] > LOOPHOLE[list.rest.first.gfh, CARDINAL] THEN { module _ NewModule[gfh]; list.rest _ CONS[module, list.rest]; RETURN [module]}; list _ list.rest; ENDLOOP; ERROR; EXITS badGuy => RETURN [NIL]; }; ValidateGFH: PROC [gfh: PrincOps.GlobalFrameHandle] = { IF gfh # PrincOps.GFT[gfh.gfi].framePtr THEN ERROR; }; NewModule: PROC [gfh: PrincOps.GlobalFrameHandle] RETURNS [module: Procedure] = { module _ NEW[ProcRec _ []]; noModules _ noModules + 1; module.gfh _ gfh; module.name _ (IF gfh = NIL THEN className ELSE RTTypesPrivate.GFHToName[gfh]); }; NewProcedure: PROC [module: Procedure, pc: CARDINAL] RETURNS [procedure: Procedure _ NIL] = { name: ROPE; entryPC, exitPC: CARDINAL _ pc; IF module.gfh # NIL THEN [entryPC, exitPC] _ PcRange[module.gfh, pc]; procedure _ NEW[ProcRec _ []]; noProcs _ noProcs + 1; procedure.gfh _ module.gfh; procedure.entryPC _ entryPC; procedure.exitPC _ exitPC; IF module.gfh = NIL THEN name _ typeName[pc]; IF module.gfh = NIL AND name = NIL THEN { stream: STREAM _ IO.ROS[]; stream.Put[[rope["type"]], [cardinal[pc]]]; typeName[pc] _ name _ IO.RopeFromROS[stream]}; procedure.name _ Rope.Cat[module.name, Rope.FromChar['.], name]; procedure.named _ name # NIL; }; PcRange: PROC [gfh: PrincOps.GlobalFrameHandle, pc: CARDINAL] RETURNS [entry, exit: CARDINAL] = { procPC, min: CARDINAL _ pc; ev: LONG POINTER TO PrincOps.CSegPrefix; entry _ 0; exit _ min _ 177777B; 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; }; <> 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: PROC [stack: ProcStack, proc: Procedure, pc: CARDINAL] = INLINE { stack.frame[stack.index] _ [proc, pc]; stack.index _ stack.index + 1}; Recursive: PROC [stack: ProcStack] RETURNS [BOOL] = INLINE { 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]; }; Undo: PROC [stack: ProcStack, count: Count] = { 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; }; <<*********************************************>> <> <<*********************************************>> ClearContainers: PROC = { 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; }; SetAllContainers: PROC = { 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; }; SetAllParents: PROC = { proc: Procedure; found: BOOL; 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; }; SetContainers: PROC [sons: LIST OF Call] = { 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; }; Container: PROC [proc: Procedure] RETURNS [c: Procedure _ NIL] = { <> 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; }; Intersection: PROC [a, b: Procedure] RETURNS [Procedure] = { 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; }; <<*********************************************>> <> <<*********************************************>> PrintStatistics: PROC [stream: STREAM] = { seconds: INTEGER; IF wakeups = 0 THEN RETURN; <> seconds _ MAX[RealOps.RoundLI[BasicTime.PulsesToSeconds[SpyOps.runningTime]], 1]; stream.PutF["Spy ran for %g minutes %g seconds.\n", [cardinal[seconds/60]], [cardinal[seconds MOD 60]]]; SELECT current FROM CPU, process, breakProcess, allocations, wordsAllocated => stream.PutF["Sampling frequency divisor was %g.\n", [integer[freqDivisor]]]; pagefaults, userDefined => NULL; ENDCASE => ERROR; stream.Put[[rope["Total wakeups = "]], [cardinal[wakeups]]]; stream.Put[[rope[" ("]], [cardinal[wakeups/seconds]], [rope[" per second).\n"]]]; IF current # pagefaults THEN { stream.Put[[rope["Total page faults = "]], [cardinal[SpyOps.pageFaults]]]; stream.Put[[rope[" ("]], [cardinal[SpyOps.pageFaults/seconds]], [rope[" per second).\n"]]]}; stream.Put[[rope["Total words allocated = "]], [cardinal[SpyOps.wordsAllocated]]]; stream.Put[[rope[" ("]], [cardinal[SpyOps.wordsAllocated/seconds]], [rope[" per second).\n"]]]; stream.Put[[rope["Total words reclaimed = "]], [cardinal[SpyOps.wordsReclaimed]]]; stream.Put[[rope[" ("]], [cardinal[SpyOps.wordsReclaimed/seconds]], [rope[" per second).\n"]]]; <> stream.Put[[rope["\nScheduled Process-Priority Summary:\n"]]]; IF notScheduled # 0 THEN { stream.Put[[rope[" processor idle"]]]; PrintCount[stream, notScheduled, 0, wakeups]}; IF skips # 0 THEN { stream.Put[[rope[" processor waiting on disk"]]]; PrintCount[stream, skips, 0, wakeups]}; FOR i: Process.Priority IN Process.Priority DO IF levelData[i] = 0 THEN LOOP; stream.Put[[rope[" priority "]], [rope[LevelName[i]]]]; PrintCount[stream, levelData[i], 0, wakeups]; ENDLOOP; stream.Put[[character['\n]]]; <> IF className # NIL THEN { header: Rope.ROPE; headerPrinted: BOOL _ FALSE; SELECT current FROM pagefaults => header _ "Breakdown By Type Of Pagefault:\n"; allocations, wordsAllocated => header _ "Breakdown By Type Of Allocation:\n"; ENDCASE => header _ "Breakdown By Type Of Action:\n"; FOR i: StackType IN StackType DO IF typeName[i] = NIL OR typeData[i] = 0 THEN LOOP; IF ~headerPrinted THEN { stream.Put[[rope[header]]]; headerPrinted _ TRUE}; IF current IN [process..breakProcess] AND i # 0 THEN stream.Put[[rope[" waiting "]], [rope[typeName[i]]]] ELSE stream.Put[[rope[" "]], IF typeName[i] # NIL THEN [rope[typeName[i]]] ELSE [integer[i]]]; PrintCount[stream, typeData[i], 0, wakeups]; ENDLOOP; stream.Put[[character['\n]]]}; }; PrintInstructions: PROC [stream: STREAM] = { stream.Put[[rope["Instructions (see SpyDoc.tioga for more information):"]], [character['\n]]]; stream.Put[[character['\t]], [rope["Indentation is used to show containment."]], [character['\n]]]; stream.Put[[character['\t]], [rope["Periods are used to keep track of procedures at the same level of indentation."]], [character['\n]]]; stream.Put[[character['\t]], [rope["Exclamation points (!) are used to group together sets of disjoint procedures."]], [character['\n]]]; stream.Put[[character['\t]], [rope["Italics are used to indicate procedures with more than one parent."]], [character['\n]]]; stream.Put[[character['\t]], [rope["Bold is used to indicate procedures which actually had allocations or page faults."]], [character['\n]]]; stream.Put[[character['\t]], [rope["Impl.Proc = x, y (o%) (z%):"]], [character['\n]]]; stream.Put[[character['\t]], [rope[" x = counts in Impl.Proc."]], [character['\n]]]; stream.Put[[character['\t]], [rope[" y = counts in procedures called from Impl.Proc."]], [character['\n]]]; stream.Put[[character['\t]], [rope[" o = x/ total % (only displayed if > 1%)."]], [character['\n]]]; stream.Put[[character['\t]], [rope[" z = (x + y)/ total = % time IN THE CALL STACK."]], [character['\n]]]; stream.Put[[character['\n]]]; }; PrintLogStatistics: PROC [stream: STREAM] = { allocated: Count; stream.Put[[rope["Statistics on execution of Cedar Spy:"]], [character['\n]]]; IF spyTime[0].count > 0 THEN { stream.Put[ [character['\t]], [cardinal[spyTime[0].count]], [rope[" spy samples averaged "]]]; PrintTime[stream, BasicTime.PulsesToMicroseconds[spyTime[0].time]/spyTime[0].count]}; IF spyTime[1].count > 0 THEN { stream.Put[ [character['\t]], [cardinal[spyTime[1].count]], [rope[" spy samples averaged "]]]; PrintTime[stream, BasicTime.PulsesToMicroseconds[spyTime[1].time]/spyTime[1].count]}; IF spyTime[2].count > 0 THEN { stream.Put[ [character['\t]], [cardinal[spyTime[2].count]], [rope[" spy samples averaged "]]]; PrintTime[stream, BasicTime.PulsesToMicroseconds[spyTime[2].time]/spyTime[2].count]}; IF spyTime[3].count > 0 THEN { stream.Put[ [character['\t]], [cardinal[spyTime[3].count]], [rope[" spy samples averaged "]]]; PrintTime[stream, BasicTime.PulsesToMicroseconds[spyTime[3].time]/spyTime[3].count]}; IF readLog IN (0..10000000] THEN {-- to avoid wrap around stream.Put[ [character['\t]], [rope["Processed log in "]]]; PrintTime[stream, BasicTime.PulsesToMicroseconds[readLog]]}; stream.Put[ [rope["\tTotal samples read from log = "]], [cardinal[processed]], [rope[".\n"]]]; IF overflow > 0 THEN { stream.Put[[character['\t]], [cardinal[overflow]], [rope[" SAMPLES WITH STACKS OF >= "]] ]; stream.Put[ [rope[Convert.RopeFromCard[from: SpyOps.stackLength, showRadix: FALSE]]], [rope[" FRAMES!\n"]] ]; }; IF error > 0 THEN stream.Put[[character['\t]], [cardinal[error]], [rope[" SAMPLES WERE CUT SHORT BECAUSE OF ERRORS!\n"]]]; IF processed > 0 THEN stream.Put[[rope["\tAverage stack depth = "]], [cardinal[stackDepth/processed]], [rope[".\n"]]]; stream.Put[[rope["\tNo. modules allocated = "]], [cardinal[noModules]], [rope[".\n"]]]; stream.Put[[rope["\tNo. procedures allocated = "]], [cardinal[noProcs]], [rope[".\n"]]]; allocated _ noProcesses*SIZE[ProcessRec] + noCalls*SIZE[Call] + noModules*SIZE[ProcRec] + noProcs*SIZE[ProcRec]; stream.Put[[rope["\tTotal words allocated = "]], [cardinal[allocated]], [rope[".\n"]]]; stream.Put[[character['\n]]]; }; LevelName: PROC [l: Process.Priority] RETURNS [ROPE] = INLINE { RETURN [SELECT l FROM 0 => "monitoring", 3 => "foreground", 1 => "background", 4 => "pagefaultLow", 2 => "normal", 5 => "pagefaultHigh", ENDCASE => "interrupt"]}; PrintCount: PUBLIC PROC [stream: STREAM, c, sonC: Count, total: Count] = { IF c = 0 AND sonC = 0 THEN {stream.Put[[character['\n]]]; RETURN}; stream.Put[[rope[" = "]], [cardinal[c]]]; IF sonC # 0 THEN stream.Put[[rope[", "]], [cardinal[sonC]]]; IF total = 0 THEN {stream.Put[[character['\n]]]; RETURN}; IF (c*100/total) > 1 AND sonC # 0 THEN PerCent[stream, c, total]; PerCent[stream, c+sonC, total]; stream.Put[[character['.]], [character['\n]]]; }; PerCent: PUBLIC PROC [stream: STREAM, x, y: Count] = { q, r: Count; sign: INT _ 1; IF y = 0 THEN y _ 1; q _ RealOps.RoundLI[(REAL[x]*1000.0)/REAL[y]]; IF q < 0 THEN {sign _ -1; q _ -q}; r _ IF q = 0 AND x # 0 THEN 1 ELSE (q MOD 10); -- to avoid 0.0% q _ (q/10); stream.Put[[rope[" ("]], [integer[sign*q]], [character['.]]]; stream.Put[[cardinal[r]], [rope["%)"]]]; }; PrintTime: PROC [stream: STREAM, ticks: LONG CARDINAL] = { secs,mSecs,uSecs: LONG CARDINAL; uSecs _ ticks; secs _ uSecs/1000000; uSecs _ uSecs - secs*1000000; mSecs _ uSecs/1000; uSecs _ uSecs - mSecs*1000; IF secs # 0 OR mSecs > 10 THEN stream.PutF["%g.%g seconds.", [cardinal[secs]], [cardinal[mSecs]]] ELSE stream.PutF["%g.%g milliseconds.", [cardinal[mSecs]], [cardinal[uSecs]]]; }; <<*********************************************>> <> <<*********************************************>> Find: PROC [name: ROPE] RETURNS [Procedure] = { 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]; }; }.. CountStorage: PROC RETURNS [process, mods, procs, sons: INTEGER, total:LONG INTEGER] = { 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]; };