<> <> <> <> <> <> <<>> DIRECTORY FS USING [Close, Create, Error, GetInfo, nullOpenFile, OpenFile, Read, SetPageCount, Write], Loader USING [MakeGlobalFrameResident, MakeGlobalFrameSwappable, MakeProcedureResident, MakeProcedureSwappable], PrincOps USING [BytePC, FrameHandle, GlobalFrameHandle, PsbHandle, wordsPerPage], PrincOpsUtils USING [GetClockPulses, GetReturnFrame, LongCopy, ReadPSB], Process USING [Detach, GetCurrent, GetPriority, MsecToTicks, Pause, Priority, priorityForeground, SetPriority, Ticks], 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 = { OPEN SpyLog; active: PUBLIC BOOL _ FALSE; -- true iff we are writing to the log. Error: PUBLIC ERROR = CODE; bufferReady: CONDITION; 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, pages: INT _ 0, doneWriting: BOOL _ FALSE, buffer: VM.Interval _ VM.nullInterval, next: VM.Interval _ VM.nullInterval, bufferProcess: PROCESS _ NIL, bufferProcessDone: BOOL _ FALSE, extendProcess: PROCESS _ NIL]; EntryBasePointer: TYPE = LONG ORDERED BASE POINTER; EntryPointer: TYPE = EntryBasePointer RELATIVE POINTER [0..177777B] TO Entry; <> <<>> bufferPages: CARDINAL = 40; buffersAhead: INT = 8; limit: EntryPointer = LOOPHOLE[bufferPages*PrincOps.wordsPerPage]; log1: Log = NEW[LogDesc _ [fileName: "///Spy/Spy.data"]]; log2: Log = NEW[LogDesc _ [fileName: "///Spy/SpyingOnSpy.data"]]; Init: PROC = { Pin: PROC[ref: REF, length: CARDINAL] = { page1: VM.PageNumber = VM.PageNumberForAddress[LOOPHOLE[LOOPHOLE[ref, LONG CARDINAL]-2]]; page2: VM.PageNumber = VM.PageNumberForAddress[LOOPHOLE[LOOPHOLE[ref, LONG CARDINAL]+length-1]]; VM.Pin[[page1, page2-page1+1]]; }; Pin[log1, LogDesc.SIZE]; Pin[log2, LogDesc.SIZE]; OpenLog[log1]; }; InvisibleProcesses: PUBLIC ENTRY PROC RETURNS [PROCESS, PROCESS] = { RETURN[IF writeLog = NIL THEN NIL ELSE writeLog.bufferProcess, IF writeLog = NIL THEN NIL ELSE writeLog.extendProcess]; }; OpenForRead: PUBLIC ENTRY PROC = { 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; }; OpenForWrite: PUBLIC ENTRY PROC[spyOnSpyLog: BOOL] = { IF ~spyOnSpyLog THEN writeLog _ log1 ELSE { readLog _ log1; readLog.filePage _ 0; readLog.index _ limit; writeLog _ log2}; InitializeWriteLog[writeLog]; MakeResident[]; }; Close: PUBLIC ENTRY PROC = { <> IF readLog # NIL THEN readLog _ NIL; IF writeLog = log1 THEN {FinalizeWriteLog[writeLog]; writeLog _ NIL}; IF writeLog = NIL THEN MakeSwappable[]; }; InitializeWriteLog: PROC[log: Log] = { <> 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; }; FinalizeWriteLog: INTERNAL PROC[log: Log] = { endWords: NAT = SIZE[endOfLog SpyLog.Entry]; end: SpyLog.Entry _ [endOfLog[0]]; IF log.bufferProcess # NIL THEN { WHILE ~log.bufferProcessDone DO WAIT bufferReady; -- drop monitor to avoid deadlock ENDLOOP; JOIN log.bufferProcess; log.bufferProcess _ NIL; log.bufferProcessDone _ FALSE; }; PrincOpsUtils.LongCopy[from: @end, nwords: endWords, to: @log.base[log.index]]; log.doneWriting _ TRUE; Process.Pause[11]; FS.SetPageCount[log.file, log.filePage + bufferPages]; FS.Write[log.file, log.filePage, bufferPages, log.base]; VM.Unpin[log.buffer]; VM.Unpin[log.next]; }; OpenLog: PROC [log: Log] = { IF log.file = FS.nullOpenFile THEN { log.file _ FS.Create[name: log.fileName, pages: 4*bufferPages]; log.buffer _ VM.Allocate[bufferPages]; log.next _ VM.Allocate[bufferPages]; } ELSE { FS.SetPageCount[file: log.file, pages: 4*bufferPages]; }; log.pages _ 4*bufferPages; log.doneWriting _ FALSE; TRUSTED {Process.Detach[log.extendProcess _ FORK LogExtender[log]];}; }; CloseLog: PROC [log: Log] = { 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; }; MakeResident: PROC = { IF ~active THEN { Loader.MakeGlobalFrameResident[WriteData]; Loader.MakeProcedureResident[WriteData]; Loader.MakeProcedureResident[WriteTrace]; Loader.MakeProcedureResident[NextWriteBuffer]; active _ TRUE}; }; MakeSwappable: PROC = { IF active THEN { active _ FALSE; Loader.MakeGlobalFrameSwappable[WriteData]; Loader.MakeProcedureSwappable[WriteData]; Loader.MakeProcedureSwappable[WriteTrace]; Loader.MakeProcedureSwappable[NextWriteBuffer]}; }; <<>> <> readLog, writeLog: Log _ NIL; <<>> NextEntry: PUBLIC ENTRY PROC RETURNS [entry: LONG POINTER TO Entry] = { IF readLog = NIL OR readLog.base = NIL THEN RETURN [NIL]; IF LOOPHOLE[readLog.index, CARDINAL] >= LOOPHOLE[limit, CARDINAL] 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]; }; WriteData: PUBLIC ENTRY PROC [data: LONG POINTER, size: [0..8000], type: CARDINAL] = { entrySizeWithoutData: CARDINAL = SIZE[data Entry]; entrySizeWithData: CARDINAL = entrySizeWithoutData + size; IF writeLog = NIL THEN RETURN; IF writeLog.bufferProcess # NIL AND writeLog.bufferProcess = Process.GetCurrent[] THEN RETURN; -- deadlock avoidance  FilePackage can do allocates during buffer write <> IF LOOPHOLE[writeLog.index+entrySizeWithData, CARDINAL] >= LOOPHOLE[limit, CARDINAL] THEN { NextWriteBuffer[writeLog]; writeLog.index _ FIRST[EntryPointer]; }; { <> ent: SpyLog.Entry _ [data[size: size, rttype: type, timestamp: PrincOpsUtils.GetClockPulses[], data: NULL]]; PrincOpsUtils.LongCopy[from: @ent, nwords: entrySizeWithoutData, to: @writeLog.base[writeLog.index]]; WITH e: writeLog.base[writeLog.index] SELECT FROM data => PrincOpsUtils.LongCopy[from: data, nwords: size, to: @e.data]; ENDCASE => ERROR; writeLog.index _ writeLog.index + entrySizeWithData; }; }; WriteTrace: PUBLIC ENTRY PROC [gfh: PrincOps.GlobalFrameHandle, pc: PrincOps.BytePC] = { entrySize: CARDINAL = SIZE[trace SpyLog.Entry]; IF writeLog = NIL THEN RETURN; <> IF LOOPHOLE[writeLog.index+SIZE[trace Entry], CARDINAL] >= LOOPHOLE[limit, CARDINAL] THEN { NextWriteBuffer[writeLog]; writeLog.index _ FIRST[EntryPointer]; }; <> IF gfh = NIL AND pc = 0 THEN { frame: PrincOps.FrameHandle = PrincOpsUtils.GetReturnFrame[]; gfh _ frame.accesslink; pc _ frame.pc; }; { ent: SpyLog.Entry _ [trace[fill: 0, gfh: gfh, pc: pc, process: PrincOpsUtils.ReadPSB[], timestamp: PrincOpsUtils.GetClockPulses[]]]; PrincOpsUtils.LongCopy[from: @ent, nwords: entrySize, to: @writeLog.base[writeLog.index]]; writeLog.index _ writeLog.index + entrySize; }; }; NextWriteBuffer: INTERNAL PROCEDURE [log: Log] = { entrySize: CARDINAL = SIZE[nullEntry SpyLog.Entry]; temp: VM.Interval; ent: SpyLog.Entry _ [nullEntry[limit-log.index]]; PrincOpsUtils.LongCopy[from: @ent, nwords: entrySize, to: @log.base[log.index]]; <> IF log.bufferProcess # NIL THEN { WHILE ~log.bufferProcessDone DO WAIT bufferReady; -- drop monitor to avoid deadlock ENDLOOP; JOIN log.bufferProcess; log.bufferProcess _ NIL; log.bufferProcessDone _ FALSE; }; <> 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; }; WriteBuffer: PROC [log: Log, filePage: INT, base: EntryBasePointer] = { writeTries: INT _ 0; writeDone: ENTRY PROC = { log.bufferProcessDone _ TRUE; BROADCAST bufferReady; }; priority: Process.Priority _ Process.GetPriority[]; IF priority > Process.priorityForeground THEN Process.SetPriority[Process.priorityForeground]; <> <> WHILE MIN[log.pages, FS.GetInfo[log.file].pages] < filePage + bufferPages DO Process.Pause[1]; -- wait for room ENDLOOP; FS.Write[log.file, filePage, bufferPages, base ! FS.Error => { <> IF error.code = $unknownPage AND writeTries < 10 THEN { nowPages: INT; hundredMilliseconds: Process.Ticks = Process.MsecToTicks[100]; writeTries _ writeTries + 1; FOR i:INT IN [0..99) DO nowPages _ FS.GetInfo[log.file].pages; IF nowPages >= filePage + bufferPages THEN EXIT; Process.Pause[hundredMilliseconds]; ENDLOOP; RETRY; }; }; ]; writeDone[]; }; LogExtender: PROC [log: Log] = { <> WHILE ~log.doneWriting DO newPages: INT; newPages _ log.filePage + (buffersAhead * bufferPages); IF (log.pages - log.filePage)/bufferPages < buffersAhead THEN { FS.SetPageCount[log.file, MAX[log.pages, newPages]]; log.pages _ FS.GetInfo[log.file].pages; }; Process.Pause[5]; ENDLOOP; }; Init[]; }. . . <> <> <> <> <> <> <>