SpyLogImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
John Maxwell November 28, 1983 2:49 pm
Bob Hagmann June 3, 1985 4:16:34 pm PDT
Russ Atkinson (RRA) March 31, 1986 7:29:10 pm PST
DIRECTORY
FS USING [Close, Create, 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 [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 = {
OPEN SpyLog;
active: PUBLIC BOOLFALSE; -- 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 = {
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];
};
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 = {
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[];
};
InitializeWriteLog: PROC[log: Log] = {
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;
};
FinalizeWriteLog: PROC[log: Log] = {
endWords: NAT = SIZE[endOfLog SpyLog.Entry];
end: SpyLog.Entry ← [endOfLog[0]];
IF log.bufferProcess # NIL THEN {
wait until it is done
JOIN log.bufferProcess;
log.bufferProcess ← NIL;
};
PrincOpsUtils.LongCopy[from: @end, nwords: endWords, to: @log.base[log.index]];
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 RETURN;
log.file ← FS.Create[name: log.fileName, pages: 2*bufferPages];
log.buffer ← VM.Allocate[bufferPages];
log.next ← VM.Allocate[bufferPages];
};
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]};
};
reading and writing
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];
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];
};
WriteData: PUBLIC ENTRY PROC [data: LONG POINTER, size: [0..8000], type: CARDINAL] = {
entrySize: CARDINAL = SIZE[data Entry] + size;
IF writeLog = NIL THEN RETURN;
are we at the end of the buffer?
IF LOOPHOLE[writeLog.index+entrySize, CARDINAL] >= LOOPHOLE[limit, CARDINAL] THEN {
NextWriteBuffer[writeLog];
writeLog.index ← FIRST[EntryPointer];
};
{
write out the entry.
ent: SpyLog.Entry ← [data[size: size, rttype: type, timestamp: PrincOpsUtils.GetClockPulses[], data: NULL]];
PrincOpsUtils.LongCopy[from: @ent, nwords: entrySize, 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 + entrySize;
};
};
WriteTrace: PUBLIC ENTRY PROC [gfh: PrincOps.GlobalFrameHandle, pc: PrincOps.BytePC] = {
entrySize: CARDINAL = SIZE[trace SpyLog.Entry];
IF writeLog = NIL THEN RETURN;
are we at the end of the buffer?
IF LOOPHOLE[writeLog.index+SIZE[trace Entry], CARDINAL] >= LOOPHOLE[limit, CARDINAL] THEN {
NextWriteBuffer[writeLog];
writeLog.index ← FIRST[EntryPointer];
};
write out the entry.
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: 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]];
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;
};
WriteBuffer: PROC[log: Log, filePage: INT, base: EntryBasePointer] = {
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];
};
Init[];
}. . .
Bob Hagmann June 3, 1985 4:15:39 pm PDT
fix off by one in Pin - if unlucky you would pin an extra page
changes to: Pin (local of Init)
Russ Atkinson (RRA) June 6, 1985 5:47:30 pm PDT
added error recovery if the log cannot be opened