ThreadsVisReadImpl.mesa
Copyright Ó1992 by Xerox Corporation. All rights reserved.
Weiser, March 13, 1993 9:36 am PST
DIRECTORY List, CardTab, Convert, Rope, RefText, Atom, IO, PFS, CommanderOps, ThreadsVisPrivate, SimpleFeedback, SymTab;
ThreadsVisReadImpl: CEDAR PROGRAM
IMPORTS List, CardTab, Rope, CommanderOps, Convert, RefText, Atom, IO, PFS, SimpleFeedback, ThreadsVisPrivate, SymTab
EXPORTS ThreadsVisPrivate
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
StripQuotes: PUBLIC PROC [text: REF TEXT] ~ {
Does not handle multi-character escaped things.
i, j: NAT ¬ 0;
UNTIL i = text.length DO
SELECT text[i] FROM
'" => { i ¬ i + 1 };
'\\ => { i ¬ i + 1; IF i = text.length THEN ERROR; text[j] ¬ text[i]; i ¬ i+1; j ¬ j+1 };
ENDCASE => { text[j] ¬ text[i]; i ¬ i + 1; j ¬ j + 1 };
ENDLOOP;
text.length ¬ j;
};
Canon: PUBLIC PROC [rope: ROPE] RETURNS [ROPE] ~ {
RETURN [Atom.GetPName[Atom.MakeAtom[rope]]];
};
ReadAny: PUBLIC PROC [stream: IO.STREAM] RETURNS [threadFacts: ThreadsVisPrivate.ThreadFacts, commentText: ROPENIL] ~ {
buffer: REF TEXT ¬ NEW[TEXT[80]];
firstRecord: BOOL; -- the first decimal after a paren is the uniqueID
tokenKind: IO.TokenKind;
charsSkipped: INT;
error: IO.TokenError;
stack: LIST OF LIST OF REF ¬ NIL;
head: LIST OF REF ¬ LIST[NIL];
last: LIST OF REF ¬ head;
threadlist: LIST OF ThreadsVisPrivate.Thread ← NIL;
thread: ThreadsVisPrivate.Thread ← NEW[ThreadsVisPrivate.ThreadRep];
thread.idList ← CardTab.Create[];
thread.nameTable ← SymTab.Create[];
threadFacts ← NEW[ThreadsVisPrivate.ThreadFactsRep];
DO
v: REF ¬ NIL;
[tokenKind, buffer, charsSkipped, error] ¬ IO.GetCedarToken[stream: stream, buffer: buffer, flushComments: FALSE];
SELECT tokenKind FROM
tokenSINGLE => {
SELECT buffer[0] FROM
'; => {
IF commentText = NIL THEN commentText ← IO.GetLineRope[stream]
ELSE commentText ← Rope.Cat[commentText, "\n", IO.GetLineRope[stream]];
LOOP
};
'( => {
head.first ¬ last;
stack ¬ CONS[head, stack];
last ¬ head ¬ LIST[NIL];
firstRecord ← TRUE;
LOOP;
};
') => {
v ¬ head.rest;
head ¬ stack.first;
stack ¬ stack.rest;
last ¬ NARROW[head.first];
head.first ¬ NIL;
IF stack # NIL AND stack.rest = NIL THEN {
leaving a new thread, record the info
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Feedback, "new stack"];
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Complaint, IO.PutFR1["list1 size %g.", IO.card[CardTab.GetSize[NARROW[thread.idList]]]]];
thread.tree ← v;
threadlist ← CONS[thread, threadlist];
thread ← NEW[ThreadsVisPrivate.ThreadRep];
thread.name ← NIL;
thread.idList ← CardTab.Create[];
thread.nameTable ← SymTab.Create[];
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Complaint, IO.PutFR1["list2 size %g.", IO.card[CardTab.GetSize[NARROW[thread.idList]]]]];
PrintThreadEvents[threadlist];
PrintThreadInfo[threadlist];
};
};
ENDCASE => GOTO ParseFailed;
};
tokenDECIMAL => {
c: CARD ¬ Convert.CardFromRope[RefText.TrustTextAsRope[buffer]];
v ¬ NEW[CARD ¬ c];
IF firstRecord THEN {
firstRecord ← FALSE;
IF CardTab.Store[NARROW[thread.idList], c, NIL] THEN {
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Complaint, IO.PutFR1["Storing %g in cardtab", IO.card[c]]];
} ELSE {
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Complaint, "duplicate thread ID"];
};
};
};
tokenID => {
v ¬ Atom.MakeAtomFromRefText[buffer];
};
tokenROPE => {
a: ATOM;
r: ROPE;
StripQuotes[buffer];
a ← Atom.MakeAtomFromRefText[buffer];
r ← Rope.FromRefText[buffer];
We canonicalize these, because we expect lots of repeats.
v ¬ Atom.GetPName[a];
IF thread.name = NIL THEN thread.name ← r;
[] ← SymTab.Insert[NARROW[thread.nameTable], r, NIL];
};
ENDCASE => GOTO ParseFailed;
IF stack = NIL THEN {
threadFacts.tlist ← threadlist;
threadFacts.tree ← v;
RETURN [threadFacts, commentText];
};
last ¬ last.rest ¬ LIST[v];
ENDLOOP;
EXITS ParseFailed => CommanderOps.Failed[IO.PutFR1["Parse error near %g", [integer[IO.GetIndex[stream]]]]];
};
ST: TYPE ~ LIST OF REF;
STName: PUBLIC PROC [st: ST] RETURNS [ROPE] ~ {
RETURN [IF st.first = $Merged THEN "*" ELSE NARROW[st.rest.first]];
};
STChildren: PUBLIC PROC [st: ST] RETURNS [LIST OF REF] ~ {
RETURN [IF st.first = $Merged THEN st.rest ELSE st.rest.rest.rest.rest];
};
ReadTreeFromFile: PUBLIC PROC [fileName: ROPE, throwAwayUnix: BOOL] RETURNS [threadFacts: ThreadsVisPrivate.ThreadFacts, eventFacts: ThreadsVisPrivate.EventFacts, commentText: ROPE ] ~ {
ENABLE PFS.Error => { CommanderOps.Failed[error.explanation] };
stream: IO.STREAM ~ PFS.StreamOpen[PFS.PathFromRope[fileName]];
spyTree: ST;
thread: ThreadsVisPrivate.Thread;
threadlist: LIST OF ThreadsVisPrivate.Thread;
line: ROPE;
[threadFacts: threadFacts, commentText: commentText] ¬ ReadAny[stream];
threadFacts.stackList ← InvertThreadTree[threadFacts.tree];
threadFacts.stackTable ← BuildStackTable[threadFacts.stackList];
DO
line ← IO.GetLineRope[stream ! IO.EndOfStream => EXIT];
IF Rope.Equal[Rope.Substr[line, 0, 1], ";"] THEN {
IF commentText = NIL THEN commentText ← line
ELSE commentText ← Rope.Cat[commentText, "\n", line];
};
IF Rope.Equal[Rope.Substr[line, 0, 9], ";;; Event"] THEN EXIT;
ENDLOOP;
Read events now
[eventFacts, commentText] ← ReadEvents[stream, commentText, throwAwayUnix];
IO.Close[stream];
Link events with threads
eventFacts.nameTable ← BuildThreadsLists[threadFacts, eventFacts];
RETURN [threadFacts, eventFacts, commentText]
};
ReadEvents: PROC [stream: IO.STREAM, inCommentText: ROPE, throwAwayUnix: BOOL] RETURNS [efacts: ThreadsVisPrivate.EventFacts, commentText: ROPE] ~ {
event: ThreadsVisPrivate.Event;
line: ROPE;
streamRope: IO.STREAM;
buffer: REF TEXT ¬ NEW[TEXT[80]];
commentText ← inCommentText;
efacts ← NEW[ThreadsVisPrivate.EventFactsRep];
efacts.elist ← NIL;
efacts.min ← LAST[CARD];
efacts.eventTable ← CardTab.Create[];
efacts.max ← 0;
DO
line ← IO.GetLineRope[stream ! IO.EndOfStream => EXIT];
IF Rope.Equal[Rope.Substr[line, 0, 1], ";"] THEN {
IF commentText = NIL THEN commentText ← line
ELSE commentText ← Rope.Cat[commentText, "\n", line];
LOOP;
};
streamRope ← IO.RIS[line];
event ← NEW[ThreadsVisPrivate.EventRep];
event.id ← IO.GetCard[streamRope ! IO.EndOfStream => GOTO Done];
event.type ← MyGetAtom[streamRope, buffer];
event.time ← IO.GetCard[streamRope];
event.node ← IO.GetCard[streamRope];
event.wakeTime ← IO.GetCard[streamRope];
event.wakeEvent ← IO.GetCard[streamRope];
event.drawn ← FALSE;
event.xstartpos ← event.xendpos ← event.ypos ← 0;
IF throwAwayUnix AND Atom.GetPName[event.type].Length[] > 1 THEN LOOP; -- hack to throw away unix kernel events that aren't understood
efacts.elist ← CONS[event, efacts.elist];
IF efacts.min > event.time AND event.time # 0 THEN efacts.min ← event.time;
IF efacts.max < event.time THEN efacts.max ← event.time;
IF efacts.min > event.wakeTime AND event.wakeTime # 0 THEN efacts.min ← event.wakeTime;
IF efacts.max < event.wakeTime AND event.wakeTime # 0 THEN efacts.max ← event.wakeTime;
[] ← CardTab.Store[NARROW[efacts.eventTable], event.id, event];
ENDLOOP;
EXITS Done => RETURN;
};
buffer: REF TEXT ¬ NEW[TEXT[80]];
MyGetAtom: PROC [s: IO.STREAM, buffer: REF TEXT] RETURNS [a: ATOM] ~ {
[] ← IO.GetToken[s, IO.IDProc, buffer];
a ← Atom.MakeAtomFromRefText[buffer];
};
BuildStackTable: PROC [t: LIST OF REF ANY] RETURNS [idTable: CardTab.Ref] ~ {
DoItem: PROC [s: REF ANY, r: LIST OF REF ANY] ~ {
stackItem: ThreadsVisPrivate.StackEntry ← NARROW[s];
[] ← CardTab.Store[idTable, stackItem.id, r];
};
DoStack: PROC [s: REF ANY, r: LIST OF REF ANY] ~ {
myStack: LIST OF REF ANYNARROW[s];
List.Map[myStack, DoItem];
};
idTable ← CardTab.Create[];
List.Map[t, DoStack];
RETURN [idTable];
};
InvertThreadTree: PROC [t: REF] RETURNS [result: LIST OF REF ANY] ~ {
workHead, workTail, tmpResult, currentWork, extraWork: LIST OF REF ANY;
stack: ThreadsVisPrivate.StackEntry;
workHead ← LIST[NEW[ThreadsVisPrivate.WorkItemRep ← [NARROW[t, LIST OF REF ANY], NIL]]];
workTail ← workHead;
result ← NIL;
step through to the bottom of each stack
UNTIL workHead = NIL DO
workItem: ThreadsVisPrivate.WorkItem ← NARROW[workHead.first];
tmpResult ← workItem.doneAlready;
currentWork ← workItem.toDo;
follow one stack all the way
UNTIL currentWork = NIL DO
stack ← NEW[ThreadsVisPrivate.StackEntryRep];
stack.name ← ThreadsVisPrivate.STName[currentWork];
stack.id ← NARROW[currentWork.first, REF CARD]^;
tmpResult ← CONS[stack, tmpResult];
IF ThreadsVisPrivate.STChildren[currentWork] # NIL THEN extraWork ← currentWork.rest.rest.rest.rest.rest ELSE extraWork ← NIL;
if we see extra children, add them to the list of further stacks to follow
UNTIL extraWork = NIL DO
workTail ← workTail.rest ← LIST[NEW[ThreadsVisPrivate.WorkItemRep ← [NARROW[extraWork.first, LIST OF REF ANY], tmpResult]]];
extraWork ← extraWork.rest;
ENDLOOP;
IF ThreadsVisPrivate.STChildren[currentWork] # NIL THEN currentWork ← NARROW[ThreadsVisPrivate.STChildren[currentWork].first] ELSE currentWork ← NIL;
ENDLOOP;
result ← CONS[tmpResult, result];
workHead ← workHead.rest;
ENDLOOP;
};
BuildThreadsLists: PROC [tfacts: ThreadsVisPrivate.ThreadFacts, efacts: ThreadsVisPrivate.EventFacts] RETURNS [nameTable: REF] ~ {
tlist: LIST OF ThreadsVisPrivate.Thread ← tfacts.tlist;
elist: LIST OF ThreadsVisPrivate.Event ← efacts.elist;
done: BOOL;
t: ThreadsVisPrivate.Thread;
e: ThreadsVisPrivate.Event;
origTlist: LIST OF ThreadsVisPrivate.Thread = tlist;
nameTable ← SymTab.Create[];
WHILE tlist # NIL DO
t ← tlist.first;
t.elist ← NIL;
tlist ← tlist.rest;
ENDLOOP;
WHILE elist # NIL DO
e ← elist.first;
done ← FALSE;
tlist ← origTlist;
WHILE tlist # NIL DO
t ← tlist.first;
IF CardTab.Fetch[NARROW[t.idList], e.node].found THEN {
myval: CardTab.Val;
myfound: BOOL;
[val: myval, found: myfound] ← CardTab.Fetch[NARROW[tfacts.stackTable], e.node];
t.elist ← CONS[e, t.elist];
done ← TRUE;
EXIT;
};
tlist ← tlist.rest;
ENDLOOP;
IF NOT done THEN {
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Feedback, IO.PutFR1["event %g references no thread.", IO.card[e.id]]];
};
elist ← elist.rest;
ENDLOOP;
event order was reversed on reading, then reversed again here. Thus t.elist is in correct order for all t.
};
PrintThreadEvents: PROC [tlist: LIST OF ThreadsVisPrivate.Thread] = {
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]]]]];
tlist ← tlist.rest;
ENDLOOP;
};
PrintThreadInfo: PROC [tlist: LIST OF ThreadsVisPrivate.Thread] = {
t: ThreadsVisPrivate.Thread;
t ← tlist.first;
WHILE tlist.rest # NIL DO
t ← tlist.first;
SimpleFeedback.Append[$ThreadsVis, oneLiner, $Feedback, IO.PutFR1["Tree size %g.", IO.card[List.Length[NARROW[t.tree]]]]];
tlist ← tlist.rest;
ENDLOOP;
};
END.