DIRECTORY CachedSpace USING [Handle], DCSFileTypes USING [tLeaderPage], Directory USING [CreateFile, Error, LookupUnlimited], Environment USING [PageNumber, wordsPerPage], File USING [Capability, GetSize, nullCapability, PageCount, SetSize], Frame USING [GetReturnFrame], Heap USING [MakeString], Inline USING [LongCOPY], LongString USING [AppendString, CompareStrings], PrincOps USING [BytePC, FrameHandle, GFTIndex], Process USING [ -- are these all resident procedures? GetPriority, Priority, priorityForeground, SetPriority], Space USING [defaultWindow, Create, Handle, LongPointerFromPage, Map, nullHandle, Unmap, virtualMemory, VMPageNumber, WindowOrigin], -- are these all resident procedures? SpyLog USING [Entry], SpecialSpace USING [ MakeResident, MakeSwappable, MakeCodeResident, MakeCodeSwappable, MakeGlobalFrameResident, MakeGlobalFrameSwappable], System USING [GetClockPulses]; SpyLogImpl: MONITOR IMPORTS Directory, File, Frame, Heap, Inline, LongString, Process, Space, SpecialSpace, System EXPORTS SpyLog = BEGIN OPEN Environment, SpyLog; Error: SIGNAL = CODE; logging: PUBLIC BOOLEAN _ FALSE; busy: PUBLIC BOOLEAN _ FALSE; skips: CARDINAL _ 0; spy: PUBLIC BOOLEAN _ FALSE; -- set up a second log so the spy can spy on itself fileName: STRING _ "spy.data"; nullBuffer: Buffer = [FALSE, Space.nullHandle, Space.defaultWindow, 0]; Buffer: TYPE = RECORD[ mapped: BOOLEAN, space: Space.Handle, window: Space.WindowOrigin, VMpage: PageNumber]; readLog, writeLog: Log _ NIL; logDesc, spylogDesc: LogDesc _ []; Log: TYPE = POINTER TO LogDesc; LogDesc: TYPE = RECORD[ base: EntryBasePointer _ NIL, circular: BOOLEAN _ FALSE, -- is this a ring buffer? logging: BOOLEAN _ FALSE, -- are we reading or writing? readerPage: PageNumber _ 1, writerPage: PageNumber _ 1, reader: EntryPointer _ FIRST[EntryPointer], writer: EntryPointer _ FIRST[EntryPointer], limit: EntryPointer _ FIRST[EntryPointer], filename: LONG STRING _ NIL, buffer: Buffer _ nullBuffer, next: Buffer _ nullBuffer, bufferSize: CARDINAL _ 0, bufferProcess: PROCESS _ NIL]; swapUnitSize: CARDINAL = 10; wordsPerUnit: CARDINAL = swapUnitSize*wordsPerPage; unitThreshold: CARDINAL _ wordsPerUnit - 64; appendThreshold: CARDINAL _ 0; Initialize: PUBLIC PROCEDURE[name: STRING, pages: CARDINAL, append: BOOLEAN] = BEGIN skips _ 0; busy _ FALSE; IF name = NIL THEN name _ fileName; readLog _ writeLog _ @logDesc; Create[readLog, name, pages, append]; END; Create: PROCEDURE[log: Log, name: STRING, pages: CARDINAL, append: BOOLEAN] = BEGIN OPEN log; newFile: BOOLEAN _ FALSE; cap: File.Capability _ File.nullCapability; Unmap[@buffer]; Unmap[@next]; -- in case the log never got closed IF name = NIL THEN Error; IF pages < swapUnitSize THEN pages _ swapUnitSize; SELECT TRUE FROM append = circular => newFile _ TRUE; pages # bufferSize => newFile _ TRUE; filename = NIL => newFile _ TRUE; LongString.CompareStrings[name, filename] # 0 => newFile _ TRUE; ENDCASE; IF newFile THEN cap _ Directory.CreateFile[name, DCSFileTypes.tLeaderPage, 5*pages ! Directory.Error => CONTINUE]; IF cap = File.nullCapability THEN cap _ Directory.LookupUnlimited[name]; File.SetSize[cap, pages*2]; IF buffer = nullBuffer THEN buffer.space _ Space.Create[size: pages, parent: Space.virtualMemory]; IF next = nullBuffer THEN next.space _ Space.Create[size: pages, parent: Space.virtualMemory]; buffer.window.file _ cap; buffer.window.base _ 0; next.window.file _ cap; next.window.base _ 0; base _ NIL; circular _ NOT append; logging _ FALSE; readerPage _ writerPage _ 1; -- skip leader page reader _ writer _ FIRST[EntryPointer]; limit _ writer + pages*wordsPerPage - Entry.SIZE; IF filename = NIL THEN filename _ Heap.MakeString[,20]; filename.length _ 0; LongString.AppendString[filename, name]; bufferSize _ pages; bufferProcess _ NIL; appendThreshold _ pages/4; IF wordsPerUnit-unitThreshold NOT IN [0..256) THEN Error; IF swapUnitSize = 0 THEN Error; END; Open: PUBLIC ENTRY PROCEDURE[write: BOOLEAN, reset: BOOLEAN] = BEGIN SpecialSpace.MakeGlobalFrameResident[SpyLogImpl]; SpecialSpace.MakeCodeResident[SpyLogImpl]; IF ~spy THEN readLog _ writeLog _ @logDesc; IF writeLog.bufferSize = 0 THEN Initialize[NIL, 40, TRUE]; IF ~write AND spy AND readLog # writeLog THEN { readLog _ writeLog; spy _ FALSE}; IF ~write AND spy THEN { readLog _ @logDesc; writeLog _ @spylogDesc; Create[writeLog, "traceSpy.data"L, readLog.bufferSize, TRUE]; OpenLog[writeLog, TRUE, FALSE]}; OpenLog[IF write THEN writeLog ELSE readLog, write, reset]; logging _ writeLog.logging; END; Close: PUBLIC ENTRY PROCEDURE = BEGIN CloseLog[readLog]; IF writeLog # readLog THEN CloseLog[writeLog]; SpecialSpace.MakeGlobalFrameSwappable[SpyLogImpl]; SpecialSpace.MakeCodeSwappable[SpyLogImpl]; END; OpenLog: PROCEDURE[log: Log, write, reset: BOOLEAN] = BEGIN OPEN log; temp: Buffer; IF reset THEN { log.reader _ FIRST[EntryPointer]; log.readerPage _ 1}; IF logging = write AND ~reset THEN RETURN; -- already open CloseLog[log]; logging _ write; NewBuffer[log, IF write THEN writerPage ELSE readerPage]; temp _ buffer; buffer _ next; next _ temp; base _ LOOPHOLE[LongPointer[buffer.space]]; IF write AND writer = FIRST[EntryPointer] AND writerPage = 1 THEN {base[writer] _ [nullEntry[1]]; writer _ writer + 1}; END; CloseLog: PROCEDURE[log: Log] = BEGIN OPEN log; IF logging THEN base[writer] _ [endOfLog[]]; IF log.bufferProcess # NIL THEN JOIN bufferProcess; log.bufferProcess _ NIL; Unmap[@buffer]; Unmap[@next]; logging _ FALSE; base _ NIL; END; ForkNewBuffer: PROCEDURE[log: Log, page: PageNumber] = BEGIN OPEN log; priority: Process.Priority _ Process.GetPriority[]; IF priority > Process.priorityForeground THEN Process.SetPriority[Process.priorityForeground]; bufferProcess _ FORK NewBuffer[log, page]; IF priority > Process.priorityForeground THEN Process.SetPriority[priority]; END; NewBuffer: PROCEDURE[log: Log, page: PageNumber] = BEGIN OPEN log; Unmap[@next]; next.window.file _ buffer.window.file; IF next.window.file = File.nullCapability THEN next.window.file _ Directory.LookupUnlimited[filename]; next.window.base _ page; IF logging THEN { current: File.PageCount _ File.GetSize[next.window.file]; IF current < (page + log.bufferSize) THEN File.SetSize[next.window.file, page + log.bufferSize]}; -- expensive Map[buffer: @next]; next.VMpage _ Space.VMPageNumber[next.space]; END; NextBuffer: PROCEDURE[log: Log] = BEGIN OPEN log; temp: Buffer; IF logging AND circular AND ~LessThan[reader,writer] THEN reader _ Next[log, FIRST[EntryPointer]]; IF logging AND circular THEN base[writer] _ [endOfLog[]]; IF NOT circular THEN { IF logging THEN base[writer] _ [nullEntry[limit-writer]]; IF bufferProcess = NIL THEN -- shouldn't happen ForkNewBuffer[log, (IF logging THEN writerPage ELSE readerPage) + bufferSize]; JOIN bufferProcess; bufferProcess _ NIL; temp _ buffer; buffer _ next; next _ temp; base _ LOOPHOLE[LongPointer[buffer.space]]; IF logging THEN writerPage _ writerPage + bufferSize ELSE readerPage _ readerPage + bufferSize}; IF logging THEN writer _ FIRST[EntryPointer] ELSE reader _ FIRST[EntryPointer]; END; LongPointer: PROCEDURE[space: Space.Handle] RETURNS[LONG POINTER] = INLINE BEGIN -- Space.LongPointer is an ENTRY procedure. handle: CachedSpace.Handle = LOOPHOLE[space]; RETURN[Space.LongPointerFromPage[handle.page]]; END; Map: PROCEDURE[buffer: POINTER TO Buffer] = INLINE BEGIN IF buffer.mapped THEN RETURN; buffer.mapped _ TRUE; Space.Map[buffer.space, buffer.window]; SpecialSpace.MakeResident[buffer.space]; END; Unmap: PROCEDURE[buffer: POINTER TO Buffer] = INLINE BEGIN IF ~buffer.mapped THEN RETURN; buffer.mapped _ FALSE; SpecialSpace.MakeSwappable[buffer.space]; Space.Unmap[buffer.space]; END; EntryBasePointer: TYPE = LONG ORDERED BASE POINTER; EntryPointer: TYPE = EntryBasePointer RELATIVE POINTER [0..177777B] TO Entry; WriteTrace: PUBLIC ENTRY PROCEDURE [gfi: PrincOps.GFTIndex, pc: PrincOps.BytePC] = BEGIN OPEN writeLog; writerNext: EntryPointer; IF ~logging THEN RETURN; IF busy THEN {skips _ skips + 1; RETURN}; IF ~LessThan[writer+SIZE[trace Entry],limit] THEN NextBuffer[writeLog]; writerNext _ writer + SIZE[trace Entry]; IF circular THEN WHILE ~LessThan[reader,writer+1] AND LessThan[reader,writerNext+1] DO reader _ Next[writeLog, reader]; ENDLOOP; IF gfi = 0 AND pc = 0 THEN { frame: PrincOps.FrameHandle = Frame.GetReturnFrame[]; gfi _ frame.accesslink.gfi; pc _ frame.pc}; base[writer] _ [trace[,gfi,pc,System.GetClockPulses[]]]; SwapperHints[writer, writerNext]; writer _ writerNext; END; WriteData: PUBLIC ENTRY PROCEDURE [type, size: CARDINAL, data: LONG POINTER] = BEGIN OPEN writeLog; writerNext: EntryPointer; IF ~logging THEN RETURN; IF busy THEN {skips _ skips + 1; RETURN}; IF size > 8000 THEN Error; IF ~LessThan[writer+SIZE[Entry]+size,limit] THEN NextBuffer[writeLog]; writerNext _ writer+SIZE[Entry]+size; IF circular THEN WHILE ~LessThan[reader,writer+1] AND LessThan[reader,writerNext+1] DO reader _ Next[writeLog, reader]; ENDLOOP; base[writer] _ [data[size,type,System.GetClockPulses[],]]; WITH e:base[writer] SELECT FROM data => Inline.LongCOPY[data,size,@e.data]; ENDCASE => Error; SwapperHints[writer, writerNext]; writer _ writerNext; END; SwapperHints: PROCEDURE[old, new: EntryPointer] = INLINE BEGIN OPEN writeLog; -- SwapperHints only used for writing newPage:CARDINAL=LOOPHOLE[new, CARDINAL]/wordsPerPage; IF ~circular AND newPage >= appendThreshold AND bufferProcess = NIL THEN ForkNewBuffer[writeLog, writerPage + bufferSize]; -- preallocate the next buffer END; LessThan: PROCEDURE[a,b: UNSPECIFIED] RETURNS[BOOLEAN] = INLINE BEGIN RETURN[LOOPHOLE[a,CARDINAL] IF circular THEN b _ FIRST[EntryPointer] -- wrap around ELSE b _ a; trace => b _ a + SIZE[trace Entry]; data => b _ a + SIZE[data Entry] + entry.size; nullEntry => b _ a + entry.size; ENDCASE => Error; IF base[b].type = nullEntry THEN b _ limit; IF LessThan[b, limit] THEN RETURN; IF logging THEN Error; -- shouldn't cause a NextBuffer!! NextBuffer[log]; b _ FIRST[EntryPointer]; IF base[b].type = nullEntry THEN Error; END; END . . . zSpyLogImpl.mesa Last Modified by: John Maxwell April 5, 1983 10:47 am ****************************************************************** initialization, opening and closing of the log ****************************************************************** we will swap in the next swap-unit/buffer whenever we are past the threshold in the current swap-unit/buffer. create a backing file (if necessary) create new spaces initialize the LogDesc test invariants read from the spy log set up a spy log create a new buffer starting with filePage page. may have to move the reader may have to install a new buffer move the appropriate index ****************************************************************** reading and writing on the log ****************************************************************** are we at the end of the buffer? are we about to overwrite the reader? write out the entry. give hints to the swapper are we at the end of the buffer? are we about to overwrite the reader? write out the entry. give hints to the swapper IF base[reader].type = endOfLog AND (reader # writer OR readerPage # writerPage) THEN SIGNAL Error; ÊJ˜J˜Jšœ™Jšœ6™6J˜šÏk ˜ Jšœ œ ˜Jšœ œ˜!Jšœ œ&˜5Jšœ œ˜-Jšœœ;˜EJšœœ˜Jšœœ˜Jšœœ ˜Jšœ œ ˜0Jšœ œ!˜/šœœÏc%˜EJ˜8—šœœ˜$J˜DJšœž%˜C—Jšœœ ˜šœ œ˜J˜J˜%J˜3—Jšœœ˜—J˜šœ ˜šœ˜J˜;J˜—Jšœ ˜—Jš˜Jšœ˜J˜Jšœœœ˜J˜Jšœ œœœ˜ Jšœœœœ˜Jšœœ˜Jšœœœœž3˜PJšœ œ˜J˜JšœB™BJšœ.™.JšœB™BJ˜Jšœœ,˜Gšœœœ˜Jšœœ˜J˜J˜J˜J˜—Jšœœ˜J˜"Jšœœœœ ˜šœ œœ˜Jšœœ˜Jšœ œœž˜4Jšœ œœž˜7J˜J˜Jšœœ˜+Jšœœ˜+Jšœœ˜*Jšœ œœœ˜J˜J˜Jšœ œ˜Jšœœœ˜J˜—Jšœœ˜Jšœœ˜3Jšœœ˜-Jšœœ˜Jšœ*™*Jšœ#™#Jšœ ™ J˜š Ïn œœ œœ œ œ˜NJšœ˜J˜ Jšœœ˜ Jšœœœ˜#J˜J˜%Jšœ˜J˜—š Ÿœ œœ œ œ˜MJšœœ˜Jšœ œœ˜J˜+Jšœž#˜AJšœ$™$Jšœœœ˜Jšœœ˜2šœœ˜Jšœœ˜$Jšœ œ˜%Jšœ œœ˜!Jšœ;œ˜@Jšœ˜ —šœ œD˜SJšœœ˜—Jšœœ'˜HJ˜Jšœ™JšœœG˜bJšœœE˜^J˜J˜J˜Jšœ˜Jšœ™Jšœœ˜ Jšœ œ˜Jšœ œ˜Jšœž˜0Jšœœ˜&Jšœ,œ˜1Jšœ œœ!˜7J˜J˜(J˜Jšœœ˜J˜Jšœ™Jšœœœ œ˜9Jšœœ˜Jšœ˜—J˜š Ÿœœœ œœ œ˜>Jš˜J˜1J˜*Jšœœ˜+Jšœœ œœ˜:šœœœœ˜/Jšœ™J˜Jšœœ˜ —šœœœ˜Jšœ™J˜J˜Jšœ7œ˜=Jšœœœ˜ —Jšœœœ œ˜;J˜Jšœ˜J˜—šŸœœœ œ˜Jš˜J˜Jšœœ˜.J˜2J˜+Jšœ˜—J˜šŸœ œœ˜5Jšœœ˜J˜ šœœ˜Jšœ œ˜!J˜—Jš œœœœž˜:J˜J˜Jšœœœ œ ˜9J˜*Jšœœ˜+šœœ œœ˜=Jšœ6˜:—Jšœ˜J˜—šŸœ œ ˜Jšœœ˜Jšœ œ˜,Jšœœœœ˜3Jšœœ˜J˜Jšœ œ˜Jšœœ˜ Jšœ˜—J˜šŸ œ œ˜7Jšœœ˜J˜3Jšœ'œ1˜^Jšœœ˜*Jšœ'œ˜LJšœ˜J˜—šŸ œ œ˜2Jšœœ˜Jšœ0™0J˜ J˜&šœ(˜*Jšœ8˜<—J˜šœ œ˜J˜9šœ#˜%Jšœ:ž ˜J——J˜J˜-Jšœ˜J˜—šŸ œ œ ˜"Jšœœ˜J˜ Jšœ™šœ œ œœ˜:Jšœœ˜(—Jšœ œ œ˜9Jšœ ™ šœœ œ˜Jšœ œ*˜9šœœœž˜/Jšœœ œ œ˜N—Jšœ˜Jšœœ˜J˜*Jšœœ˜+šœ ˜ Jšœ%˜)Jšœ'˜+——Jšœ™šœ ˜ Jšœ œ˜"Jšœ œ˜"—Jšœ˜—J˜š Ÿ œ œœœœ˜CJšœœž+˜9Jšœœ˜-Jšœ)˜/Jšœ˜J˜—šŸœ œ œœ ˜+Jšœ˜ Jšœœœ˜Jšœœ˜J˜'J˜(Jšœ˜J˜—šŸœ œ œœ ˜-Jšœ˜ Jšœœœ˜Jšœœ˜J˜)J˜Jšœ˜—J˜JšœB™BJšœ™JšœB™BJ˜Jš œœœœœœ˜3Jš œœœœœ˜MJ˜šŸ œœœ œ0˜RJšœœ ˜J˜Jšœ œœ˜Jšœœœ˜)Jšœ ™ Jšœœœ˜GJšœ%™%Jšœœ˜(šœ œœ˜2Jšœ˜$J˜ Jšœ˜—Jšœ™šœ œœ˜J˜5J˜+—J˜8Jšœ™J˜!J˜Jšœ˜J˜—šŸ œœœ œœœœ˜NJšœœ ˜J˜Jšœ œœ˜Jšœœœ˜)Jšœ œ˜Jšœ ™ Jšœœœ˜FJšœ%™%Jšœœ ˜%šœ œœ˜2Jšœ˜$J˜ Jšœ˜—Jšœ™J˜:šœœ˜J˜+Jšœ ˜—Jšœ™J˜!J˜Jšœ˜—J˜šŸ œ œ˜2Jšœ˜ Jšœ ž%˜4Jšœœœœ˜6šœ œœœ˜DJšœ3ž˜U—Jšœ˜J˜—š Ÿœ œ œœœ˜?Jšœœœœœœœ˜=J˜—š Ÿœ œ œœœ˜@Jšœ˜Jšœœ˜Jšœœ œ˜#Jšœ˜Jšœ˜—J˜Jšž ˜ J˜šŸ œœœ œœœœœ ˜EJšœœ ˜Jšœ œ˜J˜šœœ ˜-Jšœ!ž˜3—šœ%™%Jšœ-™-Jšœ™—Jšœ˜Jšœ˜J˜—šŸœ œœ˜FJšœœ˜šœœ˜šœ œ ˜Jšœœž˜+Jšœ˜ —Jšœœ˜#Jšœœ˜.J˜ Jšœ ˜—Jšœœ ˜+Jšœœœ˜"Jšœ œž!˜8J˜Jšœœ˜Jšœœ˜'Jšœ˜—J˜Jšœ˜ J˜J˜—…—)º<~