-- GCTableFaultRecorder.mesa
-- inspired by FaultMeter, but watches last 45 page faults in GC Tables
-- process stuff stolen from Knutsen's Ben
-- last edit by Willie-Sue, 15-Sep-81 15:15:39
-- last edit by Paul Rovner, August 6, 1982 10:20 am
DIRECTORY
Environment USING
[wordsPerPage],
Inline USING
[HighHalf, LowHalf],
PrincOps USING
[BytePC, Frame, GlobalFrameHandle],
Process USING
[Detach, Pause, priorityInterrupt, SecondsToTicks, SetPriority],
ProcessInternal USING
[DisableInterrupts, EnableInterrupts],
ProcessOperations USING
[HandleToIndex, IndexToHandle, LongNotify, LongReEnter, LongWait, ReadPSB],
PSB USING
[Condition, FaultIndex, PDA, PDABase, PsbHandle, PsbIndex, PsbNull, qPageFault],
RTRefCounts USING
[GCState],
RTOS USING
[UnregisterCedarProcess],
Runtime USING
[CallDebugger],
Space USING
[Create, Delete, Handle, PageCount, LongPointer, Map, defaultWindow, virtualMemory],
SpecialSpace USING
[MakeGlobalFrameResident, MakeProcedureResident, MakeResident],
Volume USING
[InsufficientSpace];
GCTableFaultRecorder: MONITOR
IMPORTS
Inline, Process, ProcessInternal, ProcessOperations,
RTOS, Runtime, SpecialSpace, Space, RTRefCounts, Volume
= BEGIN OPEN Process;
gcPageFaultInfo: TYPE = RECORD
[ addrLo: CARDINAL,
global: PrincOps.GlobalFrameHandle,
pc: PrincOps.BytePC,
process: PSB.PsbHandle,
count: LONG CARDINAL
];
faultInfo: TYPE = RECORD [str: STRING, index: INTEGER];
gcStateAddrHi: CARDINAL; -- who to watch
BufferOfFaults: TYPE = ARRAY BufferIndex OF gcPageFaultInfo;
bufferOfFaults: LONG POINTER TO BufferOfFaults;
BufferIndex: TYPE = [0..256);
bufferIndex: BufferIndex← LAST[BufferIndex];
numFaults: LONG CARDINAL ← 0;
gcFaults: CARDINAL← 0;
lastgcFaultAddrLo: CARDINAL← 0;
seriousDebugging: BOOLEAN← FALSE;
whichGlobalFrame: PrincOps.GlobalFrameHandle← NIL;
GetCurrent: PROC RETURNS[PSB.PsbIndex] = INLINE
{RETURN[ProcessOperations.HandleToIndex[ProcessOperations.ReadPSB[]]]};
LongNakedNotify: PROC [pCondition: LONG POINTER TO CONDITION] = INLINE {
-- used to notify a condition from outside the relevant monitor.
pCond: LONG POINTER TO PSB.Condition = LOOPHOLE[pCondition];
ProcessInternal.DisableInterrupts[];
IF pCond↑.tail=PSB.PsbNull
THEN
{ pCond↑.wakeup ← TRUE;
ProcessInternal.EnableInterrupts[] }
ELSE
{ ProcessInternal.EnableInterrupts[];
ProcessOperations.LongNotify[pCondition] } };
WaitForFault: PROC = INLINE {
processFaulted: PSB.PsbHandle;
addrFaulted: LONG POINTER;
frameFaulted: POINTER TO local PrincOps.Frame;
ProcessOperations.LongWait
[@recorderLock, pPageFaultCONDITION, --timeout:-- 1];
UNTIL ProcessOperations.LongReEnter
[@recorderLock, pPageFaultCONDITION] DO
NULL ENDLOOP;
-- either a new page fault came along or we timed out..
IF pda.fault[qPageFault].queue.tail = PSB.PsbNull
THEN RETURN; -- just timed out, so forget it
processFaulted ← ProcessOperations.IndexToHandle[
pda.block[pda.fault[qPageFault].queue.tail].link.next]; -- walk to tail, then to head.
addrFaulted ← pda[pda[processFaulted].context.state].memPointer;
-- conditionally wake up the Pilot fault handler:
ProcessInternal.DisableInterrupts[];
IF pPageFaultCondition↑.tail # PSB.PsbNull
THEN LongNakedNotify[pPageFaultCONDITION];
ProcessInternal.EnableInterrupts[];
-- is this one of the pages we're interested in??
numFaults ← numFaults + 1;
IF Inline.HighHalf[addrFaulted] = gcStateAddrHi THEN
{ frameFaulted← pda[pda[processFaulted].context.state].frame;
bufferIndex← IF bufferIndex = LAST[BufferIndex] THEN 0
ELSE bufferIndex + 1;
bufferOfFaults[bufferIndex]←
[ addrLo: Inline.LowHalf[addrFaulted],
global:frameFaulted.accesslink,
pc: frameFaulted.pc,
process: processFaulted,
count: numFaults
];
gcFaults ← gcFaults + 1;
IF seriousDebugging
THEN
{ IF whichGlobalFrame = frameFaulted.accesslink
THEN Runtime.CallDebugger["Global Frame of Interest"L]
ELSE { IF Inline.LowHalf[addrFaulted] = lastgcFaultAddrLo
THEN Runtime.CallDebugger["Same Page Twice"L]};
};
lastgcFaultAddrLo← Inline.LowHalf[addrFaulted];
};
};
recorderLock: MONITORLOCK;
pda: PSB.PDABase = PSB.PDA;
qPageFault: PSB.FaultIndex = PSB.qPageFault;
pPageFaultCondition: LONG POINTER TO PSB.Condition = @pda.fault[
qPageFault].condition;
pPageFaultCONDITION: LONG POINTER TO CONDITION =
LOOPHOLE[pPageFaultCondition];
FaultWatcher: PROC = {
-- make the fault watcher resident
nPages: Space.PageCount←
SIZE[gcPageFaultInfo]*(LAST[BufferIndex]+1)/Environment.wordsPerPage;
spH: Space.Handle = Space.Create[nPages, Space.virtualMemory];
Space.Map[spH, Space.defaultWindow !
Volume.InsufficientSpace => {Space.Delete[spH]; ERROR}];
SpecialSpace.MakeResident[spH];
gcStateAddrHi ← Inline.HighHalf[RTRefCounts.GCState];
-- bufferOfFaults← LOOPHOLE[RTRefCounts.GCState+300B+16];
bufferOfFaults← Space.LongPointer[spH];
SpecialSpace.MakeGlobalFrameResident[GCTableFaultRecorder ! ANY => CONTINUE];
SpecialSpace.MakeProcedureResident[FaultWatcher];
-- pause to allow Cedar to start up
Process.Pause[Process.SecondsToTicks[20]];
-- unregister self
RTOS.UnregisterCedarProcess[GetCurrent[]];
-- raise priority
Process.SetPriority[Process.priorityInterrupt];
DO
WaitForFault[];
ENDLOOP;
};
IPF: PROC[bi: BufferIndex] RETURNS[faultInfo] =
{ fi: faultInfo;
addrLo: CARDINAL ← bufferOfFaults[bi].addrLo;
IF addrLo < 100400B THEN
{ fi.str← "MapPiRce"L; fi.index← LOOPHOLE[addrLo-400B] }
ELSE
{ fi.str← "MapOiOe"L; fi.index← LOOPHOLE[(addrLo-100400B)/2]};
RETURN[fi];
};
-- Mainline Code
Detach[FORK FaultWatcher[]];
END.