DIRECTORY BasicTime, Commander, Convert, Icons, Imager, ImagerBackdoor, ImagerFont, IO, Menus, MessageWindow, PrincOps, PrincOpsUtils, Process, ProcessWatch, ProcessWatchPrinting, Real, Rope, TIPUser, ViewerClasses, ViewerOps; ProcessWatchTrace: CEDAR MONITOR IMPORTS BasicTime, Commander, Convert, Icons, Imager, ImagerBackdoor, ImagerFont, IO, Menus, MessageWindow, PrincOpsUtils, Process, ProcessWatch, ProcessWatchPrinting, Real, TIPUser, ViewerOps = { CARD: TYPE = LONG CARDINAL; ROPE: TYPE = Rope.ROPE; PsbIndex: TYPE = PrincOps.PsbIndex; UsefulPsbIndex: TYPE = ProcessWatch.UsefulPsbIndex; nUsefulPsbs: CARDINAL = ProcessWatch.nUsefulPsbs; Viewer: TYPE = ViewerClasses.Viewer; ReadyVector: TYPE = ProcessWatch.ReadyVector; ProcessWatcher: TYPE = REF ProcessWatcherPrivate; ProcessWatcherPrivate: TYPE = RECORD [ cw, ch: INTEGER _ 0, pixelsPerSecond, pixelsPerTick: REAL _ 0, fullWidth: INT _ 0, t0, visibleTicks: MyTicks _ 0, --MODed displayPeriod-- pixelsPerProcess, p0, visibleProcesses, fullHeight: INT _ 0, displayedSelection: UsefulPsbIndex _ 0, selectionDisplayed: BOOL _ FALSE ]; leftMargin, bottomMargin: INT _ 10; MyTicks: TYPE = BasicTime.Pulses; secondsPerTick: REAL = BasicTime.PulsesToSeconds[1]; displayPeriodInSeconds: INTEGER = 60; displayPeriodInTicks: MyTicks = MicrosecondsToTicks[1000000*displayPeriodInSeconds]; TicksInterval: TYPE = RECORD [start, end: MyTicks]; Run: TYPE = RECORD [int: TicksInterval, pi: UsefulPsbIndex]; CurRunIdx: TYPE = INTEGER [0 .. nCurRuns]; nCurRuns: INTEGER = 200; NullCRI: CurRunIdx = LAST[CurRunIdx]; curRuns: ARRAY CurRunIdx OF Run; curRead, curWrite: CurRunIdx _ 0; ActiveIndex: TYPE = [0 .. maxNActive]; maxNActive: CARDINAL = 31; NullAI: ActiveIndex = maxNActive; actives: ARRAY ActiveIndex OF CurRunIdx _ ALL[NullCRI]; nActive: ActiveIndex _ 0; ProcessData: TYPE = RECORD [ ai: ActiveIndex _ NullAI]; pds: ARRAY UsefulPsbIndex OF ProcessData _ ALL[[]]; OldRunArray: TYPE = ARRAY OldRunIdx OF Run; OldRunIdx: TYPE = INTEGER [0 .. nOldRuns]; nOldRuns: INTEGER = 1000; NullORI: OldRunIdx = LAST[OldRunIdx]; oldRuns: REF OldRunArray = NEW [OldRunArray]; oldRead, oldWrite: OldRunIdx _ 0; selection: UsefulPsbIndex _ 0; selectionValid: BOOL _ FALSE; me: ProcessWatch.Consumer = [ConsumeReadyList]; pwv: Viewer _ NIL; notedVectors: ProcessWatch.ReadyVector _ ALL[FALSE]; inhibited: BOOL _ FALSE; change: CONDITION; fontSize: REAL; font: ImagerFont.Font; minLabelHPad, minLabelVPad: REAL; ticLength: INT _ 3; ticPad: INT _ 1; vLabelLength, hLabelLength: REAL; idealHLabels: INT _ 5; idealVLabels: INT _ 5; SetFont: PROC [name: ROPE] = { fbb: ImagerFont.Extents = (font _ ImagerFont.Find[name]).FontBoundingBox[]; fontSize _ Ceiling[fbb.ascent + fbb.descent]; minLabelHPad _ fontSize; minLabelVPad _ fontSize/4.0; vLabelLength _ font.RopeWidth[Convert.RopeFromInt[LAST[UsefulPsbIndex]]].x; hLabelLength _ font.RopeWidth["1.234567E-5"].x; leftMargin _ ticLength + ticPad + Ceiling[vLabelLength]; bottomMargin _ ticLength + ticPad + Ceiling[fontSize]; }; SuccCRI: PROC [pred: CurRunIdx] RETURNS [succ: CurRunIdx] = INLINE { succ _ pred.SUCC; IF succ = NullCRI THEN succ _ 0; }; PredCRI: PROC [succ: CurRunIdx] RETURNS [pred: CurRunIdx] = INLINE { pred _ (IF succ = 0 THEN NullCRI ELSE succ).PRED; }; SuccORI: PROC [pred: OldRunIdx] RETURNS [succ: OldRunIdx] = INLINE { succ _ pred.SUCC; IF succ = NullORI THEN succ _ 0; }; PredORI: PROC [succ: OldRunIdx] RETURNS [pred: OldRunIdx] = INLINE { pred _ (IF succ = 0 THEN NullORI ELSE succ).PRED; }; MyNowTicks: INTERNAL PROC RETURNS [now: MyTicks] = { nowGmt: BasicTime.GMT = BasicTime.Now[]; nowPulses: BasicTime.Pulses = BasicTime.GetClockPulses[]; IF NOT (firstOkGmt.Period[nowGmt] >= 0 AND nowGmt.Period[lastOkGmt] >= 0) THEN { unpacked: BasicTime.Unpacked = BasicTime.Unpack[nowGmt]; fudgePulsesToTicks _ MicrosecondsToTicks[1000000 * (60 + unpacked.second)] - nowPulses; firstOkGmt _ nowGmt; lastOkGmt _ nowGmt.Update[Real.Fix[BasicTime.PulsesToSeconds[ LAST[BasicTime.Pulses]/2]]]; Flush[]; }; now _ nowPulses + fudgePulsesToTicks; }; firstOkGmt, lastOkGmt: BasicTime.GMT _ BasicTime.earliestGMT; fudgePulsesToTicks: BasicTime.Pulses; MicrosecondsToTicks: PROC [ms: LONG CARDINAL] RETURNS [mt: MyTicks] = { mt _ BasicTime.MicrosecondsToPulses[ms]}; PredTicks: PROC [succ: MyTicks] RETURNS [pred: MyTicks] = INLINE { pred _ IF succ > 0 THEN succ.PRED ELSE ERROR; }; SuccTicks: PROC [pred: MyTicks] RETURNS [succ: MyTicks] = INLINE { succ _ IF pred < LAST[MyTicks] THEN pred.SUCC ELSE ERROR; }; AddTicks: PROC [a, b: MyTicks] RETURNS [c: MyTicks] = INLINE { d: MyTicks = -a; IF b > d THEN ERROR; c _ a + b}; InTicks: PROC [bin: MyTicks, interval: TicksInterval] RETURNS [in: BOOL] = INLINE { in _ bin IN [interval.start .. interval.end]; }; TicksOverlap: PROC [i1, i2: TicksInterval] RETURNS [overlap: BOOL] = { overlap _ InTicks[i1.start, i2] OR InTicks[i2.start, i1]}; CreateCmd: PROC [cmd: Commander.Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] --Commander.CommandProc-- = { [] _ CreateProcessWatcher[[name: "Tracing Process Watcher", hscrollable: TRUE]]; }; CreateProcessWatcher: PROC [viewerData: ViewerClasses.ViewerRec _ [], paint: BOOL _ TRUE] RETURNS [pwv: Viewer] = { pwv _ ViewerOps.CreateViewer[pwFlavor, viewerData, paint]; SetPWV[pwv]; }; menu: Menus.Menu = Menus.CreateMenu[]; HScale: ENTRY PROC [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift, control: BOOL _ FALSE] --Menus.ClickProc-- = { ENABLE UNWIND => NULL; self: Viewer = NARROW[parent]; pw: ProcessWatcher = NARROW[self.data]; SetPixelsPerSecond[self, pw, SELECT mouseButton FROM red => MIN[pw.pixelsPerSecond, 50] * 2, blue => MAX[pw.pixelsPerSecond, 0.2] / 2, yellow => MAX[REAL[self.cw] - leftMargin - 1, 5.0] / displayPeriodInSeconds, ENDCASE => ERROR]; }; SetPixelsPerSecond: INTERNAL PROC [v: Viewer, pw: ProcessWatcher, pps: REAL] = { tc: INT = pw.t0 + pw.visibleTicks/2; pw.pixelsPerSecond _ pps; pw.pixelsPerTick _ pw.pixelsPerSecond * secondsPerTick; pw.fullWidth _ Real.Round[pw.pixelsPerTick * displayPeriodInTicks]; pw.visibleTicks _ MIN[Real.Fix[MAX[v.cw - leftMargin, 0] / pw.pixelsPerTick], displayPeriodInTicks]; pw.t0 _ MAX[MIN[tc - pw.visibleTicks/2, displayPeriodInTicks - pw.visibleTicks], 0]; ReformattingPaint[pw]; }; VScale: ENTRY PROC [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift, control: BOOL _ FALSE] --Menus.ClickProc-- = { ENABLE UNWIND => NULL; self: Viewer = NARROW[parent]; pw: ProcessWatcher = NARROW[self.data]; SetPixelsPerProcess[self, pw, SELECT mouseButton FROM red => MIN[pw.pixelsPerProcess, 50] * 2, blue => MAX[pw.pixelsPerProcess, 2] / 2, yellow => MAX[self.ch - bottomMargin - 1, nUsefulPsbs] / nUsefulPsbs, ENDCASE => ERROR]; }; SetPixelsPerProcess: INTERNAL PROC [v: Viewer, pw: ProcessWatcher, ppp: INT] = { pc: INT = pw.p0 + pw.visibleProcesses/2; pw.pixelsPerProcess _ ppp; pw.fullHeight _ pw.pixelsPerProcess * nUsefulPsbs; pw.visibleProcesses _ MIN[MAX[v.ch - bottomMargin, 0] / pw.pixelsPerProcess, nUsefulPsbs]; pw.p0 _ MAX[MIN[pc - pw.visibleProcesses/2, nUsefulPsbs - pw.visibleProcesses], 0]; ReformattingPaint[pw]; }; AdjustPaintPeriod: ENTRY PROC [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift, control: BOOL _ FALSE] --Menus.ClickProc-- = { ENABLE UNWIND => NULL; self: Viewer = NARROW[parent]; pw: ProcessWatcher = NARROW[self.data]; SetPaintPeriod[SELECT mouseButton FROM red => MIN[paintPeriod, 30] * 2, blue => MAX[paintPeriod, 0.2] / 2, yellow => 1.0, ENDCASE => ERROR]; }; SetPaintPeriod: INTERNAL PROC [pp: REAL] = { paintPeriod _ pp; BROADCAST paint; TRUSTED {Process.InitializeCondition[@paint, Process.MsecToTicks[Ceiling[paintPeriod*1000]]]}; MessageWindow.Append[ message: IO.PutFR["Painting every %g seconds.", [real[paintPeriod]]], clearFirst: TRUE]; }; FlipInhibitButton: PROC [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift, control: BOOL _ FALSE] --Menus.ClickProc-- = { FlipInhibit[]; }; pwFlavor: ATOM = $TracingProcessWatcher; pwClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ flavor: pwFlavor, init: InitProcessWatcher, scroll: ProcessWatcherVScroll, hscroll: ProcessWatcherHScroll, paint: PaintProcessWatcher, destroy: DestroyProcessWatcher, tipTable: TIPUser.InstantiateNewTIPTable["ProcessWatcher.TIP"], notify: NotifyProcessWatcher, menu: menu, icon: Icons.NewIconFromFile["ProcessWatcher.icons", 1] ]]; InitProcessWatcher: ENTRY PROC [self: Viewer] --ViewerClasses.InitProc-- = { pw: ProcessWatcher = NEW [ProcessWatcherPrivate _ []]; self.data _ pw; SetPixelsPerSecond[self, pw, 5]; SetPixelsPerProcess[self, pw, 1]; }; ProcessWatcherVScroll: ENTRY PROC [self: Viewer, op: ViewerClasses.ScrollOp, amount: INTEGER, shift, control: BOOL _ FALSE] RETURNS [top, bottom: INTEGER _ LAST[INTEGER]] --ViewerClasses.ScrollProc-- = { ENABLE UNWIND => NULL; pw: ProcessWatcher = NARROW[self.data]; SELECT op FROM query => { bottom _ 100 - 100 * pw.p0 / nUsefulPsbs; top _ 100 - 100 * (pw.p0 + pw.visibleProcesses) / nUsefulPsbs; }; up => { old: INT = pw.p0; dp: INT = amount / pw.pixelsPerProcess; pw.p0 _ MAX[0, pw.p0 - dp]; IF pw.p0 # old THEN ReformattingPaint[pw]; }; down => { old: INT = pw.p0; dp: INT = amount / pw.pixelsPerProcess; pw.p0 _ MIN[nUsefulPsbs - pw.visibleProcesses, pw.p0 + dp]; IF pw.p0 # old THEN ReformattingPaint[pw]; }; thumb => { old: INT = pw.p0; pw.p0 _ (nUsefulPsbs - pw.visibleProcesses)*LONG[100 - amount]/100; IF pw.p0 # old THEN ReformattingPaint[pw]; }; ENDCASE => ERROR; }; ProcessWatcherHScroll: ENTRY PROC [self: Viewer, op: ViewerClasses.HScrollOp, amount: INTEGER, shift, control: BOOL _ FALSE] RETURNS [left, right: INTEGER _ LAST[INTEGER]] --ViewerClasses.HScrollProc-- = { ENABLE UNWIND => NULL; pw: ProcessWatcher = NARROW[self.data]; SELECT op FROM query => { left _ Real.Round[100 * pw.t0 / displayPeriodInTicks]; right _ Real.Round[100 * (pw.t0 + pw.visibleTicks) / displayPeriodInTicks]; }; right => { old: MyTicks = pw.t0; dt: MyTicks = Real.Round[amount/pw.pixelsPerTick]; pw.t0 _ MAX[dt, pw.t0] - dt; IF pw.t0 # old THEN ReformattingPaint[pw]; }; left => { old: MyTicks = pw.t0; dt: MyTicks = Real.Round[amount/pw.pixelsPerTick]; pw.t0 _ MIN[displayPeriodInTicks - pw.visibleTicks, pw.t0 + dt]; IF pw.t0 # old THEN ReformattingPaint[pw]; }; thumb => { old: MyTicks = pw.t0; pw.t0 _ (displayPeriodInTicks - pw.visibleTicks)*LONG[amount]/100; IF pw.t0 # old THEN ReformattingPaint[pw]; }; ENDCASE => ERROR; }; ReformattingPaint: INTERNAL PROC [pw: ProcessWatcher] = TRUSTED { Process.Detach[FORK ViewerOps.PaintViewer[viewer: pwv, hint: client]]; }; DestroyProcessWatcher: PROC [self: Viewer] --ViewerClasses.DestroyProc-- = { SetPWV[NIL]; }; PaintProcessWatcher: PROC [self: Viewer, context: Imager.Context, whatChanged: REF, clear: BOOL] RETURNS [quit: BOOL _ FALSE] --ViewerClasses.PaintProc-- = { pw: ProcessWatcher = NARROW[self.data]; TCeiling: PROC [rx: REAL] RETURNS [ix: INT] = INLINE {ix _ Real.Fix[rx - pw.fullWidth] + pw.fullWidth}; PaintWithLock: ENTRY PROC = { ENABLE UNWIND => NULL; curDisplayTime: MyTicks = IF inhibited THEN lastDisplayTime ELSE MyNowTicks[]; clipTo: MyTicks = curDisplayTime - displayPeriodInTicks + 1; resize: BOOL = self.cw # pw.cw OR self.ch # pw.ch; incremental: BOOL = whatChanged = $Increment AND (NOT resize) AND (NOT clear); lineAt: INT = TCeiling[((curDisplayTime MOD displayPeriodInTicks) + 1) * pw.pixelsPerTick]; visibleWidth: REAL; visibleHeight: INT; CircularBox: PROC [xstart, ymin, xend, ymax: REAL] = INLINE { IF xstart <= xend THEN Imager.MaskBox[context, [xstart, ymin, xend, ymax]] ELSE {Imager.MaskBox[context, [xstart, ymin, pw.fullWidth, ymax]]; Imager.MaskBox[context, [0, ymin, xend, ymax]]}}; PaintRun: PROC [run: Run, active: BOOL] = { realEnd: MyTicks = IF active THEN curDisplayTime ELSE run.int.end; realStart: MyTicks = MAX[clipTo, run.int.start]; IF realStart <= realEnd THEN { y0: INT = run.pi * pw.pixelsPerProcess; yf: INT = y0 + pw.pixelsPerProcess; startMod: MyTicks = realStart MOD displayPeriodInTicks; endMod: MyTicks = realEnd MOD displayPeriodInTicks; left: INT = Real.Fix[startMod * pw.pixelsPerTick]; right: INT = TCeiling[(endMod + 1)*pw.pixelsPerTick]; IF startMod <= endMod THEN Imager.MaskBox[context, [left, y0, right, yf]] ELSE {Imager.MaskBox[context, [left, y0, pw.fullWidth, yf]]; Imager.MaskBox[context, [0, y0, right, yf]]}}; }; Feedback: PROC [pi: UsefulPsbIndex] = { Imager.MaskRectangle[context, [0, pi * pw.pixelsPerProcess, pw.fullWidth, pw.pixelsPerProcess]]; }; IF resize THEN { tc: INT = pw.t0 + pw.visibleTicks/2; pc: INT = pw.p0 + pw.visibleProcesses/2; availHeight: INT = MAX[self.ch - bottomMargin, 0]; availWidth: INT = MAX[self.cw - leftMargin, 0]; pw.visibleProcesses _ MIN[availHeight / pw.pixelsPerProcess, nUsefulPsbs]; pw.visibleTicks _ MIN[Real.Round[availWidth / pw.pixelsPerTick], displayPeriodInTicks]; pw.t0 _ MAX[MIN[tc - pw.visibleTicks/2, displayPeriodInTicks - pw.visibleTicks], 0]; pw.p0 _ MAX[MIN[pc - pw.visibleProcesses/2, nUsefulPsbs - pw.visibleProcesses], 0]; pw.ch _ self.ch; pw.cw _ self.cw; }; visibleWidth _ pw.visibleTicks * pw.pixelsPerTick; visibleHeight _ pw.visibleProcesses * pw.pixelsPerProcess; IF incremental THEN { clearToCoordRaw: INTEGER = lineAt + clearAheadPixels; clearToCoord: INTEGER = IF clearToCoordRaw > pw.fullWidth THEN clearToCoordRaw-pw.fullWidth ELSE clearToCoordRaw; Imager.TranslateT[context, [leftMargin, bottomMargin]]; Imager.ClipRectangle[context, [0, 0, visibleWidth, visibleHeight]]; Imager.TranslateT[context, [-pw.t0*pw.pixelsPerTick, -pw.p0*pw.pixelsPerProcess]]; IF pw.selectionDisplayed THEN { Imager.SetColor[context, ImagerBackdoor.invert]; Feedback[pw.displayedSelection]; }; Imager.SetColor[context, Imager.white]; CircularBox[dirtyAt, 0, clearToCoord, pw.fullHeight]; Imager.SetColor[context, Imager.black]; } ELSE { vp: REAL = MAX[minLabelVPad + fontSize, REAL[visibleHeight]/idealVLabels]; nv: INT = Ceiling[REAL[visibleHeight]/vp]; dp: INT = MAX[pw.visibleProcesses / nv, 1]; hp: REAL = MAX[hLabelLength + minLabelHPad, visibleWidth/idealHLabels]; nh: INT = Ceiling[REAL[visibleWidth]/hp]; ds: INT = MAX[Ceiling[pw.visibleTicks * secondsPerTick / nh], 1]; dt: MyTicks = Ceiling[ds / secondsPerTick]; Imager.SetColor[context, Imager.white]; Imager.MaskRectangle[context, [0, 0, self.cw, self.ch]]; Imager.SetColor[context, Imager.black]; Imager.TranslateT[context, [leftMargin, bottomMargin]]; Imager.MaskRectangle[context, [-1, -1, visibleWidth+2, 1]]; Imager.MaskRectangle[context, [-1, visibleHeight, visibleWidth+2, 1]]; Imager.MaskRectangle[context, [-1, 0, 1, visibleHeight]]; Imager.MaskRectangle[context, [visibleWidth, 0, 1, visibleHeight]]; Imager.SetFont[context, font]; FOR ti: INT _ IR[pw.t0, dt], ti + dt WHILE ti < INT[pw.t0 + pw.visibleTicks] DO x: REAL = (ti - pw.t0) * pw.pixelsPerTick; label: ROPE = Convert.RopeFromReal[ti * secondsPerTick]; extents: ImagerFont.Extents = font.RopeBoundingBox[label]; Imager.MaskRectangle[context, [x, -1-ticLength, 1.0, ticLength]]; Imager.SetXY[context, [x + extents.leftExtent, -1-ticLength-ticPad-extents.ascent]]; Imager.ShowRope[context, label]; ENDLOOP; FOR pi: INT _ IR[pw.p0, dp], pi + dp WHILE pi < pw.p0 + pw.visibleProcesses DO y: REAL = (pi - pw.p0 + 0.5) * pw.pixelsPerProcess; label: ROPE = Convert.RopeFromInt[pi]; extents: ImagerFont.Extents = font.RopeBoundingBox[label]; Imager.MaskRectangle[context, [-1-ticLength, y-0.5, ticLength, 1.0]]; Imager.SetXY[context, [-1-ticLength-ticPad - extents.rightExtent, y - 0.5 - (extents.ascent - extents.descent)/2]]; Imager.ShowRope[context, label]; ENDLOOP; Imager.ClipRectangle[context, [0, 0, visibleWidth, visibleHeight]]; Imager.TranslateT[context, [-pw.t0*pw.pixelsPerTick, -pw.p0*pw.pixelsPerProcess]]; FOR ori: OldRunIdx _ oldRead, SuccORI[ori] WHILE ori # oldWrite DO PaintRun[oldRuns[ori], FALSE]; ENDLOOP; }; FOR cri: CurRunIdx _ curRead, SuccCRI[cri] WHILE cri # curWrite DO run: Run = curRuns[cri]; ai: ActiveIndex = pds[run.pi].ai; active: BOOL = actives[ai]=cri; PaintRun[run, active]; IF NOT active THEN { oldRuns[oldWrite] _ run; oldWrite _ SuccORI[oldWrite]; IF oldRead = oldWrite THEN oldRead _ SuccORI[oldRead]; DeleteCurRun[cri]; }; ENDLOOP; Imager.MaskRectangle[context, [lineAt, 0, 1, pw.fullHeight]]; IF selectionValid THEN { Imager.SetColor[context, ImagerBackdoor.invert]; Feedback[selection]; }; pw.selectionDisplayed _ selectionValid; pw.displayedSelection _ selection; dirtyAt _ lineAt; lastDisplayTime _ curDisplayTime; }; PaintWithLock[]; }; clearAheadPixels: INT _ 10; dirtyAt: INT _ 0; lastDisplayTime: MyTicks _ 0; ReadPI: PROC RETURNS [pi: PsbIndex] = TRUSTED INLINE { pi _ PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]]}; NotifyProcessWatcher: PROC [self: Viewer, input: LIST OF REF ANY] --ViewerClasses.NotifyProc-- = { pw: ProcessWatcher = NARROW[self.data]; notifier: PsbIndex = ReadPI[]; who: UsefulPsbIndex _ 0; valid: BOOL _ FALSE; WHILE input # NIL DO first: REF ANY = input.first; input _ input.rest; WITH first SELECT FROM z: TIPUser.TIPScreenCoords => { index: INT = Real.Fix[(z.mouseY - bottomMargin) / pw.pixelsPerProcess]; IF valid _ index IN [0 .. pw.visibleProcesses) THEN who _ pw.p0 + index; }; a: ATOM => SELECT a FROM $Select => { MessageWindow.Append[ message: IF valid THEN IO.PutFR["Process %g (%bB, %xH)%g", [cardinal[who]], [cardinal[who]], [cardinal[who]], [rope[IF notifier = who THEN " --- the notifier!" ELSE NIL]]] ELSE "", clearFirst: TRUE]; SetSelection[valid, who]; }; $ShowTop => { IF valid THEN TRUSTED {Process.Detach[FORK ProcessWatchPrinting.ShowTop[who]]}; }; $ShowStack => { IF valid THEN TRUSTED {Process.Detach[FORK ProcessWatchPrinting.ShowStack[who]]}; }; $Unselect => SetSelection[FALSE, 0]; ENDCASE => ERROR; ENDCASE => ERROR; ENDLOOP; }; SetSelection: ENTRY PROC [valid: BOOL, sel: UsefulPsbIndex] = { ENABLE UNWIND => NULL; IF valid = selectionValid AND (selection = sel OR NOT valid) THEN RETURN; selectionValid _ valid; selection _ sel; IF pwv # NIL THEN TRUSTED {Process.Detach[FORK ViewerOps.PaintViewer[viewer: pwv, hint: client, clearClient: FALSE, whatChanged: $Increment]]}; }; SetPWV: ENTRY PROC [v: Viewer] = { ENABLE UNWIND => NULL; diff: BOOL = ((pwv = NIL) # (v = NIL)) AND NOT inhibited; pwv _ v; IF diff THEN { IF pwv = NIL THEN CloseDown[] ELSE ProcessWatch.AddConsumer[me]; }; BROADCAST change; }; FlipInhibit: ENTRY PROC = { ENABLE UNWIND => NULL; inhibited _ NOT inhibited; IF pwv # NIL THEN IF inhibited THEN CloseDown[] ELSE ProcessWatch.AddConsumer[me]; BROADCAST change; }; CloseDown: INTERNAL PROC = { now: MyTicks = MyNowTicks[]; ProcessWatch.RemoveConsumer[me]; --make all runs inactive:--{ WHILE nActive > 0 DO ai: ActiveIndex = nActive-1; cri: CurRunIdx = actives[ai]; pi: UsefulPsbIndex = curRuns[cri].pi; curRuns[cri].int.end _ now; DeActivate[pi, ai] ENDLOOP }; }; Flush: INTERNAL PROC = { curRead _ curWrite; oldRead _ oldWrite; nActive _ 0; pds _ ALL[[]]; }; DeleteCurRun: INTERNAL PROC [cri: CurRunIdx] = { bumpedPI: UsefulPsbIndex = curRuns[cri].pi; bumpedAI: ActiveIndex = pds[bumpedPI].ai; IF actives[bumpedAI] = cri THEN DeActivate[bumpedPI, bumpedAI]; IF cri # curRead THEN { movedPI: UsefulPsbIndex = curRuns[curRead].pi; movedAI: ActiveIndex = pds[movedPI].ai; curRuns[cri] _ curRuns[curRead]; IF actives[movedAI] = curRead THEN actives[movedAI] _ cri; }; curRead _ SuccCRI[curRead]; }; DeActivate: INTERNAL PROC [pi: UsefulPsbIndex, ai: ActiveIndex] = { lastActive: ActiveIndex = nActive-1; pds[pi].ai _ NullAI; IF ai # lastActive THEN { movedCRI: CurRunIdx = actives[ai] _ actives[lastActive]; pds[curRuns[movedCRI].pi].ai _ ai; }; nActive _ nActive - 1; }; ConsumeReadyList: PROC [REF ANY] = { ConsumeWithLock: ENTRY PROC = { ENABLE UNWIND => Flush[]; now: MyTicks = MyNowTicks[]; {ai: ActiveIndex _ 0; WHILE ai < nActive DO cri: CurRunIdx = actives[ai]; pi: UsefulPsbIndex = curRuns[cri].pi; IF NOT notedVectors[pi] THEN {curRuns[cri].int.end _ now; DeActivate[pi, ai]} ELSE ai _ ai + 1; ENDLOOP}; FOR pi: UsefulPsbIndex IN UsefulPsbIndex DO IF notedVectors[pi] THEN { ai: ActiveIndex = pds[pi].ai; IF ai # NullAI THEN curRuns[actives[ai]].int.end _ now ELSE IF nActive < maxNActive THEN { cri: CurRunIdx _ curWrite; curWrite _ SuccCRI[curWrite]; IF curRead = curWrite THEN DeleteCurRun[curRead]; curRuns[cri] _ [[now, now], pi]; pds[pi].ai _ nActive; actives[nActive] _ cri; nActive _ nActive + 1; }; }; ENDLOOP; NULL; }; notedVectors _ ProcessWatch.readyVector; ConsumeWithLock[]; }; PaintIt: PROC = { GetViewer: ENTRY PROC RETURNS [v: Viewer] = { ENABLE UNWIND => NULL; WAIT paint; UNTIL pwv # NIL AND NOT inhibited DO WAIT change ENDLOOP; v _ pwv; }; DO ViewerOps.PaintViewer[GetViewer[], client, FALSE, $Increment]; ENDLOOP; }; paint: CONDITION; paintPeriod: REAL _ 1.0; Ceiling: PROC [r: REAL] RETURNS [i: INT] = { d: INT _ Real.Round[r] + 1; i _ Real.Fix[r - d] + d}; IR: PROC [i, align: INT] RETURNS [aligned: INT] = { aligned _ align * Ceiling[REAL[i]/align]; }; Start: PROC = TRUSTED { SetFont["Xerox/TiogaFonts/TimesRoman10"]; menu.AppendMenuEntry[Menus.CreateEntry["VScale", VScale]]; menu.AppendMenuEntry[Menus.CreateEntry["HScale", HScale]]; menu.AppendMenuEntry[Menus.CreateEntry["PaintPeriod", AdjustPaintPeriod]]; menu.AppendMenuEntry[Menus.CreateEntry["ToggleInhibition", FlipInhibitButton]]; ViewerOps.RegisterViewerClass[pwFlavor, pwClass]; Process.InitializeCondition[@change, 0]; Process.DisableTimeout[@change]; Process.InitializeCondition[@paint, Process.MsecToTicks[Ceiling[paintPeriod*1000]]]; Process.Detach[FORK PaintIt[]]; Commander.Register["TraceProcessWatcher", CreateCmd, "Create a Tracing Process Watcher"]; }; Start[]; }. \ProcessWatchTrace.Mesa Spreitzer, March 29, 1986 2:10:28 pm PST [t, p] appears at [(t-t0)*pixelsPerTick, (p-p0)*pixelsPerProcess] past margin. The visible part of the graph is in the rectangle [leftMargin, bottomMargin, visibleWidth, visibleHeight]. Bins of time. Bin 0 is the first one in some minute. Covers the time from start to end, inclusive. A process was active for a certain interval of time. The runs that might not be accurately displayed: The buffer pointers. read=write means empty; no way to be completely full. Runs that have been fully displayed: Requesting selection feedback: Κ3– "cedar" style˜code™K™(—K˜KšΟk œKœŒ˜βK˜šΠbxœœ˜ KšœKœl˜ΐ—K˜Kšœ˜K˜Kšœœœœ˜Kšœœœ˜Kšœ œ˜#Kšœœ˜3Kšœ œ˜1Kšœœ˜$Kšœ œ˜-K˜Kšœœœ˜1šœœœ˜&Kšœœ˜Kšœ œ˜)Kšœ œ˜KšœΟc˜6Kšœ4œ˜œ˜ZK˜K˜—Kšœ%˜%K˜Kšœ!œ˜=K˜%—K˜š  œœΟgœœœœ˜GKšœ$‘œ˜)—K˜š  œœœœ˜BKš œœ œœœœ˜-K˜—K˜š  œœœœ˜BKš œœœ œœœœ˜9K˜—K˜š œœœœ˜>K˜Kšœœœ˜K˜ —K˜š  œœ)œœœ˜SKšœ œ"˜-K˜—K˜š  œœœ œ˜FKšœ œ˜:—K˜š  œœœ œœœœŸœ˜rKšœIœ˜PK˜—K˜š  œœ3œœœ˜sK˜:K˜ K˜—K˜K˜&K˜š œœœ œœœœœ9œœŸœ˜œKšœœœ˜Kšœœ ˜Kšœœ ˜'šœœ ˜4Kšœœ˜'Kšœœ˜)Kšœ œœ:˜LKšœœ˜—K˜—K˜š œœœ&œ˜PKšœœ˜$Kšœ˜Kšœ7˜7KšœC˜CKšœœ œB˜dKšœœœE˜TK˜K˜—K˜š œœœ œœœœœ9œœŸœ˜œKšœœœ˜Kšœœ ˜Kšœœ ˜'šœœ ˜5Kšœœ˜(Kšœœ˜(Kšœ œ8˜EKšœœ˜—K˜—K˜š œœœ&œ˜PKšœœ!˜(Kšœ˜Kšœ2˜2Kšœœœ=˜ZKšœœœD˜SK˜K˜—K˜š œœœ œœœœœ9œœŸœ˜§Kšœœœ˜Kšœœ ˜Kšœœ ˜'šœœ ˜&Kšœœ˜ Kšœœ˜"Kšœ˜Kšœœ˜—K˜—K˜š œœœœ˜,Kšœ˜Kš œ˜KšœW˜^šœ˜Kšœ œ:˜EKšœ œ˜—K˜—K˜š œœ œœœœœ9œœŸœ˜‘Kšœ˜K˜—K˜Kšœ œ˜(K˜šœ%œ!˜IKšœ˜K˜Kšœ˜Kšœ˜K˜K˜K˜?K˜Kšœ ˜ Kšœ6˜6K˜—K˜š œœœŸœ˜LKšœœ˜6Kšœ˜Kšœ ˜ Kšœ!˜!K˜—K˜š œœœ4œœœœœœœŸœ˜ΛKšœœœ˜Kšœœ ˜'šœ˜˜ K˜)Kšœ>˜>K˜—˜Kšœœ ˜Kšœœ ˜'Kšœœ˜Kšœ œ˜*K˜—˜ Kšœœ ˜Kšœœ ˜'Kšœœ0˜;Kšœ œ˜*K˜—˜ Kšœœ ˜Kšœ,œ˜CKšœ œ˜*K˜—Kšœœ˜—K˜—K˜š œœœ5œœœœœœœŸœ˜ΝKšœœœ˜Kšœœ ˜'šœ˜˜ Kšœ6˜6KšœK˜KK˜—˜ Kšœ˜Kšœ2˜2Kšœœ˜Kšœ œ˜*K˜—˜ Kšœ˜Kšœ2˜2Kšœœ5˜@Kšœ œ˜*K˜—˜ Kšœ˜Kšœ1œ ˜BKšœ œ˜*K˜—Kšœœ˜—K˜—K˜š œœœœ˜AK•StartOfExpansionw[viewer: ViewerClasses.Viewer, hint: ViewerOps.PaintHint, clearClient: BOOL _ TRUE, whatChanged: REF ANY _ NIL]šœœ3˜FKšœ˜—K˜š œœŸœ˜LKšœœ˜ K˜—K˜š œœ6œ œœœœŸœ˜Kšœœ ˜'š  œœœœœ˜+Kšœœ3˜;—š  œœœ˜Kšœœœ˜Kšœœ œœ˜NKšœ<˜˜>—K˜š œœœœœœŸœ˜bKšœœ ˜'Kšœ˜Kšœ˜Kšœœœ˜šœ œ˜Kšœœœ˜K˜šœœ˜šœ˜Kšœœ=˜GKšœœœ˜HK˜—šœœœ˜˜ šœ˜šœ œ˜Kš œœ[œœœœ˜™Kšœ˜—Kšœ œ˜—K˜K˜—šœ ˜ Kšœœœœ%˜OKšœ˜—šœ˜Kšœœœœ'˜QKšœ˜—Kšœœ˜$Kšœœ˜—Kšœœ˜—Kšœ˜—K˜—K˜š  œœœ œ˜?Kšœœœ˜Kš œœœœœœ˜IKšœ˜K˜K–w[viewer: ViewerClasses.Viewer, hint: ViewerOps.PaintHint, clearClient: BOOL _ TRUE, whatChanged: REF ANY _ NIL]š œœœœœ?œ˜K˜—K˜š œœœ˜"Kšœœœ˜Kš œœ œ œœœ ˜9Kšœ˜šœœ˜Kšœœœ œ˜@K˜—Kš œ˜K˜—K˜š  œœœ˜Kšœœœ˜Kšœ œ ˜Kš œœœœ œ œ˜RKš œ˜K˜—K˜š  œœœ˜Kšœ˜Kšœ ˜ šŸœ˜šœ ˜Kšœ˜Kšœ˜Kšœ%˜%Kšœ˜Kšœ˜Kš˜—Kšœ˜—K˜—K˜š œœœ˜K˜K˜K˜ Kšœœ˜K˜—K˜š  œœœ˜0Kšœ+˜+Kšœ)˜)Kšœœ ˜?šœœ˜K˜.K˜'K˜ Kšœœ˜:K˜—Kšœ˜K˜—K˜š  œœœ*˜CK˜$K˜šœœ˜Jšœ8˜8Jšœ"˜"J˜—J˜K˜—K˜š œœœœ˜$š œœœ˜Kšœœ ˜Kšœ˜Kšœ˜šœ˜Kšœ˜Kšœ%˜%Kšœœœ1˜MKšœ ˜Kšœ˜ —šœœ˜+šœœ˜Kšœ˜Kšœ œ#˜6šœœœ˜#Kšœ˜Kšœ˜Kšœœ˜1Kšœ ˜ K˜Kšœ˜K˜K˜—K˜—Kšœ˜—Kšœ˜K˜—Kšœ(˜(K˜K˜—K˜š œœ˜š  œœœœ˜-Kšœœœ˜Kšœ˜ Kšœœœœ œœœ˜9K˜K˜—š˜Kšœ+œ˜>Kšœ˜—K˜Kšœ œ˜Kšœ œ˜—K˜š  œœœœœ˜,Kšœœ˜K˜—K˜š œœ œœ œ˜3Kšœœ ˜)K˜—K˜š œœœ˜Kšœ)˜)Kšœ:˜:Kšœ:˜:KšœJ˜JKšœO˜OK˜1Kšœ(˜(Kšœ ˜ KšœT˜TKšœœ ˜K˜YK˜—K˜K˜K˜K˜—…—Tϊp‰