<> <> <> <<>> DIRECTORY FS USING [Close, OpenOrCreate, nullOpenFile, OpenFile, Read, SetPageCount, Write], Loader USING [MakeGlobalFrameResident, MakeGlobalFrameSwappable, MakeProcedureResident, MakeProcedureSwappable], PrincOps USING [BytePC, FrameHandle, GFTIndex, PsbHandle, wordsPerPage], PrincOpsUtils USING [GetClockPulses, GetReturnFrame, LongCOPY, ReadPSB], Process USING [GetPriority, Priority, priorityForeground, SetPriority], Rope USING [ROPE], SpyLog, VM USING [AddressForPageNumber, Allocate, Free, Interval, nullInterval, PageNumber, PageNumberForAddress, Pin, Unpin]; SpyLogImpl: MONITOR IMPORTS FS, Loader, PrincOpsUtils, Process, VM EXPORTS SpyLog = BEGIN OPEN SpyLog; active: PUBLIC BOOLEAN _ FALSE; -- true iff we are writing to the log. Error: PUBLIC ERROR = CODE; Log: TYPE = REF LogDesc; -- want it pinned in the global frame LogDesc: TYPE = RECORD [ base: EntryBasePointer _ NIL, index: EntryPointer _ FIRST[EntryPointer], file: FS.OpenFile _ FS.nullOpenFile, fileName: Rope.ROPE _ NIL, filePage: INT _ 0, buffer: VM.Interval _ VM.nullInterval, next: VM.Interval _ VM.nullInterval, bufferProcess: PROCESS _ NIL]; EntryBasePointer: TYPE = LONG ORDERED BASE POINTER; EntryPointer: TYPE = EntryBasePointer RELATIVE POINTER [0..177777B] TO Entry; <<************************************************************>> <<-- control>> <<************************************************************>> <<>> bufferPages: CARDINAL = 40; limit: EntryPointer = LOOPHOLE[bufferPages*PrincOps.wordsPerPage]; log1: Log = NEW[LogDesc _ [fileName: "///Spy/Spy.data"]]; log2: Log = NEW[LogDesc _ [fileName: "///Spy/SpyingOnSpy.data"]]; Init: PROC = BEGIN Pin: PROC[ref: REF, length: CARDINAL] = BEGIN page1: VM.PageNumber = VM.PageNumberForAddress[LOOPHOLE[LOOPHOLE[ref, LONG CARDINAL]-2]]; page2: VM.PageNumber = VM.PageNumberForAddress[LOOPHOLE[LOOPHOLE[ref, LONG CARDINAL]+length]]; VM.Pin[[page1, page2-page1+1]]; END; Pin[log1, LogDesc.SIZE]; Pin[log2, LogDesc.SIZE]; OpenLog[log1]; END; OpenForRead: PUBLIC ENTRY PROC = BEGIN IF readLog # NIL THEN RETURN; -- already have a log IF writeLog # NIL THEN { -- read from the write log FinalizeWriteLog[writeLog]; readLog _ writeLog; writeLog _ NIL}; IF readLog = NIL AND log1.file # FS.nullOpenFile THEN readLog _ log1; -- use the last log IF readLog = NIL THEN { -- create a dummy log (the one on the disk isn't valid) InitializeWriteLog[readLog _ log1]; FinalizeWriteLog[readLog]}; MakeSwappable[]; readLog.filePage _ 0; readLog.index _ limit; END; OpenForWrite: PUBLIC ENTRY PROC[spyOnSpyLog: BOOLEAN] = BEGIN IF ~spyOnSpyLog THEN writeLog _ log1 ELSE { readLog _ log1; readLog.filePage _ 0; readLog.index _ limit; writeLog _ log2}; InitializeWriteLog[writeLog]; MakeResident[]; END; Close: PUBLIC ENTRY PROC = BEGIN <> IF readLog # NIL THEN readLog _ NIL; IF writeLog = log1 THEN {FinalizeWriteLog[writeLog]; writeLog _ NIL}; IF writeLog = NIL THEN MakeSwappable[]; <> <> END; InitializeWriteLog: PROC[log: Log] = BEGIN -- initialize the log for writing IF log.file = FS.nullOpenFile THEN OpenLog[log]; VM.Pin[log.buffer]; VM.Pin[log.next]; log.base _ LOOPHOLE[VM.AddressForPageNumber[log.buffer.page]]; log.index _ FIRST[EntryPointer]; log.filePage _ 0; END; FinalizeWriteLog: PROC[log: Log] = BEGIN IF log.bufferProcess # NIL THEN { -- wait until it is done JOIN log.bufferProcess; log.bufferProcess _ NIL}; log.base[log.index] _ [endOfLog[]]; FS.SetPageCount[log.file, log.filePage + bufferPages]; FS.Write[log.file, log.filePage, bufferPages, log.base]; VM.Unpin[log.buffer]; VM.Unpin[log.next]; END; OpenLog: PROC[log: Log] = BEGIN IF log.file # FS.nullOpenFile THEN RETURN; log.file _ FS.OpenOrCreate[name: log.fileName, pages: 2*bufferPages]; log.buffer _ VM.Allocate[bufferPages]; log.next _ VM.Allocate[bufferPages]; END; CloseLog: PROC [log: Log] = BEGIN IF log.file = FS.nullOpenFile THEN RETURN; FS.Close[log.file]; log.file _ FS.nullOpenFile; VM.Free[log.buffer]; log.buffer _ VM.nullInterval; VM.Free[log.next]; log.next _ VM.nullInterval; END; MakeResident: PROC = BEGIN IF ~active THEN { Loader.MakeGlobalFrameResident[WriteData]; Loader.MakeProcedureResident[WriteData]; Loader.MakeProcedureResident[WriteTrace]; Loader.MakeProcedureResident[NextWriteBuffer]; active _ TRUE}; END; MakeSwappable: PROC = BEGIN IF active THEN { active _ FALSE; Loader.MakeGlobalFrameSwappable[WriteData]; Loader.MakeProcedureSwappable[WriteData]; Loader.MakeProcedureSwappable[WriteTrace]; Loader.MakeProcedureSwappable[NextWriteBuffer]}; END; <<>> <<************************************************************>> <<-- reading and writing>> <<************************************************************>> readLog, writeLog: Log _ NIL; <<>> NextEntry: PUBLIC ENTRY PROC RETURNS[entry: LONG POINTER TO Entry] = BEGIN IF readLog = NIL THEN Error; IF GreaterEqual[readLog.index, limit] THEN { FS.Read[readLog.file, readLog.filePage, bufferPages, readLog.base]; readLog.filePage _ readLog.filePage + bufferPages; readLog.index _ FIRST[EntryPointer]; IF readLog.base[readLog.index].type = nullEntry THEN ERROR}; entry _ @readLog.base[readLog.index]; <> WITH entry: entry SELECT FROM endOfLog => NULL; trace => readLog.index _ readLog.index + SIZE[trace Entry]; data => readLog.index _ readLog.index + SIZE[data Entry] + entry.size; nullEntry => readLog.index _ limit; ENDCASE => ERROR; RETURN[entry]; END; WriteData: PUBLIC ENTRY PROC [data: LONG POINTER, size: [0..8000], type: CARDINAL] = BEGIN entrySize: CARDINAL = SIZE[data Entry] + size; IF writeLog = NIL THEN RETURN; <> IF GreaterEqual[writeLog.index + entrySize, limit] THEN { NextWriteBuffer[writeLog]; writeLog.index _ FIRST[EntryPointer]}; <> writeLog.base[writeLog.index] _ [data[size, type, PrincOpsUtils.GetClockPulses[],]]; WITH e: writeLog.base[writeLog.index] SELECT FROM data => PrincOpsUtils.LongCOPY[data, size, @e.data]; ENDCASE => ERROR; writeLog.index _ writeLog.index + entrySize; END; WriteTrace: PUBLIC ENTRY PROC [gfi: PrincOps.GFTIndex, pc: PrincOps.BytePC] = BEGIN process: PrincOps.PsbHandle; entrySize: CARDINAL = SIZE[trace Entry]; IF writeLog = NIL THEN RETURN; <> IF GreaterEqual[writeLog.index+SIZE[trace Entry], limit] THEN { NextWriteBuffer[writeLog]; writeLog.index _ FIRST[EntryPointer]}; <> IF gfi = 0 AND pc = 0 THEN { frame: PrincOps.FrameHandle = PrincOpsUtils.GetReturnFrame[]; gfi _ frame.accesslink.gfi; pc _ frame.pc}; process _ PrincOpsUtils.ReadPSB[]; writeLog.base[writeLog.index] _ [trace[, gfi, pc, process, PrincOpsUtils.GetClockPulses[]]]; writeLog.index _ writeLog.index + entrySize; END; NextWriteBuffer: PROCEDURE[log: Log] = BEGIN temp: VM.Interval; log.base[log.index] _ [nullEntry[limit-log.index]]; <> IF log.bufferProcess # NIL THEN {JOIN log.bufferProcess; log.bufferProcess _ NIL}; log.bufferProcess _ FORK WriteBuffer[log, log.filePage, log.base]; <> temp _ log.buffer; log.buffer _ log.next; log.next _ temp; log.base _ LOOPHOLE[VM.AddressForPageNumber[log.buffer.page]]; log.filePage _ log.filePage + bufferPages; END; WriteBuffer: PROC[log: Log, filePage: INT, base: EntryBasePointer] = BEGIN priority: Process.Priority _ Process.GetPriority[]; IF priority > Process.priorityForeground THEN Process.SetPriority[Process.priorityForeground]; FS.SetPageCount[log.file, filePage + bufferPages]; FS.Write[log.file, filePage, bufferPages, base]; < Process.priorityForeground THEN Process.SetPriority[priority];>> END; GreaterEqual: PROCEDURE[a,b: UNSPECIFIED] RETURNS[BOOLEAN] = INLINE BEGIN RETURN[LOOPHOLE[a,CARDINAL]>=LOOPHOLE[b,CARDINAL]]; END; Init[]; END. . .