WVMRemote.mesa - TeleDebug protocol
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Andrew Birrell July 29, 1983 3:56 pm
Russ Atkinson, February 6, 1985 9:37:28 pm PST
Hal Murray, June 3, 1986 4:14:45 pm PDT
DIRECTORY
DebuggerFormat USING [LabelChecksum],
DiskFace USING [Label, Type],
Endian USING [FFromInt, FWORD, IntFromF],
PrincOps,
PrincOpsUtils USING [LongCopy],
Process USING [EnableAborts],
PupBuffer USING [Buffer],
Pup USING [Address, nullAddress],
PupHop USING [GetHop, Hop],
PupName USING [NameLookup, Error],
PupSocket USING [AllocBuffer, CreateEphemeral, Destroy, FreeBuffer, Get, GetLocalAddress, GetUserBytes, Put, SetUserHWords, Socket],
PupType USING [teledebugAck, teledebugGoReply, teledebugGoRequest, Type],
PupWKS USING [teleSwat],
Rope USING [ROPE],
TeledebugProtocol,
WorldVM USING [],
WVMPrivate;
WVMRemote:
MONITOR
IMPORTS DebuggerFormat, Endian, PrincOpsUtils, Process, PupHop, PupName, PupSocket
EXPORTS WorldVM, WVMPrivate = {
Machine: PUBLIC TYPE = Pup.Address;
Public procedures
ReadRemoteCore:
PUBLIC
PROC[host: Machine, data:
REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber]
RETURNS[map: WVMPrivate.MapEntry, ok:
BOOL] =
{
in: LONG POINTER TO TeledebugProtocol.CoreFetchAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.CoreFetchRequest;
Lock[host];
DO
ENABLE
UNWIND => Unlock[];
b: PupBuffer.Buffer ← PupSocket.AllocBuffer[socket];
out ← LOOPHOLE[@b.body];
out^ ← [page: mempage];
b ← SendAndReceive[b, pageFetchRequest, TeledebugProtocol.coreFetchRequestSize];
in ← LOOPHOLE[@b.body];
IF PupWords[b] # TeledebugProtocol.coreFetchAcknowledgementSize
OR WVMPrivate.PageNumber[in.page] # mempage
THEN { PupSocket.FreeBuffer[b]; LOOP };
data^ ← LOOPHOLE[in.data, WVMPrivate.PageData];
map.state.flags ← LOOPHOLE[in.flags, PrincOps.PageState].flags;
PupSocket.FreeBuffer[b];
ok ← LOOPHOLE[in.flags, PrincOps.PageState].flags#PrincOps.flagsVacant;
EXIT
ENDLOOP;
Unlock[];
};
WriteRemoteCore:
PUBLIC
PROC[host: Machine, data:
REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber, map: WVMPrivate.MapEntry]
RETURNS[readOnly:
BOOL] = {
in: LONG POINTER TO TeledebugProtocol.CoreStoreAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.CoreStoreRequest;
readOnly ← map.state.flags.readonly;
IF NOT readOnly THEN map.state.flags.dirty ← TRUE;
Lock[host];
DO
ENABLE
UNWIND => Unlock[];
b: PupBuffer.Buffer ← PupSocket.AllocBuffer[socket];
out ← LOOPHOLE[@b.body];
out.page ← mempage;
out.flags ← LOOPHOLE[map.state.flags];
LOOPHOLE[@out.data, LONG POINTER TO WVMPrivate.PageData]^ ← data^;
b ← SendAndReceive[b, pageStoreRequest, TeledebugProtocol.coreStoreRequestSize];
in ← LOOPHOLE[@b.body];
IF PupWords[b] # TeledebugProtocol.coreStoreAcknowledgementSize
OR WVMPrivate.PageNumber[in.page] # mempage
THEN { PupSocket.FreeBuffer[b]; LOOP };
IF in.flags#LOOPHOLE[map.state.flags, WORD] THEN ERROR;
PupSocket.FreeBuffer[b];
EXIT;
ENDLOOP;
Unlock[];
};
ClientDiskTellsLies: ERROR = CODE;
ReadRemoteDisk:
PUBLIC
PROC[host: Machine, data:
REF WVMPrivate.PageData, addr: WVMPrivate.DiskAddress, label:
LONG
POINTER
TO DiskFace.Label] = {
in: LONG POINTER TO TeledebugProtocol.DiskFetchAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.DiskAddressSetRequest;
Lock[host];
DO
ENABLE
UNWIND => Unlock[];
b: PupBuffer.Buffer ← PupSocket.AllocBuffer[socket];
out ← LOOPHOLE[@b.body];
out^ ← [addr.diskPage + addr.offset, addr.deviceType, addr.deviceOrdinal];
Sending diskFetchRequest with diskAddressSetRequestSize causes
a combined addressSet and diskFetch. (I didn't design it!)
b ← SendAndReceive[b, diskFetchRequest, TeledebugProtocol.diskAddressSetRequestSize];
IF PupWords[b] # TeledebugProtocol.diskFetchAcknowledgementSize
THEN { PupSocket.FreeBuffer[b]; LOOP };
in ← LOOPHOLE[@b.body];
IF data # NIL THEN data^ ← LOOPHOLE[in.data, WVMPrivate.PageData];
label^ ← LOOPHOLE[in.label, DiskFace.Label];
PupSocket.FreeBuffer[b];
IF addr.labelCheck # DebuggerFormat.LabelChecksum[label^, addr.offset]
THEN ERROR ClientDiskTellsLies[];
EXIT
ENDLOOP;
Unlock[];
};
WriteRemoteDisk:
PUBLIC
PROC[host: Machine, data:
REF WVMPrivate.PageData, addr: WVMPrivate.DiskAddress, label:
LONG
POINTER
TO DiskFace.Label] = {
b: PupBuffer.Buffer;
in: LONG POINTER TO TeledebugProtocol.DiskStoreAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.DiskStoreRequest;
Lock[host];
DO
ENABLE
UNWIND => Unlock[];
SetDiskAddress[addr.deviceType, addr.deviceOrdinal, addr.diskPage + addr.offset];
b ← PupSocket.AllocBuffer[socket];
out ← LOOPHOLE[@b.body];
LOOPHOLE[@out.label, LONG POINTER TO DiskFace.Label]^ ← label^;
LOOPHOLE[@out.data, LONG POINTER TO WVMPrivate.PageData]^ ← data^;
b ← SendAndReceive[b, diskStoreRequest, TeledebugProtocol.diskStoreRequestSize];
IF PupWords[b] # TeledebugProtocol.diskStoreAcknowledgementSize
THEN { PupSocket.FreeBuffer[b]; LOOP };
in ← LOOPHOLE[@b.body];
IF in.label = TeledebugProtocol.noLabel THEN ERROR ClientDiskTellsLies[];
PupSocket.FreeBuffer[b];
EXIT;
ENDLOOP;
Unlock[];
};
GoRemote:
PUBLIC
PROC [host: Machine] = {
p: PupBuffer.Buffer ← PupSocket.AllocBuffer[socket];
Lock[host];
{
ENABLE UNWIND => Unlock[];
p ← SendAndReceive[p, PupType.teledebugGoRequest, 0];
p.id ← Endian.FFromInt[Endian.IntFromF[p.id]+1];
Send[p, PupType.teledebugGoReply, 0];
};
Unlock[];
};
SetDiskAddress:
PROC [device: DiskFace.Type, deviceOrdinal:
CARDINAL, page:
LONG
CARDINAL] = {
in: LONG POINTER TO TeledebugProtocol.DiskAddressSetAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.DiskAddressSetRequest;
DO
b: PupBuffer.Buffer ← PupSocket.AllocBuffer[socket];
out ← LOOPHOLE[@b.body];
out^ ← [page, device, deviceOrdinal];
b ← SendAndReceive[b, diskAddress, TeledebugProtocol.diskAddressSetRequestSize];
in ← LOOPHOLE[@b.body];
IF PupWords[b] # TeledebugProtocol.diskAddressSetAcknowledgementSize
OR in^ # [page, device, deviceOrdinal]
THEN { PupSocket.FreeBuffer[b]; LOOP };
PupSocket.FreeBuffer[b];
EXIT;
ENDLOOP;
};
Actual packet transmission/reception! --
lastPupID: INT ← 0;
GetNextPupID:
ENTRY
PROC
RETURNS [Endian.
FWORD] =
INLINE {
lastPupID ← lastPupID + 1;
RETURN[Endian.FFromInt[lastPupID]]; };
PupWords:
PROC [p: PupBuffer.Buffer]
RETURNS [
CARDINAL] =
INLINE {
w: CARDINAL = PupSocket.GetUserBytes[p];
IF w MOD 2 # 0 THEN RETURN[LAST[CARDINAL]];
RETURN[w/2];
};
SendAndReceive:
PROC [b: PupBuffer.Buffer, t: PupType.Type, words:
CARDINAL]
RETURNS [answer: PupBuffer.Buffer] = {
b.id ← GetNextPupID[];
DO
copy: PupBuffer.Buffer ← PupSocket.AllocBuffer[socket];
copy.id ← b.id;
PrincOpsUtils.LongCopy[from: @b.body, nwords: words, to: @copy.body];
Send[copy, t, words];
answer ← Receive[b.id ! ReceiveTimeout => LOOP];
EXIT
ENDLOOP;
PupSocket.FreeBuffer[b];
};
Send:
PROC [b: PupBuffer.Buffer, t: PupType.Type, words:
CARDINAL] = {
b.type ← t;
PupSocket.SetUserHWords[b, words];
PupSocket.Put[socket, b];
};
ReceiveTimeout: ERROR = CODE;
getMeAPup: CONDITION;
receivedPup: PupBuffer.Buffer;
wantPup: BOOL;
Receive:
ENTRY
PROC [thisID: Endian.
FWORD]
RETURNS [PupBuffer.Buffer] = {
ENABLE UNWIND => NULL;
DO
wantPup ← TRUE;
receivedPup ← NIL;
WAIT getMeAPup;
IF receivedPup=NIL THEN RETURN WITH ERROR ReceiveTimeout;
IF receivedPup.id = thisID THEN RETURN[receivedPup];
PupSocket.FreeBuffer[receivedPup];
ENDLOOP;
};
keepReceiving: BOOL;
ReceiveProcess:
PROC = {
x: PupBuffer.Buffer;
Pass:
ENTRY
PROC = {
IF wantPup THEN { wantPup ← FALSE; receivedPup ← x; NOTIFY getMeAPup }
ELSE PupSocket.FreeBuffer[x];
};
Notify: ENTRY PROC = {NOTIFY getMeAPup};
Like:
PROC
RETURNS [
BOOL] = {
first level of screening (don't care about others)
RETURN [
x.source = remote AND x.dest = PupSocket.GetLocalAddress[socket] AND
x.type = PupType.teledebugAck];
};
WHILE keepReceiving
DO
x ← PupSocket.Get[socket];
IF x = NIL THEN Notify[] -- timeout
ELSE IF Like[] THEN Pass[] ELSE PupSocket.FreeBuffer[x];
ENDLOOP;
};
There is only one socket, protected by Lock/Unlock during remote calls --
locked: BOOL ← FALSE;
unlocked: CONDITION;
socket: PupSocket.Socket;
remote: Pup.Address;
Lock:
PROC [host: Machine] = {
LockInner[];
IF remote = host THEN RETURN;
StopTeledebuggingInner[];
remote ← host;
StartTeledebuggingInner[];
};
LockInner:
ENTRY
PROC = {
ENABLE UNWIND => NULL;
WHILE locked DO WAIT unlocked; ENDLOOP;
locked ← TRUE;
};
Unlock:
ENTRY
PROC = { locked ←
FALSE;
NOTIFY unlocked; };
LookupFailed: PUBLIC ERROR = CODE;
LocateRemote:
PUBLIC
PROC [name: Rope.
ROPE]
RETURNS [host: Machine] = {
host ← PupName.NameLookup[name, PupWKS.teleSwat !
PupName.Error => ERROR LookupFailed[] ];
};
receiveProcess: PROCESS;
StartTeledebugging:
PROC = {
Process.EnableAborts[@getMeAPup];
remote ← Pup.nullAddress;
StartTeledebuggingInner[];
Unlock[];
};
StartTeledebuggingInner:
PROC = {
hop: PupHop.Hop ← PupHop.GetHop[remote.net];
socket ← PupSocket.CreateEphemeral[remote: remote, sendBuffers: 2, getTimeout: 2500 + 500*hop];
keepReceiving ← TRUE;
wantPup ← FALSE;
receiveProcess ← FORK ReceiveProcess[];
};
StopTeledebugging:
PROC = {
Lock[remote];
StopTeledebuggingInner[];
};
StopTeledebuggingInner:
PROC = {
keepReceiving ← FALSE;
JOIN receiveProcess;
PupSocket.Destroy[socket];
socket ← NIL;
};
StartTeledebugging[];
}.