SpyLogImpl.mesa
Last Modified by: John Maxwell November 28, 1983 2:49 pm
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.log"]];
log2: Log = NEW[LogDesc ← [fileName: "spyingonspy.log"]];
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. . .