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 ~ { 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 => 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. ή SpyCommandsImpl.mesa Copyright Σ 1990, 1991 by Xerox Corporation. All rights reserved. Michael Plass, December 13, 1991 4:12 pm PST PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation _ none, new: Val _ NIL] Κ 3–(cedarcode) style•NewlineDelimiter ™codešœ™Kšœ Οeœ7™BK™,—K˜KšΟk œžœžœP˜aK˜KšΟnœžœž˜KšžœžœžœI˜^šœž˜K˜šžœžœžœ˜K˜—šŸ œ˜'Kšœ žœ ˜Kšœžœžœ˜!Kšœ2˜2Kšœžœžœ˜šž˜Kšœžœ"˜+š žœžœžœžœžœ˜šžœ˜šžœ˜Kšœžœžœ˜šžœžœžœž˜$šžœž˜šœ ˜ KšœSžœ ˜aKšœ˜—šœ ˜ Kšœžœ˜Kšœ˜Kšœ˜—šœ ˜ Kšœžœ˜Kšœ˜Kšœ˜—˜ Kšœžœ˜Kšœžœ˜Kšœ˜—Kšœ1žœ˜8Kšœžœ˜Kšžœžœ ˜—Kšžœ˜—Kšœ˜—Kšžœžœ ˜—Kšœ˜—Kšžœ˜—Kšžœžœ‡žœ-˜ΎKšžœ6˜;Kšœ˜K˜—šŸ œ˜&Kšœ˜Kšœžœžœ ˜Kšœžœ"˜+Kšžœžœžœ)˜:šžœžœžœ˜šžœžœ ˜Kšžœ`˜bKšžœ ˜Kšœ˜—Kšœ žœ žœ˜8Kšžœ˜Kšœ˜—Kš œžœžœžœžœ˜SKšžœ˜Kšžœžœžœ˜*Kšœ˜K˜—šŸ œžœžœžœ˜?Kš œžœžœžœžœ˜!Kšœ žœ ˜Kšœžœ˜Kšœžœ ˜K˜4Kšœžœ˜Kšœžœ˜šž˜Kšœ+žœ>žœ˜ršžœ ž˜šœ˜šžœ ž˜Kšœ žœžœ˜0šœ˜Kš žœžœ žœ žœžœ ˜8K˜Kšœ˜Kšœ˜—šœ˜Kšžœžœžœžœ ˜*Kšœ˜Kšžœ žœžœ ˜"Kšœ˜—Kšžœžœ ˜—Kšœ˜—šœ˜Kšœžœ9˜Dšžœž˜Kšœ˜Kšœ˜Kšœ˜Kšžœžœ ˜—Kšœžœ˜šžœžœ˜šŸœ˜Kšžœ žœ žœ)žœ™QKšœžœžœžœžœžœžœžœžœ˜;K˜Kšžœžœžœžœ ˜#Kšœ˜—Kšœ(˜(Kšœ˜—Kšœ˜—šœ ˜ Kšžœžœžœ ˜&Kšœžœ˜Kšœ˜—šœ˜Kšžœžœžœ ˜&Kšœžœ˜Kšœ˜—šœ ˜ Kšžœ˜Kšœ˜—Kšžœžœ ˜—Kšžœ˜—Kšžœ žœžœ ˜"šžœ˜Kšœžœ(žœ˜VKšœ˜—Kšœ˜K˜—šŸœ˜*Kšžœžœ1˜;Kšœ&˜&Kšœžœ˜š žœžœBžœžœž˜aKš œžœžœžœ žœ˜:Kšœ˜Kšžœ˜K˜Kšžœ˜—šžœ žœ'žœ˜>Kš œžœžœžœžœ˜Kšžœ#˜%šžœžœž˜Kšœžœžœžœ˜Kšœžœ˜šžœžœ žœ˜(Kšœžœ˜ Kšœžœ˜Kšœ˜Kšœ<˜Kšžœ˜Kšžœ˜—Kšžœ˜ Kšœ˜—Kšœžœžœ˜Kš œžœžœžœžœžœ˜EKšœ˜K˜—šŸœ˜*Kšžœžœ1˜;Kšœžœ˜š žœžœBžœžœž˜aKšœžœžœžœžœžœžœ žœ˜_Kšœžœžœžœžœ žœžœ žœ2˜Kšœ˜Kšžœ žœžœ žœ˜4K˜Kšžœ˜—Kšžœ žœ'˜8Kšœ˜K˜—˜†K˜—šœŽ˜ŽK˜—šœu˜uK˜—Kšœb˜b—K˜Kšžœ˜—…—€*΅