DIRECTORY Basics USING [BITAND], BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds], DebuggerSwap USING [CallDebugger], FastBreak USING [FastBreakProc], PrincOps, PrincOpsUtils USING [DisableInterrupts, EnableInterrupts, LongNotify, LongReEnter, LongWait, PsbIndexToHandle, PsbHandleToIndex, ReadPSB, ReadPTC], Process USING [Detach, GetCurrent, GetPriority, InitializeCondition, Priority, priorityRealTime, priorityNormal, SecondsToTicks, SetPriority], Rope USING [ROPE], SafeStorage USING [NWordsAllocated, NWordsReclaimed], Loader USING [MakeProcedureResident, MakeGlobalFrameResident], SpyClient USING [DataType], SpyLog USING [active, Here, OpenForWrite, WriteData], SpyOps USING [Count, DataType, Frame, SetAllocationBreak, SpyState, Stack, stackHeader, stackLength, StackType], Terminal USING [Current, Virtual, WaitForBWVerticalRetrace], VM USING [PageNumberForAddress], VMStatistics USING [pageFaults]; SpyKernelImpl: MONITOR IMPORTS Basics, BasicTime, DebuggerSwap, Loader, PrincOpsUtils, Process, SafeStorage, SpyLog, SpyOps, Terminal, VM, VMStatistics EXPORTS SpyClient, SpyOps = BEGIN OPEN PrincOps, Rope; -- SpyKernel parameters -- spyState: PUBLIC SpyOps.SpyState _ off; watching: PUBLIC SpyOps.DataType _ CPU; justMe: PUBLIC PrincOps.PsbIndex _ 0; runningTime: PUBLIC BasicTime.Pulses; -- total time running pageFaults: PUBLIC LONG CARDINAL; active, starts, stops: PUBLIC INTEGER _ 0; wordsAllocated: PUBLIC LONG CARDINAL; wordsReclaimed: PUBLIC LONG CARDINAL; code: PUBLIC SpyOps.Count _ 0; -- page faults on code data: PUBLIC SpyOps.Count _ 0; -- page faults on data InitializeSpy: PUBLIC ENTRY PROCEDURE[ dataType: SpyClient.DataType _ CPU, process: PrincOps.PsbIndex _ PrincOps.PsbNull, spyOnSpyLog: BOOL _ FALSE] RETURNS[errorMsg: ROPE] = BEGIN DisableSpy[]; IF dataType IN [allocations..wordsAllocated] THEN errorMsg _ SpyOps.SetAllocationBreak[]; IF errorMsg # NIL THEN RETURN[errorMsg]; InitializeTables[]; watching _ dataType; justMe _ IF dataType = process THEN process ELSE 0; SpyLog.OpenForWrite[spyOnSpyLog]; EnableSpy[]; END; InitializeTables: PROCEDURE = BEGIN runningTime _ 0; code _ data _ 0; wordsAllocated _ wordsReclaimed _ 0; END; Initialize: PROCEDURE = BEGIN dolphin _ BasicTime.PulsesToMicroseconds[1]#32; -- hack Loader.MakeProcedureResident[Spy]; Loader.MakeProcedureResident[Record]; Loader.MakeProcedureResident[UserBreak]; Loader.MakeProcedureResident[PageFaultRecorder]; Loader.MakeGlobalFrameResident[Initialize]; Process.InitializeCondition[@processDead, 10000]; SetTimeouts[]; END; SetTimeouts: PROC = BEGIN ticks: PrincOps.Ticks; cutoff: PrincOps.Ticks; found: BOOLEAN; frame: PrincOps.FrameHandle; cutoff _ Process.SecondsToTicks[10]; ticks _ PrincOpsUtils.ReadPTC[]; FOR psbi: PsbIndex IN [PrincOps.StartPsb..PrincOps.StartPsb+PrincOps.PDA.count) DO IF PrincOps.PDA.block[psbi].mds = 0 THEN LOOP; IF PrincOps.PDA.block[psbi].mds - ticks > cutoff THEN LOOP; -- not significant IF PrincOps.PDA.block[psbi].link.vector THEN frame _ PDA[PrincOps.PDA.block[psbi].context.state].frame ELSE frame _ PrincOps.PDA.block[psbi].context.frame; found _ FALSE; FOR i: CARDINAL IN [0..index) DO IF timeout[i] = [frame.accesslink.gfi, frame.pc] THEN {found _ TRUE; EXIT}; ENDLOOP; IF found THEN LOOP; timeout[index] _ [frame.accesslink.gfi, frame.pc]; index _ index + 1; IF index = maxIndex THEN EXIT; ENDLOOP; END; processDead: CONDITION; spyProcess: PROCESS _ NIL; oldSpyState: SpyOps.SpyState _ off; startTime: BasicTime.Pulses; startPageFaults: LONG CARDINAL; startWordsAllocated: LONG CARDINAL; startWordsReclaimed: LONG CARDINAL; StartCounting: PUBLIC ENTRY FastBreak.FastBreakProc = BEGIN priority: Process.Priority; starts _ starts + 1; IF active = 0 THEN WHILE spyProcess # NIL DO WAIT processDead; ENDLOOP; IF (active _ active + 1) > 1 THEN RETURN[useOldBreak: FALSE]; -- a psuedo start startPageFaults _ VMStatistics.pageFaults; startWordsAllocated _ SafeStorage.NWordsAllocated[]; startWordsReclaimed _ SafeStorage.NWordsReclaimed[]; startTime _ BasicTime.GetClockPulses[]; IF watching = breakProcess THEN justMe _ LOOPHOLE[Process.GetCurrent[]]; IF justMe # 0 THEN justMePrincOps _ @PrincOps.PDA[PrincOpsUtils.PsbIndexToHandle[justMe]]; SetTimeouts[]; -- do it a second time in case the first time missed some priority _ Process.GetPriority[]; Process.SetPriority[Process.priorityRealTime]; SELECT watching FROM CPU, process, breakProcess => Process.Detach[spyProcess _ FORK Spy[]]; pagefaults => Process.Detach[spyProcess _ FORK PageFaultRecorder[]]; allocations, wordsAllocated => { spyState _ on}; ENDCASE => spyState _ on; Process.SetPriority[priority]; RETURN[useOldBreak: FALSE]; END; StopCounting: PUBLIC ENTRY FastBreak.FastBreakProc = BEGIN IF active = 0 THEN RETURN[useOldBreak: FALSE]; -- already stopped stops _ stops + 1; IF (active _ active - 1) > 0 THEN RETURN; -- a psuedo stop spyState _ off; runningTime _ runningTime + BasicTime.GetClockPulses[] - startTime; pageFaults _ pageFaults + VMStatistics.pageFaults - startPageFaults; wordsAllocated _ wordsAllocated + SafeStorage.NWordsAllocated[] - startWordsAllocated; wordsReclaimed _ wordsReclaimed + SafeStorage.NWordsReclaimed[] - startWordsReclaimed; RETURN[useOldBreak: FALSE]; END; DisableSpy: PROCEDURE = BEGIN IF spyState = disabled THEN RETURN; oldSpyState _ spyState; spyState _ disabled; END; EnableSpy: PROCEDURE = {spyState _ oldSpyState}; justMePrincOps: LONG POINTER TO ProcessStateBlock _ NIL; wait: CARDINAL _ 0; -- used with dolphins monitor: BOOLEAN _ FALSE; -- measure performance of spy dolphin: BOOLEAN _ FALSE; maxStackDepth: CARDINAL = 200; timeout: ARRAY [0..maxIndex) OF RECORD[gfi: PrincOps.GFTIndex, pc: CARDINAL]; maxIndex: CARDINAL = 20; index: CARDINAL_ 0; sampleInterval: INT _ 10*1000; -- measured in microseconds screen: Terminal.Virtual = Terminal.Current[]; Spy: PROCEDURE = BEGIN top: PsbIndex; frame: FrameHandle; myPrincOps: PsbHandle = PrincOpsUtils.ReadPSB[]; handleMask: PsbLink = [failed: FALSE, priority: 0, next: LAST[PsbIndex], reserved: 0, vector: FALSE]; NextHandle: PROCEDURE [link: CARDINAL] RETURNS [PsbHandle] = INLINE {RETURN[LOOPHOLE[Basics.BITAND[link, LOOPHOLE[handleMask]]]]}; SearchReadyList: PROCEDURE = INLINE BEGIN skip, once: BOOLEAN _ FALSE; headOfReadyList, current: PsbHandle; PrincOpsUtils.DisableInterrupts[]; top _ PrincOps.PsbNull; headOfReadyList _ NextHandle[LOOPHOLE[PDA.ready]]; headOfReadyList _ NextHandle[LOOPHOLE[PDA[headOfReadyList].link]]; -- want the SECOND psb. FOR current _ headOfReadyList, NextHandle[LOOPHOLE[PDA[current].link]] DO psb: LONG POINTER TO ProcessStateBlock = @PDA[current]; link: PsbLink = psb.link; level: Process.Priority = link.priority; IF current = headOfReadyList THEN IF once THEN EXIT ELSE once _ TRUE; IF level = 0 THEN LOOP; IF current = myPrincOps THEN LOOP; IF ~link.vector AND PDA.state[level] = NullStateVectorHandle THEN LOOP; -- no SV. frame _ IF link.vector THEN PDA[psb.context.state].frame ELSE psb.context.frame; skip _ FALSE; FOR i: CARDINAL IN [0..maxIndex) DO IF timeout[i] = [0, 0] THEN EXIT; IF timeout[i] # [frame.accesslink.gfi, frame.pc] THEN LOOP; skip _ TRUE; EXIT; ENDLOOP; IF skip THEN LOOP; top _ PrincOpsUtils.PsbHandleToIndex[current]; EXIT; ENDLOOP; PrincOpsUtils.EnableInterrupts[]; END; spyState _ on; -- MAIN LOOP -- DO -- IF IntervalTimerFace.exists -- THEN IntervalTimer.WaitForExpirationInterval[sampleInterval] ELSE Terminal.WaitForBWVerticalRetrace[screen]; IF active <= 0 THEN EXIT; IF spyState = disabled THEN LOOP; IF dolphin AND wait > 0 THEN {wait _ wait - 1; LOOP} ELSE wait _ 8; IF monitor THEN SpyLog.Here[]; SearchReadyList[]; SELECT TRUE FROM justMe = 0 => Record[top, frame]; justMePrincOps.link.failed => Record[justMe, NIL, 2]; -- waiting ML justMePrincOps.flags.waiting => Record[justMe, NIL, 3]; -- waiting CV OnQueue[justMe, @PDA.fault[PrincOps.qPageFault].queue] => Record[justMe, NIL, 1]; -- waiting pagefault OnQueue[justMe, @PDA.fault[PrincOps.qPageFault+4].queue] => Record[justMe, NIL, 1]; -- waiting pagefault OnQueue[justMe, @PrincOps.PDA.ready] => SELECT TRUE FROM PDA.state[justMePrincOps.link.priority] = NullStateVectorHandle AND ~justMePrincOps.link.vector => Record[justMe, NIL, 5]; -- waiting SV justMe # top => Record[justMe, NIL, 4]; -- prempted by a higher priority process ENDCASE => Record[justMe]; -- ready ENDCASE => Record[justMe, NIL, 6]; -- in some unknown state IF monitor THEN SpyLog.Here[]; ENDLOOP; spyProcess _ NIL; spyState _ off; NotifyProcessDead[]; Process.SetPriority[Process.priorityNormal]; END; OnQueue: PROC[psbi: PsbIndex, queueHandle: PrincOps.QueueHandle] RETURNS[BOOL] = INLINE BEGIN tail, prev: PsbIndex; IF queueHandle^ = PrincOps.QueueEmpty THEN RETURN[FALSE]; prev _ tail _ queueHandle.tail; THROUGH [FIRST[PsbIndex]..LAST[PsbIndex]+1] -- garbage protection -- DO next: PsbIndex = PrincOps.PDA.block[prev].link.next; IF next = psbi THEN RETURN[TRUE]; prev _ next; IF prev = tail THEN RETURN[FALSE]; ENDLOOP; RETURN[FALSE] -- actually, the queue is thoroughly mangled! -- END; recordPageFaulted: BOOLEAN _ FALSE; Data: TYPE = RECORD[process: CARDINAL, page: INTEGER]; CodeBase: TYPE = LONG POINTER TO PACKED ARRAY [0..0) OF PrincOps.op; PageFaultRecorder: PROCEDURE = -- stolen from Ben.mesa BEGIN fault: Data; type: CARDINAL; codePage: INTEGER; handle: PrincOps.PsbHandle; frame: PrincOps.FrameHandle; pda: PrincOps.PDABase = PrincOps.PDA; qPageFault: PrincOps.FaultIndex = PrincOps.qPageFault; pPageFaultCondition: LONG POINTER TO PrincOps.Condition = @pda.fault[qPageFault].condition; pPageFaultCONDITION: LONG POINTER TO CONDITION = LOOPHOLE[pPageFaultCondition]; recorderLock: MONITORLOCK; shouldNotifyPilot: BOOLEAN _ FALSE; spyState _ on; DO PrincOpsUtils.LongWait[@recorderLock, pPageFaultCONDITION, 1]; UNTIL PrincOpsUtils.LongReEnter[@recorderLock, pPageFaultCONDITION] DO NULL ENDLOOP; IF pda.fault[qPageFault].queue.tail = PrincOps.PsbNull THEN { -- timed out IF shouldNotifyPilot AND pda.fault[qPageFault].condition.tail ~= PrincOps.PsbNull THEN { LongNakedNotify[pPageFaultCONDITION]; shouldNotifyPilot _ FALSE}; IF active <= 0 THEN EXIT; LOOP}; -- go back and wait again. fault.process _ pda.block[pda.fault[qPageFault].queue.tail].link.next; -- walk to tail, then to head. handle _ PrincOpsUtils.PsbIndexToHandle[fault.process]; fault.page _ VM.PageNumberForAddress[pda[pda[handle].context.state].memPointer]; frame _ IF pda[handle].link.vector THEN pda[pda[handle].context.state].frame ELSE pda[handle].context.frame; codePage _ LOOPHOLE[frame.accesslink.code.longbase+frame.pc/2, INT]/256; SELECT TRUE FROM ABS[codePage - fault.page] <= 1 => {code _ code + 1; type _ 2}; Xfer[frame.accesslink.code.longbase, frame.pc] => {code _ code + 1; type _ 3}; ENDCASE => {data _ data + 1; type _ 1}; PrincOpsUtils.DisableInterrupts[]; IF pPageFaultCondition^.tail = PrincOps.PsbNull THEN shouldNotifyPilot _ TRUE -- Pilot not ready for this fault yet... ELSE LongNakedNotify[pPageFaultCONDITION]; PrincOpsUtils.EnableInterrupts[]; IF active <= 0 THEN EXIT; IF recordPageFaulted THEN SpyLog.WriteData[@fault, SIZE[Data], CODE[Data]]; Record[fault.process, NIL, type]; ENDLOOP; spyProcess _ NIL; spyState _ off; NotifyProcessDead[]; Process.SetPriority[Process.priorityNormal]; END; Xfer: PROCEDURE[base: CodeBase, pc: PrincOps.BytePC] RETURNS[BOOLEAN] = INLINE BEGIN -- is the current pc pointing to some sort of xfer? IF base[pc] IN [PrincOps.zEFC0..PrincOps.zKFCB] THEN RETURN[TRUE]; RETURN[FALSE]; END; LongNakedNotify: PROCEDURE [pCondition: LONG POINTER TO CONDITION] = INLINE { pCond: LONG POINTER TO PrincOps.Condition = LOOPHOLE[pCondition]; PrincOpsUtils.DisableInterrupts[]; IF pCond^.tail=PrincOps.PsbNull THEN {pCond^.wakeup _ TRUE; PrincOpsUtils.EnableInterrupts[]} ELSE {PrincOpsUtils.EnableInterrupts[]; PrincOpsUtils.LongNotify[pCondition]}}; NotifyProcessDead: ENTRY PROCEDURE = INLINE {NOTIFY processDead}; stack: SpyOps.Stack; break: BOOLEAN _ FALSE; breakAtom: ATOM _ NIL; AllocationBreak: PUBLIC FastBreak.FastBreakProc = BEGIN words: CARDINAL; type: SpyOps.StackType; local: POINTER TO ARRAY [0..7] OF CARDINAL; IF active = 0 THEN RETURN; IF watching NOT IN [allocations..wordsAllocated] THEN RETURN; words _ 1; type _ 0; IF watching = wordsAllocated THEN local _ @frame.local[0]; IF data = NIL AND watching = wordsAllocated THEN words _ local[0]; IF data = LOOPHOLE[$Permanent, LONG POINTER] THEN { type _ 1; IF watching = wordsAllocated THEN words _ local[2]}; IF data = LOOPHOLE[$Unsafe, LONG POINTER] THEN { type _ 2; IF watching = wordsAllocated THEN words _ local[4]}; IF break AND data = LOOPHOLE[breakAtom, LONG POINTER] THEN DebuggerSwap.CallDebugger["Allocation break"]; Record[ psbi: LOOPHOLE[Process.GetCurrent[]], type: type, frame: frame, count: words]; END; UserBreak: PUBLIC FastBreak.FastBreakProc = TRUSTED BEGIN count: CARDINAL _ 1; type: SpyOps.StackType _ 1; IF spyState # on THEN RETURN[useOldBreak: FALSE]; IF watching # userDefined THEN RETURN[useOldBreak: FALSE]; IF data # NIL THEN type _ LOOPHOLE[data, LONG POINTER TO SpyOps.StackType]^; Record[LOOPHOLE[Process.GetCurrent[]], frame, type, count]; RETURN[useOldBreak: FALSE]; END; Record: PUBLIC ENTRY PROCEDURE[psbi: PsbIndex, frame: FrameHandle _ NIL, type: SpyOps.StackType _ 0, count: CARDINAL _ 1] = BEGIN level: Process.Priority; psb: LONG POINTER TO ProcessStateBlock; IF spyState = disabled THEN RETURN; IF active <= 0 THEN RETURN; IF psbi = PrincOps.PsbNull THEN IF PDA.fault[qPageFault].queue.tail = PrincOps.PsbNull AND PDA.fault[qPageFault+4].queue.tail = PrincOps.PsbNull THEN {LogStack[psbi, 0, NIL, count, 0]; RETURN} ELSE {LogStack[psbi, 0, NIL, count, 1]; RETURN}; -- waiting for a page fault psb _ @PDA[PrincOpsUtils.PsbIndexToHandle[psbi]]; level _ psb.link.priority; IF frame = NIL THEN frame _ IF psb.link.vector THEN PDA[psb.context.state].frame ELSE psb.context.frame; LogStack[psbi, level, frame, count, type] END; LogStack: INTERNAL PROC [process: PrincOps.PsbIndex, level: Process.Priority, frame: FrameHandle, count, type: CARDINAL] = -- Log the stack on the Trace Log -- INLINE BEGIN sdLength: CARDINAL = PrincOps.SD[PrincOps.sGFTLength]; gfi: GFTIndex _ 0; gf: GlobalFrameHandle _ NIL; length: CARDINAL _ 0; GfiToGF: PROCEDURE [gfi: GFTIndex] RETURNS [GlobalFrameHandle] = INLINE {RETURN[PrincOps.GFT[gfi].framePtr]}; ValidFrame: PROCEDURE [f: ControlLink] RETURNS [FrameHandle] = BEGIN OPEN PrincOps; IF f.proc OR f.indirect OR f.frame =NIL OR (gf _ f.frame.accesslink) = NIL OR (gfi _ gf.gfi) > sdLength OR GfiToGF[gfi] ~= gf THEN RETURN[NIL]; RETURN[f.frame] END; IF ~SpyLog.active THEN RETURN; stack.type _ type; stack.count _ count; stack.level _ level; stack.process _ process; frame _ ValidFrame[LOOPHOLE[frame]]; THROUGH [0..maxStackDepth) UNTIL frame = NIL DO IF length >= SpyOps.stackLength THEN {stack.level _ 7; length _ 0; EXIT}; stack.frame[length] _ [0, gfi, frame.pc]; IF length > 0 AND stack.frame[length] = stack.frame[length-1] THEN length _ length - 1; -- skip recursive frames length _ length + 1; frame _ ValidFrame[frame.returnlink]; ENDLOOP; SpyLog.WriteData[@stack, SpyOps.stackHeader + length*SpyOps.Frame.SIZE, SpyOps.Stack.CODE]; END; Initialize[]; END. timeout[0] _ [gfi: 100B, pc: 114B]; timeout[1] _ [gfi: 100B, pc: 143B]; timeout[2] _ [gfi: 401B, pc: 2316B]; timeout[3] _ [gfi: 401B, pc: 3307B]; timeout[4] _ [gfi: 1075B, pc: 12265B]; timeout[5] _ timeout[6] _ timeout[7] _ timeout[8] _ timeout[9] _ [0, 0]; END; ÄSpyKernelImpl.mesa Last edited by Bruce 14-Feb-81 19:30:51 Last edited by MBrown 16-Aug-81 13:30:07 Last edited by Levin 2-Nov-81 18:16:29 Last edited by Maxwell September 16, 1983 3:44 pm IntervalTimer USING [WaitForExpirationInterval], IntervalTimerFace USING [exists], PageMap USING [GetF, flagsVacant], general statistics **************************************************************************** initializing the Spy and data **************************************************************************** IF IntervalTimerFace.exists THEN RETURN; PrincOpsUtils.DisableInterrupts[]; -- stops the clock. NO PAGE FAULTS ALLOWED! PrincOpsUtils.EnableInterrupts[]; ZeroLog: PUBLIC PROCEDURE = { SpyLog.Initialize[NIL, 40, TRUE]; SpyLog.Open[TRUE]}; **************************************************************************** starting and stopping the Spy **************************************************************************** Note: These procedures need not be resident. RTStorageAccounting.AllocatorCallbackProcForSpy _ SensitiveBreak; ********************************************************************* watching CPU ********************************************************************* All code invoked by this process should be resident skip over a process that appears in the ready queue because it just timed out. (The Spy wakes up with all of the other timeouts. Since it is the highest priority, it will run first. All of the other timeouts will appear on the ready list. Most likely, they will just check some condition and then go back to sleep. This will mask the more interesting processes.) we have a good process! ********************************************************************* watching pagefaults ********************************************************************* NO PAGE FAULTS ALLOWED! wait for a page fault: figure out who faulted: wake up the Pilot fault handler: log the fault: Used ONLY to notify a condition from a high priority process outside the relevant monitor. *************************************************************************** recording the information *************************************************************************** data: FastBreakData, frame: PrincOps.FrameHandle, sv: PrincOps.SVPointer Note: as a side-effect, this procedure sets 'gf' and 'gfi', which are used by the other local procedures below and the main loop of IncrementBucket. PageMap.GetF[Basics.LongDiv[LOOPHOLE[LONG[gf]], wordsPerPage]].flags = flagsVacant OR save the current frame this also saves the last ignored frame 22-Jan-82 Maxwell: Removed cross-partition code; converted to Cedar 4-Feb-82 Maxwell: Added stack active option 11-Mar-82 Maxwell: Added page fault recorder October 14, 1982 10:07 am Maxwell: Removed Pilot Spy code since the Spy AND timeouts are synchronized to the vertical retrace, we need to skip over processes that have just timed out. The following [gfi, pc] pairs indicate WAITs on timeouts. WAIT harwareVerticalRetrace IN UserTerminalImpl.WaitForVerticalRetrace WAIT verticalRetrace IN UserTerminalImpl.WaitForVerticalRetrace Wait[Process.MSecToTicks[5000]] IN InscriptImpl.WaitForEntry WAIT pageDone IN InscriptImpl.MaintainPage WAIT iIncr IN TEditTypeScriptImpl.GetChar ÊÙ˜Jšœ™Jšœ*™*Jšœ*™*Jšœ*™*Jšœ2™2J˜šÏk ˜ Jšœœœ˜Jšœ œ0˜?Jšœ œ˜"Jšœ œ˜ Jšœœ™0Jšœœ ™!Jšœ ˜ Jšœœ€˜“Jšœœ™"Jšœœ˜ŽJšœœœ˜Jšœ œ$˜5Jšœœ2˜>Jšœ œ ˜Jšœœ)˜5Jšœœd˜pJšœ œ.˜Jšœ œœ˜4—Jšœœ˜šœœœ ˜ Jšœ/œ œœ˜KJšœ˜—Jšœœœ˜J˜2J˜Jšœœœ˜Jšœ˜—Jšœ!™!šœ˜J˜——šŸœœ œ™Jšœœœ™!Jšœ œ™—J˜JšœL™LJšœ™JšœL™LJ˜Jšœ-™-J˜Jšœ œ˜Jšœ œœ˜J˜#J˜J˜Jšœœœ˜Jšœœœ˜#Jšœœœ˜#J˜šÏb œœœ˜6Jš˜J˜J˜Jšœ œœœœœœ˜HJš œœœœž˜OJšœ*˜*J˜4J˜4J˜'Jšœœ œ˜HJšœ œœ)˜ZJšœž9˜HJ˜!Jšœ.˜.šœ ˜Jšœ7œ ˜GJšœ*œ˜D˜ J™AJ˜—Jšœ˜—J˜Jšœœ˜Jšœ˜J˜—š  œœœ˜5Jš˜Jš œ œœœž˜AJ˜Jšœœœž˜;Jšœ˜JšœC˜CJšœD˜DJ˜VJ˜VJšœœ˜Jšœ˜J˜—šŸ œ œ˜Jš˜Jšœœœ˜#J˜J˜Jšœ˜J˜—JšŸ œ œ˜0J˜JšœE™EJšœ™JšœE™EJ˜Jšœ3™3J˜Jš œœœœœ˜8Jšœœž˜)Jšœ œœž˜7Jšœ œœ˜Jšœœ˜Jš œ œœœœ˜MJšœ œ˜Jšœœ˜Jšœœ ž˜:J˜.J˜šŸœ œ˜Jš˜J˜J˜J˜0Jšœœœ!œ˜eš Ÿ œ œœœ˜CJš œœœœœ˜>—šŸœ œ˜#Jš˜Jšœ œœ˜J˜$Jšœ"˜"Jšœ˜Jšœœœ ˜2Jšœœœž˜Zšœ'œœ˜IJš œœœœœ ˜7J˜J˜(Jšœœœœœœœ˜EJšœ œœ˜Jšœœœ˜"Jš œœœ&œœž ˜QJš œœ œœœ˜PJšœN™NJšœT™TJšœZ™ZJšœR™RJšœ™Jšœœ˜ šœœœ˜#Jšœœœ˜!Jšœ/œœ˜;Jšœœœ˜Jšœ˜—Jšœœœ˜Jšœ™J˜.Jšœœ˜—Jšœ!˜!Jšœ˜—J˜Jšž˜šœœ˜"Jšœ9œ˜EJšœ*˜*Jšœ œœ˜Jšœœœ˜!Jš œ œ œœœ ˜CJšœ œ˜J˜šœœ˜J˜!Jšœ-œž ˜CJšœ/œž ˜Ešœœœ˜:Jšœœž˜,—šœœœ˜Jšœ˜—J˜JšœE™EJšœ™JšœE™EJ˜Jšœœœ˜#Jš œœœ œœ˜6Jšœ œœœœœœœ ˜DJ˜šŸœ œž˜6Jš˜J˜ Jšœœ˜Jšœ œ˜Jšœœ ˜J˜Jšœœ œœ˜%Jšœ œœ ˜6š œœœœœ ˜:J˜!—š œœœœ œ˜0Jšœ˜—Jšœ œ˜Jšœœœ˜#Jšœ™J˜šœ˜Jšœ™Jšœ>˜>šœ?˜FJšœœ˜ —šœ$œ œž ˜Jšœ˜Jšœ)œ œ˜C˜%Jšœœ˜——Jšœ œœ˜Jšœž˜"—Jšœ™JšœHž˜fJ˜7Jšœ œA˜Pšœœ˜#Jšœ&˜*Jšœ˜—Jšœ œ,œ˜Hšœœ˜Jšœ=˜@J˜PJšœ ˜'—Jšœ ™ Jšœ"˜"šœœ ˜0Jšœœž(˜GJšœ&˜*—Jšœ!˜!Jšœ™Jšœ œœ˜Jšœœœœ˜KJšœœ˜!Jšœ˜—Jšœ œ˜J˜J˜J˜0Jšœ˜J˜—šŸœ œ&œœ˜IJšœœž3˜@Jš œ œ"œœœ˜BJšœœ˜Jšœ˜J˜—šŸœ œœœœ œœ˜MJšœZ™ZJš œœœœœ œ ˜AJšœ"˜"šœ œ ˜ Jšœœ#˜=JšœK˜OJ˜——Jš Ÿœœ œœœ˜BJ˜JšœK™KJšœ™JšœK™KJ˜J˜Jšœœœ˜Jšœ œœ˜J˜š œœ˜7JšœH™HJšœœ˜Jšœ˜Jš œœœœœœ˜+Jšœ œœ˜Jš œ œœœœ˜=Jšœœœ˜OJšœœœœ˜Bš œœ œœœ˜3Jšœ œœ˜>—š œœ œœœ˜0Jšœ œœ˜>—š œœœ œœ˜6Jšœ/˜3—˜Jšœœ˜%Jšœ ˜ Jšœ ˜ Jšœ˜—Jšœ˜J˜—š  œœ˜+Jšœ˜ Jšœœ˜Jšœ˜Jšœœœœ˜1Jšœœœœ˜:Jšœœœœœœœ˜LJšœœ,˜;Jšœœ˜Jšœ˜J˜—š Ÿœœœ œ&œ˜IJšœ#œ˜3Jš˜J˜Jšœœœœ˜'Jšœœœ˜#Jšœ œœ˜šœœ œ˜ Jšœœ œ˜6šœœ"œ˜9Jšœœ œ˜/Jšœœ œž˜L——Jšœœ'˜1J˜šœ œœ˜Jš œœœœœ˜T—J˜)Jšœ˜J˜—š Ÿœœœ œEœž$˜ŸJšœ˜ Jšœ œ œ˜6J˜Jšœœ˜Jšœœ˜šŸœ œœ˜GJšœœ œ˜%—šŸ œ œœ˜>Jšœœ ˜JšœE™EJšœN™Nš œœ œ œœœ˜MJšœœœ*™UJš œœœœœ˜A—Jšœ ˜Jšœ˜—Jšœœœ˜J˜J˜J˜J˜Jšœœ ˜$šœœ œ˜/Jšœ™Jšœ&™&Jšœœœ˜IJ˜)šœ œ-˜>Jšœž˜2—J˜J˜%Jšœ˜—šœ˜Jšœ)œ˜/Jšœ œ˜—Jšœ˜—J˜J˜ J˜JšœC™CJšœ+™+Jšœ,™,Jšœ9™9J™Jšœ˜J™šœD™DJšœG™GJšœ+™+J˜$JšœF™FJ˜#Jšœ?™?J˜$Jšœ<™