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: BOOLFALSE
];
[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: BOOLFALSE;
me: ProcessWatch.Consumer = [ConsumeReadyList];
pwv: Viewer ← NIL;
notedVectors: ProcessWatch.ReadyVector ← ALL[FALSE];
inhibited: BOOLFALSE;
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: REFNIL, msg: ROPENIL] --Commander.CommandProc-- = {
[] ← CreateProcessWatcher[[name: "Tracing Process Watcher", hscrollable: TRUE]];
};
CreateProcessWatcher: PROC [viewerData: ViewerClasses.ViewerRec ← [], paint: BOOLTRUE] RETURNS [pwv: Viewer] = {
pwv ← ViewerOps.CreateViewer[pwFlavor, viewerData, paint];
SetPWV[pwv];
};
menu: Menus.Menu = Menus.CreateMenu[];
HScale: ENTRY PROC [parent: REF ANY, clientData: REF ANYNIL,
mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --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 ANYNIL,
mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --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 ANYNIL,
mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --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 ANYNIL,
mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --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: BOOLFALSE] RETURNS [top, bottom: INTEGERLAST[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: BOOLFALSE] RETURNS [left, right: INTEGERLAST[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: BOOLFALSE] --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: INTIR[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: INTIR[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: BOOLFALSE;
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[];
}.