SpyViewerImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Russ Atkinson, July 16, 1984 3:14:39 pm PDT
Maxwell, December 14, 1983 3:28 pm
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],
Convert USING [CardFromRope],
FS USING [Error, ExpandName],
Graphics
USING [
black, Box, ClipArea, Context, CurveTo, DrawArea, DrawBox, DrawRope, DrawStroke, FontRef, GetBounds, MoveTo, NewPath, Path, Rotate, SetColor, SetCP, SetPaintMode, Translate, white],
GraphicsBasic USING [ClipperRef],
GraphicsOps
USING [BitmapRef, DrawBitmap, GetClipper,
NewBitmap, NewContextFromBitmap, SetClipper],
Icons USING [DrawIconProc, IconFlavor, IconRef, IconRep, NewIcon],
IO USING [char, Close, CR, int, Put, rope, 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],
VFonts USING [EstablishFont, Font, GraphicsFont],
ViewerClasses USING [InitProc, Viewer, ViewerClass, ViewerClassRec],
ViewerIO USING [CreateViewerStreams],
ViewerOps USING [ChangeColumn, CreateViewer, DestroyViewer, FetchViewerClass, OpenIcon, PaintViewer, RegisterViewerClass],
ViewerTools USING [GetContents, GetSelectedViewer, GetSelectionContents, MakeNewTextViewer, SelPos, SetSelection];
SpyViewerImpl:
MONITOR
IMPORTS
AMTypes, AMViewerOps, Buttons, Commander, Containers, Convert, FS, Graphics, GraphicsOps, Icons, IO, Labels, Rope, Rules, SpyClient, VFonts, ViewerIO, ViewerOps, ViewerTools
SHARES IO = {OPEN IO;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
TV: TYPE = AMTypes.TV;
Global Variables
typescript: IO.STREAM ← NIL;
typescriptBox: ViewerClasses.Viewer ← NIL;
spyViewer: Containers.Container ← NIL;
iconFlavor: Icons.IconFlavor;
iconBitmap: GraphicsOps.BitmapRef;
**************************************************
class and icon initialization
**************************************************
CreateSpyClass:
PROC = {
icon: Icons.IconRef;
context: Graphics.Context;
spyClass: ViewerClasses.ViewerClass;
iconBitmap ← GraphicsOps.NewBitmap[64, 64];
context ← GraphicsOps.NewContextFromBitmap[iconBitmap];
PaintMagnifier[context, "Spy"];
icon ← NEW[Icons.IconRep ← [bits: ALL[0], proc: PaintSpyIcon]];
iconFlavor ← Icons.NewIcon[icon];
spyClass ← NEW[ViewerClasses.ViewerClassRec ← []];
spyClass^ ← ViewerOps.FetchViewerClass[$Container]^;
ContainerInit ← spyClass.init;
spyClass.init ← CreateSpyViewer;
spyClass.icon ← iconFlavor;
ViewerOps.RegisterViewerClass[$Spy, spyClass];
Commander.Register["Spy", Spy, "performance measurement tool."];
};
ContainerInit: ViewerClasses.InitProc ←
NIL;
PaintSpyIcon: Icons.DrawIconProc =
CHECKED {
Graphics.SetCP[context, 0, 64];
GraphicsOps.DrawBitmap[context, iconBitmap, 64, 64];
};
PaintMagnifier:
PROC[context: Graphics.Context, name:
ROPE] = {
box: Graphics.Box;
vfont: VFonts.Font;
clipper: GraphicsBasic.ClipperRef;
cx1, cy1, r1, cx2, cy2, r2: REAL;
timesroman10B, timesroman18B: Graphics.FontRef;
cx1 ← 28; cy1 ← 40; r1 ← 20;
cx2 ← 24; cy2 ← 37; r2 ← 20;
erase old
box ← Graphics.GetBounds[context];
Graphics.DrawBox[context, box];
box.xmin ← box.xmin+1; box.ymin ← box.ymin+1;
box.xmax ← box.xmax-1; box.ymax ← box.ymax-1;
Graphics.SetColor[context, Graphics.white];
Graphics.DrawBox[context, box];
write in name
vfont ← VFonts.EstablishFont["TimesRoman", 10, TRUE];
timesroman10B ← VFonts.GraphicsFont[vfont];
Graphics.SetCP[context, cx1+5, cy1-2];
Graphics.SetColor[context, Graphics.black];
Graphics.DrawRope[context, name,,,timesroman10B];
overwrite magnifier
Graphics.SetColor[context, Graphics.white];
Graphics.DrawArea[context, CreateCircle[cx1, cy1, r1]];
Graphics.SetColor[context, Graphics.black];
invert for magnifier
[] ← Graphics.SetPaintMode[context, invert];
Graphics.DrawArea[context, CreateCircle[cx1, cy1, r1]];
Graphics.DrawArea[context, CreateCircle[cx2, cy2, r2]];
write in NAME
vfont ← VFonts.EstablishFont["TimesRoman", 18, TRUE];
timesroman18B ← VFonts.GraphicsFont[vfont];
clipper ← GraphicsOps.GetClipper[context];
[] ← Graphics.SetPaintMode[context, opaque];
Graphics.DrawStroke[context, CreateCircle[cx1, cy1, r1], 0, TRUE];
Graphics.ClipArea[context, CreateCircle[cx2, cy2, r2]];
Graphics.SetCP[context, cx1-3, cy1-4];
Graphics.DrawRope[context, name,,,timesroman18B];
GraphicsOps.SetClipper[context, clipper];
draw in handle
Graphics.Translate[context, cx1, cy1]; -- center
Graphics.Rotate[context, 30];
box ← [-3, -r1-20, 3, -r1];
Graphics.DrawBox[context, box];
};
CreateCircle:
PROC[x, y, r:
REAL]
RETURNS[path: Graphics.Path] = {
h: REAL ← 4*r/3;
path ← Graphics.NewPath[];
Graphics.MoveTo[path, x-r, y];
Graphics.CurveTo[path, x-r, y+h, x+r, y+h, x+r, y];
Graphics.CurveTo[path, x+r, y-h, x-r, y-h, x-r, y];
};
**************************************************
spy viewer creation
**************************************************
Spy: Commander.CommandProc =
TRUSTED {
IF spyViewer =
NIL
OR spyViewer.destroyed
THEN spyViewer ← ViewerOps.CreateViewer[flavor: $Spy,
info: [name: "Spy", scrollable: FALSE, iconic: TRUE]]
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
ContainerInit[self];
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]];
SetTypeButton[];
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]];
cutoffButton ← Buttons.Create[
proc: ToggleCutoff,
documentation: "Specifies percentage cutoff for printing.",
info: [name: "cutoff: {100}",
wx: x + 15*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]];
};
**************************************************
turning the spy off and on
**************************************************
break: BOOLEAN ← FALSE;
spyOn: BOOLEAN ← FALSE;
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 typescript.Put[char[CR], rope["Start breaks enabled."], char[CR]];
Buttons.ReLabel[spyOnButton, "Break: {on}"]};
break
AND ~spyOn => {
IF ~Rope.Equal[spyOnButton.name, "Break: {off}"]
THEN typescript.Put[char[CR], rope["Start breaks disabled."], char[CR]];
Buttons.ReLabel[spyOnButton, "Break: {off}"]};
~break
AND spyOn => {
IF ~Rope.Equal[spyOnButton.name, "Spy: {on}"]
THEN typescript.Put[char[CR], rope["Spy started."], char[CR]];
Buttons.ReLabel[spyOnButton, "Spy: {on}"]};
ENDCASE => {
IF ~Rope.Equal[spyOnButton.name, "Spy: {off}"]
THEN typescript.Put[char[CR], rope["Spy stopped."], char[CR]];
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: ROPE ← NIL;
process: PrincOps.PsbIndex ← PrincOps.PsbNull;
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."};
IF error =
NIL
THEN error ← SpyClient.InitializeSpy[
dataType: dataType,
process: process];
Labels.Set[tablesLabel, NIL];
IF error # NIL THEN {typescript.Put[char[CR],rope[error],char[CR]]; RETURN};
spyOn ← TRUE;
SetSpyButton[];
SpyClient.StartSpy[];
};
**************************************************
determine the data type
**************************************************
dataType: SpyClient.DataType ← CPU;
dataTypeButton: Buttons.Button ← NIL;
processBox: ViewerClasses.Viewer ← NIL;
processBoxX, processBoxY, processBoxW, processBoxH: CARDINAL;
SetTypeButton:
PROC = {
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];
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]];
Labels.Set[bracketLabel, "}"];
ViewerTools.SetSelection[processBox]};
IF dataType # process
AND processBox #
NIL
AND ~processBox.destroyed
THEN {
ViewerOps.DestroyViewer[processBox];
Labels.Set[bracketLabel, " "]};
};
ToggleType: Buttons.ButtonProc =
TRUSTED {
dataType ←
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;
SetTypeButton[];
};
**************************************************
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: {"], int[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[];
typescript.Put[char[CR], rope["Start and Stop Breaks cleared."], char[CR]];
break ← FALSE;
StopSpy[];
};
SetStartBreak: Buttons.ButtonProc =
TRUSTED {
msg, name: ROPE;
ok: BOOLEAN ← FALSE;
section: AMModel.Section;
[section, name, msg] ← LocationFromSelection[];
IF section # NIL OR name # NIL THEN [ok, msg] ← SpyClient.SetStartBreak[section, name];
typescript.Put[char[CR], rope[msg], char[CR]];
IF ~ok THEN RETURN;
break ← TRUE;
dataType ← breakProcess;
SetTypeButton[];
StopSpy[];
};
SetStopBreak: Buttons.ButtonProc =
TRUSTED {
msg, name: ROPE;
ok: BOOLEAN ← FALSE;
section: AMModel.Section;
[section, name, msg] ← LocationFromSelection[];
IF section # NIL OR name # NIL THEN [ok, msg] ← SpyClient.SetStopBreak[section, name];
typescript.Put[char[CR], rope[msg], char[CR]];
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[];
typescript.Put[char[CR], rope["User Breaks cleared."], char[CR]];
IF dataType = userDefined THEN StopSpy[];
};
SetUserBreak: Buttons.ButtonProc =
TRUSTED {
msg, name: ROPE;
ok: BOOLEAN ← FALSE;
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, 1, name];
typescript.Put[char[CR], rope[msg], char[CR]];
IF ~ok THEN RETURN;
StopSpy[];
IF dataType = userDefined THEN RETURN;
dataType ← userDefined;
SetTypeButton[];
};
LocationFromSelection:
PROC
RETURNS[section: AMModel.Section ← NIL, name, error: ROPE ← NIL] = {
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];
};
};
};
CreateSpyClass[];
[] ← Spy[NIL];
}.