DIRECTORY AllocatorOps USING [BlockSize, REFToHP], Basics USING [BITAND], BasicTime USING [GetClockPulses, Pulses], 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, InvisibleProcesses, OpenForWrite, WriteData], SpyOps USING [Count, DataType, Frame, SetAllocationBreak, SpyState, Stack, stackHeader, stackLength, StackType], SystemVersion USING [machineType], Terminal USING [Current, Virtual, WaitForBWVerticalRetrace], VM USING [AddressFault, PageNumberForAddress], VMStatistics USING [pageFaults]; SpyKernelImpl: MONITOR IMPORTS AllocatorOps, Basics, BasicTime, DebuggerSwap, Loader, PrincOpsUtils, Process, SafeStorage, SpyLog, SpyOps, SystemVersion, Terminal, VM, VMStatistics EXPORTS SpyClient, SpyOps = { OPEN PrincOps, Rope; PsbIndex: TYPE = PrincOps.PsbIndex; Ticks: TYPE = PrincOps.Ticks; spyState: PUBLIC SpyOps.SpyState _ off; watching: PUBLIC SpyOps.DataType _ CPU; justMe: PUBLIC PsbIndex _ 0; freqDivisor: PUBLIC NAT _ 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 PROC[dataType: SpyClient.DataType _ CPU, process: PsbIndex _ PrincOps.PsbNull, spyOnSpyLog: BOOL _ FALSE, frequencyDivisor: NAT _ 1] RETURNS[errorMsg: ROPE] = { DisableSpy[]; IF dataType IN [allocations..wordsAllocated] THEN errorMsg _ SpyOps.SetAllocationBreak[]; IF errorMsg # NIL THEN RETURN[errorMsg]; IF frequencyDivisor < 1 THEN RETURN ["0 is a stupid frequency divisor"]; InitializeTables[]; watching _ dataType; justMe _ IF dataType = process THEN process ELSE 0; freqDivisor _ frequencyDivisor; allocWait _ allocDivisorMinusOne _ freqDivisor - 1; SpyLog.OpenForWrite[spyOnSpyLog]; EnableSpy[]; }; InitializeTables: PROC = { runningTime _ 0; code _ data _ 0; wordsAllocated _ wordsReclaimed _ 0; }; Initialize: PROC = { Loader.MakeProcedureResident[Spy]; Loader.MakeProcedureResident[Record]; Loader.MakeProcedureResident[UserBreak]; Loader.MakeProcedureResident[PageFaultRecorder]; Loader.MakeGlobalFrameResident[Initialize]; Process.InitializeCondition[@processDead, 10000]; SetTimeouts[]; }; SetTimeouts: PROC = { ticks: Ticks; cutoff: Ticks; found: BOOLEAN; frame: PrincOps.FrameHandle; IF index = maxIndex THEN RETURN; 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, frame.pc] THEN {found _ TRUE; EXIT}; ENDLOOP; IF found THEN LOOP; timeout[index] _ [frame.accesslink, frame.pc]; index _ index + 1; IF index = maxIndex THEN EXIT; ENDLOOP; }; 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 = { 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]; }; StopCounting: PUBLIC ENTRY FastBreak.FastBreakProc = { 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]; }; DisableSpy: PROC = { IF spyState = disabled THEN RETURN; oldSpyState _ spyState; spyState _ disabled; }; EnableSpy: PROC = {spyState _ oldSpyState}; justMePrincOps: LONG POINTER TO ProcessStateBlock _ NIL; baseWait: CARDINAL _ SELECT SystemVersion.machineType FROM dorado => 1, dolphin, dandelion, dicentra => 8, ENDCASE => ERROR; wait: CARDINAL _ 0; -- number of vertical retraces to skip before next sampling monitor: BOOLEAN _ FALSE; -- measure performance of spy maxStackDepth: CARDINAL = 200; timeout: ARRAY [0..maxIndex) OF RECORD[gfh: PrincOps.GlobalFrameHandle, pc: CARDINAL]; maxIndex: CARDINAL = 25; index: CARDINAL _ 0; sampleInterval: INT _ 10*1000; -- measured in microseconds screen: Terminal.Virtual = Terminal.Current[]; searchReadyList: BOOLEAN _ TRUE; -- sometimes the user gets the machine gets into a state where searching the ready list is a bad idea. This boolean allows the user to stop the Spy from searching the ready list. Spy: PROC = { top: PsbIndex; frame: FrameHandle; myPrincOps: PsbHandle = PrincOpsUtils.ReadPSB[]; handleMask: PsbLink = [ failed: FALSE, priority: 0, next: LAST[PsbIndex], reserved: 0, vector: FALSE]; waitPeriodMinusOne: CARDINAL = freqDivisor * baseWait - 1; NextHandle: PROC [link: CARDINAL] RETURNS [PsbHandle] = INLINE { RETURN[LOOPHOLE[Basics.BITAND[link, LOOPHOLE[handleMask]]]]}; SearchReadyList: PROC = INLINE { 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] = [NIL, 0] THEN EXIT; IF timeout[i] # [frame.accesslink, frame.pc] THEN LOOP; skip _ TRUE; EXIT; ENDLOOP; IF skip THEN LOOP; top _ PrincOpsUtils.PsbHandleToIndex[current]; EXIT; ENDLOOP; PrincOpsUtils.EnableInterrupts[]; }; spyState _ on; wait _ waitPeriodMinusOne; DO Terminal.WaitForBWVerticalRetrace[screen]; IF active <= 0 THEN EXIT; IF spyState = disabled THEN LOOP; IF wait > 0 THEN {wait _ wait - 1; LOOP} ELSE wait _ waitPeriodMinusOne; IF monitor THEN SpyLog.Here[]; IF justMe = 0 OR searchReadyList THEN 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 searchReadyList AND 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]; }; OnQueue: PROC[psbi: PsbIndex, queueHandle: PrincOps.QueueHandle] RETURNS[BOOL] = INLINE { 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! -- }; recordPageFaulted: BOOLEAN _ FALSE; Data: TYPE = RECORD[process: CARDINAL, page: INTEGER]; CodeBase: TYPE = LONG POINTER TO PACKED ARRAY [0..0) OF PrincOps.op; PageFaultRecorder: PROC = { 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]; }; Xfer: PROC[base: CodeBase, pc: PrincOps.BytePC] RETURNS[BOOLEAN] = INLINE { IF base[pc] IN [PrincOps.zEFC0..PrincOps.zKFCB] THEN RETURN[TRUE]; RETURN[FALSE]; }; LongNakedNotify: PROC [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 PROC = INLINE {NOTIFY processDead}; stack: SpyOps.Stack; break: BOOLEAN _ FALSE; breakAtom: ATOM _ NIL; allocDivisorMinusOne: CARDINAL _ 0; allocWait: CARDINAL _ 0; AllocationBreak: PUBLIC FastBreak.FastBreakProc = { words: CARDINAL; type: SpyOps.StackType; GetWordsFromStack: PROC = INLINE { blockSize: INT = AllocatorOps.BlockSize[AllocatorOps.REFToHP[LOOPHOLE[@sv.stk[0], POINTER TO REF ANY]^]]; words _ MAX[MIN[blockSize, CARDINAL.LAST], 0]; }; IF active = 0 THEN RETURN; IF watching NOT IN [allocations..wordsAllocated] THEN RETURN; IF allocWait > 0 THEN {allocWait _ allocWait - 1; RETURN} ELSE allocWait _ allocDivisorMinusOne; words _ 1; type _ 0; SELECT data FROM NIL => IF watching = wordsAllocated THEN GetWordsFromStack[]; LOOPHOLE[$Permanent, LONG POINTER] => { type _ 1; IF watching = wordsAllocated THEN GetWordsFromStack[]}; LOOPHOLE[$Unsafe, LONG POINTER] => { type _ 2; IF watching = wordsAllocated THEN GetWordsFromStack[]}; ENDCASE => NULL; IF break AND data = LOOPHOLE[breakAtom, LONG POINTER] THEN DebuggerSwap.CallDebugger["Allocation break"L]; Record[psbi: LOOPHOLE[Process.GetCurrent[]], type: type, frame: frame, count: words]; }; UserBreak: PUBLIC FastBreak.FastBreakProc = TRUSTED { 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]; }; Record: PUBLIC PROC[psbi: PsbIndex, frame: FrameHandle _ NIL, type: SpyOps.StackType _ 0, count: CARDINAL _ 1] = { recordInner: ENTRY PROC = { 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] }; writeProcess: PROCESS; extendProcess: PROCESS; [writeProcess, extendProcess] _ SpyLog.InvisibleProcesses[]; IF (writeProcess # NIL AND LOOPHOLE[writeProcess, PsbIndex] = psbi) OR (extendProcess # NIL AND LOOPHOLE[extendProcess, PsbIndex] = psbi) THEN RETURN; recordInner[]; }; LogStack: INTERNAL PROC [process: PsbIndex, level: Process.Priority, frame: FrameHandle, count, type: CARDINAL] = { sdLength: CARDINAL = PrincOps.SD[PrincOps.sGFTLength]; gf: GlobalFrameHandle _ NIL; length: CARDINAL _ 0; ValidFrame: PROC [f: ControlLink] RETURNS [FrameHandle] = { OPEN PrincOps; IF f.proc OR f.indirect OR f.frame = NIL OR (gf _ f.frame.accesslink) = NIL OR (LOOPHOLE[gf, CARDINAL] MOD 4) # 0 THEN RETURN[NIL]; RETURN[f.frame] }; IF ~SpyLog.active THEN RETURN; stack.type _ type; stack.count _ count; stack.level _ level; stack.process _ process; frame _ ValidFrame[LOOPHOLE[frame] ! VM.AddressFault => {frame _ NIL; CONTINUE}]; THROUGH [0..maxStackDepth) UNTIL frame = NIL DO IF length >= SpyOps.stackLength THEN {stack.level _ 7; length _ 0; EXIT}; stack.frame[length] _ [gf, 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 ! VM.AddressFault => EXIT]; ENDLOOP; SpyLog.WriteData[@stack, SpyOps.stackHeader + length*SpyOps.Frame.SIZE, SpyOps.Stack.CODE]; }; Initialize[]; }. ’SpyKernelImpl.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Maxwell September 16, 1983 3:44 pm Russ Atkinson March 8, 1985 7:12:36 pm PST Bob Hagmann February 6, 1986 8:53:31 am PST Russ Atkinson (RRA) March 31, 1986 2:41:59 pm PST Mike Spreitzer September 24, 1986 12:04:35 pm PDT IntervalTimer USING [WaitForExpirationInterval], IntervalTimerFace USING [exists], PageMap USING [GetF, flagsVacant], SpyKernel parameters general statistics **************************************************************************** initializing the Spy and data **************************************************************************** IF IntervalTimerFace.exists THEN RETURN; PrincOpsUtils.DisableInterrupts[]; -- stops the clock. NO PAGE FAULTS ALLOWED! PrincOpsUtils.EnableInterrupts[]; **************************************************************************** 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! MAIN LOOP IF IntervalTimerFace.exists THEN IntervalTimer.WaitForExpirationInterval[sampleInterval] ELSE ********************************************************************* watching pagefaults ********************************************************************* stolen from Ben.mesa NO PAGE FAULTS ALLOWED! wait for a page fault: figure out who faulted: wake up the Pilot fault handler: log the fault: is the current pc pointing to some sort of xfer? 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 This break is taken at exit from AllocatorImpl.NewSystemObject, at which time the new REF is the only thing on the stack; so we read it, and get the actual size from the header. Log the stack on the Trace Log -- This procedure is supposed to validate a control link. For various race conditions (presumably), we have observed address faults when the accesslink gets clobbered. Since we can't handle the address faults in here, we insist that the caller be prepared for such an eventuality. (RRA) Note: as a side-effect, this procedure sets 'gf', which is used by the other local procedures below and the main loop of IncrementBucket. 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 Bob Hagmann February 6, 1986 8:34:24 am PST SetTimeouts can be invoked twice. If the index is alread at maxIndex, it tries to store past the end of the timeout array. Moved maxIndex up to 25 from 20 changes to: SetTimeouts Bob Hagmann October 24, 1986 11:29:24 am PDT added calls to WriteProcess to avoid deadlock Κγ˜codešœ™Kšœ Οmœ=™HK–1.5 in tabStopsšœ"™"K–1.5 in tabStopsšœ*™*K–1.5 in tabStops™+K™1K™1—˜šΟk ˜ Kšœ žœ˜(Kšœžœžœ˜Kšœ žœ˜)Kšœ žœ˜"Kšœ žœ˜ Kšœžœ™0Kšœžœ ™!Kšœ ˜ Kšœžœ€˜“Kšœžœ™"Kšœžœ˜ŽKšœžœžœ˜Kšœ žœ$˜5Kšœžœ2˜>Kšœ žœ ˜Kšœžœ=˜IKšœžœd˜pKšœžœ˜"Kšœ žœ.˜Kšžœžœ˜4—Kšœžœ˜šžœžœžœ ž˜ Kšžœ+žœ žœžœ˜GKšžœ˜—Kšžœžœžœ˜K˜.K˜Kšžœžœžœ˜Kšžœ˜—Kšœ!™!Kšœ˜K˜—KšœL™LKšœ™šœL™LK˜Kšœ-™-K˜Kšœ ž œ˜Kšœ žœžœ˜K˜#K˜K˜Kšœžœžœ˜Kšœžœžœ˜#Kšœžœžœ˜#K˜—šΟb œžœžœ˜7K˜K˜Kšžœ žœžœžœžœžœžœ˜HKš žœžœžœžœŸ˜OKšœ*˜*K˜4K˜4K˜'Kšžœžœ žœ˜HKšžœ žœžœ)˜ZKšœŸ9˜HK˜!Kšœ.˜.šžœ ž˜Kšžœ7žœ ˜GKšœ*žœ˜D˜ K™AK˜—Kšžœ˜—K˜Kšžœžœ˜Kšœ˜K˜—š‘ œžœžœ˜6Kš žœ žœžœžœŸ˜AK˜KšžœžœžœŸ˜;Kšœ˜KšœC˜CKšœD˜DK˜VK˜VKšžœžœ˜Kšœ˜K˜—š  œžœ˜Kšžœžœžœ˜#K˜K˜Kšœ˜K˜—Kš  œžœ˜+K˜KšœE™EKšœ™KšœE™EK˜šœ3™3K˜Kš œžœžœžœžœ˜8šœ žœžœž˜:K˜ K˜"Kšžœžœ˜—KšœžœŸ;˜OKšœ žœžœŸ˜7Kšœžœ˜Kš œ žœžœžœ&žœ˜VKšœ žœ˜Kšœž œ˜Kšœžœ Ÿ˜:K˜.KšœžœžœŸ³˜ΤK˜—š œžœ˜ K˜K˜K˜0šœ˜Kšœžœžœ!žœ˜N—Kšœžœ˜:š   œžœžœžœžœ˜@Kšžœžœžœžœ˜=—š œžœžœ˜ Kšœ žœžœ˜K˜$Kšœ"˜"Kšœ˜Kšœžœžœ ˜2KšœžœžœŸ˜Zšžœ'žœžœž˜IKš œžœžœžœžœ ˜7K˜K˜(Kšžœžœžœžœžœžœžœ˜EKšžœ žœžœ˜Kšžœžœžœ˜"Kš žœžœžœ&žœžœŸ ˜QKš œžœ žœžœžœ˜PKšœN™NKšœT™TKšœZ™ZKšœR™RKšœ™Kšœžœ˜ šžœžœžœž˜#Kšžœžœžœžœ˜#Kšžœ+žœžœ˜7Kšœžœžœ˜Kšžœ˜—Kšžœžœžœ˜Kšœ™K˜.Kšžœžœ˜—Kšœ!˜!Kšœ˜—K˜Kšœ˜Kšœ ™ šž˜šœ™KšœB™B—Kšœ*˜*Kšžœ žœžœ˜Kšžœžœžœ˜!Kšžœ žœžœžœ˜HKšžœ žœ˜Kšžœ žœžœ˜8šžœžœž˜K˜!Kšœ-žœŸ ˜CKšœ/žœŸ ˜Ešœžœ&˜:KšœžœŸ˜,—šœžœ(˜Kšœ˜—K˜KšœE™EKšœ™KšœE™E˜Kšœžœžœ˜#Kš œžœžœ žœžœ˜6Kšœ žœžœžœžœžœžœžœ ˜DK˜—š œžœ˜Kšœ™K˜ Kšœžœ˜Kšœ žœ˜Kšœ˜K˜Kšœ!žœ˜%Kšœ6˜6šœžœžœžœ˜:K˜!—š œžœžœžœž œ˜0Kšžœ˜—Kšœž œ˜Kšœžœžœ˜#Kšœ™K˜šžœ˜Kšœ™Kšœ>˜>šžœ?ž˜FKšžœžœ˜ —šžœ5žœŸ ˜Jšžœ˜Kšžœ:žœ˜C˜%Kšœžœ˜——Kšžœ žœžœ˜KšžœŸ˜"—Kšœ™KšœHŸ˜fK˜7Kšœ žœA˜Pšœžœ˜#Kšžœ&˜*Kšžœ˜—Kšœ žœ,žœ˜Hšžœžœž˜Kšžœ=˜@K˜PKšžœ ˜'—Kšœ ™ Kšœ"˜"šžœ.˜0KšžœžœŸ(˜GKšžœ&˜*—Kšœ!˜!Kšœ™Kšžœ žœžœ˜Kšžœžœžœžœ˜KKšœžœ˜!Kšžœ˜—Kšœ žœ˜K˜K˜K˜0Kšœ˜K˜—š  œžœ&žœžœžœ˜KKšœ0™0Kš žœ žœ"žœžœžœ˜BKšžœžœ˜Kšœ˜K˜—š œžœžœžœžœž œžœ˜HKšœZ™ZKš œžœžœžœžœ ˜AKšœ"˜"šžœ˜ Kšžœžœ#˜=KšžœK˜OK˜——Kš  œžœžœžœžœ˜=K˜KšœK™KKšœ™KšœK™KK˜K˜Kšœžœžœ˜Kšœ žœžœ˜Kšœžœ˜#Kšœ žœ˜K˜š‘œžœ˜3KšœH™HKšœžœ˜Kšœ˜š œžœžœ˜"Kš œ žœ/žœ žœžœžœžœ˜iKš œžœžœ žœžœ˜.K˜—Kšžœ žœžœ˜Kš žœ žœžœžœžœ˜=Kšžœžœžœžœ"˜`Kšœ˜šžœž˜šžœžœžœ˜=K™±—šžœ žœžœ˜'Kšœ ˜ Kšžœžœ˜7—šžœ žœžœ˜$Kšœ ˜ Kšžœžœ˜7—Kšžœžœ˜—š žœžœžœ žœžœž˜:Kšœ/˜/—Kšœ žœ@˜UKšœ˜K˜—š‘ œžœžœ˜5Kšœžœ˜Kšœ˜Kšžœžœžœžœ˜1Kšžœžœžœžœ˜:Kšžœžœžœžœžœžœžœ˜LKšœžœ,˜;Kšžœžœ˜Kšœ˜K˜—š  œžœžœ&žœ%žœ ˜ršœ žœžœ˜K˜Kšœžœžœžœ˜'Kšžœžœžœ˜#Kšžœ žœžœ˜šžœžœ˜ Kšžœžœ0˜6šžœžœ2˜9Kšžœžœ žœ˜/Kšžœžœ žœŸ˜L——Kšœžœ'˜1K˜šžœ žœžœ˜Kš œžœžœžœžœ˜T—K˜)K˜—Kšœžœ˜Kšœžœ˜Kšœ<˜KšžœŸ˜2—K˜Kšœ&žœžœ˜?Kšžœ˜—šœ˜Kšœ)žœ˜/Kšœ žœ˜—Kšœ˜—K˜K˜ K˜K™Kšœ˜K™KšœC™CKšœ+™+Kšœ,™,Kšœ9™9™+KšΟr œ‘™œKšœ ’ ™—šœ,™,Kš’œ ’™-——…—>ήbc