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], 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 [Cat, Equal, Flatten, Index, 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, Graphics, GraphicsOps, Icons, IO, Labels, Rope, Rules, SpyClient, 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 rule: ViewerClasses.Viewer; x:INTEGER = 10; -- upper left corner of commands y:INTEGER = 10; sp:INTEGER = 6; line:INTEGER = 20; -- height of each line 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]; 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[]; 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]]; 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]]; 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: 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[]; 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}", 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.RopeFromROS[stream]]; stream.Close[]; END; DisplayData: Buttons.ButtonProc = TRUSTED BEGIN IF spyOn THEN StopSpy[]; SpyClient.DisplayData[cutoff, NIL, NIL, control]; IF control THEN SpyClient.DisplayData[cutoff, "Results of spying on Spy log."]; 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, 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[]; END; SetStopBreak: Buttons.ButtonProc = TRUSTED BEGIN 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[]; END; setTraceButton: Buttons.Button _ NIL; 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, 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[]; END; LocationFromSelection: PROC RETURNS[section: AMModel.Section _ NIL, name, error: Rope.ROPE _ NIL] = BEGIN viewer: ViewerClasses.Viewer; viewer _ ViewerTools.GetSelectedViewer[]; IF viewer.class.flavor # $Text OR ~Rope.Equal[Split[viewer.name].ext, "mesa"] THEN { module, proc: Rope.ROPE; contents: Rope.ROPE _ ViewerTools.GetSelectionContents[]; [module, proc] _ Split[contents]; IF module = NIL OR proc = NIL THEN RETURN[error: "bad selection"]; RETURN[name: Rope.Cat[module, ".", proc]]}; section _ AMViewerOps.SectionFromSelection[! AMTypes.Error => {error _ msg; CONTINUE}].section; END; Split: PROC [name: ROPE] RETURNS [root: Rope.Text, ext: Rope.Text] = INLINE { dotIndex: INT _ name.Index[0, "."]; bangIndex: INT _ name.Index[0, "!"]; -- find the final "." FOR nextDot: INT _ name.Index[dotIndex+1, "."], name.Index[dotIndex+1, "."] UNTIL nextDot = name.Length[] DO dotIndex _ nextDot ENDLOOP; IF dotIndex < 0 THEN RETURN[name.Flatten[], NIL]; IF bangIndex < 0 THEN bangIndex _ name.Length[]; RETURN[name.Flatten[0, dotIndex], name.Flatten[dotIndex+1, bangIndex-dotIndex-1]]}; CreateSpyClass[]; [] _ Spy[NIL]; 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] = { 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]]};  SpyViewerImpl.mesa Edited by Maxwell, December 14, 1983 3:28 pm Edited by Donahue, August 15, 1984 10:16:56 am PDT ************************************************** class and icon initialization ************************************************** erase old write in name overwrite magnifier invert for magnifier write in NAME draw in handle ************************************************** spy viewer creation ************************************************** typescript first line second line third line fourth line ************************************************** turning the spy off and on ************************************************** ************************************************** determine the data type ************************************************** ************************************************** displaying data ************************************************** ************************************************** setting and clearing start and stop breaks ************************************************** 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. ************************************************** setting and clearing user breaks ************************************************** ************************************************** utility procedures ************************************************** ÊD˜Jšœ™Jšœ,™,J™2J˜šÏk ˜ Jšœœ ˜Jšœœ œ˜Jšœ œ˜)Jšœœ'˜4Jšœ œ˜(Jšœ œ'˜7Jšœœ˜šœ œ˜J˜µ—Jšœœ˜!šœ œ%˜6J˜-—Jšœœ7˜BJš œœœœœ˜EJšœœ˜"Jšœ œ˜#Jšœœ&œ˜—Jšœ+˜+—šœ˜ šœ-˜/Jšœœœ˜>—Jšœ,˜,——Jšœ˜J˜—š  œ˜Jšœ˜ šœ˜Jšœ˜Jšœ˜Jšœœ˜—Jšœ˜J˜—šŸœ œ˜Jšœ˜Jšœœ˜J˜J˜Jšœ˜J˜—šŸœ œ˜Jš˜Jšœœœ˜Jšœ.˜.J˜'šœœœ˜+J˜4—šœœ˜J˜GJšœœ"˜D—šœ œœ!˜4J˜J˜—Jšœœ˜Jš œ œœœœœ˜LJšœœ˜ J˜J˜Jšœ˜—J™Jšž2™2J™Jšž2™2J˜Jšœœ˜#Jšœ!œ˜%Jšœ#œ˜'Jšœ4œ˜=J˜šŸ œ œ˜Jš˜˜šœ ˜Jšœ˜Jšœ)˜)Jšœ,˜,Jšœ'˜'J˜)J˜/J˜)Jšœœ˜——š œœœœœ˜Lšœ+˜+˜(J˜"Jšœœœ˜!Jšœ˜——Jšœ˜Jšœ'˜'—š œœœœœ˜KJ˜$Jšœ ˜ —Jšœ˜J˜—š  œ˜ Jšœ˜ šœ œ ˜"šœœ œ˜.Jšœœœœ ˜3—šœœ œ˜0Jšœœœœ ˜2—Jšœœ˜—J˜Jšœ˜—J˜Jšž2™2Jšž™Jšž2™2J˜Jšœœ˜Jšœ˜J˜š  œ˜#Jšœ˜ šœ ˜Jšœœ œ˜/J˜Jšœœœ˜0Jšœ˜—J˜Jšœ˜J˜—šŸœœ˜Jš˜Jšœœœ˜Jšœ œœ˜Jšœ6˜6Jšœœ˜6J˜Jšœ˜—J˜š  œ˜!Jšœ˜ Jšœœ ˜Jšœœœ ˜1Jšœœ@˜OJšœ˜—J˜Jšž2™2Jšž*™*Jšž2™2J˜Jšœ&œ˜*Jšœ%œ˜)šœ$œ˜(J˜—Jšœ(™(Jšœ=™=Jšœ™JšœE™EJšœ!™!J˜š  œ˜!Jšœ˜ J˜Jšœœ0œ˜KJšœœ˜J˜ Jšœ˜J˜—š  œ˜#Jšœ˜ Jšœ œ˜Jšœœœ˜J˜Jšœ/˜/Jš œ œœœœ4˜WJšœœœ˜.Jšœœœ˜Jšœœ˜ J˜J˜J˜ Jšœ˜J˜—š  œ˜"Jšœ˜ Jšœ œ˜Jšœœœ˜J˜Jšœ/˜/Jš œ œœœœ3˜VJšœœœ˜.Jšœœœ˜Jšœœ˜ J˜ Jšœ˜—J˜Jšž2™2Jšž"™"Jšž2™2J˜Jšœ!œ˜%Jšœ%œ˜)Jšœ(œ˜,J˜š œ˜%Jšœ˜ J˜Jšœœ&œ˜AJšœœ ˜)Jšœ˜J˜—š  œ˜#Jšœ˜ Jšœ œ˜Jšœœœ˜J˜J˜/š œ œœœœœ˜;Jšœ.˜2Jšœ6˜:—Jšœœœ˜.Jšœœœ˜J˜ Jšœœœ˜&J˜J˜Jšœ˜—J˜šŸœœ˜Jš œœœœ˜MJ˜Jšœ)˜)šœœ-œ˜TJšœœ˜Jšœœ&˜9Jšœ!˜!Jš œ œœœœœ˜BJšœ%˜+—JšœLœ ˜_Jšœ˜J˜—š Ÿœœœœ%œ˜MJšœ œ˜#Jšœ œ˜$J˜Jš œ œ<œœœ˜ˆJšœœœœ˜1Jšœœ˜0JšœM˜S—J˜J˜Jšœ œ˜J˜Jšœ˜J˜Jšž2™2Jšž™Jšž2™2J˜šŸœœœœ˜:š œ"œ œœ˜KJšœœ&˜:šœ/œ˜7Jšœ#œ&œ˜VJšœœœ8˜I—šœœœ˜Jšœ œ˜Jšœœ˜Jšœ"˜"Jšœœœ"œ˜JJšœœœ)˜9Jšœœœœ+˜FJšœœœœ+˜E—Jš œœœœœ"˜A—Jšœ˜J˜—šŸœœœœ˜;Jšœœ˜Jšœœ˜Jšœœœœ˜1Jšœ7˜=—J˜J˜—…—?¼W