SpyLogImpl.mesa
Last Modified by: John Maxwell April 5, 1983 10:47 am
DIRECTORY
CachedSpace USING [Handle],
DCSFileTypes USING [tLeaderPage],
Directory USING [CreateFile, Error, LookupUnlimited],
Environment USING [PageNumber, wordsPerPage],
File USING [Capability, GetSize, nullCapability, PageCount, SetSize],
Frame USING [GetReturnFrame],
Heap USING [MakeString],
Inline USING [LongCOPY],
LongString USING [AppendString, CompareStrings],
PrincOps USING [BytePC, FrameHandle, GFTIndex],
Process USING [ -- are these all resident procedures?
GetPriority, Priority, priorityForeground, SetPriority],
Space USING [defaultWindow, Create,
Handle, LongPointerFromPage, Map, nullHandle, Unmap, virtualMemory,
VMPageNumber, WindowOrigin], -- are these all resident procedures?
SpyLog USING [Entry],
SpecialSpace USING [
MakeResident, MakeSwappable,
MakeCodeResident, MakeCodeSwappable,
MakeGlobalFrameResident, MakeGlobalFrameSwappable],
System USING [GetClockPulses];
SpyLogImpl: MONITOR
IMPORTS
Directory, File, Frame, Heap, Inline, LongString, Process,
Space, SpecialSpace, System
EXPORTS SpyLog =
BEGIN
OPEN Environment, SpyLog;
Error: SIGNAL = CODE;
logging: PUBLIC BOOLEANFALSE;
busy: PUBLIC BOOLEANFALSE;
skips: CARDINAL ← 0;
spy: PUBLIC BOOLEANFALSE; -- set up a second log so the spy can spy on itself
fileName: STRING ← "spy.data";
******************************************************************
initialization, opening and closing of the log
******************************************************************
nullBuffer: Buffer = [FALSE, Space.nullHandle, Space.defaultWindow, 0];
Buffer: TYPE = RECORD[
mapped: BOOLEAN,
space: Space.Handle,
window: Space.WindowOrigin,
VMpage: PageNumber];
readLog, writeLog: Log ← NIL;
logDesc, spylogDesc: LogDesc ← [];
Log: TYPE = POINTER TO LogDesc;
LogDesc: TYPE = RECORD[
base: EntryBasePointer ← NIL,
circular: BOOLEANFALSE, -- is this a ring buffer?
logging: BOOLEANFALSE, -- are we reading or writing?
readerPage: PageNumber ← 1,
writerPage: PageNumber ← 1,
reader: EntryPointer ← FIRST[EntryPointer],
writer: EntryPointer ← FIRST[EntryPointer],
limit: EntryPointer ← FIRST[EntryPointer],
filename: LONG STRINGNIL,
buffer: Buffer ← nullBuffer,
next: Buffer ← nullBuffer,
bufferSize: CARDINAL ← 0,
bufferProcess: PROCESSNIL];
swapUnitSize: CARDINAL = 10;
wordsPerUnit: CARDINAL = swapUnitSize*wordsPerPage;
unitThreshold: CARDINAL ← wordsPerUnit - 64;
appendThreshold: CARDINAL ← 0;
we will swap in the next swap-unit/buffer
whenever we are past the threshold
in the current swap-unit/buffer.
Initialize: PUBLIC PROCEDURE[name: STRING, pages: CARDINAL, append: BOOLEAN] =
BEGIN
skips ← 0;
busy ← FALSE;
IF name = NIL THEN name ← fileName;
readLog ← writeLog ← @logDesc;
Create[readLog, name, pages, append];
END;
Create: PROCEDURE[log: Log, name: STRING, pages: CARDINAL, append: BOOLEAN] =
BEGIN OPEN log;
newFile: BOOLEANFALSE;
cap: File.Capability ← File.nullCapability;
Unmap[@buffer]; Unmap[@next]; -- in case the log never got closed
create a backing file (if necessary)
IF name = NIL THEN Error;
IF pages < swapUnitSize THEN pages ← swapUnitSize;
SELECT TRUE FROM
append = circular => newFile ← TRUE;
pages # bufferSize => newFile ← TRUE;
filename = NIL => newFile ← TRUE;
LongString.CompareStrings[name, filename] # 0 => newFile ← TRUE;
ENDCASE;
IF newFile THEN cap ← Directory.CreateFile[name, DCSFileTypes.tLeaderPage, 5*pages
! Directory.Error => CONTINUE];
IF cap = File.nullCapability THEN cap ← Directory.LookupUnlimited[name];
File.SetSize[cap, pages*2];
create new spaces
IF buffer = nullBuffer THEN buffer.space ← Space.Create[size: pages, parent: Space.virtualMemory];
IF next = nullBuffer THEN next.space ← Space.Create[size: pages, parent: Space.virtualMemory];
buffer.window.file ← cap;
buffer.window.base ← 0;
next.window.file ← cap;
next.window.base ← 0;
initialize the LogDesc
base ← NIL;
circular ← NOT append;
logging ← FALSE;
readerPage ← writerPage ← 1; -- skip leader page
reader ← writer ← FIRST[EntryPointer];
limit ← writer + pages*wordsPerPage - Entry.SIZE;
IF filename = NIL THEN filename ← Heap.MakeString[,20];
filename.length ← 0;
LongString.AppendString[filename, name];
bufferSize ← pages;
bufferProcess ← NIL;
appendThreshold ← pages/4;
test invariants
IF wordsPerUnit-unitThreshold NOT IN [0..256) THEN Error;
IF swapUnitSize = 0 THEN Error;
END;
Open: PUBLIC ENTRY PROCEDURE[write: BOOLEAN, reset: BOOLEAN] =
BEGIN
SpecialSpace.MakeGlobalFrameResident[SpyLogImpl];
SpecialSpace.MakeCodeResident[SpyLogImpl];
IF ~spy THEN readLog ← writeLog ← @logDesc;
IF writeLog.bufferSize = 0 THEN Initialize[NIL, 40, TRUE];
IF ~write AND spy AND readLog # writeLog THEN {
read from the spy log
readLog ← writeLog;
spy ← FALSE};
IF ~write AND spy THEN {
set up a spy log
readLog ← @logDesc;
writeLog ← @spylogDesc;
Create[writeLog, "traceSpy.data"L, readLog.bufferSize, TRUE];
OpenLog[writeLog, TRUE, FALSE]};
OpenLog[IF write THEN writeLog ELSE readLog, write, reset];
logging ← writeLog.logging;
END;
Close: PUBLIC ENTRY PROCEDURE =
BEGIN
CloseLog[readLog];
IF writeLog # readLog THEN CloseLog[writeLog];
SpecialSpace.MakeGlobalFrameSwappable[SpyLogImpl];
SpecialSpace.MakeCodeSwappable[SpyLogImpl];
END;
OpenLog: PROCEDURE[log: Log, write, reset: BOOLEAN] =
BEGIN OPEN log;
temp: Buffer;
IF reset THEN {
log.reader ← FIRST[EntryPointer];
log.readerPage ← 1};
IF logging = write AND ~reset THEN RETURN; -- already open
CloseLog[log];
logging ← write;
NewBuffer[log, IF write THEN writerPage ELSE readerPage];
temp ← buffer; buffer ← next; next ← temp;
base ← LOOPHOLE[LongPointer[buffer.space]];
IF write AND writer = FIRST[EntryPointer] AND writerPage = 1
THEN {base[writer] ← [nullEntry[1]]; writer ← writer + 1};
END;
CloseLog: PROCEDURE[log: Log] =
BEGIN OPEN log;
IF logging THEN base[writer] ← [endOfLog[]];
IF log.bufferProcess # NIL THEN JOIN bufferProcess;
log.bufferProcess ← NIL;
Unmap[@buffer]; Unmap[@next];
logging ← FALSE;
base ← NIL;
END;
ForkNewBuffer: PROCEDURE[log: Log, page: PageNumber] =
BEGIN OPEN log;
priority: Process.Priority ← Process.GetPriority[];
IF priority > Process.priorityForeground THEN Process.SetPriority[Process.priorityForeground];
bufferProcess ← FORK NewBuffer[log, page];
IF priority > Process.priorityForeground THEN Process.SetPriority[priority];
END;
NewBuffer: PROCEDURE[log: Log, page: PageNumber] =
BEGIN OPEN log;
create a new buffer starting with filePage page.
Unmap[@next];
next.window.file ← buffer.window.file;
IF next.window.file = File.nullCapability
THEN next.window.file ← Directory.LookupUnlimited[filename];
next.window.base ← page;
IF logging THEN {
current: File.PageCount ← File.GetSize[next.window.file];
IF current < (page + log.bufferSize)
THEN File.SetSize[next.window.file, page + log.bufferSize]}; -- expensive
Map[buffer: @next];
next.VMpage ← Space.VMPageNumber[next.space];
END;
NextBuffer: PROCEDURE[log: Log] =
BEGIN OPEN log;
temp: Buffer;
may have to move the reader
IF logging AND circular AND ~LessThan[reader,writer] THEN
reader ← Next[log, FIRST[EntryPointer]];
IF logging AND circular THEN base[writer] ← [endOfLog[]];
may have to install a new buffer
IF NOT circular THEN {
IF logging THEN base[writer] ← [nullEntry[limit-writer]];
IF bufferProcess = NIL THEN -- shouldn't happen
ForkNewBuffer[log, (IF logging THEN writerPage ELSE readerPage) + bufferSize];
JOIN bufferProcess;
bufferProcess ← NIL;
temp ← buffer; buffer ← next; next ← temp;
base ← LOOPHOLE[LongPointer[buffer.space]];
IF logging
THEN writerPage ← writerPage + bufferSize
ELSE readerPage ← readerPage + bufferSize};
move the appropriate index
IF logging
THEN writer ← FIRST[EntryPointer]
ELSE reader ← FIRST[EntryPointer];
END;
LongPointer: PROCEDURE[space: Space.Handle] RETURNS[LONG POINTER] =
INLINE BEGIN -- Space.LongPointer is an ENTRY procedure.
handle: CachedSpace.Handle = LOOPHOLE[space];
RETURN[Space.LongPointerFromPage[handle.page]];
END;
Map: PROCEDURE[buffer: POINTER TO Buffer] =
INLINE BEGIN
IF buffer.mapped THEN RETURN;
buffer.mapped ← TRUE;
Space.Map[buffer.space, buffer.window];
SpecialSpace.MakeResident[buffer.space];
END;
Unmap: PROCEDURE[buffer: POINTER TO Buffer] =
INLINE BEGIN
IF ~buffer.mapped THEN RETURN;
buffer.mapped ← FALSE;
SpecialSpace.MakeSwappable[buffer.space];
Space.Unmap[buffer.space];
END;
******************************************************************
reading and writing on the log
******************************************************************
EntryBasePointer: TYPE = LONG ORDERED BASE POINTER;
EntryPointer: TYPE = EntryBasePointer RELATIVE POINTER [0..177777B] TO Entry;
WriteTrace: PUBLIC ENTRY PROCEDURE [gfi: PrincOps.GFTIndex, pc: PrincOps.BytePC] =
BEGIN OPEN writeLog;
writerNext: EntryPointer;
IF ~logging THEN RETURN;
IF busy THEN {skips ← skips + 1; RETURN};
are we at the end of the buffer?
IF ~LessThan[writer+SIZE[trace Entry],limit] THEN NextBuffer[writeLog];
are we about to overwrite the reader?
writerNext ← writer + SIZE[trace Entry];
IF circular THEN WHILE ~LessThan[reader,writer+1]
AND LessThan[reader,writerNext+1] DO
reader ← Next[writeLog, reader];
ENDLOOP;
write out the entry.
IF gfi = 0 AND pc = 0 THEN {
frame: PrincOps.FrameHandle = Frame.GetReturnFrame[];
gfi ← frame.accesslink.gfi; pc ← frame.pc};
base[writer] ← [trace[,gfi,pc,System.GetClockPulses[]]];
give hints to the swapper
SwapperHints[writer, writerNext];
writer ← writerNext;
END;
WriteData: PUBLIC ENTRY PROCEDURE [type, size: CARDINAL, data: LONG POINTER] =
BEGIN OPEN writeLog;
writerNext: EntryPointer;
IF ~logging THEN RETURN;
IF busy THEN {skips ← skips + 1; RETURN};
IF size > 8000 THEN Error;
are we at the end of the buffer?
IF ~LessThan[writer+SIZE[Entry]+size,limit] THEN NextBuffer[writeLog];
are we about to overwrite the reader?
writerNext ← writer+SIZE[Entry]+size;
IF circular THEN WHILE ~LessThan[reader,writer+1]
AND LessThan[reader,writerNext+1] DO
reader ← Next[writeLog, reader];
ENDLOOP;
write out the entry.
base[writer] ← [data[size,type,System.GetClockPulses[],]];
WITH e:base[writer] SELECT FROM
data => Inline.LongCOPY[data,size,@e.data];
ENDCASE => Error;
give hints to the swapper
SwapperHints[writer, writerNext];
writer ← writerNext;
END;
SwapperHints: PROCEDURE[old, new: EntryPointer] =
INLINE BEGIN
OPEN writeLog; -- SwapperHints only used for writing
newPage:CARDINAL=LOOPHOLE[new, CARDINAL]/wordsPerPage;
IF ~circular AND newPage >= appendThreshold AND bufferProcess = NIL
THEN ForkNewBuffer[writeLog, writerPage + bufferSize]; -- preallocate the next buffer
END;
LessThan: PROCEDURE[a,b: UNSPECIFIED] RETURNS[BOOLEAN] = INLINE
BEGIN RETURN[LOOPHOLE[a,CARDINAL]<LOOPHOLE[b,CARDINAL]]; END;
Truncate: PROCEDURE[a,b: UNSPECIFIED] RETURNS[CARDINAL] = INLINE
BEGIN
ac, bc: CARDINAL;
ac ← LOOPHOLE[a]; bc ← LOOPHOLE[b];
RETURN[(ac/bc)*bc];
END;
-- reading --
NextEntry: PUBLIC ENTRY PROCEDURE RETURNS[p: LONG POINTER TO Entry] =
BEGIN OPEN readLog;
IF logging THEN Error;
reader ← Next[readLog, reader];
IF base[reader].type = endOfLog AND circular
THEN reader ← Next[readLog, reader]; -- wrap around
IF base[reader].type = endOfLog AND
(reader # writer OR readerPage # writerPage)
THEN SIGNAL Error;
RETURN[@base[reader]];
END;
Next: PROCEDURE[log: Log, a: EntryPointer] RETURNS[b: EntryPointer] =
BEGIN OPEN log;
WITH entry: base[a] SELECT FROM
endOfLog => IF circular
THEN b ← FIRST[EntryPointer] -- wrap around
ELSE b ← a;
trace => b ← a + SIZE[trace Entry];
data => b ← a + SIZE[data Entry] + entry.size;
nullEntry => b ← a + entry.size;
ENDCASE => Error;
IF base[b].type = nullEntry THEN b ← limit;
IF LessThan[b, limit] THEN RETURN;
IF logging THEN Error; -- shouldn't cause a NextBuffer!!
NextBuffer[log];
b ← FIRST[EntryPointer];
IF base[b].type = nullEntry THEN Error;
END;
END . . .