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 index = maxIndex THEN RETURN; cutoff _ Process.SecondsToTicks[10]; 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; }; 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; }; 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 _ 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; 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] = { OPEN PrincOps; 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 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[]; }. nProcessWatchImpl.Mesa Partly cloned from SpyKernelImpl of February 6, 1986 8:53:59 am PST Spreitzer, April 21, 1986 4:40:08 pm PST IF IntervalTimerFace.exists THEN RETURN; PrincOpsUtils.DisableInterrupts[]; -- stops the clock. NO PAGE FAULTS ALLOWED! PrincOpsUtils.EnableInterrupts[]; ********************************************************************* watching CPU ********************************************************************* All code invoked by this process should be resident 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.) we have a good process! 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) 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. save the current frame this also saves the last ignored frame Κ  – "cedar" style˜code™KšœC™CK™(—K˜KšΟk œj˜sK˜šΠbxœ˜KšœQ˜XKšœ ˜—K˜Kšœœ˜K˜Kšœœ˜K˜Kšœ œ ˜ Kšœœœ˜$Kšœœœ˜Kšœœ˜4—Kšœœ˜šœœœ ˜ Kšœ+œ œœ˜GKšœ˜—Kšœœœ˜K˜.K˜Kšœœœ˜Kšœ˜—Kšœ!™!Kšœ˜—K˜š Ÿ œœœœœœ˜=Kš œœœœœ˜HKšœ˜Kšœ˜Kš œ˜K˜KšŸœœœœ˜&—K˜š Ÿœœœœœœ˜@šœœœ˜"šœœ˜Kšœ'˜'Kšœ˜Kšœ˜K˜—š˜Kšœœœœ ˜)—Kšœ˜—K˜KšŸœœœœ˜—K˜šŸ œœœœ˜'Kšœœœ˜Kš œœœœ)œ˜SK˜—K˜KšœE™EKšœ™KšœE™EK˜šœ3™3K˜Kšœ œœ˜šœ œœ˜5K˜ K˜"Kšœœ˜—K˜Kšœ œœ ˜2Kšœœ˜Kšœ œ˜Kšœ œ˜Kš œ œœœ&œ˜VKšœ œ˜Kšœ œ˜K˜.Kšœœœ ³˜ΤKšœœœ˜—K˜šŸ œœœœ˜'Kš œœœœœœ˜CK˜—K˜šŸ œœœœœœœ˜`K˜ K˜—K˜šŸœœ˜Kšœœœ˜Kšœ,˜,K˜.š˜K˜Kšœ œœ˜K˜$šŸœœœ˜Kšœ˜Kšœ"˜"Kšœœœ ˜2Kšœœœ ˜ZKšœ˜Kšœœœ˜šœ'œœ˜IKš œœœœœ ˜7K˜K˜(K˜ Kšœœœœœœœ˜EKšœ œœ˜Kšœœœ˜Kš œœœ/œœ  ˜ZKš œœ œœœ˜PKšœN™NKšœT™TKšœZ™ZKšœR™RKšœ™Kšœœ˜ šœœœ˜#Kšœœœœ˜#Kšœ+œœ˜7Kšœœœ˜Kšœ˜—Kšœœœ˜Kšœ™Kšœ-˜-šœœœ˜Kšœœ˜Kšœœœ˜,Kšœ˜—šœ˜Kšœ œ˜Kšœ˜—Kšœ œœ˜Kšœ˜—Kšœ!˜!Kšœœ˜0K˜—Kšœœ+œ˜MKšœ œ˜'K˜ Kšœ˜—K˜—K˜š Ÿ œœœœœ˜@Kšœœœœ˜=šœ˜Kšœœœ!œ˜N——K˜šŸœœ.˜:K˜Kš œœœœœ'˜TK˜Kšœ œœ œœœœ˜iK˜Kšœ˜—K˜šŸœœE˜SKšœ œ œ˜6Kšœ!œ˜%Kšœœ˜K˜K˜šŸ œœœ˜DKšœ™Kšœ ˜KšœE™EKšœN™Nš œœ œ œœœ˜NKš œœœœœœœ˜4—Kšœ ˜Kšœ˜—K˜"Kš œœ œœœ˜Qšœœ œ˜1Kšœ™Kšœ&™&K˜,šœœ8œ˜QK˜Kš œ œ œ*œœ˜sK˜—Kšœœ'œœO˜“Kšœ&œœ˜?Kšœ˜—K˜Kšœ˜Kšœœ˜—K˜šŸœœ˜Kšœœ˜&K˜—K˜šŸœœ˜Kšœ-˜-Kšœ%˜%Kšœ'˜'Kšœ&˜&K˜K˜AK˜K˜ K˜—K˜K˜K˜K˜—…— )