SpyCommandsImpl.mesa
Copyright Ó 1990, 1991 by Xerox Corporation. All rights reserved.
Michael Plass, December 13, 1991 4:12 pm PST
DIRECTORY IO, PFS, Spy, Commander, Convert, RefText, Rope, RopeList, Atom, CommanderOps, CardTab;
SpyCommandsImpl: CEDAR MONITOR
IMPORTS Rope, IO, PFS, Spy, Commander, Convert, RefText, RopeList, Atom, CommanderOps, CardTab
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
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;
};
tokenEOF => {
EXIT;
};
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"];
END.