ProcessWatchTrace.Mesa
Spreitzer, March 29, 1986 2:10:28 pm PST
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
];
[t, p] appears at [(t-t0)*pixelsPerTick, (p-p0)*pixelsPerProcess] past margin.
leftMargin, bottomMargin:
INT ← 10;
The visible part of the graph is in the rectangle [leftMargin, bottomMargin, visibleWidth, visibleHeight].
MyTicks:
TYPE = BasicTime.Pulses;
Bins of time. Bin 0 is the first one in some minute.
secondsPerTick: REAL = BasicTime.PulsesToSeconds[1];
displayPeriodInSeconds: INTEGER = 60;
displayPeriodInTicks: MyTicks = MicrosecondsToTicks[1000000*displayPeriodInSeconds];
TicksInterval:
TYPE =
RECORD [start, end: MyTicks];
Covers the time from start to end, inclusive.
Run:
TYPE =
RECORD [int: TicksInterval, pi: UsefulPsbIndex];
A process was active for a certain interval of time.
The runs that might not be accurately displayed:
CurRunIdx: TYPE = INTEGER [0 .. nCurRuns];
nCurRuns: INTEGER = 200;
NullCRI: CurRunIdx = LAST[CurRunIdx];
curRuns: ARRAY CurRunIdx OF Run;
curRead, curWrite: CurRunIdx ← 0;
The buffer pointers. read=write means empty; no way to be completely full.
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[[]];
Runs that have been fully displayed:
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;
Requesting selection feedback:
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[];
}.