<> <> <> 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; <> <<(The Spy wakes up with all of the other timeouts. Since it is the highest priority,>> <> <> <> 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[]; }.