-- SpaceWatch.mesa -- last edited by John Maxwell on February 8, 1983 11:16 am -- last edited by Paul Rovner on February 10, 1983 10:14 am DIRECTORY CachedSpace USING [Desc], CommonSoftwareFileTypes, ConvertUnsafe USING [ToRope], DCSFileTypes, Directory USING [GetProperty, Lookup], File USING [ Capability, GetAttributes, ID, nullCapability, nullID, PageNumber, Unknown, Type], FileCacheImpl USING [GetPageGroup], FileInternal USING [PageGroup], Hierarchy USING [GetDescriptor], IO USING [card, char, CR, CreateViewerStreams, int, STREAM, Put, PutF, rope, SP], KernelFile USING [GetNextFile], PageMap USING [GetF, flagsVacant], PilotFileTypes, Projection USING [Get], PropertyTypes USING [tFileName], Rope USING [Concat, Equal, Find, Length, ROPE, Substr], Space USING [ defaultWindow, GetAttributes, GetWindow, Handle, nullHandle, PageCount, PageNumber, virtualMemory, VMPageNumber, WindowOrigin], SpaceImplInternal USING [EnterSpace], System USING [UniversalID], UserExec USING [CommandProc, RegisterCommand], Volume USING [ID, systemID]; SpaceWatch: PROGRAM IMPORTS ConvertUnsafe, Directory, File, FileCacheImpl, Hierarchy, IO, KernelFile, PageMap, Projection, Rope, Space, SpaceImplInternal, UserExec, Volume SHARES File, PageMap = BEGIN OPEN IO; ROPE: TYPE = Rope.ROPE; cedarVM: File.ID; bootFile: File.ID _ File.nullID; pages: RECORD[cedar, pilot, code, unmapped, unknown, anon, vm, resident, other: INT _ 0]; printIn: BOOL _ FALSE; -- printLargest: BOOL _ FALSE; see PDR ExecPrintSpaces: UserExec.CommandProc = TRUSTED {DoIt[]}; DoIt: PROC[summaryOnly: BOOL _ FALSE] = BEGIN indent: ROPE; count: INT _ 0; stream: IO.STREAM; last: Space.PageNumber _ 0; -- the first page number following the space most recently seen page: Space.PageNumber; vmPages: Space.PageNumber; free: Space.PageCount _ 0; largest: Space.PageCount _ 0; pinned: Space.PageCount _ 0; swappedIn: Space.PageCount _ 0; PrintBackingFiles: PROC[space: Space.Handle, depth: INTEGER] = { default: BOOL; mapped: BOOL; child: Space.Handle; size: Space.PageCount; window: Space.WindowOrigin; in: CARDINAL _ 0; [lowestChild: child, size: size, mapped: mapped] _ Space.GetAttributes[space]; page _ Space.VMPageNumber[space]; -- indicate free regions IF depth = 1 THEN { count _ count + 1; IF NOT summaryOnly AND page > last THEN stream.Put[rope["****** "], int[page - last], rope[" free pages *****\n"]]; free _ free + page - last; largest _ MAX[largest, page - last]; last _ page + size}; -- print line for space IF NOT summaryOnly AND indent.Length # 0 THEN stream.Put[rope[indent]]; IF NOT summaryOnly THEN stream.PutF["page: %5d size: %4d", int[page], int[size]]; -- if unmapped then print any subspaces IF ~mapped THEN { -- indicate subspaces by indentations IF depth = 1 THEN pages.unmapped _ pages.unmapped + size; IF child = Space.nullHandle THEN { -- no subspaces IF NOT summaryOnly THEN stream.Put[rope[" (unmapped)"], char[CR]]; RETURN}; IF NOT summaryOnly THEN stream.Put[char[CR]]; indent _ Rope.Concat[" ", indent]; EnumerateChildren[space, PrintBackingFiles, depth]; indent _ Rope.Substr[indent, 0, indent.Length[]-3]; RETURN}; -- if mapped then print backing file IF depth # 1 THEN pages.unmapped _ pages.unmapped - size; default _ FALSE; window _ Space.GetWindow[space]; IF window = Space.defaultWindow THEN { FindBackingFile: PROC = { desc: CachedSpace.Desc; validSpace, validSwapUnit: BOOL; [validSpace, validSwapUnit] _ Hierarchy.GetDescriptor[@desc, LOOPHOLE[space]]; IF ~(validSpace OR validSwapUnit) OR desc.dataOrFile ~= data THEN ERROR; window _ desc.window}; default _ TRUE; SpaceImplInternal.EnterSpace[FindBackingFile]}; IF NOT summaryOnly THEN stream.Put[rope[" file: "]]; PrintFile[(IF NOT summaryOnly THEN stream ELSE NIL), window.file, window.base, size, default]; IF Projection.Get[page].state = inPinned THEN { IF NOT summaryOnly THEN stream.Put[rope[" (pinned)"]]; swappedIn _ swappedIn + size; pinned _ pinned + size; IF NOT summaryOnly THEN stream.Put[char[CR]]; RETURN}; IF NOT summaryOnly AND child # Space.nullHandle THEN IF Projection.Get[page].hasSwapUnits THEN stream.Put[rope[", swapUnitSize: "], card[Space.GetAttributes[child].size]] ELSE stream.Put[rope[", firstChild: "], card[Space.GetAttributes[child].size]]; FOR i: CARDINAL IN [0..size) DO IF PageMap.GetF[page+i].valueOld.flags # PageMap.flagsVacant THEN in _ in + 1; ENDLOOP; IF NOT summaryOnly AND printIn AND in # 0 THEN stream.Put[rope[", pagesIn: "], card[in]]; swappedIn _ swappedIn + in; IF NOT summaryOnly THEN stream.Put[char[CR]]; -- IF printLargest AND size > 20 AND child # Space.nullHandle THEN { -- indent _ Rope.Concat[" ", indent]; -- EnumerateChildren[space, PrintLargeSpaces, depth]; -- indent _ Rope.Substr[indent, 0, indent.Length[]-3]} }; -- end PrintBackingFiles PrintLargeSpaces: PROC[space: Space.Handle, depth: INTEGER] = { child: Space.Handle; size: Space.PageCount; [lowestChild: child, size: size] _ Space.GetAttributes[space]; IF size < 20 THEN RETURN; IF child # Space.nullHandle THEN EnumerateChildren[space, PrintLargeSpaces, depth] ELSE IF NOT summaryOnly THEN stream.PutF["%gpage: %5d size: %4d (swap unit)\n", rope[indent], int[Space.VMPageNumber[space]], int[size]]}; -- START DoIt HERE -- pages _ []; vmPages _ Space.GetAttributes[Space.virtualMemory].size; stream _ IO.CreateViewerStreams["VM Space Log"].out; stream.Put[rope["SpaceWatch.DoIt["]]; IF summaryOnly THEN stream.Put[rope["summaryOnly: TRUE"]]; stream.Put[rope["]\n"]]; IF NOT summaryOnly THEN stream.Put[rope["'#' indicates a space mapped to Space.defaultWindow.\n\n"]]; cedarVM _ Directory.Lookup["CedarVM.DontDeleteMe"].fID; -- do the work EnumerateChildren[Space.virtualMemory, PrintBackingFiles, 0]; IF NOT summaryOnly THEN stream.Put[rope["****** "], int[vmPages - last], rope[" free pages *****\n"]]; free _ free + vmPages - last; largest _ MAX[largest, vmPages - last]; stream.Put[char[CR]]; stream.Put[rope["General Statistics ...\n"]]; stream.Put[rope[" Total pages in VM = "], int[vmPages], rope[".\n"]]; stream.Put[rope[" Total top level spaces = "], int[count], rope[".\n"]]; stream.Put[rope[" Total pages swapped in = "], int[swappedIn], rope[".\n"]]; stream.Put[rope[" Total pages pinned = "], int[pinned], rope[".\n"]]; stream.Put[rope[" Largest run of free pages = "], int[largest], rope[".\n"]]; stream.Put[rope[" Total free pages = "], int[free], rope[".\n\n"]]; stream.Put[rope["VM allocation breakdown ...\n"]]; stream.Put[rope[" Pilot pages (total never changes) = "], int[pages.pilot], rope[".\n"]]; stream.Put[rope[" Permanently pinned pages with no backing file = "], int[pages.resident], rope[".\n"]]; stream.Put[rope[" Bootfile and .bcd file pages (code, BCD headers, symbols) = "], int[pages.code], rope[".\n"]]; stream.Put[rope[" Pages from other mapped files = "], int[pages.other], rope[".\n"]]; stream.Put[rope[" Pages from files with unknown file types = "], int[pages.unknown], rope[".\n"]]; stream.Put[rope[" Anonymous backing store mapped to VMBackingFile = "], int[pages.vm], rope[".\n"]]; stream.Put[rope[" Anonymous backing store mapped to anonymous files = "], int[pages.anon], rope[".\n"]]; stream.Put[rope[" CedarVMFile pages = "], int[pages.cedar], rope[".\n"]]; stream.Put[rope[" Unmapped pages = "], int[pages.unmapped], rope[".\n\n"]]; stream.Put[rope[" TOTAL = "], int[pages.pilot+pages.resident+pages.code +pages.other+pages.unknown+pages.vm +pages.anon+pages.cedar+pages.unmapped], rope[".\n"]]; END; EnumerateChildren: PROC [space: Space.Handle, proc: PROC [Space.Handle, INTEGER], depth: INTEGER] = BEGIN FOR child: Space.Handle _ Space.GetAttributes[space].lowestChild, Space.GetAttributes[child].nextSibling UNTIL child = Space.nullHandle DO proc[child, depth + 1]; ENDLOOP; END; PrintFile: PROC[stream: IO.STREAM, cap: File.Capability, page: File.PageNumber, size: Space.PageCount, default: BOOL] = BEGIN OPEN DCSFileTypes, PilotFileTypes; type: File.Type; fileName: STRING _ [130]; temporary, fileError: BOOL _ FALSE; IF cap.fID = File.nullID THEN { IF stream # NIL THEN stream.Put[rope["resident"]]; pages.resident _ pages.resident + size; RETURN}; IF cap.fID = cedarVM THEN { IF stream # NIL THEN stream.Put[rope["CedarVM"]]; pages.cedar _ pages.cedar + size; RETURN}; [type, , temporary, ] _ File.GetAttributes[cap ! File.Unknown => {fileError _ TRUE; CONTINUE}]; IF fileError THEN { PrintID[cap.fID, stream]; IF stream # NIL THEN stream.Put[rope[" (File.Unknown)"]]; pages.unknown _ pages.unknown + size; RETURN}; SELECT type FROM tLeaderPage => { name: Rope.ROPE; fileName.length _ 0; Directory.GetProperty[cap, PropertyTypes.tFileName, DESCRIPTOR[fileName, 66]]; name _ ConvertUnsafe.ToRope[fileName]; IF name.Length[] = 0 THEN { Directory.GetProperty[cap, LOOPHOLE[213B], -- tRemoteFileName DESCRIPTOR[fileName, 66]]; name _ ConvertUnsafe.ToRope[fileName]}; IF stream # NIL THEN IF name.Length[] = 0 THEN {PrintID[cap.fID, stream]; stream.Put[rope[" (unnamed)"]]} ELSE stream.Put[rope[name]]; SELECT TRUE FROM name.Find[".bcd", 0, FALSE] > 0 => pages.code _ pages.code + size; name.Equal["Volume.DirectoryTree"] => pages.pilot _ pages.pilot + size; ENDCASE => pages.other _ pages.other + size}; tTempFileList => { IF stream # NIL THEN stream.Put[rope["tTempFileList"]]; pages.pilot _ pages.pilot + size}; tVolumeAllocationMap => { IF stream # NIL THEN stream.Put[rope["VAM"]]; pages.pilot _ pages.pilot + size}; tVolumeFileMap => { IF stream # NIL THEN stream.Put[rope["VFM"]]; pages.pilot _ pages.pilot + size}; CommonSoftwareFileTypes.tDirectory => { IF stream # NIL THEN stream.Put[rope["Pilot Directory File"]]; pages.pilot _ pages.pilot + size}; tTransactionStateFile => { IF stream # NIL THEN stream.Put[rope["TransactionStateFile"]]; pages.pilot _ pages.pilot + size}; tLogicalVolumeRootPage => { IF stream # NIL THEN stream.Put[rope["LogicalVolumeRootPage"]]; pages.pilot _ pages.pilot + size}; tAnonymousFile => { PrintID[cap.fID, stream]; IF stream # NIL THEN stream.Put[rope[" (anon.)"]]; pages.anon _ pages.anon + size}; tVMBackingFile => SELECT page FROM IN [0..40) => {IF stream # NIL THEN stream.Put[rope["MapLog"]]; pages.pilot _ pages.pilot + size}; IN [40..150) => {IF stream # NIL THEN stream.Put[rope["SystemBTrees"]]; pages.pilot _ pages.pilot + size}; ENDCASE => {IF stream # NIL THEN stream.Put[rope["VMBackingFile"]]; pages.vm _ pages.vm + size}; ENDCASE => { IF bootFile = File.nullID THEN bootFile _ cap.fID; -- always the first one IF cap.fID = bootFile THEN {IF stream # NIL THEN stream.Put[rope["BootFile"]]; pages.code _ pages.code + size} ELSE {PrintID[cap.fID, stream]; IF stream # NIL THEN stream.Put[rope[" (unknown type)"]]; pages.unknown _ pages.unknown + size}}; IF default AND stream # NIL THEN stream.Put[rope["#"]]; IF temporary AND stream # NIL THEN stream.Put[rope[" (temp.)"]]; END; PrintID: PROC[id: System.UniversalID, stream: IO.STREAM] = BEGIN idRep: RECORD[a, b, c, d, e: CARDINAL] _ LOOPHOLE[id]; IF stream = NIL THEN RETURN; stream.Put[char[SP], card[idRep.a]]; stream.Put[char[SP], card[idRep.b]]; stream.Put[char[SP], card[idRep.c]]; stream.Put[char[SP], card[idRep.d]]; stream.Put[char[SP], card[idRep.e]]; END; PrintFileMap: PROC = BEGIN success: BOOL; page: File.PageNumber; stream: IO.STREAM; length: LONG CARDINAL; group: FileInternal.PageGroup; volume: Volume.ID _ Volume.systemID; file: File.Capability _ File.nullCapability; stream _ IO.CreateViewerStreams["FileMap.log"].out; stream.Put[rope["SpaceWatch.PrintFileMap[]\n"]]; WHILE (file_KernelFile.GetNextFile[volume,file])#File.nullCapability DO stream.Put[rope["file: "]]; PrintFile[stream, file, 200, 0, FALSE]; stream.Put[rope[" = "]]; page _ 0; DO -- until we run out of page groups -- [success, group] _ FileCacheImpl.GetPageGroup[file.fID, page]; IF ~success THEN EXIT; length _ group.nextFilePage - group.filePage; page _ group.nextFilePage; IF length=0 THEN EXIT; stream.Put[rope["("], card[length], rope[")"]]; ENDLOOP; stream.Put[char[CR]]; ENDLOOP; END; UserExec.RegisterCommand["SpaceWatch", ExecPrintSpaces, "prints the current VM."]; END . . DisplayVAM:PROC = { width:CARDINAL = 200; context:Graphics.Context _ Graphics.NewContext[]; volume:Volume.ID _ Volume.systemID; page,volSize:LONG CARDINAL _ 0; point:Graphics.Box; x,y:LONG CARDINAL _ 0; busy:BOOL_FALSE; getVolSize:LogicalVolume.VolumeAccessProc = { updateMarkers _ FALSE; volSize _ volume.volumeSize}; getVAM:LogicalVolume.VolumeAccessProc = { updateMarkers _ FALSE; busy _ VolAllocMapImpl.AccessVAM[volume, page, FALSE, FALSE]}; [] _ Graphics.SetFat[context, TRUE]; [] _ VolumeImpl.VolumeAccess[@volume, getVolSize, FALSE]; point _ [0,0,width*4,4*(volSize+width-1)/width]; Graphics.SetColor[context, Graphics.white]; Graphics.DrawBox[context, point]; Graphics.SetColor[context, Graphics.black]; FOR page IN [0..volSize) DO [] _ VolumeImpl.VolumeAccess[@volume, getVAM, FALSE]; IF ~busy THEN LOOP; x _ (page MOD width)*4; y _ (page/width)*4; point _ [x,y,x+3,y+3]; Graphics.MoveTo[context, x, y]; Graphics.DrawBox[context, point]; ENDLOOP}; Error:SIGNAL = CODE; GetFileCacheInfo:PROC RETURNS[pinned,notPinned:LONG CARDINAL] = { OPEN FileCacheImpl; file:FileCEptr; pg:PageGroupCEptr; length:LONG CARDINAL; pinned _ notPinned _ 0; FOR file _ LOOPHOLE[FCache.mru], LOOPHOLE[base[file].next] DO IF file=nilCEptr THEN EXIT; FOR pg _ base[file].pgList, LOOPHOLE[base[pg].next] DO IF pg=nilCEptr THEN EXIT; length _ base[pg].group.nextFilePage - base[pg].group.filePage; IF base[pg].pinned AND length>10000 THEN LOOP; -- skip VAM, VFM IF base[pg].pinned THEN pinned _ pinned + length ELSE notPinned _ notPinned + length; ENDLOOP; ENDLOOP}; MeasureFileCache:PROC[init,pinned:BOOL] = { OPEN FileCacheImpl; file:FileCEptr; pg:PageGroupCEptr; length:LONG INTEGER; IF init THEN hist _ ALL[[0,0,0,FALSE]]; boundary _ distribution1; FOR file _ LOOPHOLE[FCache.mru], LOOPHOLE[base[file].next] DO IF file=nilCEptr THEN EXIT; FOR pg _ base[file].pgList, LOOPHOLE[base[pg].next] DO IF pg=nilCEptr THEN EXIT; length _ base[pg].group.nextFilePage - base[pg].group.filePage; IF base[pg].pinned AND length>10000 THEN LOOP; -- skip VAM, VFM IF pinned=base[pg].pinned THEN AddToHistogram[length]; ENDLOOP; ENDLOOP}; MeasureFileMap:PROC = { volume:Volume.ID _ Volume.systemID; success:BOOL; file:File.Capability _ File.nullCapability; group:FileInternal.PageGroup; page:File.PageNumber; length:LONG INTEGER; hist _ ALL[[0,0,0,FALSE]]; boundary _ distribution1; WHILE (file_KernelFile.GetNextFile[volume,file])#File.nullCapability DO page _ 0; DO -- until we run out of page groups -- [success, group] _ GetPageGroup[file, page]; IF ~success THEN EXIT; length _ group.nextFilePage - group.filePage; page _ group.nextFilePage; IF length=0 THEN EXIT; AddToHistogram[length]; ENDLOOP; ENDLOOP}; GetPageGroup:PROC[file:File.Capability,page:File.PageNumber] RETURNS[success:BOOL, group:FileInternal.PageGroup] = { volume:Volume.ID _ Volume.systemID; fileD:FileInternal.Descriptor; getPageGroup:LogicalVolume.VolumeAccessProc = { updateMarkers _ FALSE; [success, group] _ VolFileMapImpl.GetPageGroup[volume, @fileD, page]}; IF ~FileImpl.GetFileDescriptor[@file, @fileD, @volume] THEN ERROR; [success, group] _ FileCacheImpl.GetPageGroup[File.ShowCapability[file].fID, page]; IF success THEN RETURN; [] _ VolumeImpl.VolumeAccess[@volume, getPageGroup, FALSE]}; GetMap:PROC[handle:CachedSpace.Handle] RETURNS[ file:File.Capability,base:File.PageNumber,mapped:BOOL] = { spaceDesc:CachedSpace.Desc; file _ File.nullCapability; base _ 0; [] _ HierarchyImpl.GetDescriptor[@spaceDesc, handle]; mapped _ spaceDesc.state IN [mapped..beingRemapped]; IF ~mapped THEN Error; [file,base] _ spaceDesc.window}; break1:ARRAY [0..10) OF Number; break2:ARRAY [0..10) OF Number; nil:RECORD[descs,pages:INTEGER]; MeasureSwapUnits:PROC = { OPEN Space; file:File.Capability _ File.nullCapability; desc:CachedRegion.Desc; spaceDesc:CachedSpace.Desc; size,count,countMapped:CARDINAL; mapped,nilFile:BOOL; group:FileInternal.PageGroup; base,filePage:File.PageNumber; VMpage,start:Space.PageNumber_0; VMMax:Space.PageNumber _ GetAttributes[virtualMemory].size; success:BOOL; hist _ ALL[[0,0,0,FALSE]]; break1 _ break2 _ ALL[0]; nil _ [0,0]; boundary _ distribution2; FOR VMpage IN [0..VMMax) DO IF VMpage>=start THEN { desc _ ProjectionImpl.Get[VMpage]; IF desc.state IN [missing..checkedOut] THEN Error; IF ~(desc.state IN CachedRegion.Mapped) THEN LOOP; [] _ HierarchyImpl.GetDescriptor[@spaceDesc, [desc.levelMapped, VMpage]]; IF VMpage >= spaceDesc.interval.page + spaceDesc.countMapped THEN LOOP; [file, base, mapped] _ GetMap[[desc.levelMapped, VMpage]]; base _ base + (VMpage - spaceDesc.interval.page); countMapped _ spaceDesc.countMapped; [] _ HierarchyImpl.GetDescriptor[@spaceDesc, [desc.level, VMpage]]; IF ~desc.hasSwapUnits THEN size _ MIN[desc.interval.count, countMapped] ELSE size _ spaceDesc.sizeSwapUnit; start _ VMpage + size; nilFile _ File.ShowCapability[file].fID = File.nullID; IF nilFile THEN {nil.descs_nil.descs+1; nil.pages_nil.pages+1; LOOP}; AddToHistogram[size]; count _ 0; filePage _ base; DO -- over the page groups in a desc IF filePage>= base+size THEN EXIT; [success, group] _ GetPageGroup[file, filePage]; IF ~success THEN EXIT; IF group.nextFilePage=group.filePage THEN EXIT; filePage _ group.nextFilePage; count _ count+1; ENDLOOP; SELECT count FROM 0 => {break1[count] _ break1[count]+1; Error}; IN [1..8] => break1[count] _ break1[count]+1; ENDCASE => break1[9] _ break1[9]+1}; IF ~mapped THEN LOOP; -- conditional probabilities IF nilFile THEN {nil.pages_nil.pages+1; LOOP}; AddToBucket[size,histLength]; SELECT count FROM 0 => {break2[count] _ break2[count]+1; Error}; IN [1..8] => break2[count] _ break2[count]+1; ENDCASE => break2[9] _ break2[9]+1; ENDLOOP}; --******************************************************************** --histogram manipulation --******************************************************************** Number:TYPE = LONG INTEGER; extra:CARDINAL = 1; Histogram:TYPE = ARRAY [0..histLength+extra) OF RECORD[ count:Number, total:Number, average:Number, overflow:BOOL]; -- the first bucket is used for totals -- the second bucket is used for underflow -- histLength-1 is used for overflow -- the rest are for the client's use hist:Histogram; histLength:CARDINAL = 21; boundary:ARRAY [0..histLength) OF Number; distribution1:ARRAY [0..histLength) OF Number _ [0,0,1,2,3,4,5,10,15,20,30,40,50,100,150,200,250,300,500,1000,5000]; distribution2:ARRAY [0..histLength) OF Number _ [0,0,1,2,3,4,5,10,15,20,30,40,50,100,150,200,250,300,500,1000,5000]; AddToHistogram:PROC[entry:Number] = INLINE BEGIN AddToBucket[entry,0]; -- totals FOR i:CARDINAL DECREASING IN [2..histLength) DO IF entry= entry THEN RETURN; hist[i].overflow _ TRUE; hist[i].total _ hist[i].total -entry; -- undo END; ComputeAverages:PROC = BEGIN FOR i:CARDINAL IN [0..histLength+extra) DO IF hist[i].count = 0 THEN LOOP; hist[i].average _ hist[i].total/hist[i].count; ENDLOOP; END; Ę ŘJšÄ Īc‹œĪk œžœ4žœ'žœžœ#žœFžœ žœžœžœžœžœžœžœžœžœ5žœžœžœžœžœ žœžœžœ*žœžœžœžœAžœ\žœžœžœžœžœžœžœžœžœžœCžœžœžœžœžœ6žœĪbœžœžœžœžœ žœ žœžœžœ"@œÅĪnœžœžœžœžœjžœŒœžœ žœ*žœžœ žœžœ†žœ>œžœžœ žœžœ"žœžœ žœC(œžœ žœ&œ žœ žœ3žœžœœžœžœ žœ&žœžœ žœžœ žœžœēžœ %œžœ žœ:žœ1žœžœ  œžœPžœKžœžœžœžœžœžœ9žœCžœžœ žœ0žœžœ žœžœžœFžœ'žœ žœžœ žœužœžœ žœžœžœ žœžœ žœžœ žœ0žœ[žœTžœžœžœ žœ žœ;žœžœ žœžœ žœ žœžœVžœžœ žœžœ žœžœ žœžœĀœ œžœžœžœ žœžœ žœ%žœ=žœžœžœžœĄœ[žœXžœ žœIžœžœžœœHžœžœžœ€žœ/žœžœ  œžœžœžœžœžœžœ}žœžœžœžœ   œžœ žœžœ¨žœžœžœ@žœ$žœžœžœžœžœ žœžœMžœžœžœžœ žœžœFžœ]žœžœžœ žœ+žœ žœžœSžœžœžœ(žœcž œFžœžœ(žœœ ž œJžœ žœžœ žœžœFžœ žœžœžœ žœ†žœEžœ žœžœsžœ žœžœcžœ žœžœwžœ žœžœ|žœ žœžœ}žœ žœžœ˜žœ žœžœ^žœžœ žœ žœ žœžœežœžœ žœžœmžœžœ žœžœ`žœ žœžœœžœžœžœ žœžœ\žœ/žœ žœžœažœ žœ žœžœžœ žœ žœžœ"žœ  œžœ!žœžœžœ žœžœžœ žœ žœžœžœžœ'žœ'žœ'žœ'žœžœ   œžœžœžœ)žœžœžœžœ8žœRžœbžœ@žœKžœ;žœ%œQžœ žœžœ`žœ žœžœ@žœžœ žœžœcžœ   œžœžœNžœ$žœžœ$žœžœžœžœIžœižœ7žœžœ%žœ8žœžžœžœžœ6žœ žœžœžœžœ–žœžœžœ  œžœžœžœžœ žœCžœžœ žœžœžœžœžœžœžœžœžœžœ žœ žœžœFžœžœžœžœœžœžœ žœ#žœžœ œžœ žœ žœCžœžœžœžœžœžœ$žœžœžœžœžœžœžœžœžœžœ žœ žœžœFžœžœžœžœœžœžœžœžœ œžœžœžœužœžœ žœžœ#žœ@žœžœ%œ9žœ žœžœQžœ žœžœžœžœ  œžœ/žœ žœ5žœ€žœSžœ5žœžœ\žœ žœžœ9žœ œžœžœ6žœĻžœžœ žœ4žœ žœžœ žœ žœ žœ œžœžœ‹žœžœŗžœ žœžœžœ6žœžœ žœžœžœ1žœ žœžœ žœžœžœžœPžœ;žœžœâžœžœžœ(žœtžœ žœ0žœ=žœ"œžœžœžœ=žœ žœžœžœ#žœžœAžœ žœžœHžœ1žœ$žœ žœžœ œžœ žœžœ-žœžœBžœ.žœ#žœ§œžœžœžœžœžœžœžœžœ?žœœœžœžœžœžœžœ`žœžœS œžœžœžœ œžœžœž œžœžœžœžœžœœ"žœžœ œžœ  œžœžœžœžœžœžœžœžœžœžœžœžœžœRžœžœžœžœ* œžœ œžœžœžœžœžœžœžœžœžœ=žœžœ˜Ļ­—…—V¨`s