-- Copyright (C) 1983, 1985 by Xerox Corporation. All rights reserved.
-- LineUse.mesa, HGM, 12-Mar-85 23:32:50
DIRECTORY
Display USING [Bitmap, Invert, replaceFlags, White],
Format USING [], -- Needed by Put.Number
FormSW USING [
ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine, CommandItem,
StringItem, NumberItem],
FileSW USING [GetFile, IsIt, SetFile],
MFile USING [Acquire, Error, GetLength, Handle, Release],
MsgSW USING [Post],
MStream USING [GetLength, Handle, IsIt, Log, SetAccess, SetLength, SetLogReadLength],
Process USING [Detach, MsecToTicks, Pause, SetTimeout],
Put USING [Char, CR, Line, Number, Text],
Runtime USING [GetBcdTime],
Stream USING [GetByteProcedure, GetWordProcedure, GetProcedure, Handle],
String USING [AppendLongNumber, AppendNumber, AppendString, Equivalent],
System USING [GreenwichMeanTime, GetGreenwichMeanTime, AdjustGreenwichMeanTime],
Time USING [Append, AppendCurrent, Unpack],
Tool USING [
Create, MakeSWsProc, UnusedLogName, MakeMsgSW, MakeFormSW, MakeFileSW,
AddThisSW],
ToolWindow USING [CreateSubwindow, DisplayProcType, nullBox, TransitionProcType],
Window USING [Handle, Box],
Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
GateControlDefs USING [pupStatsAck, pupStatsNak, pupStatsSend],
SlaFormat,
Sla,
PupWireFormat USING [BcplToMesaLongNumber],
PupDefs USING [
AppendPupAddress, PupPackageMake, PupPackageDestroy, PupBuffer,
PupAddress, GetPupAddress,
PupNameTrouble, SetPupContentsWords, PupSocket, PupSocketDestroy,
PupSocketMake, MsToTocks],
PupTypes USING [fillInSocketID, statSoc],
Driver;
LineUse: MONITOR
IMPORTS
Display, FileSW, FormSW, MFile, MsgSW, MStream, Process, Put,
Runtime, String, System, Time, Tool, ToolWindow,
Buffer, PupDefs, PupWireFormat
SHARES Driver =
BEGIN OPEN PupDefs;
msg, form, boxes, log: Window.Handle;
running: BOOLEAN ← FALSE;
pleaseStop: BOOLEAN ← FALSE;
indicator: {left, right, off} ← off;
seconds: CARDINAL ← 60;
target: LONG STRING ← [30];
netNumber: CARDINAL ← 7;
where: PupAddress ← [[0], [0], PupTypes.statSoc];
activeHosts: Sla.SlaHost ← LAST[Sla.SlaHost];
activeLines: Sla.Line ← LAST[Sla.Line];
pool: Buffer.AccessHandle;
soc: PupSocket;
overheadPerPacket: CARDINAL = 4; -- Flag, CRC, CRC, flag
Start: FormSW.ProcType =
BEGIN
oldLogFileName: LONG STRING ← FileSW.GetFile[log].name;
newLogFileName: STRING = [100];
IF running THEN { MsgSW.Post[msg, "Somebody is already running..."L]; RETURN; };
String.AppendString[newLogFileName, target];
String.AppendString[newLogFileName, "-"L];
String.AppendNumber[newLogFileName, netNumber, 8];
String.AppendString[newLogFileName, ".data"L];
IF ~String.Equivalent[oldLogFileName, newLogFileName] THEN
BEGIN -- All this to get append mode. Yetch.
fh: MFile.Handle ← NIL;
bytes: LONG CARDINAL ← 0;
sh: MStream.Handle;
fh ← MFile.Acquire[newLogFileName, anchor, [] ! MFile.Error => CONTINUE];
IF fh # NIL THEN {
bytes ← MFile.GetLength[fh];
MFile.Release[fh]; };
sh ← MStream.Log[newLogFileName, []];
IF bytes # 0 THEN {
getByte: Stream.GetByteProcedure = sh.getByte;
getWord: Stream.GetWordProcedure = sh.getWord;
get: Stream.GetProcedure = sh.get;
MStream.SetAccess[sh, writeOnly];
MStream.SetLength[sh, bytes];
MStream.SetAccess[sh, log];
sh.getByte ← getByte;
sh.getWord ← getWord;
sh.get ← get; };
FileSW.SetFile[log, newLogFileName, sh];
IF fh = NIL THEN Put.Line[log, -- New File: Insert header for ChartPlot (and people)
"
* Packets Bytes Bits/Sec Errors
Date Time P-Sent P-Recv B-Sent B-Recv Sent Recv CRC Lost Dev
"L];
END;
Put.CR[log];
Put.Text[log, "* Watching net "L];
O[netNumber];
Put.Text[log, " on "L];
IF ~FindPath[] THEN RETURN;
running ← TRUE;
Process.Detach[FORK Watch[seconds]];
END;
Stop: FormSW.ProcType = BEGIN Off[]; END;
Off: PROCEDURE =
BEGIN
IF ~running THEN RETURN;
pleaseStop ← TRUE;
WHILE running DO Process.Pause[1]; ENDLOOP;
pleaseStop ← FALSE;
END;
FindPath: PROCEDURE RETURNS [BOOLEAN] =
BEGIN
Put.Text[log, target];
Put.Char[log, '=];
GetPupAddress[
@where, target !
PupNameTrouble =>
BEGIN MsgSW.Post[msg, e]; Put.Line[log, e]; GOTO Trouble; END];
PrintPupAddress[where];
Put.Line[log, "."L];
RETURN[TRUE];
EXITS Trouble => RETURN[FALSE];
END;
Watch: ENTRY PROCEDURE [seconds: CARDINAL] =
BEGIN
startTime, stopTime, targetTime: System.GreenwichMeanTime;
actual: LONG CARDINAL;
pause: CONDITION;
MakeConnection[];
SetupBoxes[];
Process.SetTimeout[@pause, Process.MsecToTicks[500]];
UNTIL pleaseStop DO -- Wait until even multiple of seconds
targetTime ← System.GetGreenwichMeanTime[];
IF (targetTime MOD seconds) = 0 THEN EXIT;
WAIT pause;
ENDLOOP;
startTime ← targetTime;
GetInfo[];
oldLineInfo ← newLineInfo;
Put.CR[log];
UNTIL pleaseStop DO
WHILE System.GetGreenwichMeanTime[] - targetTime >= seconds DO
IF targetTime > System.GetGreenwichMeanTime[] THEN EXIT; -- Clock set backwards ==> hang
-- Oops, it took us more than a cycle to process everything.
-- Maybe we were sitting in the debugger.
targetTime ← System.AdjustGreenwichMeanTime[targetTime, seconds];
ENDLOOP;
UNTIL pleaseStop OR (System.GetGreenwichMeanTime[] - targetTime) >= seconds DO
WAIT pause; ENDLOOP;
IF pleaseStop THEN EXIT;
GetInfo[];
stopTime ← System.GetGreenwichMeanTime[];
actual ← stopTime - startTime;
IF pleaseStop THEN EXIT;
PrintInfo[actual];
oldLineInfo ← newLineInfo;
startTime ← stopTime;
targetTime ← System.AdjustGreenwichMeanTime[targetTime, seconds];
ENDLOOP;
SetDownBoxes[];
KillConnection[];
running ← FALSE;
END;
StatsEntry: TYPE = RECORD [
packetsSent: LONG CARDINAL,
packetsRecv: LONG CARDINAL,
bytesSent: LONG CARDINAL,
bytesRecv: LONG CARDINAL,
syncErrors: CARDINAL,
badCrc: CARDINAL,
controlError: CARDINAL,
state: SlaFormat.LineState];
oldLineInfo, newLineInfo: ARRAY Sla.Line OF StatsEntry;
newRoutingTable: ARRAY Sla.SlaHost OF SlaFormat.RoutingTableEntry;
oldDest: ARRAY Sla.Line OF CARDINAL;
thisID: CARDINAL ← 0;
GetInfo: PROCEDURE =
BEGIN
b: PupBuffer;
thisID ← thisID + 1;
DO -- until we get the answer
b ← Buffer.GetBuffer[pup, pool, send];
b.pup.pupID ← [thisID, thisID];
b.pup.pupType ← GateControlDefs.pupStatsSend;
b.pup.pupWords[0] ← netNumber;
SetPupContentsWords[b, 1];
soc.put[b];
FlipBoxes[];
DO
b ← soc.get[];
IF b = NIL THEN EXIT;
IF b.pup.pupType = error OR b.pup.pupType = GateControlDefs.pupStatsNak
OR b.pup.pupID.b # thisID THEN BEGIN Buffer.ReturnBuffer[b]; LOOP; END;
IF b.pup.pupType # GateControlDefs.pupStatsAck THEN ERROR;
IF b.pup.pupWords[0] # SlaFormat.slaStatsReply THEN ERROR;
IF b.pup.pupWords[1] # SlaFormat.slaVersion THEN ERROR;
EXIT;
ENDLOOP;
IF b # NIL THEN EXIT;
IF pleaseStop THEN RETURN;
LOOP;
ENDLOOP;
FlipBoxes[];
CopyData[b];
Buffer.ReturnBuffer[b];
END;
CopyData: PROCEDURE [b: PupBuffer] =
BEGIN
rte: LONG POINTER TO SlaFormat.RoutingTableEntry;
sse: LONG POINTER TO SlaFormat.SlaStatsEntry;
lib: LONG POINTER TO StatsEntry;
p: LONG POINTER TO CARDINAL;
lastHost: Sla.SlaHost ← b.pup.pupWords[2];
activeHosts ← MIN[lastHost, LAST[Sla.SlaHost]];
rte ← LOOPHOLE[@b.pup.pupWords[3]];
FOR host: Sla.SlaHost IN (0..activeHosts] DO
newRoutingTable[host] ← rte↑;
rte ← rte + SIZE[SlaFormat.RoutingTableEntry];
ENDLOOP;
rte ← LOOPHOLE[@b.pup.pupWords[3]]; -- Get in sync again in case we don't have room
FOR host: Sla.SlaHost IN (0..lastHost] DO
rte ← rte + SIZE[SlaFormat.RoutingTableEntry]; ENDLOOP;
p ← LOOPHOLE[rte];
activeLines ← MIN[(p↑ + 1), LAST[Sla.Line]];
sse ← LOOPHOLE[p + 1];
FOR line: Sla.Line IN [0..activeLines) DO
lib ← @newLineInfo[line];
lib.packetsSent ← PupWireFormat.BcplToMesaLongNumber[sse.packetsSent];
lib.packetsRecv ← PupWireFormat.BcplToMesaLongNumber[sse.packetsRecv];
lib.bytesSent ← PupWireFormat.BcplToMesaLongNumber[sse.bytesSent];
lib.bytesRecv ← PupWireFormat.BcplToMesaLongNumber[sse.bytesRecv];
lib.syncErrors ← sse.syncErrors;
lib.badCrc ← sse.badCrc;
lib.controlError ← sse.controlError;
lib.state ← sse.state;
sse ← sse + SIZE[SlaFormat.SlaStatsEntry];
ENDLOOP;
END;
PrintInfo: PROCEDURE [actual: LONG CARDINAL] =
BEGIN
FOR line: Sla.Line IN [0..activeLines) DO
lib: StatsEntry;
temp: LONG INTEGER;
PrintTime[];
Put.Text[log, " "];
lib.packetsSent ←
newLineInfo[line].packetsSent - oldLineInfo[line].packetsSent;
lib.packetsRecv ←
newLineInfo[line].packetsRecv - oldLineInfo[line].packetsRecv;
lib.bytesSent ← newLineInfo[line].bytesSent - oldLineInfo[line].bytesSent;
lib.bytesRecv ← newLineInfo[line].bytesRecv - oldLineInfo[line].bytesRecv;
lib.badCrc ← newLineInfo[line].badCrc - oldLineInfo[line].badCrc;
lib.syncErrors ←
newLineInfo[line].syncErrors - oldLineInfo[line].syncErrors;
lib.controlError ←
newLineInfo[line].controlError - oldLineInfo[line].controlError;
IF LOOPHOLE[lib.packetsSent, LONG INTEGER] < 0 THEN lib.packetsSent ← 0;
IF LOOPHOLE[lib.packetsRecv, LONG INTEGER] < 0 THEN lib.packetsRecv ← 0;
IF LOOPHOLE[lib.bytesSent, LONG INTEGER] < 0 THEN lib.bytesSent ← 0;
IF LOOPHOLE[lib.bytesRecv, LONG INTEGER] < 0 THEN lib.bytesRecv ← 0;
IF LOOPHOLE[lib.badCrc, INTEGER] < 0 THEN lib.badCrc ← 0;
IF LOOPHOLE[lib.syncErrors, INTEGER] < 0 THEN lib.syncErrors ← 0;
IF LOOPHOLE[lib.controlError, INTEGER] < 0 THEN lib.controlError ← 0;
LD7[lib.packetsSent];
LD7[lib.packetsRecv];
LD9[lib.bytesSent];
LD9[lib.bytesRecv];
temp ← lib.bytesSent + overheadPerPacket*lib.packetsSent;
LD7[temp*8/actual];
temp ← lib.bytesRecv + overheadPerPacket*lib.packetsRecv;
LD7[temp*8/actual];
IF TRUE OR (lib.badCrc # 0) OR (lib.syncErrors # 0) OR (lib.controlError # 0) THEN
BEGIN LD5[lib.badCrc]; LD5[lib.syncErrors]; LD5[lib.controlError]; END;
Put.CR[log];
ENDLOOP;
ForceOutInfo[log];
END;
CheckLineState: PROCEDURE =
BEGIN
changed: BOOLEAN;
FOR line: Sla.Line IN [0..activeLines) DO
changed ← FALSE;
-- find out who this line is connected to
FOR host: Sla.SlaHost IN Sla.SlaHost DO
rte: POINTER TO SlaFormat.RoutingTableEntry;
rte ← @newRoutingTable[host];
IF rte.line = line AND rte.hops = 1 THEN
BEGIN
IF oldDest[line] # host THEN changed ← TRUE;
oldDest[line] ← host;
EXIT;
END;
ENDLOOP;
IF oldLineInfo[line].state # newLineInfo[line].state THEN changed ← TRUE;
IF ~changed THEN LOOP;
Put.Text[log, "* "];
PrintTime[];
Put.Text[log, " Line "];
O[line];
Put.Text[log, " is "];
SELECT newLineInfo[line].state FROM
up => BEGIN Put.Text[log, "up to host "]; O[oldDest[line]]; END;
down => Put.Text[log, "down"L];
loopedBack => Put.Text[log, "looped back"L];
ENDCASE => Put.Text[log, "??"L];
Put.Char[log, '.];
Put.CR[log];
ENDLOOP;
END;
PrintTime: PROCEDURE =
BEGIN
text: STRING = [20];
Time.AppendCurrent[text];
Put.Text[log, text]
END;
PrintPupAddress: PROCEDURE [a: PupAddress] =
BEGIN
text: STRING = [20];
AppendPupAddress[text, a];
Put.Text[log, text];
END;
O: PROCEDURE [n: CARDINAL] =
BEGIN
Put.Number[log, n, [8, FALSE, TRUE, 0]];
END;
O2: PROCEDURE [n: CARDINAL] =
BEGIN
Put.Char[log, ' ];
Put.Number[log, n, [8, FALSE, TRUE, 1]];
END;
O3: PROCEDURE [n: CARDINAL] =
BEGIN
Put.Char[log, ' ];
Put.Number[log, n, [8, FALSE, TRUE, 2]];
END;
D: PROCEDURE [n: CARDINAL] =
BEGIN
Put.Number[log, n, [10, FALSE, TRUE, 0]];
END;
D2: PROCEDURE [n: CARDINAL] =
BEGIN
Put.Char[log, ' ];
Put.Number[log, n, [10, FALSE, TRUE, 1]];
END;
D3: PROCEDURE [n: CARDINAL] =
BEGIN
Put.Char[log, ' ];
Put.Number[log, n, [10, FALSE, TRUE, 2]];
END;
D4: PROCEDURE [n: CARDINAL] =
BEGIN
Put.Char[log, ' ];
Put.Number[log, n, [10, FALSE, TRUE, 3]];
END;
LD2: PROCEDURE [num: LONG INTEGER] =
BEGIN
s: STRING = [20];
Put.Char[log, ' ];
String.AppendLongNumber[s, num, 10];
THROUGH [s.length..1) DO Put.Char[log, ' ]; ENDLOOP;
Put.Text[log, s];
END;
LD3: PROCEDURE [num: LONG INTEGER] =
BEGIN
s: STRING = [20];
Put.Char[log, ' ];
String.AppendLongNumber[s, num, 10];
THROUGH [s.length..2) DO Put.Char[log, ' ]; ENDLOOP;
Put.Text[log, s];
END;
LD5: PROCEDURE [num: LONG INTEGER] =
BEGIN
s: STRING = [20];
Put.Char[log, ' ];
String.AppendLongNumber[s, num, 10];
THROUGH [s.length..4) DO Put.Char[log, ' ]; ENDLOOP;
Put.Text[log, s];
END;
LD7: PROCEDURE [num: LONG INTEGER] =
BEGIN
s: STRING = [20];
Put.Char[log, ' ];
String.AppendLongNumber[s, num, 10];
THROUGH [s.length..6) DO Put.Char[log, ' ]; ENDLOOP;
Put.Text[log, s];
END;
LD9: PROCEDURE [num: LONG INTEGER] =
BEGIN
s: STRING = [20];
String.AppendLongNumber[s, num, 10];
Put.Char[log, ' ];
THROUGH [s.length..8) DO Put.Char[log, ' ]; ENDLOOP;
Put.Text[log, s];
END;
MakeConnection: PROCEDURE =
BEGIN
pool ← Buffer.MakePool[send: 1, receive: 10];
soc ← PupSocketMake[PupTypes.fillInSocketID, where, MsToTocks[1000]];
END;
KillConnection: PROCEDURE =
BEGIN
PupSocketDestroy[soc];
Buffer.DestroyPool[pool];
END;
indicatorBox: Window.Box = [[25, 10], [16, 16]];
DisplayBoxes: ToolWindow.DisplayProcType =
BEGIN
pattern: ARRAY [0..1] OF ARRAY [0..8) OF WORD;
left: WORD = 177400B;
right: WORD = 000377B;
SELECT indicator FROM
left => pattern ← [ALL[left], ALL[right]];
right => pattern ← [ALL[right], ALL[left]];
off => pattern ← [ALL[0], ALL[0]];
ENDCASE;
Display.Bitmap[window, indicatorBox, [@pattern, 0, 0], 16, Display.replaceFlags]
END;
SetupBoxes: PROCEDURE = BEGIN indicator ← left; DisplayBoxes[boxes]; END;
FlipBoxes: PROCEDURE =
BEGIN
SELECT indicator FROM
left => indicator ← right;
off, right => indicator ← left;
ENDCASE;
Display.Invert[boxes, indicatorBox];
END;
SetDownBoxes: PROCEDURE =
BEGIN indicator ← off; Display.White[boxes, indicatorBox]; END;
MakeBoxesSW: PROCEDURE [window: Window.Handle] =
BEGIN
box: Window.Box ← ToolWindow.nullBox;
box.dims.h ← 36;
boxes ← ToolWindow.CreateSubwindow[parent: window, display: DisplayBoxes, box: box];
Tool.AddThisSW[window: window, sw: boxes, swType: vanilla];
END;
MakeSWs: Tool.MakeSWsProc =
BEGIN
logFileName: STRING = [40];
msg ← Tool.MakeMsgSW[window: window, lines: 5];
form ← Tool.MakeFormSW[window: window, formProc: MakeForm];
MakeBoxesSW[window];
Tool.UnusedLogName[logFileName, "LineUse.log$"L];
log ← Tool.MakeFileSW[window: window, name: logFileName, allowTypeIn: FALSE];
END;
MakeForm: FormSW.ClientItemsProcType =
BEGIN
nParams: CARDINAL = 5;
items ← FormSW.AllocateItemDescriptor[nParams];
items[0] ← FormSW.CommandItem[
tag: "Stop"L, proc: Stop, place: FormSW.newLine];
items[1] ← FormSW.CommandItem[tag: "Start"L, proc: Start];
items[2] ← FormSW.NumberItem[tag: "Seconds"L, value: @seconds, default: 60];
items[3] ← FormSW.NumberItem[
tag: "NetNumber"L, value: @netNumber, default: 7, radix: octal];
items[4] ← FormSW.StringItem[tag: "Target"L, string: @target];
RETURN[items, TRUE];
END;
ClientTransition: ToolWindow.TransitionProcType =
BEGIN
SELECT TRUE FROM
old = inactive =>
BEGIN
IF target.length = 0 THEN String.AppendString[target, "ME"L];
PupDefs.PupPackageMake[];
END;
new = inactive =>
BEGIN IF running THEN Off[]; PupDefs.PupPackageDestroy[]; END;
ENDCASE;
END;
ForceOutInfo: PROCEDURE [wh: Window.Handle] =
BEGIN
sh: Stream.Handle;
IF ~FileSW.IsIt[wh] THEN RETURN;
sh ← FileSW.GetFile[wh].s;
IF ~MStream.IsIt[sh] THEN RETURN;
MStream.SetLogReadLength[sh, MStream.GetLength[sh]];
END;
Init: PROCEDURE =
BEGIN
herald: STRING = [100];
String.AppendString[herald, "LineUse of "L];
Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
[] ← Tool.Create[
name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition];
END;
Init[];
END.