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; typescript: IO.STREAM _ NIL; typescriptBox: ViewerClasses.Viewer _ NIL; spyViewer: Containers.Container _ NIL; iconFlavor: Icons.IconFlavor; iconBitmap: GraphicsOps.BitmapRef; 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; 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]; }; 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: 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 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]]; }; 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[]; }; 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[]; }; 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."]; }; setStartBreakButton: Buttons.Button _ NIL; setStopBreakButton: Buttons.Button _ NIL; clearBreaksButton: Buttons.Button _ NIL; 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[]; }; 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]; }. ÎSpyViewerImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Russ Atkinson, July 16, 1984 3:14:39 pm PDT Maxwell, December 14, 1983 3:28 pm Global Variables ************************************************** 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 ************************************************** Êà˜šœ™Jšœ Ïmœ1™—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™šŸ2™2J˜Jšœžœ˜#Jšœ!žœ˜%Jšœ#žœ˜'Jšœ4žœ˜=J˜—š  œžœ˜˜šžœ ž˜Jšžœ˜Jšœ)˜)Jšœ,˜,Jšœ'˜'J˜)J˜/J˜)Jšžœžœ˜——š žœžœžœžœžœ˜Lšœ+˜+˜(J˜"Jšœžœžœ˜!Jšœ˜——Jšœ˜Jšœ'˜'—š žœžœžœžœžœ˜KJ˜$Jšœ ˜ —Jšœ˜J˜—š¡ œžœ˜*šœ žœ ž˜"šœžœ žœ˜.Jšžœžœžœžœ ˜3—šœžœ žœ˜0Jšžœžœžœžœ ˜2—Jšžœžœ˜—J˜Jšœ˜—J˜JšŸ2™2JšŸ™šŸ2™2J˜Jšœžœ˜Jšœ˜J˜—š¡ œžœ˜-šžœ ž˜Jšœžœ žœ˜/J˜Jšœžœžœ˜0Jšžœ˜—J˜Jšœ˜J˜—š œžœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšœ6˜6Jšœžœ˜6J˜Jšœ˜—J˜š¡ œžœ˜+Jšžœžœ ˜Jšœžœžœ ˜1Jšžœžœ@˜OJšœ˜—J˜JšŸ2™2JšŸ*™*šŸ2™2J˜Jšœ&žœ˜*Jšœ%žœ˜)šœ$žœ˜(J˜—Jšœ(™(Jšœ=™=Jšœ™JšœE™EJšœ!™!J˜—š¡ œžœ˜+J˜Jšœžœ0žœ˜KJšœžœ˜J˜ Jšœ˜J˜—š¡ œžœ˜-Jšœ žœ˜Jšœžœžœ˜J˜Jšœ/˜/Jš žœ žœžœžœžœ4˜WJšœžœžœ˜.Jšžœžœžœ˜Jšœžœ˜ J˜J˜J˜ Jšœ˜J˜—š¡ œžœ˜,Jšœ žœ˜Jšœžœžœ˜J˜Jšœ/˜/Jš žœ žœžœžœžœ3˜VJšœžœžœ˜.Jšžœžœžœ˜Jšœžœ˜ J˜ Jšœ˜—J˜JšŸ2™2JšŸ"™"JšŸ2™2˜Jšœ!žœ˜%Jšœ%žœ˜)Jšœ(žœ˜,J˜—š¡œžœ˜/J˜Jšœžœ&žœ˜AJšžœžœ ˜)Jšœ˜J˜—š¡ œžœ˜-Jšœ žœ˜Jšœžœžœ˜J˜J˜/š žœ žœžœžœžœžœ˜;Jšžœ.˜2Jšžœ6˜:—Jšœžœžœ˜.Jšžœžœžœ˜J˜ Jšžœžœžœ˜&J˜J˜Jšœ˜—J˜š œžœ˜Jšžœžœžœžœ˜DJ˜Jšœ)˜)Jšžœ žœžœžœ˜:šžœžœ-žœ˜TJšœžœ˜Jšœ žœ&˜4Jšœ!˜!Jš žœ žœžœžœžœžœ˜;Jšœ˜—šœ+˜+Jšœ!žœ ˜4—Jšœ˜J˜—š  œžœžœžœ$žœ˜LJš œžœžœžœžœ ˜Jšžœžœžœ˜Jšœžœ˜Jšœ žœ˜Jšœ žœ˜Jšœžœ˜J˜šžœ ž˜šžœ!ž˜+Jšœ˜Jšœ˜Jšœžœ˜Jšžœ˜—Jšžœ˜—J˜šžœ˜Jšžœ˜šžœ˜Jšœ!˜!Jšœ5˜5J˜——J˜—Jšœ˜—J˜J˜Jšœ žœ˜J˜Jšœ˜J˜—…—;:Pè