SpyViewerImpl.mesa
Copyright Ó 1984, 1986, 1987 by Xerox Corporation. All rights reserved.
Maxwell, December 14, 1983 3:28 pm
Pier, June 24, 1985 2:37:30 pm PDT
Russ Atkinson (RRA) February 19, 1987 11:12:34 am PST
Bob Hagmann July 11, 1986 8:10:35 am PDT
Mike Spreitzer January 24, 1987 3:08:54 pm PST
DIRECTORY
AMModel USING [Section],
AMTypes USING [Error, TV],
AMViewerOps USING [SectionFromSelection],
Buttons USING [Button, ButtonProc, Create, ReLabel],
Commander USING [CommandProc, Register],
Containers USING [ChildXBound, ChildYBound, Container, Create],
Convert USING [CardFromRope, Error, IntFromRope, RopeFromInt],
FS USING [Error, ExpandName],
Icons USING [IconFlavor, NewIconFromFile],
IO USING [Close, Put, PutFR, PutRope, RopeFromROS, ROS, STREAM],
Labels USING [Create, Label, Set],
PrincOps USING [PsbIndex, PsbNull],
Rope USING [Equal, Fetch, Flatten, Length, ROPE, Text],
Rules USING [Create],
SpyClient USING [ClearBreaks, ClearUserBreaks, DataType, DisplayData, InitializeSpy, SetStartBreak, SetStopBreak, SetTrace, SetUserBreak, StartSpy, StopSpy],
ViewerClasses USING [InitProc, Viewer, ViewerClass],
ViewerIO USING [CreateViewerStreams],
ViewerOps USING [ChangeColumn, CreateViewer, DestroyViewer, MoveViewer, OpenIcon, PaintViewer],
ViewerTools USING [GetContents, GetSelectedViewer, GetSelectionContents, MakeNewTextViewer, SelPos, SetContents, SetSelection];
SpyViewerImpl: MONITOR
IMPORTS AMTypes, AMViewerOps, Buttons, Commander, Containers, Convert, FS, Icons, IO, Labels, Rope, Rules, SpyClient, ViewerIO, ViewerOps, ViewerTools
SHARES IO = {
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
TV: TYPE = AMTypes.TV;
Global Variables
typescript: IO.STREAMNIL;
typescriptBox: ViewerClasses.Viewer ← NIL;
spyViewer: Containers.Container ← NIL;
icon: Icons.IconFlavor ← Icons.NewIconFromFile["Spy.icon", 0];
Spy viewer creation
Spy: Commander.CommandProc = TRUSTED {
IF spyViewer = NIL OR spyViewer.destroyed
THEN {
spyViewer ← Containers.Create[info: [name: "Spy", icon: icon, scrollable: FALSE, iconic: TRUE]];
CreateSpyViewer[spyViewer];
}
ELSE IF spyViewer.iconic
THEN ViewerOps.OpenIcon[spyViewer]
ELSE ViewerOps.PaintViewer[spyViewer, all];
IF spyViewer.offDeskTop THEN ViewerOps.ChangeColumn[spyViewer, left];
};
CreateSpyViewer: ViewerClasses.InitProc = TRUSTED {
rule: ViewerClasses.Viewer;
x: INTEGER = 10; -- upper left corner of commands
y: INTEGER = 10;
sp: INTEGER = 6;
line: INTEGER = 20; -- height of each line
typescript
rule ← Rules.Create[info: [parent: self, wx: 0, wy: y + 4*line, ww: 100, wh: 1]];
Containers.ChildXBound[self, rule];
typescriptBox ← ViewerOps.CreateViewer[flavor: $Typescript,
info: [wx: 0, wy: y + 4*line + 1,
ww: 70*sp+x-5, wh: 40*line,
border: FALSE,
parent: self]];
typescript ← ViewerIO.CreateViewerStreams["Spy.Log", typescriptBox].out;
Containers.ChildXBound[self, typescriptBox];
Containers.ChildYBound[self, typescriptBox];
first line
spyOnButton ← Buttons.Create[
proc: ToggleSpy,
documentation: "Spy records data only when this is 'on'.",
info: [name: "Spy: {off} ",
wx: x, wy: y,
border: FALSE,
parent: self]];
SetSpyButton[];
dataTypeButton ← Buttons.Create[
proc: ToggleType,
documentation: "Gives the resource to be monitored. Toggle through for list.",
info: [name: "Watching: {CPU} ",
wx: x + 15*sp, wy: y,
border: FALSE,
parent: self]];
processBoxX ← 475 - 34*sp;
processBoxY ← y + 1;
processBoxW ← 8*sp;
processBoxH ← line;
bracketLabel ← Labels.Create[
info: [name: " ", -- used to print the trailing bracket when dataType = process
wx: processBoxX + processBoxW, wy: processBoxY,
border: FALSE, parent: self]];
tablesLabel ← Labels.Create[
info: [name: " ", -- indicates when tables are being built
wx: processBoxX + processBoxW + 3*sp, wy: y,
border: FALSE, parent: self]];
second line
displayButton ← Buttons.Create[
fork: TRUE,
documentation: "Displays the current data on a separate typescript.",
proc: DisplayData,
info: [name: "DisplayData!",
wx: x, wy: y + line,
parent: self]];
divisorButtonX ← x + 15*sp;
divisorButton ← Buttons.Create[
proc: ToggleDivisor,
documentation: "Manipulates the frequency divisor",
info: [name: "Frequency Divisor:",
wx: divisorButtonX, wy: y + line,
border: FALSE,
parent: self]];
divisorBoxX ← x + 35*sp;
divisorBox ← ViewerTools.MakeNewTextViewer[
info: [wx: divisorBoxX, wy: y + line,
ww: 8*sp, wh: line,
data: "1",
border: FALSE, scrollable: FALSE,
parent: self]];
cutoffButton ← Buttons.Create[
proc: ToggleCutoff,
documentation: "Specifies percentage cutoff for printing.",
info: [name: "cutoff: {100}",
wx: x + 43*sp, wy: y + line,
border: FALSE,
parent: self]];
SetCutoffButton[];
third line
setStartBreakButton ← Buttons.Create[
proc: SetStartBreak,
documentation: "Select a source or a rope of the form ModuleImpl.Proc.",
info: [name: "SetStartBreak!",
wx: x, wy: y + 2*line,
parent: self]];
setStopBreakButton ← Buttons.Create[
proc: SetStopBreak,
documentation: "Spy will record data as long as the number of start breaks exceeds the number of stops.",
info: [name: "SetStopBreak!",
wx: x + 15*sp, wy: y + 2*line, -- 31
parent: self]];
clearBreaksButton ← Buttons.Create[
proc: ClearBreaks,
documentation: "Clears the breaks given by user; resets the mode.",
info: [name: "ClearBreaks!",
wx: x + 31*sp, wy: y + 2*line, -- 47
parent: self]];
fourth line
setUserBreakButton ← Buttons.Create[
proc: SetUserBreak,
documentation: "Spy will count the given breaks as they are encountered.",
info: [name: "SetUserBreak!",
wx: x, wy: y + 3*line,
parent: self]];
setTraceButton ← Buttons.Create[
proc: SetUserBreak,
clientData: $Trace,
documentation: "Logs a trace whenever the break is encountered.",
info: [name: "SetTrace!",
wx: x + 15*sp, wy: y + 3*line,
parent: self]];
clearUserBreaksButton ← Buttons.Create[
documentation: "Clears the user breaks given.",
proc: ClearUserBreaks,
info: [name: "ClearUserBreaks!",
wx: x + 27*sp, wy: y + 3*line,
parent: self]];
SetDataType[CPU];
};
turning the spy off and on
break: BOOLFALSE;
spyOn: BOOLFALSE;
displayButton: Buttons.Button ← NIL;
spyOnButton: Buttons.Button ← NIL;
tablesLabel: Labels.Label ← NIL;
bracketLabel: Labels.Label ← NIL;
SetSpyButton: PROC = {
SELECT TRUE FROM
break AND spyOn => {
IF ~Rope.Equal[spyOnButton.name, "Break: {on}"] THEN
IO.PutRope[typescript, "\nStart breaks enabled.\n"];
Buttons.ReLabel[spyOnButton, "Break: {on}"];
};
break AND ~spyOn => {
IF ~Rope.Equal[spyOnButton.name, "Break: {off}"] THEN
IO.PutRope[typescript, "\nStart breaks disabled.\n"];
Buttons.ReLabel[spyOnButton, "Break: {off}"];
};
~break AND spyOn => {
IF ~Rope.Equal[spyOnButton.name, "Spy: {on}"] THEN
IO.PutRope[typescript, "\nStart started.\n"];
Buttons.ReLabel[spyOnButton, "Spy: {on}"];
};
ENDCASE => {
IF ~Rope.Equal[spyOnButton.name, "Spy: {off}"] THEN
IO.PutRope[typescript, "\nStart stopped.\n"];
Buttons.ReLabel[spyOnButton, "Spy: {off}"];
};
};
ToggleSpy: Buttons.ButtonProc = TRUSTED {
SELECT spyOn FROM
TRUE => StopSpy[];
FALSE => StartSpy[];
ENDCASE => ERROR;
};
StopSpy: PROC = {
spyOn ← FALSE;
SpyClient.StopSpy[];
SetSpyButton[];
};
StartSpy: PROC = {
error: ROPENIL;
process: PrincOps.PsbIndex ← PrincOps.PsbNull;
divisor: INT ← 1;
Labels.Set[tablesLabel,"initializing"];
IF dataType = breakProcess AND ~break THEN
error ← "Watching break process but no breaks set!";
IF dataType = process THEN {
process ← Convert.CardFromRope[ViewerTools.GetContents[processBox], 8];
IF process = PrincOps.PsbNull THEN error ← "No process specified.";
};
divisor ← Convert.IntFromRope[ViewerTools.GetContents[divisorBox]
! Convert.Error => {
error ← "Malformed frequency divisor --- it should be an integer."; CONTINUE;
}];
IF error = NIL AND divisor NOT IN [1 .. LAST[NAT]] THEN
error ← IO.PutFR["Frequency divisor %g is out of range [1 .. %g].", [integer[divisor]], [integer[LAST[NAT]]]];
IF error = NIL THEN error ← SpyClient.InitializeSpy[
dataType: dataType,
process: process,
frequencyDivisor: divisor];
Labels.Set[tablesLabel, NIL];
IF error # NIL THEN {
IO.Put[typescript, [character['\n]], [rope[error]], [character['\n]]];
RETURN;
};
spyOn ← TRUE;
SetSpyButton[];
SpyClient.StartSpy[];
};
Frequency divisor
divisorButton: Buttons.Button ← NIL;
divisorButtonX: NAT;
divisorBox: ViewerClasses.Viewer ← NIL;
divisorBoxX: NAT;
ToggleDivisor: Buttons.ButtonProc = TRUSTED {
Set: PROC [divisor: INT] = {
ViewerTools.SetContents[divisorBox, Convert.RopeFromInt[divisor]];
};
SELECT mouseButton FROM
yellow => Set[1];
red, blue => {
ENABLE Convert.Error => {
IO.PutRope[typescript, "\nMalformed frequency divisor --- it should be an integer.\n"];
CONTINUE};
divisor: INT = Convert.IntFromRope[ViewerTools.GetContents[divisorBox]];
SELECT mouseButton FROM
blue => IF divisor >= 5 THEN Set[divisor/5];
yellow => ERROR;
red => IF divisor <= LAST[NAT]/5 THEN Set[divisor*5];
ENDCASE => ERROR;
};
ENDCASE => ERROR;
};
Determine the data type
dataType: SpyClient.DataType ← CPU;
dataTypeButton: Buttons.Button ← NIL;
processBox: ViewerClasses.Viewer ← NIL;
processBoxX, processBoxY, processBoxW, processBoxH: CARDINAL;
SetDataType: PROC [dt: SpyClient.DataType] = {
needPaint: BOOLFALSE;
dataType ← dt;
IF dataType = process AND (processBox = NIL OR processBox.destroyed) THEN {
processBox ← ViewerTools.MakeNewTextViewer[
info: [wx: processBoxX, wy: processBoxY,
ww: processBoxW, wh: processBoxH,
border: FALSE, scrollable: FALSE,
parent: spyViewer],
paint: FALSE];
Labels.Set[bracketLabel, "}", FALSE];
ViewerTools.SetSelection[processBox];
needPaint ← TRUE;
};
IF dataType # process AND processBox # NIL AND ~processBox.destroyed THEN {
ViewerOps.DestroyViewer[processBox, FALSE];
Labels.Set[bracketLabel, " ", FALSE];
needPaint ← TRUE;
};
SELECT dataType FROM
CPU, process, breakProcess, allocations, wordsAllocated => IF divisorButton.wx # divisorButtonX THEN {
ViewerOps.MoveViewer[divisorButton, divisorButtonX, divisorButton.wy, divisorButton.ww, divisorButton.wh, FALSE];
ViewerOps.MoveViewer[divisorBox, divisorBoxX, divisorBox.wy, divisorBox.ww, divisorBox.wh, FALSE];
needPaint ← TRUE;
};
pagefaults, userDefined => IF divisorBox.wx+divisorBox.ww >= 0 THEN {
invisibleX: INTEGER = -100 - divisorBox.ww;
ViewerOps.MoveViewer[divisorButton, invisibleX, divisorButton.wy, divisorButton.ww, divisorButton.wh, FALSE];
ViewerOps.MoveViewer[divisorBox, invisibleX, divisorBox.wy, divisorBox.ww, divisorBox.wh, FALSE];
needPaint ← TRUE;
};
ENDCASE => ERROR;
Buttons.ReLabel[dataTypeButton,
SELECT dataType FROM
CPU => "Watching: {CPU}",
process => "Watching: {process number: ",
breakProcess => "Watching: {break process}",
pagefaults => "Watching: {pagefaults}",
allocations => "Watching: {allocations}",
wordsAllocated => "Watching: {wordsAllocated}",
userDefined => "Watching: {user breaks}",
ENDCASE => ERROR,
NOT needPaint];
IF needPaint THEN ViewerOps.PaintViewer[spyViewer, client];
};
ToggleType: Buttons.ButtonProc = TRUSTED {
SetDataType[SELECT mouseButton FROM
red => IF dataType = LAST[SpyClient.DataType]
THEN FIRST[SpyClient.DataType] ELSE SUCC[dataType],
blue => IF dataType = FIRST[SpyClient.DataType]
THEN LAST[SpyClient.DataType] ELSE PRED[dataType],
ENDCASE => CPU];
};
Displaying Data
cutoff: CARDINAL ← 3;
cutoffButton: Buttons.Button;
ToggleCutoff: Buttons.ButtonProc = TRUSTED {
SELECT mouseButton FROM
blue => IF cutoff > 0 THEN cutoff ← cutoff - 1;
yellow => cutoff ← 3;
red => IF cutoff < 100 THEN cutoff ← cutoff + 1;
ENDCASE;
SetCutoffButton[];
};
SetCutoffButton: PROC = {
stream: IO.STREAM;
stream ← IO.ROS[];
stream.Put[[rope["cutoff: {"]], [integer[cutoff]], [rope["}"]]];
Buttons.ReLabel[cutoffButton, IO.RopeFromROS[stream]];
stream.Close[];
};
DisplayData: Buttons.ButtonProc = TRUSTED {
IF spyOn THEN StopSpy[];
SpyClient.DisplayData[cutoff, NIL, NIL, control];
IF control THEN SpyClient.DisplayData[cutoff, "Results of spying on Spy log."];
};
Setting and clearing start and stop breaks
setStartBreakButton: Buttons.Button ← NIL;
setStopBreakButton: Buttons.Button ← NIL;
clearBreaksButton: Buttons.Button ← NIL;
set your breaks and then turn the spy on
as long as there have been more start breaks than stop breaks
the spy will keep on counting
turning the spy off will stop the counting (although not the breaks).
hit ClearBreaks to delete breaks.
ClearBreaks: Buttons.ButtonProc = TRUSTED {
SpyClient.ClearBreaks[];
IO.PutRope[typescript, "\nStart and Stop Breaks cleared.\n"];
break ← FALSE;
StopSpy[];
};
SetStartBreak: Buttons.ButtonProc = TRUSTED {
msg, name: ROPE;
ok: BOOLFALSE;
section: AMModel.Section;
[section, name, msg] ← LocationFromSelection[];
IF section # NIL OR name # NIL THEN [ok, msg] ← SpyClient.SetStartBreak[section, name];
IO.Put[typescript, [character['\n]], [rope[msg]], [character['\n]]];
IF ~ok THEN RETURN;
break ← TRUE;
SetDataType[breakProcess];
StopSpy[];
};
SetStopBreak: Buttons.ButtonProc = TRUSTED {
msg, name: ROPE;
ok: BOOLFALSE;
section: AMModel.Section;
[section, name, msg] ← LocationFromSelection[];
IF section # NIL OR name # NIL THEN [ok, msg] ← SpyClient.SetStopBreak[section, name];
IO.Put[typescript, [character['\n]], [rope[msg]], [character['\n]]];
IF ~ok THEN RETURN;
break ← TRUE;
StopSpy[];
};
Setting and clearing user breaks
setTraceButton: Buttons.Button ← NIL;
setUserBreakButton: Buttons.Button ← NIL;
clearUserBreaksButton: Buttons.Button ← NIL;
ClearUserBreaks: Buttons.ButtonProc = TRUSTED {
SpyClient.ClearUserBreaks[];
IO.Put[typescript, [rope["\nUser Breaks cleared.\n"]]];
IF dataType = userDefined THEN StopSpy[];
};
SetUserBreak: Buttons.ButtonProc = TRUSTED {
msg, name: ROPE;
ok: BOOLFALSE;
section: AMModel.Section;
[section, name, msg] ← LocationFromSelection[];
IF section # NIL OR name # NIL THEN IF clientData = $Trace
THEN [ok, msg] ← SpyClient.SetTrace[section, name]
ELSE [ok, msg] ← SpyClient.SetUserBreak[section, userBreak, name];
IO.Put[typescript, [character['\n]], [rope[msg]], [character['\n]]];
IF ~ok THEN RETURN;
StopSpy[];
IF dataType = userDefined THEN RETURN;
SetDataType[userDefined];
};
LocationFromSelection: PROC RETURNS
[section: AMModel.Section ← NIL, name, error: ROPENIL] = {
viewer: ViewerClasses.Viewer;
viewer ← ViewerTools.GetSelectedViewer[];
IF viewer = NIL THEN RETURN [error: "no selected viewer"];
IF viewer.class.flavor # $Text OR ~Rope.Equal[Split[viewer.file].ext, "mesa"] THEN {
module, proc: ROPE;
contents: ROPE ← ViewerTools.GetSelectionContents[];
[module, proc] ← Split[contents];
IF module # NIL AND proc # NIL THEN RETURN[name: contents];
};
section ← AMViewerOps.SectionFromSelection[
! AMTypes.Error => {error ← msg; CONTINUE}].section;
};
Split: PROC [name: ROPE] RETURNS [root: Rope.Text, ext: Rope.Text ← NIL] = {
name ← FS.ExpandName[name ! FS.Error => {name ← NIL; CONTINUE}].fullFName;
IF name # NIL THEN {
len: INT = Rope.Length[name];
dotIndex: INT ← len;
bangIndex: INT ← len;
pos: INT ← len;
WHILE pos > 0 DO
SELECT Rope.Fetch[name, pos ← pos - 1] FROM
'! => bangIndex ← pos;
'. => dotIndex ← pos;
'], '>, '/ => EXIT;
ENDCASE;
ENDLOOP;
IF dotIndex >= bangIndex
THEN root ← Rope.Flatten[name]
ELSE {
root ← name.Flatten[0, dotIndex];
ext ← name.Flatten[dotIndex+1, bangIndex-dotIndex-1];
};
};
};
Commander.Register[key: "Spy", proc: Spy, doc: "spies on program performance"];
}.
Bob Hagmann July 11, 1986 8:10:35 am PDT
changes to: Spy