ProcessWatchImpl.Mesa
Partly cloned from SpyKernelImpl of February 6, 1986 8:53:59 am PST
Spreitzer, April 21, 1986 4:40:08 pm PST
DIRECTORY Basics, Loader, PrincOps, PrincOpsUtils, Process, ProcessWatch, SafeStorage, SystemVersion, Terminal, VM;
ProcessWatchImpl:
MONITOR
IMPORTS Basics, Loader, PrincOpsUtils, Process, SafeStorage, SystemVersion, Terminal, VM
EXPORTS ProcessWatch
= {OPEN ProcessWatch;
Ticks: TYPE = PrincOps.Ticks;
readyVector: PUBLIC ReadyVector;
lastReady: ReadyVector ← ALL[FALSE];
phs: ProcessHistories = NEW [ProcessHistoryArray ← ALL[[]]];
SetTimeouts:
PROC = {
ticks: Ticks;
cutoff: Ticks;
found: BOOLEAN;
frame: PrincOps.FrameHandle;
IF IntervalTimerFace.exists THEN RETURN;
IF index = maxIndex THEN RETURN;
cutoff ← Process.SecondsToTicks[10];
PrincOpsUtils.DisableInterrupts[]; -- stops the clock. NO PAGE FAULTS ALLOWED!
ticks ← PrincOpsUtils.ReadPTC[];
FOR psbi: PsbIndex
IN [PrincOps.StartPsb..PrincOps.StartPsb+PrincOps.
PDA.count)
DO
IF PrincOps.PDA.block[psbi].mds = 0 THEN LOOP;
IF PrincOps.PDA.block[psbi].mds - ticks > cutoff THEN LOOP; -- not significant
IF PrincOps.
PDA.block[psbi].link.vector
THEN frame ← PDA[PrincOps.PDA.block[psbi].context.state].frame
ELSE frame ← PrincOps.PDA.block[psbi].context.frame;
found ← FALSE;
FOR i:
CARDINAL
IN [0..index)
DO
IF timeout[i] = [frame.accesslink, frame.pc] THEN {found ← TRUE; EXIT};
ENDLOOP;
IF found THEN LOOP;
timeout[index] ← [frame.accesslink, frame.pc];
index ← index + 1;
IF index = maxIndex THEN EXIT;
ENDLOOP;
PrincOpsUtils.EnableInterrupts[];
};
AddConsumer:
PUBLIC
ENTRY
SAFE
PROC [c: Consumer] =
CHECKED {
IF nConsumers = maxNConsumers THEN RETURN WITH ERROR TooManyConsumers[];
consumers[nConsumers] ← c;
nConsumers ← nConsumers + 1;
BROADCAST change;
};
TooManyConsumers: PUBLIC ERROR = CODE;
RemoveConsumer:
PUBLIC
ENTRY
SAFE
PROC [c: Consumer] =
CHECKED {
FOR i:
INT
IN [0 .. nConsumers)
DO
IF consumers[i] = c
THEN {
consumers[i] ← consumers[nConsumers-1];
nConsumers ← nConsumers - 1;
EXIT;
};
REPEAT
FINISHED => RETURN WITH ERROR NotFound[];
ENDLOOP;
};
NotFound: PUBLIC ERROR = CODE;
CallConsumers:
INTERNAL
PROC =
INLINE {
ENABLE UNWIND => NULL;
FOR i: INT IN [0 .. nConsumers) DO consumers[i].Consume[consumers[i].data] ENDLOOP;
};
*********************************************************************
watching CPU
*********************************************************************
All code invoked by this process should be resident
headOnly: BOOL ← FALSE;
retraces:
INT ←
SELECT SystemVersion.machineType
FROM
dorado => 1,
dolphin, dandelion, dicentra => 2,
ENDCASE => ERROR;
pause: Ticks ← 0;
consumers: ARRAY [0 .. maxNConsumers) OF Consumer;
maxNConsumers: INT = 5;
nConsumers: INT ← 0;
change: CONDITION;
timeout: ARRAY [0..maxIndex) OF RECORD[gfh: PrincOps.GlobalFrameHandle, pc: CARDINAL];
maxIndex: CARDINAL = 25;
index: CARDINAL ← 0;
screen: Terminal.Virtual = Terminal.Current[];
searchReadyList: BOOLEAN ← TRUE; -- sometimes the user gets the machine gets into a state where searching the ready list is a bad idea. This boolean allows the user to stop the Spy from searching the ready list.
tooFew: BOOL ← FALSE;
GetPermission:
INTERNAL
PROC =
INLINE {
WHILE nConsumers = 0 OR NOT searchReadyList DO WAIT change ENDLOOP;
};
GetHistory:
PUBLIC
ENTRY
SAFE
PROC [pi: UsefulPsbIndex]
RETURNS [ph: ProcessHistory] =
CHECKED {
ph ← phs[pi];
};
WatchReadyList:
PROC [] = {
ENABLE UNWIND => NULL;
except: PsbHandle = PrincOpsUtils.ReadPSB[];
Process.SetPriority[Process.priorityRealTime];
DO
frame: FrameHandle;
skip, once: BOOLEAN ← FALSE;
headOfReadyList, current: PsbHandle;
Sample:
ENTRY
PROC = {
GetPermission[];
PrincOpsUtils.DisableInterrupts[];
headOfReadyList ← NextHandle[LOOPHOLE[PDA.ready]];
headOfReadyList ← NextHandle[LOOPHOLE[PDA[headOfReadyList].link]]; -- want the SECOND psb.
lastReady ← readyVector;
readyVector ← ALL[FALSE];
FOR current ← headOfReadyList, NextHandle[
LOOPHOLE[
PDA[current].link]]
DO
psb: LONG POINTER TO ProcessStateBlock = @PDA[current];
link: PsbLink = psb.link;
level: Process.Priority = link.priority;
it: PsbIndex;
IF current = headOfReadyList THEN IF once THEN EXIT ELSE once ← TRUE;
IF level = 0 THEN LOOP;
IF current = except THEN LOOP;
IF ~link.vector AND PDA.state[level] = PrincOps.NullStateVectorHandle THEN LOOP; -- no SV.
frame ← IF link.vector THEN PDA[psb.context.state].frame ELSE psb.context.frame;
skip over a process that appears in the ready queue because it just timed out.
(The Spy wakes up with all of the other timeouts. Since it is the highest priority,
it will run first. All of the other timeouts will appear on the ready list. Most likely,
they will just check some condition and then go back to sleep. This will mask the
more interesting processes.)
skip ← FALSE;
FOR i:
CARDINAL
IN [0..maxIndex)
DO
IF timeout[i] = [NIL, 0] THEN EXIT;
IF timeout[i] # [frame.accesslink, frame.pc] THEN LOOP;
skip ← TRUE; EXIT;
ENDLOOP;
IF skip THEN LOOP;
we have a good process!
it ← PrincOpsUtils.PsbHandleToIndex[current];
IF it
IN UsefulPsbIndex
THEN {
readyVector[it] ← TRUE;
IF NOT lastReady[it] THEN Record[it, frame];
}
IF headOnly THEN EXIT;
ENDLOOP;
PrincOpsUtils.EnableInterrupts[];
IF lastReady # readyVector THEN CallConsumers[];
};
THROUGH [0 .. retraces) DO Terminal.WaitForBWVerticalRetrace[screen] ENDLOOP;
IF pause > 0 THEN Process.Pause[pause];
Sample[];
ENDLOOP;
};
NextHandle:
PROC [link:
CARDINAL]
RETURNS [PsbHandle] =
INLINE {
RETURN[LOOPHOLE[Basics.BITAND[link, LOOPHOLE[handleMask]]]]};
handleMask: PsbLink = [
failed: FALSE, priority: 0, next: LAST[PsbIndex], reserved: 0, vector: FALSE];
Record:
PROC[psbi: UsefulPsbIndex, frame: FrameHandle] = {
level: Process.Priority;
psb: LONG POINTER TO ProcessStateBlock = @PDA[PrincOpsUtils.PsbIndexToHandle[psbi]];
level ← psb.link.priority;
IF frame = NIL THEN frame ← IF psb.link.vector THEN PDA[psb.context.state].frame ELSE psb.context.frame;
LogStack[psbi, level, frame]
};
LogStack:
PROC [process: PsbIndex, level: Process.Priority, frame: FrameHandle] = {
sdLength: CARDINAL = PrincOps.SD[PrincOps.sGFTLength];
gf: PrincOps.GlobalFrameHandle ← NIL;
length: CARDINAL ← 0;
stack: Stack ← [];
prevFrameIndex: FrameIndex ← 0;
ValidFrame:
PROC [f: PrincOps.ControlLink]
RETURNS [FrameHandle] = {
This procedure is supposed to validate a control link. For various race conditions (presumably), we have observed address faults when the accesslink gets clobbered. Since we can't handle the address faults in here, we insist that the caller be prepared for such an eventuality. (RRA)
OPEN PrincOps;
Note: as a side-effect, this procedure sets 'gf' and 'gfi', which are
used by the other local procedures below and the main loop of IncrementBucket.
IF f.proc
OR f.indirect
OR f.frame =
NIL
OR (gf ← f.frame.accesslink) =
NIL
OR
(LOOPHOLE[gf, CARDINAL] MOD 4) # 0 THEN RETURN[NIL];
RETURN[f.frame]
};
phs[process].lastPriority ← level;
frame ← ValidFrame[LOOPHOLE[frame] ! VM.AddressFault => {frame ← NIL; CONTINUE}];
THROUGH [0..maxExamineDepth)
UNTIL frame =
NIL
DO
save the current frame
this also saves the last ignored frame
stack.frames[stack.end] ← [1, gf, frame.pc];
IF stack.end = 0
OR stack.frames[stack.end] # stack.frames[prevFrameIndex]
THEN {
prevFrameIndex ← stack.end;
IF stack.end = LAST[FrameIndex] THEN {stack.end ← warmFrames; stack.wrapped ← TRUE} ELSE stack.end ← stack.end + 1;
}
ELSE IF stack.frames[prevFrameIndex].repeat < LAST[RepeatCount] THEN stack.frames[prevFrameIndex].repeat ← stack.frames[prevFrameIndex].repeat + 1;
frame ← ValidFrame[frame.returnlink ! VM.AddressFault => EXIT];
ENDLOOP;
phs[process].lastStack ← stack;
};
maxExamineDepth: INT ← 200;
ForkIt:
PROC = {
Process.Detach[FORK WatchReadyList[]];
};
Start:
PROC = {
Loader.MakeProcedureResident[WatchReadyList];
Loader.MakeProcedureResident[Record];
Loader.MakeProcedureResident[LogStack];
Loader.MakeGlobalFrameResident[Start];
SafeStorage.PinObject[phs];
Process.InitializeCondition[@change, Process.SecondsToTicks[10]];
SetTimeouts[];
ForkIt[];
};
Start[];
}.