SpyLogImpl.mesa
John Maxwell November 28, 1983 2:49 pm
Russ Atkinson June 20, 1984 11:53:50 am PDT
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 BOOLEANFALSE; -- 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.ROPENIL,
filePage: INT ← 0,
buffer: VM.Interval ← VM.nullInterval,
next: VM.Interval ← VM.nullInterval,
bufferProcess: PROCESSNIL];
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
don't close the alternate log
IF readLog # NIL THEN readLog ← NIL;
IF writeLog = log1 THEN {FinalizeWriteLog[writeLog]; writeLog ← NIL};
IF writeLog = NIL THEN MakeSwappable[];
IF log1.file # FS.nullOpenFile THEN CloseLog[log1];
IF log2.file # FS.nullOpenFile AND log2 # writeLog THEN CloseLog[log2];
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];
move the index to the next entry
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;
are we at the end of the buffer?
IF GreaterEqual[writeLog.index + entrySize, limit] THEN {
NextWriteBuffer[writeLog];
writeLog.index ← FIRST[EntryPointer]};
write out the entry.
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;
are we at the end of the buffer?
IF GreaterEqual[writeLog.index+SIZE[trace Entry], limit] THEN {
NextWriteBuffer[writeLog];
writeLog.index ← FIRST[EntryPointer]};
write out the entry.
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]];
wait until next buffer is written, then fork a write
IF log.bufferProcess # NIL THEN {JOIN log.bufferProcess; log.bufferProcess ← NIL};
log.bufferProcess ← FORK WriteBuffer[log, log.filePage, log.base];
update the indices
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];
IF priority > 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. . .