RemoteCirioImpl.mesa
Copyright Ó 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
Sturgis, April 20, 1990 2:29 pm PDT
Last changed by Theimer on November 28, 1989 3:36:23 pm PST
Last tweaked by Mike Spreitzer on July 24, 1992 6:20 pm PDT
Linda Howe January 8, 1990 11:02:07 am PST
Coolidge, July 29, 1990 1:51 pm PDT
Laurie Horton, March 31, 1992 12:48 pm PST
Philip James, February 3, 1992 10:41 am PST
Udagawa, February 12, 1991 8:57 pm PST
DIRECTORY
AmpersandContext USING[CreateAnAmpersandContext, MakeNodeFromNode, StripAMNode],
Atom USING [GetPName],
BasicTime USING[Now],
CCTypes USING[CCError, CCErrorCase, CreateCedarCompilerContext, CreateNodeFromRefAny],
CedarCode USING[GetNodeRepresentation, GetTypeOfNode, LoadThroughIndirectNode, SelectFieldFromNode, StoreThroughIndirectNode],
CirioBackstop USING[Protect],
CirioBreakAccess USING[CirioBreakSet, CreateCirioBreakSet, ClearAllBreaks, ClearBreakAtAbsAddr, ClearBreakAtIndex, ListBreaks, SetBreakAtAbsAddr, BreakSetBroken],
CirioDeltaFace USING[IsDMachine],
CirioNub USING [Error],
CirioNubAccess USING[CreateRemoteNub, DbgMsgNone, DbgMsgNoHandlerRequest, DbgMsgHandlerRequest, DbgMsgAbort, DbgMsgBadProceedRequest, DbgMsgBreakRequest, DbgMsgClientRequest, DbgMsgExit, DbgMsgProceed, DestroyNub, Error, FileEntry, GetFileEntry, GetThreads, Handle, IssueThreadCommand, KillWorld, LookupSymEntryByName, LookupSymEntryByValue, LookupSymEntryByID, Null, PCInfo, PCtoInfo, Read32BitsAsCard, SchedStateFree, SchedStateReady, SchedStateRun, SchedStateWaitML, SchedStateWaitCV, SchedStateHandlee, SetDBStat, SymEntry, ThreadInfo, WaitSig],
CirioTargets USING[Target],
CirioTypes USING[CirioAddress, CirioAddressBody, CompilerContext, Node, Type],
Commander USING[CommandProc, Register],
CommandTool USING[ArgumentVector, Parse],
Convert USING[CardFromRope, RopeFromCard],
FileNames USING[CurrentWorkingDirectory],
IO USING[card, char, Close, Error, PutF, PutFR, PutRope, RopeFromROS, ROS, rope, STREAM],
LoadStateAccess USING[CreateLoadStateHandle, LoadStateHandle],
LocalCirio USING[Connection, ExtractCcFromConnection, GetConnection, nilBreakDest],
NewAmpersandProcs USING[Handle, HandleBody, InstallItems],
NewRMTW USING[CedarModuleSet, CreateCedarModuleSet, CreateRemoteMimosaTargetWorld, FlushUnknownFileCache, FlushUnknownSymbolCache, FlushUnknownTypeCodes, RemoteMimosaTargetWorld, ResetSearchPaths],
ObjectFiles USING[CreateParsed, GetLineNumForPC, GetPCForLineNum, Module, ModuleFromParsedAndPC, Parsed],
OneCasabaParser USING[ParserTable],
PBasics USING[BITLSHIFT, BITOR, BITRSHIFT],
PFS USING [AbsoluteName, RopeFromPath, PathFromRope],
PFSNames USING [PATH, Equal],
Process USING [Detach, PauseMsec, priorityBackground, SetPriority],
RemoteCirio USING[ThreadProperty],
Rope USING[Cat, Concat, Equal, FromChar, Length, Map, Substr, ROPE],
SourceFileOpsExtras USING [FullOpenSource, Position],
StackCirio USING[FrameDiagnosticInfo, InterpretTextLine, NoteFileCacheFlush, OpenDummyStack, OpenStack, QuickGenPossibleModuleNames, ResetStack, ShowCurrentFrame, ShowQuickSummary, Stack, WalkStack, WalkStackToCProcedure],
SystemInterface USING [ShowReport, CloseFileSet, CirioFile, CreateFileSet, FileSet, GetCirioFileFromDebuggee];
RemoteCirioImpl: CEDAR MONITOR
LOCKS connection USING connection: Connection
IMPORTS AmpersandContext, Atom, BasicTime, CCTypes, CedarCode, CirioBackstop, CirioBreakAccess, CirioDeltaFace, CirioNub, CirioNubAccess, Commander, CommandTool, Convert, FileNames, IO, LoadStateAccess, LocalCirio, NewAmpersandProcs, NewRMTW, ObjectFiles, PBasics, PFS, PFSNames, Process, Rope, SourceFileOpsExtras, StackCirio, SystemInterface
EXPORTS RemoteCirio =
BEGIN OPEN ObjF: ObjectFiles, LSA: LoadStateAccess;
Debugging aids
DebugFlag: BOOLEANFALSE;
This variable can be set/reset by the ampersand routines &&DebugOn and &&DebugOff.
Types
CC: TYPE = CirioTypes.CompilerContext;
CCE: ERROR[case: CCTypes.CCErrorCase, msg: Rope.ROPENIL] ← CCTypes.CCError;
Connection: TYPE = REF ConnectionBody;
ConnectionBody: PUBLIC TYPE = MONITORED RECORD[
open: BOOLEAN,
doradoWorkingDirectory: Rope.ROPE,
serverName: Rope.ROPE,
searchDirectories: LIST OF PFSNames.PATH,
tsOut: IO.STREAM, -- WARNING: this field is very transient. It is only valid during an interpretation of an expression, and was placed here for the convenience of ampersand routines and (local target wrld) user routines called through the interpreter.
ampersandContext1: CirioTypes.Node,
ampersandContext2: CirioTypes.Node,
cc, localCc: CC,
localConnection: LocalCirio.Connection,
rmtw: NewRMTW.RemoteMimosaTargetWorld,
cedarModules: NewRMTW.CedarModuleSet,
nub: CirioNubAccess.Handle,
target: CirioTargets.Target,
daemon: PROCESS,
daemonKillFlag: BOOLEAN,
breaks: CirioBreakAccess.CirioBreakSet,
fileSet: SystemInterface.FileSet,
lsh: LoadStateAccess.LoadStateHandle,
aph: NewAmpersandProcs.Handle,
cParserTable: OneCasabaParser.ParserTable,
remoteWorldRunning: BOOLEANTRUE,
dbxActive: BOOLEANFALSE,
currentThread: CARD ← 0,
threads: REF ThreadSet];
ThreadSet: TYPE = RECORD[SEQUENCE nThreads: CARDINAL OF REF ThreadData];
ThreadData: TYPE = RECORD[
thread: REF CirioNubAccess.ThreadInfo,
stack: StackCirio.Stack
];
Connections
The caller is responsible for eventually calling CloseConnection. No subsequent connections can be made to the same <remoteName, portNum> pair until Close has been called. Note: closing the connection invalidates all RemoteAddresses held for connection.nub.
Question: how much of the items constructed below can/should we reused?
OpenConnection: PUBLIC PROC[remoteName: Rope.ROPE, portNum: CARD, workingDirectories: LIST OF Rope.ROPE, reports: IO.STREAM] RETURNS[connection: Connection] = {
outerMsg: Rope.ROPE ← CirioBackstop.Protect[Openit, reports];
Openit: PROC RETURNS[Rope.ROPE] ~ {
connection ← FunctionalOpen[];
RETURN[NIL]};
FunctionalOpen: PROC RETURNS[Connection] ~ {
nub: CirioNubAccess.Handle ← NIL;
target: CirioTargets.Target ← NIL;
fileSet: SystemInterface.FileSet;
breaks: CirioBreakAccess.CirioBreakSet;
searchDirs: LIST OF PFSNames.PATHNIL;
lastSearchDir: LIST OF PFSNames.PATHNIL;
defaultSearchDirs: LIST OF PFSNames.PATHLIST[
PFS.PathFromRope["/PCedar/Cirio/"],
PFS.PathFromRope["/PCedar/CirioThings/"],
PFS.PathFromRope["/PCedar/Atom/"],
PFS.PathFromRope["/PCedar/Rope/"] ];
A remote world needs to have the navel examination in CreateRemoteMimosaTargetWorld succeed; that requires finding CirioThings/CirioRopeHelper.mob, Rope/Rope.mob, and Atom/AtomPrivate.mob (on August 17, 1991).
ClearBreaksForOpen: PROC RETURNS[Rope.ROPE] = {
CirioBreakAccess.ClearAllBreaks[breaks];
RETURN[NIL]};
Build the list of search directories, starting with the user supplied directories.
searchDirs ← LIST [PFS.PathFromRope["-compiled:/CirioCompiledData/"]];
lastSearchDir ← searchDirs;
FOR thisDir: LIST OF Rope.ROPE ← workingDirectories, thisDir.rest WHILE thisDir # NIL DO
lastSearchDir.rest ← LIST [PFS.AbsoluteName[PFS.PathFromRope[thisDir.first]]];
lastSearchDir ← lastSearchDir.rest;
ENDLOOP;
Join the user supplied and default search directories.
lastSearchDir.rest ← defaultSearchDirs;
nub ← CirioNubAccess.CreateRemoteNub[debuggee: remoteName, port: portNum, timeoutMsec: 10000
!CirioNub.Error => {
reports.PutF["Failed to open DebugNub connection to port %g on %g.\n", [cardinal[portNum]], [rope[remoteName]] ];
CONTINUE}];
IF nub=NIL THEN RETURN [NIL];
target ← CirioTargets.CreateTarget[nub];
target ← NARROW[nub.target];
fileSet ← SystemInterface.CreateFileSet[];
breaks ← CirioBreakAccess.CreateCirioBreakSet[nub, "CirioThingsImpl", "CallDebugger"];
IF breaks.BreakSetBroken[] THEN reports.PutRope["You won't be able to set breakpoints in the debuggee, because I was unable to find CirioThingsImpl.CallDebugger in the debuggee. Are you sure you loaded an unoptimized version of CirioThingsImpl into the debuggee somewhere along the line?\n"];
We nest a block so that we can provide an unwind catch phrase to close the nub in the event of trouble before we hand responsibility to our caller. (We also close the file set and clear all breaks.)
{
ENABLE UNWIND => { -- all attempts to give a report will be ignored
finalMsg: Rope.ROPE ← CirioBackstop.Protect[ClearBreaksForOpen, reports];
CirioNubAccess.DestroyNub[nub];
SystemInterface.CloseFileSet[fileSet];
IF Rope.Length[finalMsg] # 0 THEN IO.PutF[reports, "%g\N", IO.rope[finalMsg]];
};
cc, lcc: CC;
cedarModules: NewRMTW.CedarModuleSet;
lsh: LoadStateAccess.LoadStateHandle;
rmtw: NewRMTW.RemoteMimosaTargetWorld;
ampersandContext1, ampersandContext2: CirioTypes.Node;
aph: NewAmpersandProcs.Handle;
connection: Connection;
IF NOT CirioNubAccess.Null[nub, 7] THEN CCE[cirioError, "debuggee doesn't implement debug nub protocol version 7"];
cc ← CCTypes.CreateCedarCompilerContext[];
cedarModules ← NewRMTW.CreateCedarModuleSet[fileSet, remoteName];
NewRMTW.ResetSearchPaths[cedarModules, searchDirs, BasicTime.Now[]];
lsh ← LoadStateAccess.CreateLoadStateHandle[remoteName, nub, fileSet];
rmtw ← NewRMTW.CreateRemoteMimosaTargetWorld[remoteName, nub, cc, cedarModules, lsh, reports, TRUE];
ampersandContext1 ← AmpersandContext.CreateAnAmpersandContext[cc];
ampersandContext2 ← AmpersandContext.CreateAnAmpersandContext[cc];
aph ← NEW[NewAmpersandProcs.HandleBody←[
out: NIL, -- filled in during a line interpretation in StackCirioImpl
rmtw: rmtw,
cedarModules: cedarModules,
nub: nub,
fileSet: fileSet,
lsh: lsh]];
connection← NEW[ConnectionBody←[
open: TRUE,
doradoWorkingDirectory: workingDirectories.first,
serverName: remoteName,
searchDirectories: searchDirs,
ampersandContext1: ampersandContext1,
ampersandContext2: ampersandContext2,
cc: cc,
localCc: lcc,
localConnection: LocalCirio.GetConnection[LocalCirio.nilBreakDest, reports],
rmtw: rmtw,
cedarModules: cedarModules,
nub: nub,
target: target,
daemon: NIL,
daemonKillFlag: FALSE,
breaks: breaks,
fileSet: fileSet,
lsh: lsh,
aph: aph,
cParserTable: NIL]];
connection.localCc ← LocalCirio.ExtractCcFromConnection[connection.localConnection];
connection.ampersandContext1 ← AmpersandContext.CreateAnAmpersandContext[connection.localCc];
connection.ampersandContext2 ← AmpersandContext.CreateAnAmpersandContext[connection.localCc];
ampersandContext1 ← connection.ampersandContext1;
ampersandContext2 ← connection.ampersandContext2;
NewAmpersandProcs.InstallItems[connection.ampersandContext2, aph, connection.localCc];
InstallBreakCheckDaemon[connection, reports];
IF CirioDeltaFace.IsDMachine[] = FALSE THEN RETURN[connection];
IF TRUE THEN RETURN[connection];
disables the following ampersand routines. Should move any useful ones to NewAmpersandProcs.
InstallOneAmpersandItem[ampersandContext2, "&&H", CCTypes.CreateNodeFromRefAny[NEW[Connection ← connection], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&RdCards", CCTypes.CreateNodeFromRefAny[NEW[PROC[a: CirioTypes.CirioAddress, nCards: CARD] ← RdCards], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&RdHexCards", CCTypes.CreateNodeFromRefAny[NEW[PROC[a: CirioTypes.CirioAddress, nCards: CARD] ← RdHexCards], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&RdBytes", CCTypes.CreateNodeFromRefAny[NEW[PROC[a: CirioTypes.CirioAddress, nBytes: CARD] ← RdBytes], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&RdChars", CCTypes.CreateNodeFromRefAny[NEW[PROC[a: CirioTypes.CirioAddress, nChars: CARD] ← RdChars], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&RdRope", CCTypes.CreateNodeFromRefAny[NEW[PROC[a: CirioTypes.CirioAddress, nChars: CARD ← 100] ← RdRope], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&MkAddress", CCTypes.CreateNodeFromRefAny[NEW[PROC[byteAddress: CARD, h: Connection] RETURNS[CirioTypes.CirioAddress] ← MkAddress], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&FrameDiagnosticInfo", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection] ← FrameDiagnosticInfo], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&SetBreakAtAbsAddr", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection, absAddr: CARD32, mesaPos: SourceFileOpsExtras.Position, stopAll: BOOLEAN] ← SetBreakAtAbsAddr], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&ListBreaks", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection] ← ListBreaks], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&ClearBreakAtAbsAddr", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection, absAddr: CARD32] ← ClearBreakAtAbsAddr], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&ClearBreakAtIndex", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection, index: CARD] ← ClearBreakAtIndex], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&ClearAllBreaks", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection] ← ClearAllBreaks], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&AddDir", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection, dirName: Rope.ROPE, reports: IO.STREAM] ← AddDir], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&ListDir", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection] ← ListDir], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&ClearDir", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection] ← ClearDir], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&LookupSym", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection, name: Rope.ROPE, numToSkip: INT ← 0, externOnly: BOOLTRUE] RETURNS[CirioNubAccess.SymEntry] ← LookupSym], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&LookupAddr", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection, val: CARD, numToSkip: INT ← 0] RETURNS[CirioNubAccess.SymEntry] ← LookupAddr], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&LookupID", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection, symID: CARD] RETURNS[CirioNubAccess.SymEntry] ← LookupID], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&LookupFile", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection, seqNum: CARD] RETURNS[entry: CirioNubAccess.FileEntry] ← LookupFile], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&GetPCInfo", CCTypes.CreateNodeFromRefAny[NEW[PROC[h: Connection, absPC: CARD] RETURNS[CirioNubAccess.PCInfo] ← GetPCInfo], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&GetRelPCForCLine", CCTypes.CreateNodeFromRefAny[NEW[PROC[dotOName: Rope.ROPE, cLineNum: CARD, h: Connection] RETURNS[CARD] ← GetRelPCForCLine], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&GetCLineForRelPC", CCTypes.CreateNodeFromRefAny[NEW[PROC[dotOName: Rope.ROPE, relPC: CARD, h: Connection] RETURNS[CARD] ← GetCLineForRelPC], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&DebugOn", CCTypes.CreateNodeFromRefAny[NEW[PROC[] ← DebugOn], cc], cc];
InstallOneAmpersandItem[ampersandContext2, "&&DebugOff", CCTypes.CreateNodeFromRefAny[NEW[PROC[] ← DebugOff], cc], cc];
RETURN[connection]}};
IF outerMsg#NIL THEN reports.PutF["debug connection not opened with %g (port %g) because of uncaught error (%g).\n", [rope[remoteName]], [cardinal[portNum]], [rope[outerMsg]] ];
RETURN};
FlushUnknownFileCache: PUBLIC ENTRY PROC[connection: Connection, reports: IO.STREAM] =
BEGIN
ENABLE UNWIND => NULL;
IF NOT connection.open THEN RETURN;
NewRMTW.FlushUnknownFileCache[connection.cedarModules, BasicTime.Now[]];
NewRMTW.FlushUnknownSymbolCache[connection.rmtw, BasicTime.Now[], reports];
NewRMTW.FlushUnknownTypeCodes[connection.rmtw];
IF connection.threads # NIL THEN
FOR ti: CARDINAL IN [0..connection.threads.nThreads) DO
td: REF ThreadData ← connection.threads[ti];
StackCirio.NoteFileCacheFlush[td.stack];
ENDLOOP;
END;
CloseConnection: PUBLIC ENTRY PROC[connection: Connection, reports: IO.STREAM] = {
ENABLE UNWIND => NULL;
InerCloseConnection[connection, reports]};
InerCloseConnection: INTERNAL PROC[connection: Connection, reports: IO.STREAM] = {
ClearBreaksForClose: PROC RETURNS[Rope.ROPE] =
{CirioBreakAccess.ClearAllBreaks[connection.breaks]; RETURN[NIL]};
finalMsg: Rope.ROPENIL;
IF connection = NIL OR NOT connection.open THEN RETURN;
StopBreakCheckDaemon[connection, reports];
connection.open ← FALSE;
finalMsg ← CirioBackstop.Protect[ClearBreaksForClose, reports];
CirioNubAccess.DestroyNub[connection.nub];
SystemInterface.CloseFileSet[connection.fileSet];
IF Rope.Length[finalMsg] # 0 THEN IO.PutF[reports, "%g\N", IO.rope[finalMsg]];
};
DestroyRemoteWorld: PUBLIC ENTRY PROC[connection: Connection] =
BEGIN
ENABLE UNWIND => NULL;
IF NOT connection.open THEN RETURN;
CirioNubAccess.KillWorld[connection.nub];
connection.open ← FALSE;
CirioNubAccess.DestroyNub[connection.nub];
SystemInterface.CloseFileSet[connection.fileSet];
END;
Threads
StopRemoteWorld: PUBLIC ENTRY PROC[connection: Connection, reports: IO.STREAM] RETURNS[nThreads: CARD] =
BEGIN
ENABLE UNWIND => NULL;
WHILE NOT WaitSig[nub, 1000] DO ENDLOOP;
IF NOT CirioNubAccess.SetDBStat[connection.nub, -2, 5000] THEN CCE[cirioError];
nThreads ← AcquireThreadInfo[connection, reports];
connection.remoteWorldRunning ← FALSE;
RETURN[nThreads];
END;
AcquireThreadInfo: PROC[connection: Connection, reports: IO.STREAM] RETURNS[nThreads: CARD] =
BEGIN
threads: LIST OF REF CirioNubAccess.ThreadInfo;
threadX: CARD ← 0;
threads ← CirioNubAccess.GetThreads[connection.nub, 0, 100];
nThreads ← 0;
FOR ths: LIST OF REF CirioNubAccess.ThreadInfo ← threads, ths.rest WHILE ths # NIL DO nThreads ← nThreads+1 ENDLOOP;
connection.threads ← NEW[ThreadSet[nThreads]];
FOR ths: LIST OF REF CirioNubAccess.ThreadInfo ← threads, ths.rest WHILE ths # NIL DO
td: REF ThreadData ← connection.threads[threadX] ← NEW[ThreadData];
td.thread ← ths.first;
td.stack ← StackCirio.OpenStack[
ampersandContext1: connection.ampersandContext1,
ampersandContext2: connection.ampersandContext2,
cc: connection.cc,
rmtw: connection.rmtw,
cedarModules: connection.cedarModules,
nub: connection.nub,
target: connection.target,
fileSet: connection.fileSet,
breaks: connection.breaks,
lsh: connection.lsh,
aph: connection.aph,
cParserTable: connection.cParserTable,
hotFramePC: ths.first.pc,
hotFrameSP: ths.first.stackPointer,
skipKFrames: 0,
ShowSource: SourceFileOpsExtras.FullOpenSource,
reports: reports];
threadX ← threadX + 1;
ENDLOOP;
RETURN[nThreads];
END;
FindThreadsWithProperty: PUBLIC ENTRY PROC[connection: Connection, property: RemoteCirio.ThreadProperty, reports: IO.STREAM] RETURNS[LIST OF -- threadIndex -- CARD] =
BEGIN
ENABLE UNWIND => NULL;
IF NOT connection.open THEN CCE[cirioError];
IF connection.remoteWorldRunning THEN CCE[cirioError];
BEGIN
newThreads: LIST OF CARDNIL;
lastNewThread: LIST OF CARDNIL;
IO.PutF[reports, "\N\Tchecking thread with index "];
FOR x: CARD IN [0..connection.threads.nThreads) DO
IF ThreadSatisfies[connection.threads[x], property, reports] THEN
BEGIN
cell: LIST OF CARDLIST[x];
IF newThreads = NIL THEN newThreads ← cell ELSE lastNewThread.rest ← cell;
lastNewThread ← cell;
END;
ENDLOOP;
IO.PutF[reports, "\N"];
RETURN[newThreads];
END;
END;
ThreadSatisfies: PROC[thread: REF ThreadData, property: RemoteCirio.ThreadProperty, reports: IO.STREAM] RETURNS[BOOLEAN] =
BEGIN
WITH property SELECT FROM
tp: any RemoteCirio.ThreadProperty => RETURN[TRUE];
tp: callingDebugger RemoteCirio.ThreadProperty => RETURN[thread.thread.dbgMsg # 0];
tp: ready RemoteCirio.ThreadProperty => RETURN[thread.thread.schedState = CirioNubAccess.SchedStateReady OR thread.thread.schedState = CirioNubAccess.SchedStateRun];
tp: context RemoteCirio.ThreadProperty =>
BEGIN
found: BOOLEANFALSE; -- tentative
CheckName: PROC[name: PFSNames.PATH] RETURNS[--stop-- BOOLEAN] =
BEGIN
found ← Rope.Equal[PFS.RopeFromPath[name], tp.name, FALSE];
RETURN[found];
END;
IO.PutF[reports, "%g ", IO.card[thread.thread.index]];
StackCirio.QuickGenPossibleModuleNames[thread.stack, CheckName, reports];
IF found THEN IO.PutF[reports, "(bingo )"];
RETURN[found];
END;
ENDCASE => CCE[cirioError];
END;
GetThreadTitleText: PUBLIC ENTRY PROC[connection: Connection, threadIndex: CARD] RETURNS[Rope.ROPE] =
BEGIN
ENABLE UNWIND => NULL;
RETURN[ConstructThreadTitleText[connection, threadIndex]];
END;
ConstructThreadTitleText: PROC[connection: Connection, threadIndex: CARD] RETURNS[Rope.ROPE] =
BEGIN
threadData: REF ThreadData ← connection.threads[threadIndex];
remoteIndex: CARD ← threadData.thread.index;
schedStateText: Rope.ROPE ← RopeForSchedState[threadData.thread.schedState];
dbgMsgText: Rope.ROPE ← RopeForDbgMsg[threadData.thread.dbgMsg];
freezeText: Rope.ROPEIF threadData.thread.frozen THEN " (frozen)" ELSE NIL;
RETURN[IO.PutFR["%g: (%g) (%g)%g (%g)", IO.card[remoteIndex], IO.card[threadData.thread.priority], IO.rope[schedStateText], IO.rope[freezeText], IO.rope[dbgMsgText]]];
END;
ThreadIndexFromID: PUBLIC PROC[ID: CARD, connection: Connection] RETURNS [index: CARDLAST[CARD]] ~ {
FOR i: CARDINAL IN [0..connection.threads.nThreads) DO
IF connection.threads[i].thread.index = ID THEN
RETURN[i];
ENDLOOP;
};
ThreadIDFromIndex: PUBLIC PROC[index: CARD, connection: Connection] RETURNS [ID: CARDLAST[CARD]] ~ {
IF index < connection.threads.nThreads THEN
RETURN[connection.threads[index].thread.index];
};
ShowQuickSummary: PUBLIC ENTRY PROC[connection: Connection, threadIndex: CARD, stopFlag: REF BOOLEAN, on: IO.STREAM, long: BOOL] =
BEGIN
ENABLE UNWIND => NULL;
threadData: REF ThreadData ← connection.threads[threadIndex];
IO.PutF[on, "quick summary for thread with index %g\N", IO.card[connection.threads[threadIndex].thread.index]];
IO.PutF[on, "\Tdebug message = %g\N", IO.rope[RopeForDbgMsg[threadData.thread.dbgMsg]]];
StackCirio.ShowQuickSummary[threadData.stack, stopFlag, on, long];
END;
GetThreadDebuggingBanner: PUBLIC ENTRY PROC[connection: Connection, threadIndex: CARD, reports: IO.STREAM] RETURNS[Rope.ROPE] =
BEGIN
ENABLE UNWIND => NULL;
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
RETURN[IO.PutFR["debugging thread with index %g", IO.card[connection.threads[threadIndex].thread.index]]];
END;
RETURN[CirioBackstop.Protect[inner, reports]];
END;
AbortThread: PUBLIC ENTRY PROC[connection: Connection, threadIndex: CARD, reports: IO.STREAM] RETURNS[newThreadTitleText: Rope.ROPE] =
BEGIN
ENABLE UNWIND => NULL;
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
RETURN[IssueFullThreadCommand[connection, threadIndex, TRUE, FALSE, TRUE, -3]];
END;
RETURN[CirioBackstop.Protect[inner, reports]];
END;
KillThread: PUBLIC ENTRY PROC[connection: Connection, threadIndex: CARD, reports: IO.STREAM] RETURNS[newThreadTitleText: Rope.ROPE] =
BEGIN
ENABLE UNWIND => NULL;
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
RETURN[IssueFullThreadCommand[connection, threadIndex, TRUE, FALSE, TRUE, -2]];
END;
RETURN[CirioBackstop.Protect[inner, reports]];
END;
FreezeThread: PUBLIC ENTRY PROC[connection: Connection, threadIndex: CARD, reports: IO.STREAM] RETURNS[newThreadTitleText: Rope.ROPE] =
BEGIN
ENABLE UNWIND => NULL;
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
RETURN[IssueFullThreadCommand[connection, threadIndex, TRUE, TRUE, FALSE, connection.threads[threadIndex].thread.dbgMsg]];
END;
RETURN[CirioBackstop.Protect[inner, reports]];
END;
ProceedThread: PUBLIC ENTRY PROC[connection: Connection, threadIndex: CARD, reports: IO.STREAM] RETURNS[newThreadTitleText: Rope.ROPE] =
BEGIN
ENABLE UNWIND => NULL;
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
RETURN[IssueFullThreadCommand[connection, threadIndex, TRUE, FALSE, TRUE, -1]];
END;
RETURN[CirioBackstop.Protect[inner, reports]];
END;
also resets the thread data as needed.
from [palain-uX]<>jaune>xrhome>DEVELOPMENT>INCLUDE>xr>Threads.h we have:
XRDBMSGPROCEEDREPLY (-1)
XRDBMSGEXITREPLY  (-2)
XRDBMSGABORTREPLY (-3)
IssueFullThreadCommand: PROC[connection: Connection, threadIndex: CARD, setFreeze: BOOL, freeze: BOOLEAN, setMsg: BOOLEAN, msg: INT] RETURNS[--new title text-- Rope.ROPE] =
BEGIN
remoteIndex: CARD ← connection.threads[threadIndex].thread.index;
IF NOT CirioNubAccess.IssueThreadCommand[connection.nub, remoteIndex, setFreeze, freeze, setMsg, msg]
THEN CCE[cirioError];
now we have to reconstruct our thread info for this thread
BEGIN
threads: LIST OF REF CirioNubAccess.ThreadInfo ← CirioNubAccess.GetThreads[connection.nub, 0, 100];
FOR ths: LIST OF REF CirioNubAccess.ThreadInfo ← threads, ths.rest WHILE ths # NIL DO
IF ths.first.index = remoteIndex THEN -- here we are
BEGIN
connection.threads[threadIndex].thread ← ths.first;
EXIT;
END;
ENDLOOP;
RETURN[ConstructThreadTitleText[connection, threadIndex]];
END;
END;
DbxExamineThread: PUBLIC ENTRY PROC[connection: Connection, threadIndex: CARD, reports: IO.STREAM] RETURNS[nThreads: CARD] =
BEGIN
ENABLE UNWIND => NULL;
IF NOT connection.remoteWorldRunning THEN
BEGIN
remoteIndex: CARD ← connection.threads[threadIndex].thread.index;
connection.remoteWorldRunning ← TRUE;
connection.dbxActive ← TRUE;
connection.threads ← NIL;
IF NOT CirioNubAccess.SetDBStat[connection.nub, remoteIndex, 5000] THEN CCE[cirioError];
WHILE NOT CirioNubAccess.SetDBStat[connection.nub, -2, 5000] DO ENDLOOP;
nThreads ← AcquireThreadInfo[connection, reports];
connection.dbxActive ← FALSE;
connection.remoteWorldRunning ← FALSE;
RETURN[nThreads];
END
ELSE CCE[cirioError];
END;
ResumeRemoteWorldWithOnlyVP0: PUBLIC ENTRY PROC[connection: Connection] =
BEGIN
ENABLE UNWIND => NULL;
IF NOT connection.remoteWorldRunning THEN
BEGIN
IF NOT CirioNubAccess.SetDBStat[connection.nub, -1, 5000] THEN CCE[cirioError];
connection.threads ← NIL;
connection.remoteWorldRunning ← TRUE;
END;
END;
ResumeRemoteWorld: PUBLIC ENTRY PROC[connection: Connection] =
BEGIN
ENABLE UNWIND => NULL;
IF NOT connection.remoteWorldRunning THEN
BEGIN
IF NOT CirioNubAccess.SetDBStat[connection.nub, 0, 5000] THEN CCE[cirioError];
connection.threads ← NIL;
connection.remoteWorldRunning ← TRUE;
END;
END;
Stack Frames
GetDummyStack: PUBLIC ENTRY PROC[connection: Connection, reports: IO.STREAM]RETURNS[StackCirio.Stack] = {
ENABLE UNWIND => NULL;
RETURN StackCirio.OpenDummyStack[
ampersandContext1: connection.ampersandContext1,
ampersandContext2: connection.ampersandContext2,
cc: connection.cc,
rmtw: connection.rmtw,
cedarModules: connection.cedarModules,
nub: connection.nub,
target: connection.target,
fileSet: connection.fileSet,
breaks: connection.breaks,
lsh: connection.lsh,
aph: connection.aph,
cParserTable: connection.cParserTable,
ShowSource: SourceFileOpsExtras.FullOpenSource,
reports: reports]};
FocusOnThread: PUBLIC ENTRY PROC[connection: Connection, threadIndex: CARD, reports: IO.STREAM] RETURNS[newRemoteThreadIndex: CARD] = {
ENABLE UNWIND => NULL;
connection.currentThread ← IF threadIndex < connection.threads.nThreads THEN threadIndex ELSE 0;
RETURN[connection.threads[connection.currentThread].thread.index];
};
GetStackForCurrentThread: PUBLIC ENTRY PROC[connection: Connection, reports: IO.STREAM] RETURNS[StackCirio.Stack] =
BEGIN
ENABLE UNWIND => NULL;
threadData: REF ThreadData ← connection.threads[connection.currentThread];
RETURN[threadData.stack];
END;
support
RopeForDbgMsg: PROC[msg: INT] RETURNS[Rope.ROPE] =
BEGIN
RETURN[SELECT msg FROM
CirioNubAccess.DbgMsgNone => "None",
CirioNubAccess.DbgMsgNoHandlerRequest => "NoHandlerRequest",
CirioNubAccess.DbgMsgHandlerRequest => "HandlerRequest",
CirioNubAccess.DbgMsgBadProceedRequest => "BadProceedRequest",
CirioNubAccess.DbgMsgBreakRequest => "BreakRequest",
CirioNubAccess.DbgMsgClientRequest => "ClientRequest",
CirioNubAccess.DbgMsgProceed => "Proceed",
CirioNubAccess.DbgMsgExit => "Kill",
CirioNubAccess.DbgMsgAbort => "Abort",
ENDCASE => "??"];
END;
RopeForSchedState: PROC[state: CARD] RETURNS[Rope.ROPE] =
BEGIN
RETURN[SELECT state FROM
CirioNubAccess.SchedStateFree => "Free",
CirioNubAccess.SchedStateReady => "Ready",
CirioNubAccess.SchedStateRun => "Run",
CirioNubAccess.SchedStateWaitML => "MLWait",
CirioNubAccess.SchedStateWaitCV => "CVWait",
CirioNubAccess.SchedStateHandlee => "Handlee",
ENDCASE => IO.PutFR["??: %g", IO.card[state]]];
END;
Temporary access to C frame nodes etc.

This procedure interfaces to RCTWImpl.GetCCAndFrameFromDotO.
GetCFrameNode: PROC[serverName: Rope.ROPE, frameInfo: May4Exp.ProcedureFrameInfo, nub: CirioNubAccess.Handle, symbols: REF SymbolFinding.FoundSymbols] RETURNS[CC, CirioTypes.Node] =
BEGIN
cFrameInfo: RCTW.ProcedureFrameInfo ← NEW[RCTW.ProcedureFrameInfoBody←[
dotOLongName: frameInfo.dotOInfo.fileName,
mTime: frameInfo.dotOInfo.mtime,
size: frameInfo.dotOInfo.size,
fmagic: frameInfo.dotOInfo.fmagic,
smagic: frameInfo.dotOInfo.smagic,
stamp: frameInfo.dotOInfo.stamp,
codeBase: frameInfo.dotOInfo.textReloc,
dataBase: frameInfo.dotOInfo.dataReloc,
bssBase: frameInfo.dotOInfo.bssReloc,
relativePC: frameInfo.relativePC,
framePointer: frameInfo.framePointer,
stackPointer: frameInfo.stackPointer]];
cc: CC; node: CirioTypes.Node;
[cc, node] ← RCTW.GetCCAndFrameFromDotO[serverName, cFrameInfo, nub, symbols.embeddedDotO
! CCTypes.CCError =>
BEGIN
cc ← NIL;
node ← NIL;
May4Exp.ShowReport[RopeForCCError[case, msg]];
CONTINUE
END];
RETURN[cc, node];
END;
InstallOneAmpersandItem: PROC[ampersandContext: CirioTypes.Node, name: Rope.ROPE, item: CirioTypes.Node, cc: CC] =
BEGIN
IF NOT Rope.Equal[Rope.Substr[name, 0, 2], "&&"] THEN ERROR
ELSE
BEGIN
ampersandContextType: CirioTypes.Type ← CedarCode.GetTypeOfNode[ampersandContext];
indirect: CirioTypes.Node ← CedarCode.SelectFieldFromNode[name, ampersandContextType, ampersandContext, cc];
indirectType: CirioTypes.Type ← CedarCode.GetTypeOfNode[indirect];
encapsulatedItem: CirioTypes.Node ← AmpersandContext.MakeNodeFromNode[item, cc];
encapsulatedType: CirioTypes.Type ← CedarCode.GetTypeOfNode[encapsulatedItem];
CedarCode.StoreThroughIndirectNode[encapsulatedType, encapsulatedItem, indirectType, indirect, cc];
END
END;
ampersand support
ReadOneAmpersandItem: PROC[name: Rope.ROPE, nameScope: CirioTypes.Node, cc: CC] RETURNS[CirioTypes.Node] =
BEGIN
IF NOT Rope.Equal[Rope.Substr[name, 0, 2], "&&"] THEN ERROR
ELSE
BEGIN
nameScopeType: CirioTypes.Type ← CedarCode.GetTypeOfNode[nameScope];
indirect: CirioTypes.Node ← CedarCode.SelectFieldFromNode[name, nameScopeType, nameScope, cc];
indirectType: CirioTypes.Type ← CedarCode.GetTypeOfNode[indirect];
encapsulatedItem: CirioTypes.Node ← CedarCode.LoadThroughIndirectNode[indirectType, indirect, cc];
RETURN[AmpersandContext.StripAMNode[encapsulatedItem]]
END;
END;
ReadOneAmpersandInt: PROC[name: Rope.ROPE, defaultVal: INT, nameScope: CirioTypes.Node, cc: CC] RETURNS[INT] =
BEGIN
valNode: CirioTypes.Node ← NIL;
valNode ← ReadOneAmpersandItem[name, nameScope, cc
! CCTypes.CCError => {valNode ← NIL; CONTINUE}];
IF valNode = NIL THEN RETURN[defaultVal]
ELSE
BEGIN
val: REF INTNARROW[CedarCode.GetNodeRepresentation[valNode, cc]];
need to install a catch phrase for NARROW fault
RETURN[val^];
END;
END;
source and break positions
AddSearchDirectory: PUBLIC PROC [connection: Connection, directoryPath: Rope.ROPE, reports: IO.STREAM] =
Queries the user for a directory name and adds it to the front of the list of search directories.
BEGIN
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
Set the search directory.
AddDir[connection, directoryPath, reports];
IO.PutF[reports, " %g added to search directories\n", IO.rope[directoryPath]];
RETURN [NIL];
END;
eMsg: Rope.ROPE;
eMsg ← CirioBackstop.Protect[inner, reports];
IF eMsg # NIL THEN IO.PutF[reports, "%g\N", [rope[eMsg]]];
END;
RemoveSearchDirectory: PUBLIC PROC [connection: Connection, directoryPath: Rope.ROPE, reports: IO.STREAM] =
Queries the user for a directory name and removes it from the list of search directories.
BEGIN
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
Remove the search directory.
RemoveDir[connection, directoryPath, reports];
IO.PutF[reports, " %g removed from search directories\n", IO.rope[directoryPath]];
RETURN [NIL];
END;
eMsg: Rope.ROPE;
eMsg ← CirioBackstop.Protect[inner, reports];
IF eMsg # NIL THEN IO.PutF[reports, "%g\N", [rope[eMsg]]];
END;
ListSearchDirectory: PUBLIC PROC [connection: Connection, reports: IO.STREAM] =
List all current search directories.
BEGIN
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
ListDir[connection];
RETURN [" done"];
END;
eMsg: Rope.ROPE;
eMsg ← CirioBackstop.Protect[inner, reports];
IF eMsg # NIL THEN IO.PutF[reports, "%g\N", [rope[eMsg]]];
END;
ClearSearchDirectory: PUBLIC PROC [connection: Connection, reports: IO.STREAM] =
Clear all search directories.
BEGIN
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
ClearDir[connection];
RETURN [" done"];
END;
eMsg: Rope.ROPE;
eMsg ← CirioBackstop.Protect[inner, reports];
IF eMsg # NIL THEN IO.PutF[reports, "%g\N", [rope[eMsg]]];
END;
ampersand procedures
experimental
AddressData: TYPE = RECORD[byteAddress: CARD, h: Connection];
MkAddress: PROC[byteAddress: CARD, h: Connection] RETURNS[CirioTypes.CirioAddress] =
BEGIN
addressData: REF AddressData ← NEW[AddressData←[byteAddress, h]];
RETURN[NEW[CirioTypes.CirioAddressBody←[CirioAddressIsNil, ReadRemoteBits, WriteRemoteBits, FollowRemotePointer, AsCardForRemote, addressData]]];
END;
ByteBitSize: CARD = 8;
CardByteSize: CARD = 4;
CirioAddressIsNil: PROC [data: CirioTypes.CirioAddress] RETURNS [BOOL] ~ {
addressData: REF AddressData ← NARROW[data.data];
RETURN [addressData.byteAddress=0]};
this is adapted from NewRMTWImplA.NRFBFReadBits
perhaps we could define some common code somewhere?
ReadRemoteBits: PROC[byteOffset: INT ← 0, bitOffset: INT ← 0, bitSize: CARD, data: CirioTypes.CirioAddress] RETURNS[CARD] =
BEGIN
addressData: REF AddressData ← NARROW[data.data];
byteAddress: INT ← addressData.byteAddress + byteOffset + bitOffset/8;
remainingBitOffset: INT ← bitOffset MOD 8;
as per our charter, 32 bits at most
IF bitSize > 32 THEN CCE[operation];
here is where we do the read
BEGIN
cardAddr: INT ← (byteAddress/CardByteSize)*CardByteSize;
finalBitOffset: INT ← (byteAddress MOD CardByteSize )*ByteBitSize + remainingBitOffset;
nextBitOffset: INT ← finalBitOffset+bitSize;
IF nextBitOffset <= 32 THEN -- no word crossing
BEGIN
container: CARD ← CirioNubAccess.Read32BitsAsCard[[
addressData.h.nub,
cardAddr,
0,
FALSE,
TRUE]];
RETURN[PBasics.BITRSHIFT[PBasics.BITLSHIFT[container, finalBitOffset], 32-bitSize]];
END
ELSE -- splits across 4 byte boundary (I am not sure if this will even get tested in the near future, since the rumor is that Mimosa prevents such splits).
BEGIN
lContainer: CARD ← CirioNubAccess.Read32BitsAsCard[[
addressData.h.nub,
cardAddr,
0,
FALSE,
TRUE]];
rContainer: CARD ← CirioNubAccess.Read32BitsAsCard[[
addressData.h.nub,
cardAddr+1,
0,
FALSE,
TRUE]];
RETURN[PBasics.BITOR[
PBasics.BITLSHIFT[lContainer, finalBitOffset],
PBasics.BITRSHIFT[rContainer, (32*2)-(finalBitOffset+bitSize)]]];
END;
END;
END;
WriteRemoteBits: PROC[byteOffset: INT ← 0, bitOffset: INT ← 0, bitSize: CARD, data: CirioTypes.CirioAddress, bits: CARD] =
BEGIN
addressData: REF AddressData ← NARROW[data.data];
CCE[unimplemented];
END;
FollowRemotePointer: PROC[byteOffset: INT ← 0, data: CirioTypes.CirioAddress] RETURNS[CirioTypes.CirioAddress] =
BEGIN
addressData: REF AddressData ← NARROW[data.data];
newAddressVal: CARD ← ReadRemoteBits[0, 0, 32, data];
RETURN[MkAddress[newAddressVal, addressData.h]];
END;
AsCardForRemote: PROC[data: CirioTypes.CirioAddress] RETURNS[CARD] =
BEGIN
addressData: REF AddressData ← NARROW[data.data];
RETURN[addressData.byteAddress];
END;
RdCards: PROC[a: CirioTypes.CirioAddress, nCards: CARD] =
BEGIN
CardsPerLine: CARD = 10;
FOR x: CARD IN [0..(nCards+CardsPerLine-1)/CardsPerLine) DO
n: CARDMIN[CardsPerLine, nCards - CardsPerLine*x];
textLine: IO.STREAMIO.ROS[];
IO.PutF[textLine, "\T"];
FOR I: CARD IN [0..n) DO
oneCard: CARD ← a.readBits[CardsPerLine*4*x+4*I, 0, 32, a];
IO.PutF[textLine, " %g", IO.card[oneCard]];
ENDLOOP;
SystemInterface.ShowReport[IO.RopeFromROS[textLine], $urgent];
ENDLOOP;
END;
RdHexCards: PROC[a: CirioTypes.CirioAddress, nCards: CARD] =
BEGIN
CardsPerLine: CARD = 10;
FOR x: CARD IN [0..(nCards+CardsPerLine-1)/CardsPerLine) DO
n: CARDMIN[CardsPerLine, nCards - CardsPerLine*x];
textLine: IO.STREAMIO.ROS[];
IO.PutF[textLine, "\T"];
FOR I: CARD IN [0..n) DO
oneCard: CARD ← a.readBits[CardsPerLine*4*x+4*I, 0, 32, a];
IO.PutF[textLine, " %g", IO.rope[Convert.RopeFromCard[oneCard, 16]]];
ENDLOOP;
SystemInterface.ShowReport[IO.RopeFromROS[textLine], $urgent];
ENDLOOP;
END;
RdBytes: PROC[a: CirioTypes.CirioAddress, nBytes: CARD] =
BEGIN
BytesPerLine: CARD = 20;
FOR x: CARD IN [0..(nBytes+BytesPerLine-1)/BytesPerLine) DO
n: CARDMIN[BytesPerLine, nBytes - BytesPerLine*x];
textLine: IO.STREAMIO.ROS[];
IO.PutF[textLine, "\T"];
FOR I: CARD IN [0..n) DO
oneByte: CARD ← a.readBits[BytesPerLine*x+I, 0, 8, a];
IO.PutF[textLine, " %g", IO.rope[Convert.RopeFromCard[oneByte, 16]]];
ENDLOOP;
SystemInterface.ShowReport[IO.RopeFromROS[textLine], $urgent];
ENDLOOP;
END;
RdChars: PROC[a: CirioTypes.CirioAddress, nChars: CARD] =
BEGIN
CharsPerLine: CARD = 20;
FOR x: CARD IN [0..(nChars+CharsPerLine-1)/CharsPerLine) DO
n: CARDMIN[CharsPerLine, nChars - CharsPerLine*x];
textLine: IO.STREAMIO.ROS[];
IO.PutF[textLine, "\T"];
FOR I: CARD IN [0..n) DO
oneChar: CARD ← a.readBits[CharsPerLine*x+I, 0, 8, a];
IO.PutF[textLine, " %g", IO.char[VAL[BYTE[oneChar]]]];
ENDLOOP;
SystemInterface.ShowReport[IO.RopeFromROS[textLine], $urgent];
ENDLOOP;
END;
This procedure assumes that a is the address of a variable holding a rope.
That is, the contents of the 32 bits at address a is (roughly) a pointer to a rope body.
RdRope: PROC[a: CirioTypes.CirioAddress, nChars: CARD ← 100] =
BEGIN
ropeBodyAddr: CirioTypes.CirioAddress ← a.followPointer[0, a];
ropeBodyContents: CARD ← ropeBodyAddr.readBits[0, 0, 32, ropeBodyAddr];
rope: Rope.ROPENIL;
SeeOneChar: PROC[c: CHAR] =
{rope ← Rope.Cat[rope, Rope.FromChar[c]]};
GenCharsForRope[a.followPointer[0, a], 0, nChars, SeeOneChar];
SystemInterface.ShowReport[rope, $urgent];
END;
Field: TYPE = RECORD[offset: INT, size: CARD];
The field declarations in the following procedures must agree with Rope.RopeRep in the PCedar world (see [PCedar2.0]<Rope>Rope.mesa)
This procedure assumes that a is the address of a rope body.
GenCharsForRope: PROC[a: CirioTypes.CirioAddress, first: CARD, nChars: CARD, for: PROC[CHAR]] =
BEGIN
tagF: Field = [0, 1];
tag: CARD ← a.readBits[tagF.offset, 0, tagF.size, a];
ShowClient: PROC[c: CHAR] RETURNS[quit: BOOL] =
{for[c]; RETURN[FALSE]};
SystemInterface.ShowReport[IO.PutFR["tag = %g", IO.rope[Convert.RopeFromCard[tag, 16]]], $urgent];
SELECT tag FROM
0 => -- we are looking at a text
BEGIN
lengthF: Field = [1, 15];
maxF: Field = [16, 16];
length: CARD ← a.readBits[0, lengthF.offset, lengthF.size, a];
max: CARD ← a.readBits[0, maxF.offset, maxF.size, a];
n: CARDIF first >= length THEN 0 ELSE MIN[length-first, nChars];
FOR I: CARD IN [0..n) DO
byte: CARD ← a.readBits[4+first+I, 0, 8, a];
for[VAL[BYTE[byte]]];
ENDLOOP;
END;
1 => -- we are looking at a node
BEGIN
sizeF: Field = [1, 31];
depthF: Field = [32, 30];
casesF: Field = [62, 2];
size: CARD ← a.readBits[0, sizeF.offset, sizeF.size, a];
depth: CARD ← a.readBits[0, depthF.offset, depthF.size, a];
cases: CARD ← a.readBits[0, casesF.offset, casesF.size, a];
n: CARDIF first >= size THEN 0 ELSE MIN[size-first, nChars];
IF n > 0 THEN
SELECT cases FROM
0 => -- substr
BEGIN
baseF: Field = [64, 32];
startF: Field = [96, 32];
base: CirioTypes.CirioAddress ← a.followPointer[baseF.offset/8, a];
start: CARD ← a.readBits[0, startF.offset, startF.size, a];
[] ← Rope.Map[base: "??substr??", action: ShowClient];
GenCharsForRope[base, start, n, for];
END;
1 => -- concat
BEGIN
baseF: Field = [64, 32];
restF: Field = [96, 32];
posF: Field = [128, 32];
base: CirioTypes.CirioAddress ← a.followPointer[baseF.offset/8, a];
rest: CirioTypes.CirioAddress ← a.followPointer[restF.offset/8, a];
pos: CARD ← a.readBits[0, posF.offset, posF.size, a];
[] ← Rope.Map[base: "??concat??", action: ShowClient];
IF first < pos THEN
BEGIN
GenCharsForRope[base, first, MIN[pos-first, n], for];
IF n > pos-first THEN GenCharsForRope[rest, 0, n-pos+first, for];
END
ELSE
GenCharsForRope[rest, first-pos, n, for];
END;
2 =>
BEGIN
baseF: Field = [64, 32];
replaceF: Field = [96, 32];
startF: Field = [128, 32];
oldPosF: Field = [160, 32];
newPosF: Field = [192, 32];
base: CirioTypes.CirioAddress ← a.followPointer[baseF.offset/8, a];
replace: CirioTypes.CirioAddress ← a.followPointer[replaceF.offset/8, a];
start: CARD ← a.readBits[0, startF.offset, startF.size, a];
oldPos: CARD ← a.readBits[0, oldPosF.offset, oldPosF.size, a];
newPos: CARD ← a.readBits[0, newPosF.offset, newPosF.size, a];
[] ← Rope.Map[base: "??replace??", action: ShowClient];
IF n > 0 AND first < start THEN
BEGIN
GenCharsForRope[base, first, MIN[n, start-first], for];
first ← start;
n ← n-MIN[n, start-first];
END;
IF n > 0 AND first < newPos THEN
BEGIN
GenCharsForRope[replace, first-start, MIN[n, newPos-first], for];
first ← newPos;
n ← n - MIN[n, newPos-first];
END;
IF n > 0 AND first < size THEN
GenCharsForRope[base, first-newPos+oldPos, n, for];
END;
3 => [] ← Rope.Map[base: "??ObjectRope??", action: ShowClient];
ENDCASE => CCE[cirioError];
END;
ENDCASE => CCE[cirioError];
END;
FrameDiagnosticInfo: PROC[h: Connection] =
BEGIN
threadIndex: CARD ← h.currentThread;
td: REF ThreadData ← h.threads[threadIndex];
StackCirio.FrameDiagnosticInfo[td.stack, h.tsOut];
END;
breakpoints (tentative, using ampersand procedures)
SetBreakAtAbsAddr: PROC[h: Connection, absAddr: CARD32, mesaPos: SourceFileOpsExtras.Position, stopAll: BOOLEAN] =
{CirioBreakAccess.SetBreakAtAbsAddr[h.breaks, absAddr, mesaPos, stopAll]};
ListBreaks: PROC[h: Connection] =
{CirioBreakAccess.ListBreaks[h.breaks]};
ClearBreakAtAbsAddr: PROC[h: Connection, absAddr: CARD32] =
{CirioBreakAccess.ClearBreakAtAbsAddr[h.breaks, absAddr]};
ClearBreakAtIndex: PROC[h: Connection, index: CARD] =
{CirioBreakAccess.ClearBreakAtIndex[h.breaks, index]};
ClearAllBreaks: PROC[h: Connection] =
{CirioBreakAccess.ClearAllBreaks[h.breaks]};
AddDir: PROC [h: Connection, dirName: Rope.ROPE, reports: IO.STREAM] =
BEGIN
properDirName: Rope.ROPE ← CirioDeltaFace.ConvertToLocalDirectoryFormat[dirName];
properDirName: PFSNames.PATHPFS.PathFromRope[dirName];
prev: LIST OF PFSNames.PATHNIL;
first, we remove any existing entry for the path
WHILE TRUE DO
current: LIST OF PFSNames.PATHIF prev # NIL THEN prev.rest ELSE h.searchDirectories;
IF current = NIL THEN EXIT;
IF current.first.Equal[properDirName] THEN
BEGIN
IF prev = NIL THEN h.searchDirectories ← current.rest ELSE prev.rest ← current.rest;
EXIT;
END;
prev ← current;
ENDLOOP;
now we install the path at the front of the list
h.searchDirectories ← CONS[properDirName, h.searchDirectories];
now we inform those who need to know
NewRMTW.ResetSearchPaths[h.cedarModules, h.searchDirectories, BasicTime.Now[]];
IF h.rmtw#NIL THEN NewRMTW.FlushUnknownSymbolCache[h.rmtw, BasicTime.Now[], reports];
IF h.threads # NIL THEN
FOR ti: CARDINAL IN [0..h.threads.nThreads) DO
td: REF ThreadData ← h.threads[ti];
StackCirio.NoteFileCacheFlush[td.stack];
ENDLOOP;
END;
RemoveDir: PROC [h: Connection, dirName: Rope.ROPE, reports: IO.STREAM] =
BEGIN
properDirName: PFSNames.PATHPFS.PathFromRope[dirName];
prev: LIST OF PFSNames.PATHNIL;
first, we remove any existing entry for the path
WHILE TRUE DO
current: LIST OF PFSNames.PATHIF prev # NIL THEN prev.rest ELSE h.searchDirectories;
IF current = NIL THEN EXIT;
IF current.first.Equal[properDirName] THEN
BEGIN
IF prev = NIL THEN h.searchDirectories ← current.rest ELSE prev.rest ← current.rest;
EXIT;
END;
prev ← current;
ENDLOOP;
now we inform those who need to know
NewRMTW.ResetSearchPaths[h.cedarModules, h.searchDirectories, BasicTime.Now[]];
NewRMTW.FlushUnknownSymbolCache[h.rmtw, BasicTime.Now[], reports];
IF h.threads # NIL THEN
FOR ti: CARDINAL IN [0..h.threads.nThreads) DO
td: REF ThreadData ← h.threads[ti];
StackCirio.NoteFileCacheFlush[td.stack];
ENDLOOP;
END;
ListDir: PROC [h: Connection] =
BEGIN
searchDirectories: Rope.ROPENIL;
FOR paths: LIST OF PFSNames.PATH ← h.searchDirectories, paths.rest WHILE paths # NIL DO
searchDirectories ← Rope.Cat[searchDirectories, PFS.RopeFromPath[paths.first], " "];
ENDLOOP;
SystemInterface.ShowReport[IO.PutFR["%g", IO.rope[searchDirectories]], $urgent];
END;
ClearDir: PROC [h: Connection] =
BEGIN
SystemInterface.ShowReport[IO.PutFR["Clearing search directory list"], $urgent];
SystemInterface.ShowReport[IO.PutFR["Was: "], $urgent];
ListDir[h];
h.searchDirectories ← NIL;
now we inform those who need to know
NewRMTW.ResetSearchPaths[h.cedarModules, h.searchDirectories, BasicTime.Now[]];
note: we need not do a NewRMTW.FlushUnknownSymbolCache[h.rmtw, BasicTime.Now[]], because that need only be called when new definition mobs become accessible, not when some become inaccessible.
END;
symbols
LookupSym: PROC[h: Connection, name: Rope.ROPE, numToSkip: INT ← 0, externOnly: BOOLTRUE] RETURNS[CirioNubAccess.SymEntry] =
{RETURN[CirioNubAccess.LookupSymEntryByName[h.nub, name, FALSE, externOnly, numToSkip]]};
LookupAddr: PROC[h: Connection, val: CARD, numToSkip: INT ← 0] RETURNS[CirioNubAccess.SymEntry] =
{RETURN[CirioNubAccess.LookupSymEntryByValue[h.nub, val, numToSkip]]};
LookupID: PROC[h: Connection, symID: CARD] RETURNS[CirioNubAccess.SymEntry] =
{RETURN[CirioNubAccess.LookupSymEntryByID[h.nub, symID]]};
LookupFile: PROC[h: Connection, seqNum: CARD] RETURNS[entry: CirioNubAccess.FileEntry] =
{RETURN[CirioNubAccess.GetFileEntry[h.nub, seqNum]]};
GetPCInfo: PROC[h: Connection, absPC: CARD] RETURNS[CirioNubAccess.PCInfo] =
{RETURN[CirioNubAccess.PCtoInfo[h.nub, absPC]]};
misc
DebugOn: PROC =
BEGIN
DebugFlag ← TRUE;
END;
DebugOff: PROC =
BEGIN
DebugFlag ← FALSE;
END;
GetRelPCForCLine: PROC[dotOName: Rope.ROPE, cLineNum: CARD, h: Connection] RETURNS[CARD] =
BEGIN
dotOName1: Rope.ROPE ← CirioDeltaFace.ConvertFileNameToLocalFormat[dotOName];
dotOPath: PFSNames.PATHPFS.PathFromRope[dotOName];
file: SystemInterface.CirioFile ← SystemInterface.GetCirioFileFromDebuggee[h.fileSet, dotOPath, h.serverName];
IF file=NIL THEN CCE[cirioError, IO.PutFR["Couldn't access %g", [rope[dotOName]] ]];
{container: ObjF.Parsed ← ObjF.CreateParsed[file];
AcquireModule: PROC RETURNS[ObjF.Module] =
BEGIN
range: ObjF.StabRange ← ObjF.AlternativeFindModuleStabRange[container, 1];
RETURN[ObjF.ModuleFromParsedAndPC[container, [[0, ""], 1]]];
END;
dotO: ObjF.Module ← AcquireModule[];
relPC: CARD ← ObjF.GetPCForLineNum[dotO, cLineNum].relPC;
RETURN[relPC];}
END;
another Kludge
GetCLineForRelPC: PROC[dotOName: Rope.ROPE, relPC: CARD, h: Connection] RETURNS[CARD] =
BEGIN
dotOPath: PFSNames.PATHPFS.PathFromRope[dotOName];
file: SystemInterface.CirioFile ← SystemInterface.GetCirioFileFromDebuggee[h.fileSet, dotOPath, h.serverName];
IF file=NIL THEN CCE[cirioError, IO.PutFR["Couldn't access %g", [rope[dotOName]] ]];
{container: ObjF.Parsed ← ObjF.CreateParsed[file];
AcquireModule: PROC RETURNS[ObjF.Module] =
BEGIN
range: ObjF.StabRange ← ObjF.AlternativeFindModuleStabRange[container, 1];
RETURN[ObjF.ModuleFromParsedAndPC[container, [[0, ""], 1]]];
END;
dotO: ObjF.Module ← AcquireModule[];
cLine: CARD ← ObjF.GetLineNumForPC[dotO, [[0, ""], relPC]];
RETURN[cLine];}
END;
GetMesaPosForCLine: PROC[mesaFileName: Rope.ROPE, cLine: CARD, h: Connection] =
BEGIN
reports: IO.STREAMIO.ROS[];
cFileName: Rope.ROPE ← SystemInterface.MesaFileNameToCFileName[mesaFileName, reports];
reportsText: Rope.ROPEIO.RopeFromROS[reports];
IF Rope.Length[reportsText] # 0 THEN May4Exp.ShowReport[reportsText];
IF cFileName = NIL THEN CCE[cirioError];
BEGIN
cFile: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[h.fileSet, cFileName];
cFileInfo: May4Exp.CFileInfo ← May4Exp.CreateCFileInfo[cFile];
resultingMesaSourcePos: CARD ← May4Exp.LookupCLineNum[cFileInfo, cLine];
mesaFileStream: IO.STREAM; -- to test for the presence of the file
May4Exp.ShowReport[IO.PutFR["corresponding position is %g in %g", IO.card[resultingMesaSourcePos], IO.rope[mesaFileName]]];
lets test to see if the mesa file is around
mesaFileStream ← FS.StreamOpen[cFileName
! FS.Error => CONTINUE];
IF mesaFileStream = NIL THEN
{May4Exp.ShowReport["unable to open mesa file"]; RETURN};
IO.Close[mesaFileStream];
SystemInterface.ShowSource[fileName: mesaFileName, index: resultingMesaSourcePos, feedBack: reports];
END;
END;
Misc support
GetCurrentFrameInfo: PROC [connection: Connection, reports: IO.STREAM] RETURNS [dotOFileName: Rope.ROPE, procName: Rope.ROPE, relPC: CARD] =
BEGIN
ENABLE UNWIND => NULL;
inner: PROC RETURNS[Rope.ROPE] =
BEGIN
td: REF ThreadData ← connection.threads[connection.currentThread];
IF td.currentFrameSymbols = NIL THEN td.currentFrameSymbols ← FindFrameSymbols[connection, td];
BEGIN
embeddedDotOInfo: REF ObjectFiles.ModuleInfo ← ObjectFiles.GetModuleInfo[td.currentFrameSymbols.embeddedDotO];
frame: May4Exp.ProcedureFrameInfo ← td.currentFrame;
dotOFileName1: Rope.ROPE;
frameDotOFileName: Rope.ROPE ← frame.dotOInfo.fileName;
loc: INT ← Rope.FindBackward[frameDotOFileName, "/"] + 1;
dotOFileName ← Rope.Replace[base: frameDotOFileName, start: loc, with: LoadStateRootFileName[connection, frame]];
Convert to the standard file system format.
dotOFileName ← SystemInterface.ConvertFileNameToLocalFormat[dotOFileName];
Find out which file system view we need for this file.
dotOFileName1 ← SystemInterface.DetermineFileSystemView[dotOFileName, reports];
IF dotOFileName1 # NIL THEN
BEGIN
dotOFileName ← dotOFileName1;
END
ELSE
BEGIN
May4Exp.ShowReport[IO.PutFR["File %g can't be found.", IO.rope[dotOFileName]]];
CCE[cirioError];
END;
procName ← frame.pcInfo.procName;
relPC ← frame.relativePC - embeddedDotOInfo.startPC;
RETURN [dotOFileName];
END;
END;
[] ← CirioBackstop.Protect[inner, reports];
RETURN;
END;
LoadStateRootFileName: PROC [connection: Connection, frame: May4Exp.ProcedureFrameInfo] RETURNS [dotOFileName: Rope.ROPE] =
BEGIN
frameDotOFileName: Rope.ROPE ← frame.dotOInfo.fileName;
loc: INT ← Rope.FindBackward[frameDotOFileName, "/"] + 1;
frameDotORootFileName: Rope.ROPE ← Rope.Substr[frameDotOFileName, loc];
rootDotOName: Rope.ROPE ← frame.pcInfo.guessedEmbeddedFileName;
symEntryResult: CirioNubAccess.LookupSymEntryResult;
symValue: CARD;
numToSkip: INT;
IF Rope.IsEmpty[rootDotOName] THEN RETURN [NIL];
symEntryResult ← LookupID[connection, frame.pcInfo.guessedEmbeddedFileSymID];
WITH symEntryResult SELECT FROM
ser: REF CirioNubAccess.LookupSymEntryResultBody.case0 => symValue ← ser.symEntry.value;
ENDCASE => CCE[cirioError, "Bad args to LookupID"];
IF Rope.Equal[frameDotORootFileName, rootDotOName] THEN
dotOFileName ← rootDotOName
ELSE
BEGIN
numToSkip ← -1;
DO
symEntryResult ← LookupAddr[connection, symValue, numToSkip];
WITH symEntryResult SELECT FROM
ser: REF CirioNubAccess.LookupSymEntryResultBody.case0 => dotOFileName ← ser.symEntry.name;
ENDCASE => CCE[cirioError, "Bad args to LookupAddr"];
IF NOT Rope.Equal[dotOFileName, rootDotOName] THEN EXIT;
numToSkip ← numToSkip - 1;
ENDLOOP;
END;
RETURN [dotOFileName];
END;
Test driver
keyName: Rope.ROPE ← "𡤌irioDebuggeeTestFrame←";
use
RemoteCirioTest menhir 4816
the subject is [Cirio]<development>sturgis>test>test.mesa
and lives in [Cirio]<development>sturgis>SturgisLocalSun.df
the test lists were last updated: December 12, 1989 2:03:22 pm PST
RemoteCirioTest: Commander.CommandProc =
BEGIN
args: CommandTool.ArgumentVector ← CommandTool.Parse[cmd];
serverName: Rope.ROPE ← args[1];
portNum: CARDINALIF args.argc <= 2 THEN 4815 ELSE CARDINAL[Convert.CardFromRope[args[2]]];
reports: IO.STREAMIO.ROS[];
connection: Connection ← OpenConnection[serverName, portNum, LIST[FileNames.CurrentWorkingDirectory[]], reports];
remoteWorldStopped: BOOLEANFALSE;
CleanUp: PROC =
BEGIN
IF remoteWorldStopped THEN ResumeRemoteWorld[connection];
remoteWorldStopped ← FALSE;
CloseConnection[connection, reports];
IO.Close[reports]
END;
open a block to so that unwind catches can cleanup
(note: we need not lock a monitor, since only one thread has access to the connection data.)
BEGIN
ENABLE UNWIND => CleanUp[];
TestInner: PROC =
BEGIN
nThreads: CARD ← StopRemoteWorld[connection, reports];
remoteWorldStopped ← TRUE;
BEGIN
interestingThreads: LIST OF CARD ← FindThreadsWithProperty[connection, [callingDebugger[]], cmd.out];
FOR ts: LIST OF CARD ← interestingThreads, ts.rest WHILE ts # NIL DO
x: CARD ← ts.first;
stack: StackCirio.Stack;
[] ← FocusOnThread[connection, x, cmd.out];
stack ← GetStackForCurrentThread[connection, cmd.out];
[] ← StackCirio.ResetStack[stack, cmd.out];
IO.PutF[cmd.out, "searching thread %g\N", IO.rope[GetThreadTitleText[connection, x]]];
IF StackCirio.WalkStackToCProcedure[stack, keyName, "", cmd.out] THEN
BEGIN
nUnexpectedResults: CARD ← 0;
now we examine the frame
BEGIN
frameText: Rope.ROPE ← StackCirio.ShowCurrentFrame[stack, cmd.out];
IO.PutF[cmd.out, "\n\n%g\n\n", IO.rope[frameText]];
END;
now we perform the individual expression tests
BEGIN
frame1Tests: LIST OF TestDescriptor ← LIST[
["key", "44444"],
["inner", "(invalid proc descriptor address)"],
["result", "237864", 0, uninitializedVar],
["b", "[a:6, b:7]"],
["w", "[x:[a:4, b:5], y:[u:6, v:7]]"],
["y", "failure due to illegal operation: y undefined"],
["action", "???"],
["foo", "test.(no name)(pc=D30C4Ch, descr at C7BCC0h, src=1412)", 18],
["bar", "???"],
["x", "10"],
["veryBad", "???"],
["CallDebugger", "failure due to illegal operation: CallDebugger undefined"]];
nUnexpectedResults ← ExamineOneFrame[connection, frame1Tests, cmd.out] + nUnexpectedResults;
END;
lets look at the next frame
[] ← StackCirio.WalkStack[stack, 1, cmd.out];
and examine
BEGIN
frameText: Rope.ROPE ← StackCirio.ShowCurrentFrame[stack, cmd.out];
IO.PutF[cmd.out, "\n\n%g\n\n", IO.rope[frameText]];
END;
now we perform the individual expression tests
BEGIN
frame2Tests: LIST OF TestDescriptor ← LIST[
["c", "'a"],
 ["quit", "FALSE"],
 ["bool1", "FALSE"],
 ["bool2", "TRUE"],
 ["charA", "'A"],
 ["charB", "'b"],
 ["array1", "(7)[6, 8, 10, 12, 14, 16, 18]"],
 ["array2", "(6)[11, 13, 15, 17, 19, 21]"],
 ["refR", "^[a:7, b:8]"],
 ["bb", "[ref:^[ref:NIL, a:45, b:46], a:47, b:48]"],
 ["refSeq", "^[x:10, (5)[3, 7, 11, 15, 19]]"],
 ["refSeq2", "^[y:45, (7)[TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE]]"],
 ["ref2Seq2", "^[y:77, (32)[TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, ...]]"],
 ["b", "[a:6, b:7]"],
 ["w", "[x:[a:4, b:5], y:[u:6, v:7]]"],
 ["y", "failure due to illegal operation: y undefined"],
 ["action", "???"],
 ["foo", "test.(no name)(pc=D3217Ch, descr at C7BE90h, src=1412)", 18],
 ["bar", "???"],
 ["x", "10"],
 ["veryBad", "???"],
 ["CallDebugger", "failure due to illegal operation: CallDebugger undefined"]];
nUnexpectedResults ← ExamineOneFrame[connection, frame2Tests, cmd.out] + nUnexpectedResults;
END;
lets skip a frame and look at the following frame
[] ← StackCirio.WalkStack[stack, 2, cmd.out];
and examine
BEGIN
frameText: Rope.ROPE ← StackCirio.ShowCurrentFrame[stack, cmd.out];
IO.PutF[cmd.out, "\n\n%g\n\n", IO.rope[frameText]];
END;
now we perform the individual expression tests
BEGIN
frame3Tests: LIST OF TestDescriptor ← LIST[
["a", "22222"],
 ["deep", "1606016"],
 ["rope", "failure due to internal Cirio error"],
 ["arg1", "1234"],
 ["arg2", "5678"],
 ["res1", "node?"],
 ["res2", "node?"],
 ["z", "8518320"],
 ["yy", "3807"],
 ["ll", "x"],
 ["mm", "f"],
 ["q", "(invalid proc descriptor address)"],
 ["b", "[a:6, b:7]"],
 ["w", "[x:[a:4, b:5], y:[u:6, v:7]]"],
 ["y", "failure due to illegal operation: y undefined"],
 ["action", "???"],
 ["foo", "test.(no name)(pc=D3217Ch, descr at C7BE90h, src=1412)", 18],
 ["bar", "???"],
 ["x", "10"],
 ["veryBad", "???"],
 ["CallDebugger", "failure due to illegal operation: CallDebugger undefined"]];
nUnexpectedResults ← ExamineOneFrame[connection, frame3Tests, cmd.out] + nUnexpectedResults;
END;
overall result:
IF nUnexpectedResults = 0 THEN IO.PutF[cmd.out, "\N\NThere were NO unexpected results\N\N"]
ELSE IO.PutF[cmd.out, "\N\NThere were %g unexpected results\N\N", IO.card[nUnexpectedResults]];
finally, lets end the test
EXIT;
END;
ENDLOOP;
END;
END;
TestInner[]; -- so that unwinds from aborts from breakpoints will be caught
END;
CleanUp[]
END;
TestDescriptor: TYPE = RECORD[
expression: Rope.ROPE,
expectedResult: Rope.ROPE,
compareNCharsOnly: CARD ← 0,
validity: Validity ← valid];
Validity: TYPE = {valid, uninitializedVar, WRONG};
ExamineOneFrame: PROC[connection: Connection, tryThis: LIST OF TestDescriptor, reports: IO.STREAM] RETURNS[--nUnexpectedResults-- CARD] =
BEGIN
ResultDescriptor: TYPE = RECORD[
expression: Rope.ROPE,
expectedResult: Rope.ROPE,
actualResult: Rope.ROPE];
deltas: LIST OF ResultDescriptor ← NIL;
lastDelta: LIST OF ResultDescriptor ← NIL;
news: LIST OF TestDescriptor ← NIL;
lastNew: LIST OF TestDescriptor ← NIL;
nUnexpectedResults: CARD ← 0;
stack: StackCirio.Stack ← GetStackForCurrentThread[connection, reports];
FOR tests: LIST OF TestDescriptor ← tryThis, tests.rest WHILE tests # NIL DO
result: Rope.ROPE ← StackCirio.InterpretTextLine[stack, tests.first.expression, reports];
new: LIST OF TestDescriptor ← LIST[[tests.first.expression, result, tests.first.compareNCharsOnly, tests.first.validity]];
nChars: CARDIF tests.first.compareNCharsOnly > 0 THEN tests.first.compareNCharsOnly ELSE Rope.Length[tests.first.expectedResult];
IF news = NIL THEN news ← new ELSE lastNew.rest ← new;
lastNew ← new;
IO.PutF[reports, "for expression: %g we get\N\T%g\N", IO.rope[tests.first.expression], IO.rope[result]];
IF (tests.first.validity # uninitializedVar) AND NOT Rope.Equal[Rope.Substr[result, 0, nChars], Rope.Substr[tests.first.expectedResult, 0, nChars]] THEN
BEGIN
delta: LIST OF ResultDescriptor ← LIST[[tests.first.expression, tests.first.expectedResult, result]];
IO.PutF[reports, " THIS IS WRONG, we expected\N\T%g\N\N", IO.rope[tests.first.expectedResult]];
IF deltas = NIL THEN deltas ← delta ELSE lastDelta.rest ← delta;
lastDelta ← delta;
nUnexpectedResults ← nUnexpectedResults + 1;
END;
ENDLOOP;
IO.PutF[reports, "\N\Nhere is a list of expressions with their actual results\N"];
FOR items: LIST OF TestDescriptor ← news, items.rest WHILE items # NIL DO
IO.PutF[reports, "\T[\"%g\", \"%g\"", IO.rope[items.first.expression], IO.rope[items.first.expectedResult]];
IF items.first.compareNCharsOnly > 0 OR items.first.validity # valid THEN
IO.PutF[reports, ", %g", IO.card[items.first.compareNCharsOnly]];
IF items.first.validity # valid THEN
IO.PutF[reports, ", %g", IO.rope[SELECT items.first.validity FROM
uninitializedVar => "uninitializedVar",
WRONG => "WRONG",
ENDCASE => ERROR]];
IO.PutF[reports, "],\N"];
ENDLOOP;
IF deltas # NIL THEN
BEGIN
IO.PutF[reports, "\N\Nthere were unexpected results\N"];
FOR items: LIST OF ResultDescriptor ← deltas, items.rest WHILE items # NIL DO
IO.PutF[reports, "\Texpression: %g\N", IO.rope[items.first.expression]];
IO.PutF[reports, "\Texpected result: %g\N", IO.rope[items.first.expectedResult]];
IO.PutF[reports, "\Tactual result: %g\N", IO.rope[items.first.actualResult]];
ENDLOOP;
END
ELSE IO.PutF[reports, "\N\Nthere were NO unexpected results\N\N"];
RETURN[nUnexpectedResults];
END;
InstallBreakCheckDaemon: PUBLIC PROC [connection: Connection, reports: IO.STREAM] =
BEGIN
IO.PutF[reports, "Installing daemon for breakpoint hit ...\n"];
connection.daemonKillFlag ← FALSE;
connection.daemon ← FORK BreakCheckDaemon[connection: connection, reports: reports];
Process.Detach[connection.daemon];
IO.PutF[reports, " fork finished.\n"];
END;
StopBreakCheckDaemon: PUBLIC PROC [connection: Connection, reports: IO.STREAM] =
BEGIN
connection.remoteWorldRunning ← FALSE;
IO.PutF[reports, "killing BreakCheckDaemon...\n"];
connection.daemonKillFlag ← TRUE;
Process.PauseMsec[3000];
END;
BreakCheckDaemon: PROC [connection: Connection, reports: IO.STREAM] =
BEGIN
whichError: Rope.ROPE ← "$unkown";
{
Process.SetPriority[Process.priorityBackground];
DO
IF connection.daemonKillFlag = TRUE THEN GOTO terminate;
WHILE connection.remoteWorldRunning = FALSE DO
Process.PauseMsec[2000];
IF connection.daemonKillFlag = TRUE THEN GOTO terminate;
ENDLOOP;
IF CirioNubAccess.WaitSig[h: connection.nub, timeoutMilliSec: 100
!
CirioNub.Error => {
whichError ← Rope.Cat["CirioNub (", Atom.GetPName[code], ")"];
GOTO connectionError; };
CirioNubAccess.Error => {
l: LIST OF ATOM ← codes;
didAny: CARD ← 0;
whichError ← Rope.Cat["CirioNubAccess: ", msg, " ("];
WHILE l # NIL DO
IF didAny # 0 THEN
whichError ← whichError.Concat[", "];
whichError ← whichError.Concat[Atom.GetPName[l.first]];
l ← l.rest;
ENDLOOP;
GOTO connectionError; };] = FALSE THEN
IO.PutF[reports, "SIGINT: Debuggee needs attention.\n"! IO.Error => GOTO ret];
ELSE
IO.PutF[reports, "W"];
Process.PauseMsec[2000];
ENDLOOP;
EXITS
terminate => {IO.PutF[reports, "BreakCheckDaemon killed.\n"! IO.Error => CONTINUE];RETURN};
connectionError => {
IO.PutF[reports, "connection lost! %g BreakCheckDaemon exited.\n", [rope[whichError]]]; connection.open ← FALSE; RETURN};
ret => RETURN;
};
END;
main code
Commander.Register["RemoteCirioTest", RemoteCirioTest];
END..