Constants
sleepColor: Imager.Color = ImagerColorFromName["blue"];
faintSleepColor: Imager.Color = ImagerColorFromName["very very light vivid blue"];
uncertainSleepColor: Imager.Color = ImagerColorFromName["light blue"];
wakeColor: Imager.Color = ImagerColorFromName["red"];
faintWakeColor: Imager.Color = ImagerColorFromName["very very light vivid red"];
unknownColor: Imager.Color = ImagerColorFromName["light orange"];
connectCVColor: Imager.Color = ImagerColorFromName["vivid red"];
connectMonitorColor: Imager.Color = ImagerColorFromName["vivid purple"];
connectForkColor: Imager.Color = ImagerColorFromName["vivid green"];
notifyTailColor: Imager.Color = ImagerColorFromName["vivid red"];
broadcastTailColor: Imager.Color = ImagerColorFromName["dark vivid red"];
nakedNotifyTailColor: Imager.Color = ImagerColorFromName["dark vivid cyan"];
monitorExitTailColor: Imager.Color = ImagerColorFromName["vivid purple"];
forkTailColor: Imager.Color = ImagerColorFromName["vivid green"];
killTailColor: Imager.Color = ImagerColorFromName["vivid cyan"];
unixIntrColor: Imager.Color = ImagerColorFromName["vivid green"];
unixFaultColor: Imager.Color = ImagerColorFromName["vivid red"];
runSampleColor: Imager.Color = ImagerColorFromName["vivid yellow"];
xSampleColor: Imager.Color = ImagerColorFromName["vivid purple"];
runxColor: Imager.Color = ImagerColorFromName["light vivid purple"];
runyColor: Imager.Color = ImagerColorFromName["light vivid green"];
runzColor: Imager.Color = ImagerColorFromName["light vivid yellow"];
commentTextColor: Imager.Color = ImagerColorFromName["black"];
blackColor: Imager.Color = ImagerColorFromName["black"];
xTickColor: Imager.Color = ImagerColorFromName["very light gray"];
sleepSize: CARD = 4;
uncertainSleepSize: CARD =6;
wakeSize: CARD = 10;
runSize: CARD = 14;
unknownSize: CARD = 1;
connectSize: REAL = 1.0;
arrowTailSize: CARD = 3;
commentTextSize: REAL = 12.0;
yThreadIncrement: CARD = 14;
drawArrowHeads: BOOL = FALSE;
Alias: TYPE ~ REF AliasRep;
AliasRep: TYPE ~ RECORD [name, alias: ROPE];
ShiftX: TYPE ~ REF ShiftXRep;
ShiftXRep: TYPE ~ RECORD [pos: REAL, cnt: CARD];
ButtonInfo: TYPE ~ REF ButtonInfoRep;
ButtonInfoRep: TYPE ~ RECORD [msg: ROPE, e, we: ThreadsVisPrivate.Event, t: ThreadsVisPrivate.Thread];
Build Gargoyle Scene
MergeEvents:
PROC [into, from: ThreadsVisPrivate.EventFacts] ~ {
elLast: LIST OF ThreadsVisPrivate.Event;
into.max ¬ MIN[into.max, from.max];
into.min ¬ MAX[into.min, from.min];
FOR el:
LIST
OF ThreadsVisPrivate.Event ¬ from.elist, el.rest
WHILE el#
NIL
DO
[] ← CardTab.Store[NARROW[into.eventTable], el.first.id, el.first];
IF el.rest = NIL THEN elLast ¬ el;
ENDLOOP;
elLast.rest ¬ into.elist;
into.elist ¬ from.elist;
};
MergeThreads:
PROC [into, from: ThreadsVisPrivate.ThreadFacts] ~ {
EachPair: CardTab.EachPairAction ~ {
[] ¬ CardTab.Store[NARROW[into.stackTable], key, val];
};
tlLast: LIST OF ThreadsVisPrivate.Thread;
FOR tl:
LIST
OF ThreadsVisPrivate.Thread ¬ from.tlist, tl.rest
WHILE tl#
NIL
DO
IF tl.rest=NIL THEN tlLast ¬ tl;
ENDLOOP;
tlLast.rest ¬ into.tlist;
into.tlist ¬ from.tlist;
[] ¬ CardTab.Pairs[NARROW[from.stackTable], EachPair];
I've not copied the tree and stackList from "from" into "into. I don't think they're needed here. - chauser
};
DrawThreads:
PROC [sliceQueue: ThreadsVisPrivate.Queue, tfacts: ThreadsVisPrivate.ThreadFacts, efacts: ThreadsVisPrivate.EventFacts] ~ {
y: CARD ← 0;
drawBackground: BOOL ← FALSE;
prevX: CARD ← 0;
butInfo: ButtonInfo ← NEW[ButtonInfoRep];
AddCount:
PROC [pos:
CARD] ~ {
found: BOOL;
val: CardTab.Val;
myVal: REF CARD;
[found, val] ← CardTab.Fetch[xPosCountTable, pos];
IF found
THEN {
myVal ← NARROW[val];
myVal^ ← myVal^ + 1;
}
ELSE {
myVal ← NEW[CARD ← 1];
};
[] ← CardTab.Store[xPosCountTable, pos, myVal];
};
DoEvent:
PROC [e: ThreadsVisPrivate.Event, r:
LIST
OF ThreadsVisPrivate.Event] ~ {
start: CARD ← e.time*xTransform;
finish: CARD ← e.wakeTime*xTransform;
name: ROPE ← Convert.RopeFromAtom[e.type, FALSE];
t: ATOM;
butInfo.e ← e;
butInfo.we ← NIL;
e.ypos ← y;
IF (start < efacts.min AND e.time # 0) OR (start > efacts.max) OR (finish > efacts.max) OR (finish # 0 AND finish < efacts.min) THEN RETURN;
IF (e.time*xTransform < efacts.min AND e.time # 0) OR (e.time*xTransform > efacts.max) OR (e.wakeTime*xTransform > efacts.max) THEN RETURN;
SELECT
TRUE
FROM
Rope.Match["*sleep*", name, FALSE] => t ← $UnixRun;
Rope.Match["*intr*", name, FALSE] OR Rope.Match["*syscall*", name, FALSE] OR Rope.Match["*trap*", name, FALSE] OR Rope.Match["*Fault*", name, FALSE] => t ← $UnixIntr;
Rope.Match["*SWTCH*", name, FALSE] => t ← $UnixIgnore;
ENDCASE => t ← e.type;
SELECT t
FROM
$UnixRun => {
Unix run events show a process awake. Finish time is when they woke up, and start time is when they went to sleep. Finish <= Start.
e.xstartpos ← finish;
butInfo.msg ← GetEventRope[e];
IF prevX # efacts.min
AND prevX < finish
THEN {
AddToLevel[sliceQueue, ready, MakeLine[prevX, y, finish, y, sleepColor, sleepSize, butInfo]];
IF drawBackground THEN AddToBack[sliceQueue, MakeLine[prevX, yMax/2, start, yMax/2, faintSleepColor, yMax, butInfo, FALSE, butt]];
};
IF prevX = efacts.min
AND prevX < start
THEN AddToLevel[sliceQueue, unknownThread, MakeLine[prevX, y, finish, y, unknownColor, unknownSize, butInfo]];
e.xendpos ← finish;
AddToLevel[sliceQueue, wait, MakeLine[finish-xShoulders, y, start+xShoulders, y, wakeColor, wakeSize, butInfo]];
AddToLevel[sliceQueue, ready, MakeLine[start-xShoulders, y, start+xShoulders, y, sleepColor, sleepSize, butInfo]];
prevX ← start;
};
$S => {
S events show a process sleeping. Finish time is when they woke up, and start time is when they went to sleep. Start <= Finish.
e.xstartpos ← start;
butInfo.msg ← GetEventRope[e];
IF finish = 0
THEN {
finish ← start;
AddToLevel[sliceQueue, uncertainSleep, MakeLine[start, y, finish, y, uncertainSleepColor, uncertainSleepSize, butInfo]];
};
IF prevX # efacts.min
AND prevX < start
THEN {
AddToLevel[sliceQueue, ready, MakeLine[prevX, y, start, y, wakeColor, wakeSize, butInfo]];
IF drawBackground THEN AddToBack[sliceQueue, MakeLine[prevX, yMax/2, start, yMax/2, faintWakeColor, yMax, butInfo, FALSE, butt]];
};
IF prevX = efacts.min
AND prevX < start
THEN AddToLevel[sliceQueue, unknownThread, MakeLine[prevX, y, start, y, unknownColor, unknownSize, butInfo]];
e.xendpos ← finish;
AddToLevel[sliceQueue, ready, MakeLine[finish-xShoulders, y, finish+xShoulders, y, wakeColor, wakeSize, butInfo]];
AddToLevel[sliceQueue, wait, MakeLine[start-xShoulders, y, finish+xShoulders, y, sleepColor, sleepSize, butInfo]];
prevX ← finish;
};
$X, $Y, $Z => {
color: Imager.Color ¬
SELECT e.type
FROM
$X => runxColor,
$Y => runyColor,
$Z => runzColor,
ENDCASE => ERROR;
e.xstartpos ← start;
butInfo.msg ← GetEventRope[e];
IF finish = 0 THEN finish ¬ start;
e.xendpos ← finish;
AddToLevel[sliceQueue, execute, MakeLine[start, y, finish, y, color, runSize, butInfo, FALSE, butt]];
AddCount[e.time*xTransform];
};
$W, $R, $UnixIntr => {
e.xstartpos ← start;
AddCount[e.time*xTransform];
};
ENDCASE => {
IO.PutF1[globalOutStream, "unrecognized duration type '%g'\n", IO.atom[e.type]];
AddCount[e.time*xTransform];
};
};
DoThread:
PROC [t: ThreadsVisPrivate.Thread, r:
LIST
OF ThreadsVisPrivate.Thread] ~ {
prevX ← efacts.min;
y ← y + yThreadIncrement;
butInfo.t ← t;
IF Rope.Match[backgroundName, t.name,
FALSE]
THEN drawBackground ← TRUE
ELSE drawBackground ← FALSE;
EMap[t.elist, DoEvent];
};
TMap[tfacts.tlist, DoThread];
};
DrawEvents:
PROC [sliceQueue: ThreadsVisPrivate.Queue, tfacts: ThreadsVisPrivate.ThreadFacts, efacts: ThreadsVisPrivate.EventFacts, type:
ATOM] ~ {
This must be called only after DrawThreads, which initializes the xpos/ypos values in events
tmpEvent: CardTab.Val;
butInfo: ButtonInfo ← NEW[ButtonInfoRep];
found: BOOL;
popupMsg: ROPE ← NIL;
missingEvents: CARD ← 0;
PaintArrow:
PROC [we: ThreadsVisPrivate.Event, xstart, wypos, xend, eypos:
REAL, fakeEvent:
BOOL, evenOdd:
CARD, thickness:
REAL] ~ {
color: ImagerColor.Color;
weName: ROPE ← Convert.RopeFromAtom[we.type, FALSE];
SELECT
TRUE
FROM
CheckName[tfacts, we, "ThreadsQueues.←XR←Notify"] => color ← notifyTailColor;
CheckName[tfacts, we, "ThreadsQueues.←XR𡤋roadcast"] => color ← broadcastTailColor;
CheckName[tfacts, we, "ThreadsQueues.←XR←MonitorExitOutOfLine"] => color ← monitorExitTailColor;
CheckName[tfacts, we, "Threads1.←XR𡤏ork"] OR CheckName[tfacts, we, "Threads1.←XR←TryFork"] => color ← forkTailColor;
CheckName[tfacts, we, "ThreadsQueues.←XR←NakedNotifyInner"] => color ← nakedNotifyTailColor;
CheckName[tfacts, we, "kill.←kill"] => color ← killTailColor;
we.type = $R => color ¬ runSampleColor;
we.type = $X => color ¬ xSampleColor;
Rope.Match["*intr*", weName, FALSE] => color ← unixIntrColor;
Rope.Match["*Fault*", weName, FALSE] => color ← unixFaultColor;
CheckName[tfacts, we, "*unixkernel*"] OR CheckName[tfacts, we, "*unixidle*"]=> color ← blackColor;
ENDCASE => {
color ← blackColor;
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Feedback, IO.PutFR1["wakeup event %g of unrecognized type by stack name", IO.card[we.id]]];
};
IF
NOT fakeEvent
AND color # blackColor
THEN {
IF Basics.OddCard[evenOdd] THEN wypos ← wypos + 1 ELSE wypos ← wypos - 1;
IF eypos > wypos THEN eypos ← eypos - (wakeSize/2) ELSE eypos ← eypos + (wakeSize/2);
};
AddToLevel[sliceQueue, event, MakeLine[xstart, wypos, xstart, wypos, color, arrowTailSize, butInfo, FALSE, round]];
AddToLevel[sliceQueue, event, MakeLine[xstart, wypos, xend, eypos, color, thickness, butInfo, drawArrowHeads]]
};
DoEvent:
PROC [e: ThreadsVisPrivate.Event, r:
LIST
OF ThreadsVisPrivate.Event] ~ {
cnt: CARD;
t: ATOM;
computedThickness: REAL;
name: ROPE ← Convert.RopeFromAtom[e.type, FALSE];
butInfo.e ← e;
butInfo.we ← NIL;
IF (e.time*xTransform < efacts.min AND e.time # 0) OR (e.wakeTime*xTransform > efacts.max) OR (e.time*xTransform > efacts.max) THEN RETURN;
IF e.type = type
OR (type = $Other
AND Rope.Length[name] > 1)
THEN {
IF Rope.Match["*sleep*", name,
FALSE]
OR Rope.Match["*SWTCH*", name,
FALSE]
THEN t ← $UnixRun
ELSE IF Rope.Match["*intr*", name, FALSE] OR Rope.Match["*syscall*", name, FALSE] OR Rope.Match["*trap*", name, FALSE] OR Rope.Match["*Fault*", name, FALSE] THEN t ← $UnixIntr
ELSE t ← e.type;
SELECT t FROM
$UnixRun => {
wakeEvent: ThreadsVisPrivate.Event ← NARROW[tmpEvent];
fixedxstart: REAL;
fixedxend: REAL;
butInfo.msg ← IO.PutFR["from: %g to: %g", IO.card[wakeEvent.id], IO.card[e.id]];
butInfo.we ← wakeEvent;
IF wakeEvent.xstartpos = 0 THEN {
An event not being displayed. Fake its position
wakeEvent.ypos ← e.ypos+yThreadIncrement/2;
wakeEvent.xstartpos ← wakeEvent.xendpos ← e.xendpos;
butInfo.msg ← IO.PutFR["from: %g to: %g (thread not shown)", IO.card[wakeEvent.id], IO.card[e.id]];
fake ← TRUE;
};
[fixedxstart, cnt, computedThickness] ← GetFixedX[xPosTable, wakeEvent.xstartpos];
IF wakeEvent.xstartpos = e.xendpos THEN fixedxend ← fixedxstart ELSE [fixedxstart, cnt, computedThickness] ← GetFixedX[xPosTable, e.xendpos];
butInfo.msg ← IO.PutFR["from: %g to: %g", IO.card[wakeEvent.id], IO.card[e.id]];
PaintArrow[wakeEvent, fixedxstart, wakeEvent.ypos, fixedxend, e.ypos, fake, cnt, computedThickness];
wakeEvent.drawn ← TRUE;
};
$S => {
fake: BOOL ← FALSE;
IF e.wakeEvent # 0
THEN {
[found, tmpEvent] ← CardTab.Fetch[NARROW[efacts.eventTable], e.wakeEvent] ;
IF
NOT found
THEN {
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Feedback, IO.PutFR1["ThreadsVis: event id %g not found.", IO.card[e.wakeEvent]]];
}
ELSE {
wakeEvent: ThreadsVisPrivate.Event ← NARROW[tmpEvent];
fixedxstart: REAL;
fixedxend: REAL;
butInfo.msg ← IO.PutFR["from: %g to: %g", IO.card[wakeEvent.id], IO.card[e.id]];
butInfo.we ← wakeEvent;
IF wakeEvent.xstartpos = 0
THEN {
An event not being displayed. Fake its position
wakeEvent.ypos ← e.ypos+yThreadIncrement/2;
wakeEvent.xstartpos ← wakeEvent.xendpos ← e.xendpos;
butInfo.msg ← IO.PutFR["from: %g to: %g (thread not shown)", IO.card[wakeEvent.id], IO.card[e.id]];
fake ← TRUE;
};
[fixedxstart, cnt, computedThickness] ← GetFixedX[xPosTable, wakeEvent.xstartpos, wakeEvent];
IF wakeEvent.xstartpos = e.xendpos THEN fixedxend ← fixedxstart ELSE [fixedxstart, cnt, computedThickness] ← GetFixedX[xPosTable, e.xendpos, e];
butInfo.msg ← IO.PutFR["from: %g to: %g", IO.card[wakeEvent.id], IO.card[e.id]];
PaintArrow[wakeEvent, fixedxstart, wakeEvent.ypos, fixedxend, e.ypos, fake, cnt, computedThickness];
wakeEvent.drawn ← TRUE;
};
};
};
$W, $UnixIntr => {
fixedxstart: REAL;
IF
NOT e.drawn
THEN {
butInfo.msg ← IO.PutFR1["from: %g to: unknown", IO.rope[GetEventRope[e]]];
[fixedxstart, cnt, computedThickness] ← GetFixedX[xPosTable, e.xstartpos, e];
PaintArrow[e, fixedxstart, e.ypos, fixedxstart, e.ypos+yThreadIncrement/2, TRUE, cnt, computedThickness];
e.drawn ← TRUE;
};
};
$R => {
fixedxstart: REAL;
IF
NOT e.drawn
THEN {
butInfo.msg ← IO.PutFR1["from: %g to: NA", IO.rope[GetEventRope[e]]];
[fixedxstart, cnt, computedThickness] ← GetFixedX[xPosTable, e.xstartpos, e];
PaintArrow[e, fixedxstart, e.ypos, fixedxstart, e.ypos+yThreadIncrement/2, TRUE, cnt, computedThickness];
e.drawn ← TRUE;
};
};
ENDCASE => {
IO.PutF1[globalOutStream, "unrecognized event type '%g'\n", IO.atom[e.type]];
fixedxstart: REAL;
IF NOT e.drawn THEN {
butInfo.msg ← IO.PutFR1["from: %g to: NA", IO.rope[GetEventRope[e]]];
[fixedxstart, cnt, computedThickness] ← GetFixedX[xPosTable, e.xstartpos];
PaintArrow[e, fixedxstart, e.ypos, fixedxstart, e.ypos+yThreadIncrement/2, TRUE, cnt, computedThickness];
e.drawn ← TRUE;
};
};
};
};
DoThread:
PROC [t: ThreadsVisPrivate.Thread, r:
LIST
OF ThreadsVisPrivate.Thread] ~ {
butInfo.t ← t;
EMap[t.elist, DoEvent];
};
TMap[tfacts.tlist, DoThread];
IF missingEvents > 0 THEN SimpleFeedback.Append[$ThreadsVis, oneLiner, $Feedback, IO.PutFR1["ThreadsVis: %g uncaused wakeups.", IO.card[missingEvents]]];
};
CheckName:
PROC [tfacts: ThreadsVisPrivate.ThreadFacts, e: ThreadsVisPrivate.Event, name:
ROPE]
RETURNS [v:
BOOL] ~ {
nameTable: REF;
stack: LIST OF REF ANY;
stack ← NARROW[CardTab.Fetch[NARROW[tfacts.stackTable], e.node].val];
v ← SearchStackForName[name, stack];
RETURN [v];
};
SearchStackForName:
PROC [pattern:
ROPE, stack:
LIST
OF
REF
ANY]
RETURNS [returnVal:
BOOL] ~ {
FOR each:
LIST
OF
REF
ANY ← stack, each.rest
UNTIL each =
NIL
DO
IF Rope.Match[pattern, NARROW[each.first, ThreadsVisPrivate.StackEntry].name] THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE];
};
GetFixedX:
PROC [t: CardTab.Ref, x:
CARD, e: ThreadsVisPrivate.Event]
RETURNS [pos:
REAL, cnt:
CARD, thickness:
REAL] ~ {
found: BOOL;
myVal: ShiftX;
val, countVal: CardTab.Val;
oldCount: CARD;
[found, countVal] ← CardTab.Fetch[xPosCountTable, x];
IF NOT found THEN CommanderOps.Failed[IO.PutFR["ThreadsVis: time %g (event %g) not in count table.", IO.card[x], IO.card[e.id]]];
oldCount ← NARROW[countVal, REF CARD]^;
thickness ← MAX[(xTransform-3)/(1.5*oldCount), 0.2];
IF thickness > maximumArrowThickness THEN thickness ← maximumArrowThickness;
[found, val] ← CardTab.Fetch[t, x];
IF found
THEN {
myVal ← NARROW[val];
myVal.pos ← myVal.pos + thickness*1.5;
myVal.cnt ← myVal.cnt + 1;
}
ELSE {
myVal ← NEW[ShiftXRep ← [x, 0]];
};
[] ← CardTab.Store[t, x, myVal];
RETURN [myVal.pos, myVal.cnt, thickness];
};
MakeLine:
PROC [startx, starty, finishx, finishy:
REAL, color: Imager.Color, thickness:
REAL, msg: ButtonInfo, arrow:
BOOL ←
FALSE, endType: Imager.StrokeEnd ← butt --round--]
RETURNS [return: GGSlice.Slice] =
{
FixSeg:
PROC [seg: GGSlice.Segment, width:
REAL] ~ {
seg.color ← color;
seg.strokeWidth ← width;
seg.strokeEnd ← endType;
};
slice: GGSlice.Slice ← GGTraj.CreateTraj[[startx, starty]];
seg: GGSlice.Segment ← GGSegment.MakeLine[[startx, starty], [finishx, finishy], NIL];
FixSeg[seg, thickness];
[] ← GGTraj.AddSegment[slice, hi, seg, lo] ;
IF arrow
THEN {
assume a vertical arrow only
arrowSeg: GGSlice.Segment;
sign: INT;
IF starty > finishy THEN sign ← 1 ELSE sign ← -1;
arrowSeg ← GGSegment.MakeLine[[finishx, finishy], [finishx-1, finishy+(1*sign)], NIL];
FixSeg[arrowSeg, thickness*1.5];
[] ← GGTraj.AddSegment[slice, hi, arrowSeg, lo] ;
arrowSeg ← GGSegment.MakeLine[[finishx-1, finishy+(1*sign)], [finishx, finishy], NIL];
FixSeg[arrowSeg, thickness*1.5];
[] ← GGTraj.AddSegment[slice, hi, arrowSeg, lo] ;
arrowSeg ← GGSegment.MakeLine[[finishx, finishy], [finishx+1, finishy+(1*sign)], NIL];
FixSeg[arrowSeg, thickness*1.5];
[] ← GGTraj.AddSegment[slice, hi, arrowSeg, lo] ;
};
IF msg # NIL THEN AddSliceButton[slice, NIL, msg];
IO.PutFL[globalOutStream, "drew %g/%g-%g/%g. %g\n", LIST[IO.real[startx], IO.real[starty], IO.real[finishx], IO.real[finishy], IO.rope[GetColorName[color]]]];
RETURN [slice];
};
AddText:
PROC [sliceQueue: ThreadsVisPrivate.Queue, x, y:
REAL, text:
ROPE, where: ThreadsVisPrivate.FrontBackType ← front] =
{
router: Feedback.MsgRouter ← Feedback.CreateRouter[];
slice: GGSlice.Slice;
fontdata: GGSlice.FontData ← GGFont.CreateFontData[];
bar: BOOLEAN;
newlinePos: INT;
thisText: ROPE;
currentY: REAL ← y;
fontdata ← GGFont.InitFontData[fontdata];
fontdata.literal ← "xerox/xc1-2-2/Modern-bold-italic";
fontdata.prefix ← "xerox/xc1-2-2/";
fontdata.literalFSF ← "Modern-BI";
fontdata.userFSF ← "Modern";
UNTIL Rope.Length[text] = 0
DO
newlinePos ← Rope.SkipTo[text, 0, "\n"];
thisText ← Rope.Substr[text, 0, newlinePos]; -- don't get the newline
IF Rope.Length[text] = newlinePos THEN text ← NIL ELSE text ← Rope.Substr[text, newlinePos+1];
thisText ← ExpandTabs[thisText];
slice ← GGSlice.MakeTextSlice[thisText, commentTextColor, screen];
fontdata.transform ← ImagerTransformation.Scale[commentTextSize];
fontdata.transform ← ImagerTransformation.TranslateTo[fontdata.transform, [x, currentY]];
bar ← GGSlice.SetTextFontAndTransform[slice, fontdata, router, NIL];
SELECT where
FROM
front => AddToBack[sliceQueue, slice];
back => AddToFront[sliceQueue, slice];
ENDCASE;
currentY ← currentY - commentTextSize;
ENDLOOP;
};
ImagerColorFromName:
PROC [name:
ROPE]
RETURNS [Imager.Color] = {
color: Imager.Color ← ImagerColor.ColorFromRGB[ColorFns.RGBFromHSL[NamedColors.RopeToHSL[name]]];
RETURN [color];
};
GetColorName:
PROC [color: Imager.Color]
RETURNS [r:
ROPE] = {
IF color = sleepColor THEN RETURN ["sleep"];
IF color = wakeColor THEN RETURN ["wake"];
IF color = unknownColor THEN RETURN ["unknown"];
IF color = uncertainSleepColor THEN RETURN ["uncertain"];
IF color = notifyTailColor THEN RETURN ["notifyTailColor"];
IF color = broadcastTailColor THEN RETURN ["broadcastTailColor"];
IF color = monitorExitTailColor THEN RETURN ["broadcastTailColor"];
IF color = forkTailColor THEN RETURN ["forkTailColor"];
RETURN ["badcolor"];
};
DrawLegend:
PROC [sliceQueue: ThreadsVisPrivate.Queue, xFinal, yFinal:
REAL] ~ {
y: REAL ← yFinal;
PutText:
PROC [s:
ROPE] ~ {
AddText[sliceQueue, xFinal+15, y-5, s, back];
y ← y - commentTextSize;
};
AddToFront[sliceQueue, MakeLine[xFinal, y, xFinal, y, uncertainSleepColor, uncertainSleepSize, NIL]];
PutText["uncaused event (timeout?)"];
AddToFront[sliceQueue, MakeLine[xFinal, y, xFinal+4, y, wakeColor, wakeSize, NIL]];
PutText["ready thread"];
AddToFront[sliceQueue, MakeLine[xFinal, y, xFinal+4, y, unknownColor, unknownSize, NIL]];
PutText["unknown thread state"];
AddToFront[sliceQueue, MakeLine[xFinal, y, xFinal+4, y, sleepColor, sleepSize, NIL]];
PutText["sleeping thread"];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y-2, notifyTailColor, arrowTailSize, NIL]];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y+6, notifyTailColor, connectSize, NIL, drawArrowHeads]];
PutText["notify"];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y-2, broadcastTailColor, arrowTailSize, NIL]];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y+6, broadcastTailColor, connectSize, NIL, drawArrowHeads]];
PutText["broadcast"];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y-2, monitorExitTailColor, arrowTailSize, NIL]];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y+6, monitorExitTailColor, connectSize, NIL, drawArrowHeads]];
PutText["monitor exit"];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y-2, forkTailColor, arrowTailSize, NIL]];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y+6, forkTailColor, connectSize, NIL, drawArrowHeads]];
PutText["fork"];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y-2, nakedNotifyTailColor, arrowTailSize, NIL]];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y+6, nakedNotifyTailColor, connectSize, NIL, drawArrowHeads]];
PutText["naked notify"];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y-2, killTailColor, arrowTailSize, NIL]];
AddToFront[sliceQueue, MakeLine[xFinal, y-2, xFinal, y+6, killTailColor, connectSize, NIL, drawArrowHeads]];
PutText["unix 'kill' (of IOP)"];
};
LabelThreads:
PROC [sliceQueue: ThreadsVisPrivate.Queue, tfacts: ThreadsVisPrivate.ThreadFacts, efacts: ThreadsVisPrivate.EventFacts] ~ {
y: INT ← 0;
DoThread:
PROC [t: ThreadsVisPrivate.Thread, r:
LIST
OF ThreadsVisPrivate.Thread] ~ {
name: ROPE ← GetThreadNameAndAliases[t];
y ← y + yThreadIncrement;
AddText[sliceQueue, efacts.max+10, y-3, name, back];
};
TMap[tfacts.tlist, DoThread];
};
GetThreadNameAndAliases:
PROC [t: ThreadsVisPrivate.Thread]
RETURNS [val:
ROPE] ~ {
IF t #
NIL
THEN {
val ← Rope.Concat[ThreadsVisPrivate.STName[NARROW[t.tree]], t.alias];
}
ELSE {
val ← "*noname*";
};
};
DrawXTicks:
PROC [sliceQueue: ThreadsVisPrivate.Queue, efacts: ThreadsVisPrivate.EventFacts, frequency:
CARD] ~ {
buttonData: ButtonInfo;
FOR x:
CARD ← 0, x+xTransform*frequency
UNTIL x > efacts.max
DO
buttonData ← NEW[ButtonInfoRep ← [IO.PutFR1["time is %d.", IO.card[(x/xTransform)+efacts.min-1]], NIL, NIL]];
AddToBack[sliceQueue, MakeLine[x, 0, x, yMax, xTickColor, 0.1, buttonData ]];
ENDLOOP;
};
GetEventRope:
PROC [e: ThreadsVisPrivate.Event]
RETURNS [out:
ROPE] ~ {
RETURN [IO.PutFLR["%g %g %g %g %g %g", LIST[ IO.card[e.id], IO.atom[e.type], IO.card[e.time], IO.card[e.node], IO.card[e.wakeTime], IO.card[e.wakeEvent]]]];
};
InitAliases:
PROC []
RETURNS [returnVal:
BOOL ← FALSE] ~ {
ENABLE {
IO.EndOfStream => GOTO Done;
PFS.Error => {returnVal ← FALSE; GOTO Done};
};
name, alias: ROPE;
in: IO.STREAM ← PFS.StreamOpen[PFS.PathFromRope["ThreadsVis.aliases"], read];
DO
name ← IO.GetRopeLiteral[in];
alias ← IO.GetRopeLiteral[in];
aliasList ← CONS[NEW[AliasRep ← [name, alias]], aliasList];
returnVal ← TRUE;
ENDLOOP;
};
ConstructAliases:
PROC [tfacts: ThreadsVisPrivate.ThreadFacts] ~ {
myThread: ThreadsVisPrivate.Thread;
alias: Alias;
ForEachName: SymTab.EachPairAction ~ {
PROC [key: Key, val: Val] RETURNS [quit: BOOL ¬ FALSE]
IF Rope.Match[alias.name, key,
FALSE]
THEN {
myThread.alias ← Rope.Cat[myThread.alias, " - ", alias.alias];
RETURN [TRUE];
};
};
ForEachThread:
PROC [t: ThreadsVisPrivate.Thread, r:
LIST
OF ThreadsVisPrivate.Thread] ~ {
myThread ← t;
[] ← SymTab.Pairs[NARROW[t.nameTable], ForEachName];
};
ForEachAlias:
PROC[item:
REF
ANY, l:
LIST
OF
REF
ANY] ~ {
alias ← NARROW[item];
TMap[tfacts.tlist, ForEachThread];
};
IF aliasList # NIL THEN List.Map[aliasList, ForEachAlias];
};
Button Procs
theButtonDataKey: ATOM ~ $ButtonData;
theButtonDataRope:
ROPE ~
"Poppy1
Class: PopUpButton
MessageHandler: Tioga
Menu: (
((\"%g\") \"%g\" \"Put event id at tioga caret\")
(\"\" \"%g\" \"The event id.\")
(\"\" \"%g\" \"The thread name.\")
(<TVGetStack %g> \"put S stack\" \"Insert stack of sleeping thread to tioga caret.\")
(<TVPopupXStack %g> \"pop S stack\" \"Popup stack of sleeping thread to tioga caret.\")
(\"\" \"\" \"\")
(<TVGetStack %g> \"put W stack\" \"Insert stack of signalling thread to tioga caret.\")
(<TVPopupXStack %g> \"pop W stack\" \"Popup stack of sleeping thread to tioga caret.\")
)
Feedback: (
(MouseMoved <SetCursor bullseye>)
)";
theButtonDataValue: REF ~ NodeProps.DoSpecs[theButtonDataKey, theButtonDataRope];
TVGetStackRopeHelper: EBTypes.EBLanguageProc = {
PROC [arguments: LIST OF REF ANY, buttonInfo: ButtonInfo, clientData: REF] RETURNS [REF ANY ← NIL];
outRope: ROPE ← NIL;
eventId: CARD ← 0;
eventCardTab: CardTab.Val;
event: ThreadsVisPrivate.Event;
found: BOOL;
cardTabStackList: CardTab.Val;
stackList: LIST OF REF ANY;
WITH arguments.first
SELECT
FROM
c: REF CARD => eventId ← c^;
i: REF INT => eventId ← i^;
ENDCASE;
IF globalThreadFacts = NIL THEN RETURN["-----ThreadsVis has no active file."];
[found, eventCardTab] ← CardTab.Fetch[NARROW[globalEventFacts.eventTable], eventId];
IF NOT found THEN RETURN[Rope.Concat[outRope, IO.PutFR1["-----%g is not a known id.", IO.card[eventId]]]];
event ← NARROW[eventCardTab];
outRope ← IO.PutFR1["-----stack for event: %g\n", IO.rope[GetEventRope[event]]];
[found, cardTabStackList] ← CardTab.Fetch[NARROW[globalThreadFacts.stackTable], event.node];
IF NOT found THEN RETURN[Rope.Concat[outRope, IO.PutFR1["-----id %g is not on any stack.", IO.card[event.node]]]];
stackList ← NARROW[cardTabStackList];
outRope ← Rope.Concat[outRope, IO.PutFR1["%g", IO.rope[GetStackRope[stackList]]]];
RETURN[outRope];
};
TVGetStack: EBTypes.EBLanguageProc = {
PROC [arguments: LIST OF REF ANY, buttonInfo: ButtonInfo, clientData: REF, context: Context] RETURNS [REF ANY ← NIL];
RETURN[TVGetStackRopeHelper[arguments, buttonInfo, clientData, context]];
};
TVPopupXStack: EBTypes.EBLanguageProc = {
rope: ROPE ← NARROW[TVGetStackRopeHelper[arguments, buttonInfo, clientData, context]];
PopupText["ThreadsVis Stacks", rope];
};
AddSliceButton:
PROC [slice: GGSlice.Slice, parts: GGSlice.SliceParts, buttonInfo: ButtonInfo] ~ {
GetNameFromEvent:
PROC [we: ThreadsVisPrivate.Event]
RETURNS [r:
ROPE] ~ {
tmpEvent: CardTab.Val;
oldName: ROPE ← NIL;
newName: ROPE ← NIL;
cardTabStackList: CardTab.Val;
stackList: LIST OF REF ANY;
found: BOOL;
IF NOT Rope.Match["*from:*", buttonInfo.msg] THEN RETURN [NIL];
IF buttonInfo.we = NIL THEN RETURN [NIL];
[found, cardTabStackList] ← CardTab.Fetch[NARROW[globalThreadFacts.stackTable], buttonInfo.we.node];
IF NOT found THEN RETURN [NIL];
stackList ← NARROW[cardTabStackList];
FOR stack:
LIST
OF
REF
ANY ← stackList, stack.rest
UNTIL stack =
NIL
DO
oldName ← newName;
newName ← NARROW[stack.first, ThreadsVisPrivate.StackEntry].name;
ENDLOOP;
RETURN [oldName];
};
MakeFancyName:
PROC [a, b:
ROPE]
RETURNS [r:
ROPE] ~ {
IF b = NIL THEN RETURN [a] ELSE RETURN [IO.PutFR["from %g to %g", IO.rope[b], IO.rope[a]]];
};
fancyName: ROPE ← MakeFancyName[GetThreadNameAndAliases[buttonInfo.t], GetNameFromEvent[buttonInfo.e]];
r: REF;
weId, eId: CARD;
IF buttonInfo.we = NIL THEN weId ← 0 ELSE weId ← buttonInfo.we.id;
IF buttonInfo.e = NIL THEN eId ← 0 ELSE eId ← buttonInfo.e.id;
r ← NodeProps.DoSpecs[theButtonDataKey, IO.PutFLR[theButtonDataRope, LIST[ IO.rope[buttonInfo.msg], IO.rope[buttonInfo.msg], IO.rope[buttonInfo.msg], IO.rope[fancyName], IO.card[eId], IO.card[eId], IO.card[weId], IO.card[weId]]]];
GGProps.Put[slice, parts, theButtonDataKey, r];
};
X Popup Windows
Widget: TYPE = XTk.Widget;
PopupText:
PROC [header, text:
ROPE] ~ {
shell: Widget ~ XTkWidgets.CreateShell[
className: $ThreadsVis,
windowHeader: header,
packageName: "ThreadsVis",
shortName: "ThreadsVis"
];
doneButton, refreshButton, stuffXButton, streamWidget, scroller, innerContainer, container, menu: Widget;
stream: IO.STREAM;
streamWidget ← XTkWidgets.CreateStreamWidget[widgetSpec: [geometry: [size: [-1, 5000]]]];
stream ← XTkWidgets.CreateStream[streamWidget];
doneButton ← XTkWidgets.CreateButton[[], " Done ", [], XPopupDoneProc];
refreshButton ← XTkWidgets.CreateButton[[], " Refresh ", [], XPopupRefreshProc, stream, text];
stuffXButton ← XTkWidgets.CreateButton[[], " Stuff X ", [], XPopupStuffXProc, text];
menu ← XTkWidgets.CreateXStack[stack: LIST[doneButton, refreshButton, stuffXButton]];
scroller ← XTkXBiScroller.CreateXBiScroller[widgetSpec: [geometry: [size: [-1, 500]]], child: streamWidget, hsbar: FALSE];
container ← XTkWidgets.CreateYStack[widgetSpec: [geometry: [size: [400, 500]]], stack: LIST[menu, XTkWidgets.HRule[], scroller]];
XTkWidgets.SetShellChild[shell, container];
XTkWidgets.RealizeShell[shell];
IO.PutRope[stream, text];
};
XPopupDoneProc: XTkWidgets.ButtonHitProcType = {
XTkWidgets.DestroyShell[XTk.RootWidget[widget]];
};
XPopupStuffXProc: XTkWidgets.ButtonHitProcType = {
XlCutBuffers.Put[widget.connection, NARROW[registerData]];
};
XPopupRefreshProc: XTkWidgets.ButtonHitProcType = {
stream: IO.STREAM ~ NARROW[registerData];
IO.PutChar[stream, 'L - 100B];
IO.PutRope[stream, NARROW[callData]];
};
Commander.Register["TVStackPrint", StackPrintCommand, "Threads Visualizer Stack Print Command"];