<> <> 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.STREAM _ NIL; typescriptBox: ViewerClasses.Viewer _ NIL; spyViewer: Containers.Container _ NIL; iconFlavor: Icons.IconFlavor; iconBitmap: GraphicsOps.BitmapRef; <<**************************************************>> <> <<**************************************************>> 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; <> 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]; <> 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]; <> Graphics.SetColor[context, Graphics.white]; Graphics.DrawArea[context, CreateCircle[cx1, cy1, r1]]; Graphics.SetColor[context, Graphics.black]; <> [] _ Graphics.SetPaintMode[context, invert]; Graphics.DrawArea[context, CreateCircle[cx1, cy1, r1]]; Graphics.DrawArea[context, CreateCircle[cx2, cy2, r2]]; <> 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]; <> 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: 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; <<>> <> 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]; <> 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]]; <> 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[]; <> 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]]; <> 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; <<**************************************************>> <> <<************************************************** >> break: BOOLEAN _ FALSE; spyOn: BOOLEAN _ FALSE; 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: ROPE _ NIL; 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; <<>> <<**************************************************>> <> <<**************************************************>> 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; <<**************************************************>> <> <<**************************************************>> 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; <<**************************************************>> <> <<**************************************************>> setStartBreakButton: Buttons.Button _ NIL; setStopBreakButton: Buttons.Button _ NIL; clearBreaksButton: Buttons.Button _ NIL; <> <> <> <> <> 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: BOOLEAN _ FALSE; 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: BOOLEAN _ FALSE; 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: BOOLEAN _ FALSE; 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: BOOLEAN _ FALSE] RETURNS[loc: BBObjectLocation.Location _ NIL, msg: Rope.ROPE _ NIL] = 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.TV _ NIL; viewer: ViewerClasses.Viewer; module, proc: ROPE _ NIL; <> <> 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]}; <> 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]]};