SpyViewerImpl.mesa
Edited by Maxwell, April 28, 1983 1:04 pm
DIRECTORY
AMTypes USING [TV],
BBContext USING [GlobalFrameSearch],
BBObjectLocation USING [EntryLocation, ExitLocation, Location, TVToLocation],
BBVOps USING [LocationFromSelection, SourceError],
Buttons USING [Button, ButtonProc, Create, ReLabel],
Commander USING [CommandProc, Register],
Containers USING [ChildXBound, ChildYBound, Container],
Convert USING [Parse, Value],
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, GetOutputStreamRope, int, Put, rope, ROS, STREAM],
Labels USING [Create, Label, Set],
LongString USING [AppendString],
PSB USING [PsbIndex, PsbNull],
Rope USING [Equal, Flatten, Index, Length, ROPE, Text],
Runtime USING [GetBcdTime, IsBound],
SpyClient USING [
ClearBreaks, ClearUserBreaks, DataType, DisplayData, InitializeSpy, SetStartBreak, SetStopBreak, SetUserBreak, StartSpy, StopSpy],
SpyLog USING [spy],
Time USING [Append, Unpack],
TypeScript USING [Create],
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, GetSelectionContents, MakeNewTextViewer, SelPos, SetSelection];
SpyViewerImpl: MONITOR
IMPORTS
BBContext, BBObjectLocation, BBVOps, Buttons, Commander, Containers, Convert, Graphics, GraphicsOps, Icons, IO, Labels, LongString, Rope, Runtime, SpyClient, SpyLog, Time, TypeScript, VFonts, ViewerIO, ViewerOps, ViewerTools
SHARES IO =
BEGIN
OPEN IO;
ROPE: TYPE = Rope.ROPE;
TV: TYPE = AMTypes.TV;
-- Global Variables --
typescript: IO.STREAMNIL;
typescriptBox: ViewerClasses.Viewer ← NIL;
spyViewer: Containers.Container ← NIL;
iconFlavor: Icons.IconFlavor;
iconBitmap: GraphicsOps.BitmapRef;
**************************************************
class and icon initialization
**************************************************
CreateSpyClass: PROCEDURE =
BEGIN
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."];
END;
ContainerInit: ViewerClasses.InitProc;
PaintSpyIcon: Icons.DrawIconProc =
CHECKED BEGIN
Graphics.SetCP[context, 0, 64];
GraphicsOps.DrawBitmap[context, iconBitmap, 64, 64];
END;
PaintMagnifier: PROCEDURE[context: Graphics.Context, name: Rope.ROPE] =
BEGIN
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];
END;
CreateCircle: PROCEDURE[x, y, r: REAL] RETURNS[path: Graphics.Path] =
BEGIN
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];
END;
**************************************************
spy viewer creation
**************************************************
Spy: Commander.CommandProc = TRUSTED BEGIN
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];
END;
CreateSpyViewer: ViewerClasses.InitProc =
TRUSTED BEGIN
x:INTEGER = 10; -- upper left corner of commands
y:INTEGER = 10;
sp:INTEGER = 6;
line:INTEGER = 20; -- height of each line
title: STRING = "Spy of "L;
herald: STRING ← [40];
IF spyViewer # NIL AND ~spyViewer.destroyed THEN RETURN;
LongString.AppendString[to: herald, from: title];
Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
herald.length ← herald.length - 3;
typescript
ContainerInit[self];
typescriptBox ← TypeScript.Create[
info: [wx: 0, wy: y + 4*line,
ww: 70*sp+x-5, wh: 40*line,
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
setUserBreakButton ← Buttons.Create[
proc: SetUserBreak,
documentation: "Spy will count the given breaks as they are encountered.",
info: [name: "SetUserBreak!",
wx: x, wy: y + 2*line,
parent: self]];
clearUserBreaksButton ← Buttons.Create[
documentation: "Clears the user breaks given.",
proc: ClearUserBreaks,
info: [name: "ClearUserBreaks!",
wx: x + 15*sp, wy: y + 2*line,
parent: self]];
fourth 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 + 3*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 + 3*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 + 3*line, -- 47
parent: self]];
END;
**************************************************
turning the spy off and on
**************************************************
break: BOOLEANFALSE;
spyOn: BOOLEANFALSE;
displayButton: Buttons.Button ← NIL;
spyOnButton: Buttons.Button ← NIL;
tablesLabel: Labels.Label ← NIL;
bracketLabel: Labels.Label ← NIL;
SetSpyButton: PROCEDURE =
BEGIN
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}"]};
END;
ToggleSpy: Buttons.ButtonProc =
TRUSTED BEGIN
SELECT spyOn FROM
TRUE => StopSpy[];
FALSE => StartSpy[];
ENDCASE => ERROR;
END;
StopSpy: PROCEDURE =
BEGIN
spyOn ← FALSE;
SpyClient.StopSpy[];
SetSpyButton[];
END;
StartSpy: PROCEDURE =
BEGIN
error: ROPENIL;
process: PSB.PsbIndex ← PSB.PsbNull;
Labels.Set[tablesLabel,"initializing"];
IF dataType = breakProcess AND ~break THEN
error ← "Watching break process but no breaks set!";
IF dataType = process THEN {
process ← ReadOctal[ViewerTools.GetContents[processBox]];
IF process = PSB.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[];
END;
ReadOctal: PROCEDURE[r: ROPE] RETURNS[CARDINAL] =
BEGIN
value: Convert.Value;
value ← Convert.Parse[[rope[r]], [unsigned[0,8]]].value;
WITH v: value SELECT FROM
unsigned => RETURN[v.unsigned];
signed => IF v.signed < 0 THEN RETURN[0] ELSE RETURN[v.signed];
ENDCASE => RETURN[0];
END;
**************************************************
determine the data type
**************************************************
dataType: SpyClient.DataType ← CPU;
dataTypeButton: Buttons.Button ← NIL;
processBox: ViewerClasses.Viewer ← NIL;
processBoxX, processBoxY, processBoxW, processBoxH: CARDINAL;
SetTypeButton: PROCEDURE =
BEGIN
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}",
spaces => "Watching: {spaces}",
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, " "]};
END;
ToggleType: Buttons.ButtonProc =
TRUSTED BEGIN
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[];
END;
**************************************************
displaying data
**************************************************
cutoff: CARDINAL ← 3;
cutoffButton: Buttons.Button;
ToggleCutoff: Buttons.ButtonProc =
TRUSTED BEGIN
SELECT mouseButton FROM
blue => IF cutoff > 0 THEN cutoff ← cutoff - 1;
yellow => cutoff ← 3;
red => IF cutoff < 100 THEN cutoff ← cutoff + 1;
ENDCASE;
SetCutoffButton[];
END;
SetCutoffButton: PROC =
BEGIN
stream: IO.STREAM;
stream ← IO.ROS[];
stream.Put[rope["cutoff: {"], int[cutoff], rope["}"]];
Buttons.ReLabel[cutoffButton, IO.GetOutputStreamRope[stream]];
stream.Close[];
END;
DisplayData: Buttons.ButtonProc =
TRUSTED BEGIN
OPEN BBObjectLocation;
IF spyOn THEN StopSpy[];
IF control THEN SpyLog.spy ← TRUE;
SpyClient.DisplayData[cutoff, NIL, NIL];
IF control THEN SpyClient.DisplayData[cutoff, NIL, NIL];
END;
**************************************************
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 BEGIN
SpyClient.ClearBreaks[];
typescript.Put[char[CR], rope["Start and Stop Breaks cleared."], char[CR]];
break ← FALSE;
StopSpy[];
END;
SetStartBreak: Buttons.ButtonProc =
TRUSTED BEGIN
msg: ROPE;
ok: BOOLEANFALSE;
loc: BBObjectLocation.Location;
[loc, msg] ← LocationFromSelection[entry: TRUE];
IF loc # NIL THEN [ok, msg] ← SpyClient.SetStartBreak[,loc];
typescript.Put[char[CR], rope[msg], char[CR]];
IF ~ok THEN RETURN;
break ← TRUE;
StopSpy[];
END;
SetStopBreak: Buttons.ButtonProc =
TRUSTED BEGIN
msg: ROPE;
ok: BOOLEANFALSE;
loc: BBObjectLocation.Location;
[loc, msg] ← LocationFromSelection[exit: TRUE];
IF loc # NIL THEN [ok, msg] ← SpyClient.SetStopBreak[,loc];
typescript.Put[char[CR], rope[msg], char[CR]];
IF ~ok THEN RETURN;
break ← TRUE;
StopSpy[];
END;
**************************************************
setting and clearing user breaks
**************************************************
setUserBreakButton: Buttons.Button ← NIL;
clearUserBreaksButton: Buttons.Button ← NIL;
ClearUserBreaks: Buttons.ButtonProc =
TRUSTED BEGIN
SpyClient.ClearUserBreaks[];
typescript.Put[char[CR], rope["User Breaks cleared."], char[CR]];
IF dataType = userDefined THEN StopSpy[];
END;
SetUserBreak: Buttons.ButtonProc =
TRUSTED BEGIN
msg: ROPE;
ok: BOOLEANFALSE;
loc: BBObjectLocation.Location;
[loc, msg] ← LocationFromSelection[];
IF loc # NIL THEN [ok, msg] ← SpyClient.SetUserBreak[,loc];
typescript.Put[char[CR], rope[msg], char[CR]];
IF ~ok THEN RETURN;
StopSpy[];
IF dataType = userDefined THEN RETURN;
dataType ← userDefined;
SetTypeButton[];
END;
LocationFromSelection: PROC[entry, exit: BOOLEANFALSE]
RETURNS[loc: BBObjectLocation.Location ← NIL, msg: Rope.ROPENIL] = BEGIN
selection: Rope.ROPE ← ViewerTools.GetSelectionContents[];
IF Runtime.IsBound[BBVOps.LocationFromSelection] THEN {
loc ← BBVOps.LocationFromSelection[NIL, entry, exit ! BBVOps.SourceError => CONTINUE];
IF loc = NIL THEN msg ← "couldn't get a location out of the selection."};
IF loc = NIL THEN {
tv: AMTypes.TV;
module, proc: Rope.ROPE;
[module, proc] ← Split[selection];
IF proc # NIL THEN tv ← BBContext.GlobalFrameSearch[NIL, module, proc].tv;
IF tv # NIL THEN loc ← BBObjectLocation.TVToLocation[tv];
IF loc # NIL AND entry THEN loc ← BBObjectLocation.EntryLocation[loc];
IF loc # NIL AND exit THEN loc ← BBObjectLocation.ExitLocation[loc]};
IF loc = NIL AND msg = NIL THEN msg ← "Please load BugBane.bcd.";
END;
Split: PROC [name: ROPE] RETURNS [Rope.Text, Rope.Text] = INLINE {
length: INT ← name.Length[];
pos: INT ← name.Index[0, "."];
IF pos = length THEN RETURN[name.Flatten[], NIL];
RETURN[name.Flatten[0, pos], name.Flatten[pos + 1, length]]};
CreateSpyClass[];
[] ← Spy[NIL];
END.
**************************************************
utility procedures
**************************************************
LocationFromSelection: PROC RETURNS [loc: BBObjectLocation.Location] = {
{-- stolen from Russ's BBVExec (who should export this?)
OPEN BBObjectLocation;
ENABLE {UNWIND => NULL; ANY => GO TO oops};
start: LONG INTEGER;
selPos: ViewerTools.SelPos;
gf, tv: AMTypes.TVNIL;
viewer: ViewerClasses.Viewer;
module, proc: ROPENIL;
foundOne: SAFE PROC [lgf, ltv: TV, actualName: ROPE] RETURNS [BOOLEAN] =
CHECKED {gf ← lgf; tv ← ltv; module ← actualName; RETURN [TRUE]};
viewer ← ViewerTools.GetSelectedViewer[];
proc ← ViewerTools.GetSelectionContents[];
selPos ← ViewerTools.GetSelection[viewer];
IF viewer = NIL THEN GO TO oops;
start ← selPos.start;
[module,] ← Split[viewer.name];
[gf, tv] ← BBContext.GlobalFrameSearch[NIL, module, NIL];
IF gf # NIL THEN {-- we found such a frame!
loc: Location ← SourceToLocation[gf, Inline.LowHalf[start]];
proc: TV ← TVFromLocation[loc];
RETURN[loc]};
maybe its a definition file?
module ← Rope.Cat[module, "Impl"];
[gf, tv] ← BBContext.GlobalFrameSearch[NIL, module, proc];
IF tv = NIL THEN { -- perhaps the selection is of the form Module.Proc?
[module, proc] ← Split[proc];
IF proc # NIL THEN [gf, tv] ← BBContext.GlobalFrameSearch[NIL, module, proc]};
IF tv # NIL THEN RETURN[TVToLocation[tv]];
EXITS oops => {}};
RETURN [NIL]};
Split: PROC [name: ROPE] RETURNS [Rope.Text, Rope.Text] = {
length: INT ← name.Length[];
pos: INT ← name.Index[0, "."];
IF pos = length THEN RETURN[name.Flatten[], NIL];
RETURN[name.Flatten[0, pos], name.Flatten[pos + 1, length]]};