<<>> <> <> <> <> <<>> 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; <> 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"]; <> <> <> 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]; <> collecting: BOOL _ FALSE; globalOutStream: IO.STREAM _ NIL; 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 ANY _ NIL; yMax: CARD _ 0; backgroundQueue: ThreadsVisPrivate.Queue _ NIL; backgroundName: ROPE _ NIL; <> 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: PROCESS _ NIL; 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; <> 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]; <> 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]; }; <> 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, $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]; <> 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[]]]]; <> }; }; <> 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]; <> }; 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; < 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 => { <> 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 => { <> 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]]; <> }; }; 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] ~ { <> 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_Broadcast"] => color _ broadcastTailColor; CheckName[tfacts, we, "ThreadsQueues._XR_MonitorExitOutOfLine"] => color _ monitorExitTailColor; CheckName[tfacts, we, "Threads1._XR_Fork"] 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 => { <> <> <> <> <> <> <> <> <> <> <> <<};>> <<[fixedxstart, cnt, computedThickness] _ GetFixedX[xPosTable, wakeEvent.xstartpos];>> <> <> <> <> }; $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 { <> 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, cnt, computedThickness] _ GetFixedX[xPosTable, e.xstartpos];>> <> <> <<};>> }; }; }; 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 { <> 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]; <> 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; EXITS Done => RETURN; }; ConstructAliases: PROC [tfacts: ThreadsVisPrivate.ThreadFacts] ~ { myThread: ThreadsVisPrivate.Thread; alias: Alias; ForEachName: SymTab.EachPairAction ~ { <> 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]; }; <> 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]; }; <> <> <> <> <<[] _ GGSliceOps.GetType[list.first];>> <> <> <<};>> <<>> PrintThreadTree: PROC [stream: IO.STREAM, t: LIST OF REF ANY] ~ { DoStack: PROC [s: REF ANY, r: LIST OF REF ANY] ~ { stack: LIST OF REF ANY _ NARROW[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 ~ { <> 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"]]; }; <> ExpandTabs: PROC [in: ROPE, tabStop: CARD _ 8] RETURNS [out: ROPE _ NIL] ~ { 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: ROPE _ NIL] ~ { 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.STREAM _ IO.RIS[threadListRope]; nameList: LIST OF ROPE _ NIL; DO currentName _ IO.GetTokenRope[inTok, IO.IDProc ! IO.EndOfStream => EXIT].token; TMap[tfacts.tlist, DoPerThread]; ENDLOOP; IF newTlist # NIL THEN tfacts.tlist _ newTlist; }; <> 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; }; <