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: BOOLFALSE;
retraces: INTSELECT 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: BOOLEANTRUE; -- 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: BOOLFALSE;
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: BOOLEANFALSE;
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];
}
ELSE {
tooFew ← TRUE;
};
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[];
}.