ComputeServerDebuggerImpl.mesa
The Compute Server side of the Summoner.
Last Edited by: Bob Hagmann, July 24, 1986 2:01:53 pm PDT
Hal Murray, March 22, 1986 11:34:20 pm PST
Copyright © 1984 by Xerox Corporation. All rights reserved.
DIRECTORY
AMBridge,
AMEvents,
AMEventsBackdoor,
AMEventsBackdoorExtra,
AMModel,
AMModelBridge,
AMTypes,
AMViewerOps,
Basics,
BasicTime,
CedarProcess,
Commander,
ComputeServerClient,
ComputeServer,
ComputeServerControl,
ComputeServerCallbacks,
ComputeServerCallbacksRpcControl,
ComputeServerInternal,
EvalQuote,
FS,
Interpreter,
InterpreterOps,
InterpreterToolPrivate,
IO,
MBQueue,
PrincOps,
PrincOpsUtils,
Process,
Pup USING [Address, nullAddress],
PupStream USING [AllocateSocket, LocalAddress, SendMark, Sockets, StreamClosing, waitForever, WaitForRendezvous],
SymTab,
Rope,
RPC,
RuntimeError,
WorldVM;
ComputeServerDebuggerImpl:
CEDAR
PROGRAM
IMPORTS AMBridge, AMEvents, AMEventsBackdoor, AMEventsBackdoorExtra, AMModel, AMModelBridge, AMTypes, AMViewerOps, BasicTime, CedarProcess, ComputeServerCallbacksRpcControl, ComputeServerInternal, EvalQuote, FS, Interpreter, InterpreterOps, IO, MBQueue, PrincOpsUtils, Process, PupStream, Rope, RPC, RuntimeError, SymTab, WorldVM
= BEGIN
Uncaught Signals
guestLocalEvent:
PROC[event: AMEvents.Event, f: PrincOps.FrameHandle,
stack:
POINTER
TO PrincOps.StateVector, return:
BOOL ←
FALSE]
RETURNS[debugLocally:
BOOL ←
FALSE] =
TRUSTED
BEGIN
modified from AMEventsImpl.LocalEvent
serverPupAddress: Pup.Address;
outcome: AMEvents.Outcome;
interface: ComputeServerCallbacksRpcControl.InterfaceRecord ← NIL;
serviceItemPointer: ActiveServicesPointer;
IF ~PrincOpsUtils.IsBound[LOOPHOLE[AMEventsBackdoorExtra.PSBIToTV]] THEN RETURN[TRUE]; -- skip if the interface isn't exported
procPSBIToTV: PROC [psbi: CARDINAL, world: WorldVM.World] RETURNS [p: AMTypes.TV];
serviceItemPointer ← LOOPHOLE[ComputeServerInternal.GetGuestProcessData[LOOPHOLE[Process.GetCurrent[]]]];
IF serviceItemPointer = NIL THEN RETURN[TRUE];
interface ← serviceItemPointer.callbacksInterface;
interface ← ComputeServerCallbacksRpcControl.ImportNewInterface[
interfaceName: [ type: "ComputeServerCallbacks.summoner", instance: serviceItemPointer.clientNetAddressRope, version: [1,1]]
! RPC.ImportFailed => {
CONTINUE;
};
];
IF interface = NIL THEN RETURN[TRUE];
event.world ← AMEventsBackdoorExtra.LocalActor.world;
event.session ← AMEventsBackdoorExtra.LocalActor.bootCount;
event.process ←
AMEventsBackdoorExtra.PSBIToTV[PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]], AMEventsBackdoorExtra.LocalActor.world];
procPSBIToTV← LOOPHOLE[AMBridge.TVToProc[Interpreter.Evaluate["AMEventsImpl.PSBIToTV"].result]];
event.process ←
procPSBIToTV[PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]], AMEventsBackdoorExtra.LocalActor.world];
event.frame ← AMBridge.TVForFrame[f, stack, return, event.type = break];
event.worry ← FALSE;
IF AMEventsBackdoorExtra.Informing THEN RuntimeError.InformationalSignal[AMEvents.Debugging];
[outcome, serverPupAddress, debugLocally] ← remoteEventHandler[event, interface, serviceItemPointer]; --used to call InvokeEvent who called InterpreterToolImpl.EventHandler
[] ← ComputeServerInternal.DeletePupAddress[serverPupAddress];
IF AMEventsBackdoorExtra.Informing THEN RuntimeError.InformationalSignal[AMEvents.Debugged];
IF ~debugLocally
THEN {
WITH o: outcome
SELECT
FROM
proceed => NULL;
quit => ERROR ABORTED -- I don't like it, but that's the convention for now --;
retry, returnFrom => ERROR AMEventsBackdoorExtra.NotImplemented[];
ENDCASE => ERROR;
};
END;
remoteEventHandler:
PROC[event: AMEvents.Event,
interface: ComputeServerCallbacksRpcControl.InterfaceRecord,
serviceItemPointer: ActiveServicesPointer]
RETURNS[outcome: AMEvents.Outcome, serverPupAddress: Pup.Address, debugLocally:
BOOL ←
FALSE] =
TRUSTED {
InterpreterToolImpl.EventHandler basically calls InterpreterToolImpl.CreateTool if it can't find a suitable window to nest the call in. CreateTool is copied below, less the dormant Event window finding stuff.
h: InterpreterToolPrivate.Handle;
context: AMModel.Context =
IF event = NIL THEN AMModel.RootContext[WorldVM.LocalWorld[]] ELSE AMModelBridge.ContextForFrame[event.frame];
remoteWorld: WorldVM.World ← NIL;
oldPriority: CedarProcess.Priority ← CedarProcess.GetPriority[];
debugServicesItem: ActiveServicesItem ← NIL;
byteStreamOK: BOOL;
name: ROPE ← NIL;
in, out: STREAM;
inData, outData: BufStreamData ;
copyProcess: PROCESS;
procMainLoop: PROC [h: InterpreterToolPrivate.Handle] RETURNS [outcome: AMEvents.Outcome ];
sockets: PupStream.Sockets ← PupStream.AllocateSocket[serviceItemPointer.listenerPupAddress];
serverPupAddress ← PupStream.LocalAddress[sockets];
CedarProcess.SetPriority[normal];
IF context = NIL THEN ERROR;
IF WorldVM.LocalWorld[] # AMModel.ContextWorld[context]
THEN remoteWorld ← AMModel.ContextWorld[context];
h ←
NEW[
InterpreterToolPrivate.InterpreterObject
← [remoteWorld: remoteWorld,
context: context,
event: event,
menuHitQueue: MBQueue.Create[],
symTab: SymTab.Create[],
readEvalPrintProcess: LOOPHOLE[Process.GetCurrent[]]]]; -- LOOPHOLE should not be needed, but the compiler give a bogus type error
name ← NewViewer[h ! AMTypesError => GOTO err];
glue the streams together and wake up remote window
debugServicesItem ← ComputeServerInternal.AddPupAddress[serverPupAddress, NIL, sockets];
debugServicesItem.debugItem ← TRUE;
debugServicesItem.h ← h;
debugServicesItem.originalListenerPupAddress ← serviceItemPointer.listenerPupAddress;
TRUSTED { Process.Detach[FORK ComputeServerDebuggerRendezvousProcess[debugServicesItem, sockets]]};
byteStreamOK ← interface.OpenDebugStream[listenerPupAddress: serviceItemPointer.listenerPupAddress, newListenerPupAddress: serverPupAddress, name: name, serverMachine: ComputeServerInternal.myHostName !
RPC.CallFailed => {
byteStreamOK ← FALSE;
debugServicesItem.quitRendezvousAttempt ← TRUE;
CONTINUE;
};
];
IF byteStreamOK
THEN {
-- RPC might be faster than stream establishment
start: BasicTime.GMT;
start ← BasicTime.Now[];
WHILE ~debugServicesItem.rendezvousDone
DO
counter: INT ← 0 ;
WHILE ~debugServicesItem.rendezvousDone
AND counter < 100
DO
Process.Pause[1];
counter ← counter + 1;
ENDLOOP;
IF ~debugServicesItem.rendezvousDone AND BasicTime.Period[from: start, to: BasicTime.Now[]] > 7 THEN EXIT;
ENDLOOP;
IF ~debugServicesItem.rendezvousDone THEN byteStreamOK ← FALSE;
};
IF byteStreamOK
THEN {
in ← IO.CreateStream[streamProcs: ComputeServerInternal.inStreamProcs,
streamData: inData ← NEW[BufStreamDataObject ← [listenerItem: debugServicesItem]]];
out ← IO.CreateStream[streamProcs: ComputeServerInternal.outStreamProcs,
streamData: outData ← NEW[BufStreamDataObject ← [listenerItem: debugServicesItem]]];
debugServicesItem.remoteStream.PutRope[Rope.Cat[name, "\n"]];
debugServicesItem.remoteStream.Flush[];
h.tsInStream ← in;
h.tsOutStream ← out;
TRUSTED { Process.Detach[copyProcess ←
FORK DebugStreamCopy[debugServicesItem, in, out, inData, outData]]};
streams connected: start up the interpreter
procMainLoop ← LOOPHOLE[AMBridge.TVToProc[Interpreter.Evaluate["InterpreterToolImpl.MainLoop"].result]];
outcome ← procMainLoop[h];
}
ELSE {
outcome ← [quit[]];
};
out.Close[];
debugServicesItem.remoteStream.Close[! IO.Error => CONTINUE;] ;
debugServicesItem.callOver ← TRUE;
[] ← ComputeServerInternal.DeletePupAddress[serverPupAddress];
CedarProcess.SetPriority[oldPriority];
EXITS
err => {
RETURN[[quit[]], Pup.nullAddress, FALSE]
};
};
ComputeServerDebuggerRendezvousProcess:
PROC[debugServicesItem: ActiveServicesItem, sockets: PupStream.Sockets] = {
WHILE debugServicesItem.remoteStream =
NIL
AND ~debugServicesItem.quitRendezvousAttempt
DO
debugServicesItem.remoteStream ← PupStream.WaitForRendezvous[sockets: sockets, getTimeout: 1000, putTimeout: PupStream.waitForever, waitTimeout: 1000 ! PupStream.StreamClosing => {
debugServicesItem.quitRendezvousAttempt ← TRUE;
CONTINUE;
};
];
ENDLOOP;
IF ~debugServicesItem.quitRendezvousAttempt THEN debugServicesItem.rendezvousDone ← TRUE;
};
DebugStreamCopy:
PROC[debugServicesItem: ActiveServicesItem, in, out:
STREAM, inData, outData: BufStreamData] = {
lastLoop: BOOL ← FALSE;
WHILE ~debugServicesItem.inEOF
OR debugServicesItem.outEOF ~= true
DO
doneSomething:
BOOL ←
FALSE;
copy from the internal stream to the remote stream
IF debugServicesItem.outEOF ~= true
AND ComputeServerInternal.inCharsAvail[out,
FALSE] > 0
THEN {
WHILE ComputeServerInternal.inCharsAvail[out,
FALSE] > 0
AND debugServicesItem.outEOF = false
DO
debugServicesItem.remoteStream.PutChar[ComputeServerInternal.inBufGetChar[out ! IO.EndOfStream => {debugServicesItem.outEOF ← pending; CONTINUE;}]];
ENDLOOP;
debugServicesItem.remoteStream.Flush[ ! PupStream.StreamClosing => {debugServicesItem.outEOF ← true; CONTINUE;}; ];
IF debugServicesItem.outEOF = pending
THEN {
PupStream.SendMark[debugServicesItem.remoteStream, 27B
! PupStream.StreamClosing => CONTINUE];
debugServicesItem.outEOF ← true;
};
doneSomething ← TRUE;
};
copy from the remote stream to the internal stream
WHILE debugServicesItem.remoteStream.CharsAvail[] > 0
AND ~debugServicesItem.inEOF
AND (inData.inPointer - (inData.outPointer + 1))
MOD ComputeServerInternal.BufStreamBufferSize # 0
DO
doneSomething ← TRUE;
ComputeServerInternal.outBufPutChar[in, debugServicesItem.remoteStream.GetChar[
!
IO.EndOfStream => {
FOR i:INT IN [0..5) DO ComputeServerInternal.outBufPutChar[in, '\n]; ENDLOOP;
TRUSTED {abort[debugServicesItem.h]; };
inData.EOF ← pending;
debugServicesItem.inEOF ← TRUE;
CONTINUE;
}
]];
ENDLOOP;
IF lastLoop THEN EXIT;
IF debugServicesItem.callOver THEN { lastLoop ← TRUE; LOOP;};
IF ~doneSomething THEN Process.Pause[5];
ENDLOOP;
PupStream.SendMark[debugServicesItem.remoteStream, 26B
! PupStream.StreamClosing => CONTINUE];
debugServicesItem.remoteStream.Close[! IO.Error => CONTINUE;] ;
};
NewViewer:
PROC[h: InterpreterToolPrivate.Handle]
RETURNS[name:
ROPE ←
NIL] =
TRUSTED {
LocalRegister:
PROC [name:
ROPE, ref:
REF, help:
ROPE] =
TRUSTED {
ENABLE AMTypes.Error => GO TO err;
InterpreterOps.RegisterTV[
name: name, tv: AMBridge.TVForReferent[ref], help: help, symTab: h.symTab
];
EXITS err => {
IO.PutF[h.tsOutStream, "** Failed to register %g\n", [rope[name]]];
ERROR AMTypesError;
};
};
IF h.event #
NIL
THEN {
procEventToName: PROC [event: AMEvents.Event] RETURNS [name: ROPE ];
procEventToName ← LOOPHOLE[AMBridge.TVToProc[Interpreter.Evaluate["InterpreterToolImpl.EventToName"].result]];
IF h.event.world # WorldVM.LocalWorld[] THEN name ← "WORLDSWAP ";
name ← Rope.Cat[name, "Event: ", procEventToName[h.event]];
}
ELSE name ← Rope.Cat["Interp: ", AMModel.ContextName[h.context]];
LocalRegister[
"&H", NEW[InterpreterToolPrivate.Handle ← h], "this interpretertool's handle"];
LocalRegister[
"&depth", NEW[INT ← 4], "printing depth for this interpretertool"];
LocalRegister[
"&width", NEW[INT ← 32], "printing width for this interpretertool"];
LocalRegister[
"&WalkStack",
NEW[UNSAFE PROC[nFrames: INT ← 1, h: InterpreterToolPrivate.Handle ← NIL] ← LOOPHOLE[ AMBridge.TVToProc[Interpreter.Evaluate["InterpreterToolImpl.WalkStack"].result]] ],
"set a local frame context (eventhandler)"];
LocalRegister[
"&slf",
NEW[UNSAFE PROC[nFrames: INT ← 1, h: InterpreterToolPrivate.Handle ← NIL] ← LOOPHOLE[ AMBridge.TVToProc[Interpreter.Evaluate["InterpreterToolImpl.WalkStack"].result]] ],
"set a local frame context (eventhandler)"];
LocalRegister[
"&ShowFrame",
NEW[UNSAFE PROC[h: InterpreterToolPrivate.Handle ← NIL] ← LOOPHOLE[ AMBridge.TVToProc[Interpreter.Evaluate["InterpreterToolImpl.ShowFrame"].result]] ],
"show the current lf context (eventhandler)"];
LocalRegister[
"&Source", NEW[UNSAFE PROC[h: InterpreterToolPrivate.Handle ← NIL] ← Source],
"show source loc of current lf context (eventhandler)"];
EvalQuote.Register["&sgf", LOOPHOLE[AMBridge.TVToProc[Interpreter.Evaluate[ "InterpreterToolImpl.SetGlobalFrameHelper"].result]], h.symTab, h];
LocalRegister[
"&sgf",
NEW[UNSAFE PROC[progName: ROPE, h: InterpreterToolPrivate.Handle ← NIL] ← LOOPHOLE[ AMBridge.TVToProc[Interpreter.Evaluate["InterpreterToolImpl.SetGlobalFrameContext"].result]] ],
"&sgf[rope]\tset context to be that for the given module"];
EvalQuote.Register["&gf", LOOPHOLE[AMBridge.TVToProc[Interpreter.Evaluate[ "InterpreterToolImpl.GlobalFrameHelper"].result]], h.symTab, h];
LocalRegister[
"&gf",
NEW[UNSAFE PROC[progName: ROPE, h: InterpreterToolPrivate.Handle ← NIL] RETURNS [AMTypes.TV] ← LOOPHOLE[ AMBridge.TVToProc[Interpreter.Evaluate[ "InterpreterToolImpl.GlobalFrameContext"].result]] ],
"&gf[rope]\tget the given module's context"];
EvalQuote.Register["&sir", LOOPHOLE[AMBridge.TVToProc[Interpreter.Evaluate[ "InterpreterToolImpl.SetIRHelper"].result]], h.symTab, h];
LocalRegister[
"&sir",
NEW[UNSAFE PROC[interfaceName: ROPE, h: InterpreterToolPrivate.Handle ← NIL] ← LOOPHOLE[ AMBridge.TVToProc[Interpreter.Evaluate[ "InterpreterToolImpl.SetIRContext"].result]] ],
"&sir[rope]\tset context to be that for the given IR"];
EvalQuote.Register["&type", LOOPHOLE[AMBridge.TVToProc[Interpreter.Evaluate[ "InterpreterToolImpl.TypeHelper"].result]], h.symTab, h];
LocalRegister[
"&type",
NEW[UNSAFE PROC[expr: ROPE, h: InterpreterToolPrivate.Handle ← NIL] RETURNS[AMTypes.Type] ← LOOPHOLE[ AMBridge.TVToProc[Interpreter.Evaluate[ "InterpreterToolImpl.TypeGetter"].result]] ],
"&type[expr]\tget the type of the given expr"];
EvalQuote.Register["&ir", LOOPHOLE[AMBridge.TVToProc[Interpreter.Evaluate[ "InterpreterToolImpl.IRHelper"].result]], h.symTab, h];
LocalRegister[
"&ir",
NEW[UNSAFE PROC[interfaceName: ROPE, h: InterpreterToolPrivate.Handle ← NIL] RETURNS[AMTypes.TV] ← LOOPHOLE[ AMBridge.TVToProc[Interpreter.Evaluate[ "InterpreterToolImpl.IRContext"].result]] ],
"&ir[rope]\tget the given IR"];
LocalRegister[
"&ABORT", NEW[UNSAFE PROC[h: InterpreterToolPrivate.Handle ← NIL] ← abort],
"abort button hit"];};
abort:
UNSAFE
PROC[h: InterpreterToolPrivate.Handle ←
NIL] =
TRUSTED {
giveFinalCR: BOOL← TRUE;
procSetAbortRequested: PROC[h: InterpreterToolPrivate.Handle];
procSetAbortRequested ← LOOPHOLE[ AMBridge.TVToProc[Interpreter.Evaluate["InterpreterToolImpl.SetAbortRequested"].result]];
procSetAbortRequested[h];
Process.Abort[h.readEvalPrintProcess ! Process.InvalidProcess => {giveFinalCR ← FALSE; CONTINUE;}; ]; -- ERROR possible due to timing error, but the Process.Abort is only being used to wake up the interperter and the timeing error is that the interperter woke up, so it is ok.
IF giveFinalCR THEN StuffIt[h, "\n"];
};
Source:
PROC [h: InterpreterToolPrivate.Handle ←
NIL] = {
name: ROPE;
index: INT;
fName: ROPE ← NIL;
report: AMViewerOps.ReportProc =
-- [msg: ROPE, severity: Severity]
TRUSTED {h.tsOutStream.PutRope[msg]};
TRUSTED {
IF h =
NIL
THEN {
procGetHandlePlease: PROC RETURNS [InterpreterToolPrivate.Handle];
procGetHandlePlease ← LOOPHOLE[AMBridge.TVToProc[Interpreter.Evaluate["InterpreterToolImpl.GetHandlePlease"].result]];
h ← procGetHandlePlease[];
};
};
[name, index] ← AMViewerOps.SourceFromTV[h.context, report];
IF name #
NIL
THEN {
fName ← FS.FileInfo[name: name, remoteCheck: FALSE ! FS.Error => CONTINUE;].attachedTo;
};
IF fName #
NIL
THEN {
interface: ComputeServerCallbacksRpcControl.InterfaceRecord ← NIL;
debuggerItem: ActiveServicesItem ← ComputeServerInternal.findDebuggerItemFromInterpreterHandle[h].item;
IF debuggerItem =
NIL
OR debuggerItem.callbacksInterface =
NIL
THEN {
h.tsOutStream.PutRope["\nUnable to contact server for debugging\n"];
}
ELSE {
debuggerItem.callbacksInterface.OpenSourceViewer[debuggerItem.originalListenerPupAddress, fName, index ! RPC.CallFailed => CONTINUE];
};
};
};
StuffIt:
PROC [h: InterpreterToolPrivate.Handle, rope:
ROPE] = {
h.tsOutStream.PutRope[rope];
};
disableGuest:
PROC [localFS:
BOOL] = {
process: PROCESS = LOOPHOLE[Process.GetCurrent[]];
IF localFS THEN ComputeServerInternal.MarkProcessNotGuest[process]
ELSE ComputeServerInternal.RemoveMarkProcessNotGuest[process];
};