ThreadsVisImpl.mesa
Copyright Ó 1992 by Xerox Corporation. All rights reserved.
Weiser, March 14, 1993 5:54 pm PST
Chauser, December 3, 1992 4:23 pm PST
DIRECTORY
Atom,
Basics,
CardTab,
Commander,
CommanderOps,
Imager,
IO,
PFS,
Real,
Rope,
SF,
GGFileOut,
GGScene,
GGFont,
GGSlice,
GGModelTypes,
GGCoreTypes,
GGProps,
GGSegment,
GGSegmentTypes,
NodeProps,
GGTraj,
ColorFns,
NamedColors,
ColorTypes,
ImagerTransformation,
ImagerColor,
ThreadsVisPrivate,
Feedback,
SimpleFeedback,
List,
GGSliceOps,
Convert,
BasicTime,
SymTab,
Process,
GCCallBack,
EBTypes,
EmbeddedButtons,
Xl,
XlCutBuffers,
XTk,
XTkWidgets,
XTkXBiScroller;
ThreadsVisImpl: CEDAR MONITOR
IMPORTS Basics, CardTab, CommanderOps, Commander, GGFileOut, ImagerTransformation, IO, GGScene, GGSlice, NodeProps, GGProps, GGSegment, GGTraj, ImagerColor, PFS, ColorFns, NamedColors, ThreadsVisPrivate, GGFont, Feedback, SimpleFeedback, List, Convert, Rope, BasicTime, SymTab, Process, GCCallBack, EmbeddedButtons, XlCutBuffers, XTkWidgets, XTk, XTkXBiScroller =
BEGIN
ROPE: TYPE = Rope.ROPE;
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];
Global Variables (should get rid of these)
collecting: BOOLFALSE;
globalOutStream: IO.STREAMNIL;
globalThreadFacts: ThreadsVisPrivate.ThreadFacts ← NIL;
globalEventFacts: ThreadsVisPrivate.EventFacts ← NIL;
currentFileName: ROPE;
xTransform: CARD ¬ 10;
xShoulders: CARD ¬ MAX[(xTransform/2)-1, 1];
xPosTable: CardTab.Ref ← CardTab.Create[];
xPosCountTable: CardTab.Ref ← CardTab.Create[];
maximumArrowThickness: REAL ← arrowTailSize - 1.0;
aliasList: LIST OF REF ANYNIL;
yMax: CARD ← 0;
backgroundQueue: ThreadsVisPrivate.Queue ← NIL;
backgroundName: ROPENIL;
CommanderProcs
StackPrintCommand: Commander.CommandProc = {
stackIdRope: ROPE ← CommanderOps.NextArgument[cmd];
stackId: CARD ← Convert.CardFromRope[stackIdRope ! Convert.Error => CommanderOps.Failed[IO.PutFR1["'%g' is not a valid argument.", IO.rope[stackIdRope]]]];
found: BOOL;
cardTabStackList: CardTab.Val;
stackList: LIST OF REF ANY;
IF globalThreadFacts = NIL THEN CommanderOps.Failed["no data to analyze yet."];
[found, cardTabStackList] ← CardTab.Fetch[NARROW[globalThreadFacts.stackTable], stackId];
IF NOT found THEN CommanderOps.Failed[IO.PutFR1["id %g is not on any stack.", IO.card[stackId]]];
stackList ← NARROW[cardTabStackList];
IO.Put1[globalOutStream, IO.rope[GetStackRope[stackList]]];
};
ThreadsVisCommand: Commander.CommandProc = {
threadCount: CARD ← 0;
CountThreads: PROC [ThreadsVisPrivate.Thread, LIST OF ThreadsVisPrivate.Thread] ~ {
threadCount ← threadCount + 1;
};
stackList: LIST OF REF ANY;
fileName: ROPE ~ CommanderOps.NextArgument[cmd];
outputFileName: ROPE;
globalTransform: ImagerTransformation.Transformation;
outStream: IO.STREAM ← cmd.out;
sliceQueue: ThreadsVisPrivate.Queue ← NewQueue[];
s: IO.STREAM;
scene: GGScene.Scene;
tlist: LIST OF ThreadsVisPrivate.Thread;
eventList: LIST OF ThreadsVisPrivate.Event;
efacts: ThreadsVisPrivate.EventFacts;
tfacts: ThreadsVisPrivate.ThreadFacts;
commentText: ROPE;
readStart, readEnd, buildEnd: BasicTime.GMT;
tickProc: PROCESSNIL;
nextItem: ROPE;
IF fileName = NIL
THEN CommanderOps.Failed[cmd.procData.doc]
ELSE {
ENABLE UNWIND => IF tickProc # NIL THEN { KillTicker[tickProc]; };
globalOutStream ← outStream;
readStart ← BasicTime.Now[];
GCCallBack.RegisterBefore[NoticeCollection];
IO.PutF1[outStream, "%g", IO.rope["Reading and internalizing"]];
tickProc ← FORK StartTicker[outStream];
[eventFacts: efacts, threadFacts: tfacts, commentText: commentText] ← ThreadsVisPrivate.ReadTreeFromFile[fileName, FALSE];
readEnd ← BasicTime.Now[];
KillTicker[tickProc];
IO.PutF1[outStream, "%g secs. Building scene ", IO.card[BasicTime.Period[from: readStart, to: readEnd]]];
currentFileName ← fileName;
outputFileName ← Rope.Concat[fileName, ".gg"];
tickProc ← FORK StartTicker[outStream];
DO
nextItem ← CommanderOps.NextArgument[cmd];
IF nextItem = NIL THEN EXIT;
SELECT TRUE FROM
Rope.Equal[nextItem, "threadList:", FALSE] => {
threadList: ROPE ← CommanderOps.NextArgument[cmd];
selected: ROPE ← SelectThreads[threadList, tfacts];
out: ROPE;
IF Rope.Length[selected] = 0 THEN out ← "threadList matched no threads: continuing with all."
ELSE out ← IO.PutFR1["using only `%g' ", IO.rope[selected]];
IO.Put1[outStream, IO.rope[out]];
commentText ← Rope.Cat[out, "\n", commentText];
};
Rope.Equal[nextItem, "doCedar:", FALSE] => {
selected: ROPE ← SelectThreads["*Package* T*.G* VP* IOP* *xnews* *unixkernel* *unixidle*", tfacts];
out: ROPE;
IF Rope.Length[selected] = 0 THEN out ← "doCedar matched no threads: continuing with all."
ELSE out ← IO.PutFR1["doCedar selected only `%g' ", IO.rope[selected]];
IO.Put1[outStream, IO.rope[out]];
commentText ← Rope.Cat[out, "\n", commentText];
};
Rope.Equal[nextItem, "timeRange:", FALSE] => {
out: ROPE;
don't specify a time range before any additional input files
ScaleAllEvents[efacts]; -- want times to be relative to start time of events known so far
efacts.min ← Convert.CardFromRope[CommanderOps.NextArgument[cmd] ! Convert.Error => GOTO badRange];
efacts.max ← Convert.CardFromRope[CommanderOps.NextArgument[cmd] ! Convert.Error => GOTO badRange];
out ← IO.PutFR["restricting to %g-%g ", IO.card[efacts.min], IO.card[efacts.max]];
IO.Put1[outStream, IO.rope[out]];
commentText ← Rope.Cat[out, "\n", commentText];
FilterEvents[efacts, efacts.min, efacts.max];
EXITS
badRange => CommanderOps.Failed["Bad values following 'timeRange:' option."];
};
Rope.Equal[nextItem, "backgroundThread:", FALSE] => {
backgroundName ← CommanderOps.NextArgument[cmd];
commentText ← Rope.Concat[IO.PutFR1[".background thread of '%g'.\n", IO.rope[backgroundName]], commentText];
IO.PutF1[outStream, "using '%g' for background ", IO.rope[backgroundName]];
};
Rope.Equal[nextItem, "moreInput:", FALSE] => {
moreFileName: ROPE ¬ CommanderOps.NextArgument[cmd];
efs: ThreadsVisPrivate.EventFacts;
tfs: ThreadsVisPrivate.ThreadFacts;
ct: ROPE;
GCCallBack.RegisterBefore[NoticeCollection];
IO.PutF1[outStream, "%g", IO.rope["Reading and internalizing"]];
[eventFacts: efs, threadFacts: tfs, commentText: ct] ← ThreadsVisPrivate.ReadTreeFromFile[moreFileName, FALSE];
readEnd ← BasicTime.Now[];
IO.PutF1[outStream, "%g secs. ", IO.card[BasicTime.Period[from: readStart, to: readEnd]]];
MergeEvents[into: efacts, from: efs];
MergeThreads[into: tfacts, from: tfs];
};
Rope.Equal[nextItem, "xTransform:", FALSE] => {
out: ROPE;
xTransform ¬ MAX[Convert.CardFromRope[CommanderOps.NextArgument[cmd] ! Convert.Error => GOTO badTransform], 3];
xShoulders ¬ MAX[(xTransform/2)-1, 1];
out ← IO.PutFR1["xTransform %g ", IO.card[xTransform]];
IO.Put1[outStream, IO.rope[out]];
commentText ← Rope.Cat[out, "\n", commentText];
EXITS
badTransform => CommanderOps.Failed["Bad value following 'xTransform:' option."];
};
ENDCASE => CommanderOps.Failed[IO.PutFR1["Unrecognized command option '%g'.", IO.rope[nextItem]]];
ENDLOOP;
ScaleAllEvents[efacts]; -- facts are now 1 based;
globalThreadFacts ← tfacts;
globalEventFacts ← efacts;
efacts.min ← efacts.min*xTransform;
efacts.max ← efacts.max*xTransform;
threadCount ← 0;
TMap[tfacts.tlist, CountThreads];
yMax ← threadCount*yThreadIncrement;
IF InitAliases[] THEN {
IO.Put1[outStream, IO.rope["using ThreadsVis.aliases"]];
ConstructAliases[tfacts];
};
painters algorithm order
DrawXTicks[sliceQueue, efacts, 1];
DrawThreads[sliceQueue, tfacts, efacts];
DrawEvents[sliceQueue, tfacts, efacts, $S];
DrawEvents[sliceQueue, tfacts, efacts, $W];
DrawEvents[sliceQueue, tfacts, efacts, $R];
DrawEvents[sliceQueue, tfacts, efacts, $X];
DrawEvents[sliceQueue, tfacts, efacts, $Y];
DrawEvents[sliceQueue, tfacts, efacts, $Z];
DrawEvents[sliceQueue, tfacts, efacts, $Other];
DrawXTicks[sliceQueue, efacts, 1];
commentText ← Rope.Cat[fileName, "\n",
IO.PutFR["min time %g ticks, max time %g ticks\n", IO.card[efacts.min/xTransform], IO.card[efacts.max/xTransform]],
commentText];
AddText[sliceQueue, efacts.min + (efacts.max-efacts.min)/2, -10-2*commentTextSize, commentText];
DrawLegend[sliceQueue, efacts.min, -10];
LabelThreads[sliceQueue, tfacts, efacts];
scene ← GGScene.CreateScene[];
FOR l: ThreadsVisPrivate.PicLevel IN [background..foreground] DO
GGScene.AddSlices[scene, sliceQueue[l]];
ENDLOOP;
buildEnd ← BasicTime.Now[];
s ← PFS.StreamOpen[PFS.PathFromRope[outputFileName], create];
KillTicker[tickProc];
IO.PutF[outStream, "%g secs. Writing file '%g'.", IO.card[BasicTime.Period[from: readEnd, to: buildEnd]], IO.rope[outputFileName]];
tickProc ← FORK StartTicker[outStream];
GGFileOut.FileoutSceneOnly[s, scene, outputFileName];
IO.Close[s];
KillTicker[tickProc];
IO.PutF1[outStream, " %g secs.\n", IO.card[BasicTime.Period[from: buildEnd, to: BasicTime.Now[]]]];
globalThreadFacts ← NIL;
};
};
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: BOOLFALSE;
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];
};
$UnixIgnore => {
};
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: ROPENIL;
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: BOOLFALSE;
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;
};
};
$X, $Y, $Z => {
};
$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: BOOLFALSE, 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.STREAMPFS.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;
EXITS
Done => RETURN;
};
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];
};
Debugging routines for printing things out (unused in normal operation)
PrintThreadsLists: PROC [s: IO.STREAM, tlist: LIST OF ThreadsVisPrivate.Thread] ~ {
DoEvent: PROC [e: ThreadsVisPrivate.Event, r: LIST OF ThreadsVisPrivate.Event] ~ {
IO.PutF1[s, "%g\n", IO.rope[GetEventRope[e]]];
};
DoThread: PROC [t: ThreadsVisPrivate.Thread, r: LIST OF ThreadsVisPrivate.Thread] ~ {
IO.PutF1[s, "------------ %g\n", IO.rope[t.name]];
EMap[t.elist, DoEvent];
};
TMap[tlist, DoThread];
};
WalkSlices: PROC [q: ThreadsVisPrivate.Queue] ~ {
list: LIST OF GGSlice.Slice ← q.head;
UNTIL list = NIL DO
IF list.first = NIL THEN CommanderOps.Failed["NIL value in slice list."];
[] ← GGSliceOps.GetType[list.first];
list ← list.rest;
ENDLOOP;
};
PrintThreadTree: PROC [stream: IO.STREAM, t: LIST OF REF ANY] ~ {
DoStack: PROC [s: REF ANY, r: LIST OF REF ANY] ~ {
stack: LIST OF REF ANYNARROW[s];
IO.PutF1[stream, "%g\n", IO.rope["-------------------"]];
IO.Put1[stream, IO.rope[GetStackRope[stack]]];
};
List.Map[t, DoStack];
};
GetStackRope: PROC [t: LIST OF REF ANY] RETURNS [rope: ROPE] ~ {
PrintIt: PROC [s: REF ANY, r: LIST OF REF ANY] ~ {
stack: ThreadsVisPrivate.StackEntry ← NARROW[s];
rope ← Rope.Concat[rope, IO.PutFR1["***%g\n", IO.rope[stack.name]]];
};
List.Map[t, PrintIt];
};
PrintThreadEvents: PROC [tlist: LIST OF ThreadsVisPrivate.Thread] = {
printProc: CardTab.EachPairAction = {
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Feedback, IO.PutFR1["event %g in thread.", IO.card[key]]];
};
t: ThreadsVisPrivate.Thread;
t ← tlist.first;
WHILE tlist.rest # NIL DO
t ← tlist.first;
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Feedback, IO.PutFR1["size %g.", IO.card[CardTab.GetSize[NARROW[t.idList]]]]];
[] ← CardTab.Pairs[NARROW[t.idList], printProc];
tlist ← tlist.rest;
ENDLOOP;
};
DumpSymTab: PROC [tab: REF] ~ {
action: SymTab.EachPairAction ~ {
PROC [key: Key, val: Val] RETURNS [quit: BOOL ¬ FALSE]
IO.PutF1[globalOutStream, "'%g' ", IO.rope[key]];
};
IO.PutF1[globalOutStream, "%g", IO.rope["-------symtab--------: "]];
[] ← SymTab.Pairs[NARROW[tab], action];
IO.PutF1[globalOutStream, "%g", IO.rope["\n"]];
};
Utilities for various kinds of lists
ExpandTabs: PROC [in: ROPE, tabStop: CARD ← 8] RETURNS [out: ROPENIL] ~ {
lastPos: INT ← 0;
skipPos: INT;
DO
skipPos ← Rope.SkipTo[in, lastPos, "\t"];
IF skipPos = Rope.Length[in] THEN RETURN [Rope.Concat[out,Rope.Substr[in, lastPos]]];
out ← Rope.Concat[out, Rope.Substr[in, lastPos, skipPos-lastPos]];
out ← Rope.Concat[out, Rope.Substr[" ", Rope.Length[out] MOD tabStop]];
lastPos ← skipPos + 1;
ENDLOOP;
};
ScaleAllEvents: PROC [efacts: ThreadsVisPrivate.EventFacts] ~ {
ForEachEvent: PROC [e: ThreadsVisPrivate.Event, r: LIST OF ThreadsVisPrivate.Event] ~ {
IF e.time # 0 THEN e.time ← e.time - efacts.min + 1 ELSE e.time ¬ efacts.min + 1;
IF e.wakeTime # 0 THEN e.wakeTime ← e.wakeTime - efacts.min + 1;
};
EMap[efacts.elist, ForEachEvent];
efacts.max ← efacts.max - efacts.min + 1;
efacts.min ← 1;
};
ReverseSliceList: PROC [in: LIST OF GGSlice.Slice] RETURNS [out: LIST OF GGSlice.Slice] ~ {
out ¬ NIL;
UNTIL in = NIL DO
out ¬ CONS[in.first, out];
in ¬ in.rest;
ENDLOOP;
RETURN[out];
};
MapSlices: PROC [in: LIST OF GGSlice.Slice, p: PROC [GGSlice.Slice]] ~ {
s: GGSlice.Slice;
UNTIL in = NIL DO
s ← in.first;
p[s];
in ← in.rest;
ENDLOOP;
};
TMap: PROC [list: LIST OF ThreadsVisPrivate.Thread, proc: PROC[ThreadsVisPrivate.Thread, LIST OF ThreadsVisPrivate.Thread]] ~ {
WHILE list # NIL DO
proc[list.first, list];
list ← list.rest;
ENDLOOP;
};
EMap: PROC [list: LIST OF ThreadsVisPrivate.Event, proc: PROC[ThreadsVisPrivate.Event, LIST OF ThreadsVisPrivate.Event]] ~ {
WHILE list # NIL DO
proc[list.first, list];
list ← list.rest;
ENDLOOP;
};
NewQueue: PROC [] RETURNS [newQueue: ThreadsVisPrivate.Queue] ~ {
newQueue ← NEW[ThreadsVisPrivate.QueueRep];
};
AddToFront: PROC [q: ThreadsVisPrivate.Queue, r: GGSlice.Slice] ~ {
q[foreground] ¬ CONS[r, q[foreground]];
};
AddToLevel: PROC [q: ThreadsVisPrivate.Queue, l: ThreadsVisPrivate.PicLevel, r: GGSlice.Slice] ~ {
q[l] ¬ CONS[r, q[l]];
};
AddToBack: PROC [q: ThreadsVisPrivate.Queue, r: GGSlice.Slice] ~ {
q[background] ¬ CONS[r, q[background]];
};
SelectThreads: PROC [threadListRope: ROPE, tfacts: ThreadsVisPrivate.ThreadFacts] RETURNS [selected: ROPENIL] ~ {
newTlist: LIST OF ThreadsVisPrivate.Thread ← NIL;
newTail: LIST OF ThreadsVisPrivate.Thread ← NIL;
currentName: ROPE;
DoPerThread: PROC [t: ThreadsVisPrivate.Thread, r: LIST OF ThreadsVisPrivate.Thread] ~ {
IF Rope.Match[currentName, ThreadsVisPrivate.STName[NARROW[t.tree]]] THEN {
IF newTail = NIL THEN newTlist ¬ newTail ¬ CONS[t, NIL]
ELSE {
newTail ¬ newTail.rest ¬ CONS[t, NIL];
};
selected ← Rope.Cat[selected, " ", ThreadsVisPrivate.STName[NARROW[t.tree]]];
};
};
inTok: IO.STREAMIO.RIS[threadListRope];
nameList: LIST OF ROPENIL;
DO
currentName ← IO.GetTokenRope[inTok, IO.IDProc ! IO.EndOfStream => EXIT].token;
TMap[tfacts.tlist, DoPerThread];
ENDLOOP;
IF newTlist # NIL THEN tfacts.tlist ← newTlist;
};
I/O Tricks
KillTicker: PROC [tickProc: PROCESS] ~ {
ENABLE Process.InvalidProcess => CONTINUE;
Process.Abort[tickProc]; Process.Detach[tickProc];
};
StartTicker: PROC [s: IO.STREAM] ~ {
maxTicks: CARD = 50;
ticks: CARD ← 0;
collecting ← FALSE;
DO
Process.CheckForAbort[];
IF collecting THEN { IO.Put1[s, IO.char['g]]; collecting ← FALSE; };
IO.Put1[s, IO.char['.]];
Process.PauseMsec[1000];
ticks ← ticks + 1;
IF ticks > maxTicks THEN Process.Abort[Process.GetCurrent[]];
ENDLOOP;
};
NoticeCollection: PROC [r: REF] ~ {
collecting ← TRUE;
};
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 ANYNIL];
outRope: ROPENIL;
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 ANYNIL];
RETURN[TVGetStackRopeHelper[arguments, buttonInfo, clientData, context]];
};
TVPopupXStack: EBTypes.EBLanguageProc = {
rope: ROPENARROW[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]];
};
description: ROPE = "Threads Visualizer\nTV filename\n\t[threadList: <threadnames>]\n\t[doCedar:]\n\t[timeRange: starttime endtime]\n\t[backgroundThread: <???>]\n\t[xTransform: realnumber]\n\t[moreInput: filename]";
Commander.Register["ThreadsVis", ThreadsVisCommand, description];
Commander.Register["TV", ThreadsVisCommand, description];
Commander.Register["TVStackPrint", StackPrintCommand, "Threads Visualizer Stack Print Command"];
EmbeddedButtons.RegisterPoppyProc[$TVGetStack, TVGetStack];
EmbeddedButtons.RegisterPoppyProc[$TVPopupXStack, TVPopupXStack];
END.