SpyCommandsImpl.mesa
Copyright Ó 1990, 1991 by Xerox Corporation. All rights reserved.
Michael Plass, December 13, 1991 4:12 pm PST
~
BEGIN
StartCommand: Commander.CommandProc ~ {
nodeCount: NAT ¬ 10000;
watchThreadSwitches: BOOL ¬ TRUE;
watchAllocations: Spy.WatchAllocationType ¬ $none;
watchSignals: BOOL ¬ FALSE;
DO
arg: ROPE ~ CommanderOps.NextArgument[cmd];
IF arg =
NIL
THEN
EXIT
ELSE {
IF Rope.Match["-*", arg]
THEN {
sense: BOOL ¬ TRUE;
FOR i:
INT
IN [1..Rope.Size[arg])
DO
SELECT Rope.Fetch[arg, i]
FROM
'n, 'N => {
nodeCount ¬ Convert.CardFromRope[CommanderOps.NextArgument[cmd] ! Convert.Error => GOTO BadArgs];
};
'a, 'A => {
watchThreadSwitches ¬ FALSE;
watchAllocations ¬ $count;
};
'w, 'W => {
watchThreadSwitches ¬ FALSE;
watchAllocations ¬ $words;
};
's, 'S => {
watchThreadSwitches ¬ FALSE;
watchSignals ¬ TRUE;
};
't, 'T => { watchThreadSwitches ¬ sense; sense ¬ TRUE };
'~ => sense ¬ NOT sense;
ENDCASE => GOTO BadArgs;
ENDLOOP;
}
ELSE GOTO BadArgs;
};
ENDLOOP;
IF NOT Spy.Start[watchThreadSwitches: watchThreadSwitches, watchAllocations: watchAllocations, watchSignals: watchSignals, count: nodeCount] THEN CommanderOps.Failed["Spy already running!"];
EXITS BadArgs => { CommanderOps.Failed[cmd.procData.doc] };
};
StopCommand: Commander.CommandProc ~ {
spy: Spy.Ref ¬ Spy.Stop[];
stream: IO.STREAM ¬ cmd.out;
arg: ROPE ~ CommanderOps.NextArgument[cmd];
IF spy = NIL THEN CommanderOps.Failed["Spy not running!"];
IF arg #
NIL
THEN {
ENABLE
PFS.Error => {
IO.PutF1[cmd.err, "PFS.Error[%g] - writing to standard out instead\n", [rope[error.explanation]]];
GOTO Recovery;
};
stream ¬ PFS.StreamOpen[PFS.PathFromRope[arg], $create];
EXITS Recovery => {};
};
Spy.WriteTree[stream, spy ! UNWIND => {IF stream # cmd.out THEN IO.Close[stream]}];
IO.PutChar[stream, '\n];
IF stream # cmd.out THEN IO.Close[stream];
};
ReadSpyCounts:
PROC [table: CardTab.Ref, stream:
IO.
STREAM] ~ {
buffer: REF TEXT ¬ NEW[TEXT[80]];
tokenKind: IO.TokenKind;
charsSkipped: INT;
error: IO.TokenError;
state: {init, count, name, pc, offset, post} ¬ init;
nest: INT ¬ 0;
count, pc, offset: CARD ¬ 0;
DO
[tokenKind, buffer, charsSkipped, error] ¬ IO.GetCedarToken[stream: stream, buffer: buffer, flushComments: FALSE];
SELECT tokenKind
FROM
tokenSINGLE => {
SELECT buffer[0]
FROM
'; => { [] ¬ IO.GetLine[stream, buffer]; LOOP };
'( => {
IF NOT (state=init OR state=post) THEN GOTO ParseFailed;
state ¬ count;
nest ¬ nest + 1;
};
') => {
IF NOT (state=post) THEN GOTO ParseFailed;
nest ¬ nest - 1;
IF nest < 0 THEN GOTO ParseFailed;
};
ENDCASE => GOTO ParseFailed;
};
tokenDECIMAL => {
value: CARD ¬ Convert.CardFromRope[RefText.TrustTextAsRope[buffer]];
SELECT state
FROM
count => count ¬ value;
pc => pc ¬ value;
offset => offset ¬ value;
ENDCASE => GOTO ParseFailed;
state ¬ state.SUCC;
IF state = post
THEN {
Tally: CardTab.UpdateAction ~ {
PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation ← none, new: Val ← NIL]
c: REF CARD ¬ IF found THEN NARROW[val] ELSE NEW[CARD ¬ 0];
c ¬ c + count;
IF NOT found THEN RETURN [store, c]
};
CardTab.Update[table, pc+offset, Tally];
};
};
tokenID => {
IF state # name THEN GOTO ParseFailed;
state ¬ state.SUCC;
};
tokenROPE => {
IF state # name THEN GOTO ParseFailed;
state ¬ state.SUCC;
};
ENDCASE => GOTO ParseFailed;
ENDLOOP;
IF nest # 0 THEN GOTO ParseFailed;
EXITS ParseFailed => {
CommanderOps.Failed[IO.PutFR1["Parse error near %g", [integer[IO.GetIndex[stream]]]]];
};
};
PCFilterCommand: Commander.CommandProc ~ {
ENABLE PFS.Error => CommanderOps.Failed[error.explanation];
table: CardTab.Ref ~ CardTab.Create[];
nlogs: INT ¬ 0;
FOR arg:
ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd]
UNTIL arg =
NIL
DO
stream: IO.STREAM ~ PFS.StreamOpen[PFS.PathFromRope[arg]];
ReadSpyCounts[table, stream];
IO.Close[stream];
nlogs ¬ nlogs + 1;
ENDLOOP;
IF nlogs = 0
THEN CommanderOps.Failed[cmd.procData.doc]
ELSE {
buf: REF TEXT ¬ NEW[TEXT[100]];
IO.PutF1[cmd.out, "%L", [rope["f"]]];
UNTIL
IO.EndOf[cmd.in]
DO
c: REF CARD ¬ NIL;
buf ¬ IO.GetLine[cmd.in, buf];
IF buf.length > 8
AND buf[8] = ':
THEN {
pc: CARD ¬ 0;
save: NAT ¬ buf.length;
buf.length ¬ 8;
pc ¬ Convert.CardFromRope[RefText.TrustTextAsRope[buf], 16];
buf.length ¬ save;
c ¬ NARROW[CardTab.Fetch[table, pc].val];
};
IF c #
NIL
THEN IO.PutF1[cmd.out, "%7g ", [cardinal[c]]]
ELSE IO.PutRope[cmd.out, " "];
buf ¬ RefText.AppendChar[buf, '\n];
IO.PutBlock[cmd.out, buf];
ENDLOOP;
IO.PutF1[cmd.out, "%L", [rope["F"]]];
};
};
Drop1:
PROC [list:
LIST
OF
ROPE]
RETURNS [
LIST
OF
ROPE] ~ {
RETURN [
SELECT
TRUE
FROM
list = NIL => NIL,
list.rest = NIL => NIL,
ENDCASE => CONS[list.first, Drop1[list.rest]]]
};
SpyLogFromDU:
PROC [in:
IO.
STREAM, out:
IO.
STREAM] ~ {
DULine: TYPE ~ RECORD [count: NAT, fname: LIST OF ROPE];
ReadDu:
PROC
RETURNS [du:
LIST
OF DULine ¬
NIL] ~ {
SlashBreak:
IO.BreakProc ~ {
RETURN [SELECT char FROM '\l => break, '/ => sepr, ENDCASE => other]
};
DO {
ENABLE IO.EndOfStream => EXIT;
number: INT ~ IO.GetInt[in ! Convert.Error => {[] ¬ IO.GetLineRope[in]; LOOP}];
fname: LIST OF ROPE ¬ NIL;
[] ¬ IO.SkipWhitespace[stream: in, flushComments: FALSE];
BEGIN
ENABLE
IO.EndOfStream =>
CONTINUE;
DO
rope: ROPE ~ IO.GetTokenRope[in, SlashBreak].token;
IF Rope.Equal[rope, "\l"] THEN EXIT;
fname ¬ CONS[Atom.GetPName[Atom.MakeAtom[rope]], fname];
ENDLOOP;
END;
du ¬ CONS[[number, RopeList.DReverse[fname]], du];
} ENDLOOP;
};
DuMatching:
PROC [d:
LIST
OF DULine, match:
LIST
OF
ROPE]
RETURNS [
LIST
OF DULine] ~ {
WHILE d #
NIL
DO
m: LIST OF ROPE ¬ match;
f: LIST OF ROPE ¬ d.first.fname;
UNTIL m =
NIL
OR f =
NIL
OR m.first # f.first
DO
m ¬ m.rest;
f ¬ f.rest;
ENDLOOP;
IF m # NIL OR f = NIL THEN RETURN [d];
IO.PutF1[out, "(%g ", [cardinal[d.first.count]]];
IO.PutRope[out, Convert.RopeFromRope[f.first]];
IO.PutF1[out, " %g 0 ", [cardinal[LOOPHOLE[f.first]]]];
d ¬ DuMatching[d.rest, RopeList.Append[match, LIST[f.first]]];
IO.PutRope[out, ")"];
ENDLOOP;
RETURN [d]
};
du: LIST OF DULine ¬ ReadDu[];
[] ¬ DuMatching[du, IF du = NIL THEN NIL ELSE Drop1[du.first.fname]];
};
DUFilterCommand: Commander.CommandProc ~ {
ENABLE PFS.Error => CommanderOps.Failed[error.explanation];
nlogs: INT ¬ 0;
FOR arg:
ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd]
UNTIL arg =
NIL
DO
in: IO.STREAM ~ IF Rope.Equal[arg, "-"] THEN cmd.in ELSE PFS.StreamOpen[PFS.PathFromRope[arg]];
out: IO.STREAM ~ IF Rope.Equal[arg, "-"] THEN cmd.out ELSE PFS.StreamOpen[PFS.PathFromRope[Rope.Concat[arg, ".spy"]], $create];
SpyLogFromDU[in: in, out: out];
IF in # cmd.in THEN { IO.Close[in]; IO.Close[out] };
nlogs ¬ nlogs + 1;
ENDLOOP;
IF nlogs = 0 THEN CommanderOps.Failed[cmd.procData.doc];
};
Commander.Register["SpyStart", StartCommand, "Start the spy. Arguments:\n-a => watch allocations\n-w => watch words allocated\n-t => watch thread switches (default)\n-s => watch SIGNALs and ERRORs\n-n <n> => preallocate n spy tree nodes (default 10000)"];
Commander.Register["SpyStop", StopCommand, "Stop the spy, write results to specified file (or to standard out, if no argument is provided)."];
Commander.Register["SpyPCFilter", PCFilterCommand, "Prepend counts onto disassembled code.\n args: spy log name(s)"];
Commander.Register["SpyDUFilter", DUFilterCommand, "Convert output of du command into a spy log"];