SpyLogReaderImpl.mesa
last modified by: John Maxwell on March 23, 1983 10:03 am
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: BOOLEANFALSE;
current: SpyOps.DataType;
procedures listed in top down order
DestroyLog: PUBLIC PROCEDURE = {
NilLinks: PROC[proc: Procedure] RETURNS[BOOLEANFALSE] = {
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;
*********************************************
building a call tree from the trace log
*********************************************
statistics
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: BOOLEANFALSE;
ReadSpyLog: PROCEDURE =
BEGIN
i: CARDINAL;
data: BOOLEANFALSE;
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
IF UserInput.userAbort THEN EXIT;
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];
IF logging THEN SpyLog.Open[TRUE];
IF UserInput.userAbort THEN ERROR SpyOps.UserAborted;
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};
INVARIANT: exactly n proc.calls or proc.count should be incremented.
process: ProcessRef ← NIL;
proc, newSon: Procedure ← NIL;
processed ← processed + 1;
stackDepth ← stackDepth + n;
add the first frame to the appropriate process list
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
are we at the end of the stack?
IF n = 0 THEN EXIT ELSE n ← n - 1;
proc.calls ← proc.calls + s.count;
add the current frame to the current node
[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 {
proc.marked ← TRUE;
Undo[stack, s.count]};
ENDLOOP;
add in the leaf counts
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;
should we add it to the beginning of the list?
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;
is this the process we want?
IF process.psb = psb THEN RETURN[process];
should the process follow this one?
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
INVARIANT: the lists remain sorted
INVARIANT: exactly one LIST OF Call.calls will be incremented
IF sorted THEN SIGNAL Error; -- sorted by count rather than pc.
procedure ← NIL;
newList ← list;
should we add it to the beginning of the 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;
is this the procedure we want?
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]};
should the procedure follow this one?
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
INVARIANT: the lists remain sorted
INVARIANT: exactly one LIST OF Call.calls will be decremented
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;
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;
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
INVARIANT: the lists remain sorted
INVARIANT: exactly one ref will be incremented
module: Procedure;
list: LIST OF Call;
module ← AddModule[gfi];
procedure ← NIL;
list ← module.sons;
should we add it to the beginning of the list?
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;
is this the procedure we want?
IF Equal[gfi, pc, procedure] THEN {
procedure.refs ← procedure.refs + 1;
RETURN[procedure]};
should the procedure follow this one?
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
INVARIANT: the lists remain sorted
list: LIST OF Procedure;
module ← NIL;
list ← modules;
should we add it to the beginning of the list?
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;
is this the module we want?
IF gfi = module.gfi THEN RETURN[module];
should the module follow this one?
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.STREAMIO.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
heuristics to see if we are at the end of the entry vector
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;
determine the entry and exit PCs.
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;
*********************************************
determining the precedence relationships
*********************************************
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[];
set the root containers
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;
chase down the trees
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;
catch all of the ones we missed
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
determine the intersection of the procedures that references proc
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;
*********************************************
some printing
*********************************************
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;
*********************************************
debugging facilities
*********************************************
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..