-- File: GateDebug.mesa, Last Edit: HGM  December 20, 1980  3:11 PM

-- This module lives and runs in the debugger.   It looks in the users core image to find and print things.

DIRECTORY
  DebugUsefulDefs USING [
    Enumerate, Name, ShortREAD, ShortCopyREAD, LongREAD, LongCopyREAD],
  Event USING [AddNotifier, Item, Masks, Notifier],
  Format USING [],
  Menu USING [ItemObject, MCRType, Create, Instantiate],
  UserInput USING [GetDefaultWindow],
  Put USING [Char, CR, Date, Line, LongNumber, Number, Octal, Text],
  Storage USING [Node, FreeNodeNil],
  String USING [EquivalentStrings],
  Window USING [Handle],

  Boss USING [bigBoy],
  File USING [nullCapability],
  StatsDefs USING [StatCounterIndex],
  Lock USING [Lock, LockObject],
  BootServerDefs USING [BootFile, BootFileObject, timeNotKnown],
  NameServerDefs USING [CacheEntry, CacheEntryObject],
  DriverDefs USING [GiantVector],
  GateDefs USING [GateDebugRecord],
  GateInit USING [gateDebug],
  AltoSlaDefs,
  BufferDefs USING [PupAddress];

GateDebug: PROGRAM
  IMPORTS DebugUsefulDefs, Event, Menu, UserInput, Put, Storage, String
  SHARES Boss, GateInit, GateDefs, BufferDefs =
  BEGIN OPEN BufferDefs;

  wh: Window.Handle = NIL;
  giantVector: POINTER TO DriverDefs.GiantVector ← NIL;
  gateVector: POINTER TO GateDefs.GateDebugRecord ← NIL;

  GetThings: PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    IF boss = NIL OR gate = NIL THEN
      BEGIN
      Put.Line[NIL, "Scanning GFT...."L];
      LookForFrames[];
      IF boss = NIL OR gate = NIL THEN
	BEGIN
	IF boss = NIL THEN Put.Line[NIL, "**** Can't find Boss!"L];
	IF gate = NIL THEN Put.Line[NIL, "**** Can't find GateInit!"L];
	RETURN[FALSE];
	END;
      END;
    CopyRead[
      to: giantVector, from: @boss.bigBoy, nwords: SIZE[DriverDefs.GiantVector]];
    CopyRead[
      to: gateVector, from: @gate.gateDebug,
      nwords: SIZE[GateDefs.GateDebugRecord]];
    RETURN[TRUE];
    END;

  All: PROCEDURE =
    BEGIN
    IF ~GetThings[] THEN RETURN;
    Put.CR[wh];
    ShowString[gateVector.version];
    Boot[];
    Cache[];
    Locks[];
    END;

  Boot: PROCEDURE =
    BEGIN
    bf: BootServerDefs.BootFile;
    bfo: BootServerDefs.BootFileObject;
    IF ~GetThings[] THEN RETURN;
    bf ← Read[gateVector.boot];
    Put.CR[wh];
    Put.Line[wh, "Boot File Table:"L];
    Put.Line[wh, "  Code Count AvgMs      Create Time     FileName"L];
    THROUGH [0..50) UNTIL bf = NIL DO
      CopyRead[to: @bfo, from: bf, nwords: SIZE[BootServerDefs.BootFileObject]];
      O6[bfo.code];
      LD6[bfo.count];
      IF bfo.count = 0 THEN Put.Text[wh, "      "L] ELSE LD6[bfo.ms/bfo.count];
      Put.Text[wh, "  "L];
      SELECT TRUE FROM
	(bfo.file = File.nullCapability) => Put.Text[wh, "Not on this disk  "L];
	(bfo.create = BootServerDefs.timeNotKnown) =>
	  Put.Text[wh, "Unknown           "L];
	ENDCASE => Put.Date[wh, bf.create, dateTime];
      Put.Text[wh, "  "L];
      ShowString[bfo.fileName];
      Put.CR[wh];
      bf ← bfo.next;
      ENDLOOP;
    END;

  Cache: PROCEDURE =
    BEGIN
    limit: CARDINAL = 10;
    ce: NameServerDefs.CacheEntry;
    ceo: NameServerDefs.CacheEntryObject;
    i: CARDINAL;
    names: ARRAY [0..limit) OF STRING;
    addrs: ARRAY [0..limit) OF PupAddress;
    IF ~GetThings[] THEN RETURN;
    Put.CR[wh];
    Put.Line[wh, "Cache for Name Lookup Server:"L];
    Put.Line[wh, " Count  Seq  size Name(s) <=> Address(es)"L];
    ce ← Read[gateVector.cache];
    THROUGH [0..50) UNTIL ce = NIL DO
      CopyRead[to: @ceo, from: ce, nwords: SIZE[NameServerDefs.CacheEntryObject]];
      CopyRead[to: @names, from: BASE[ceo.names], nwords: limit*1];
      CopyRead[to: @addrs, from: BASE[ceo.addrs], nwords: limit*SIZE[PupAddress]];
      LD6[ceo.count];
      D5[ceo.sequence];
      D5[ceo.size];
      Put.Text[wh, "  "L];
      IF LENGTH[ceo.names] = 0 THEN Put.Char[wh, '?]
      ELSE
	FOR i IN [0..MIN[limit, LENGTH[ceo.names]]) DO
	  IF i # 0 THEN Put.Text[wh, ", "L]; ShowString[names[i]]; ENDLOOP;
      Put.Text[wh, " <=> "L];
      IF LENGTH[ceo.addrs] = 0 THEN Put.Char[wh, '?]
      ELSE
	FOR i IN [0..MIN[limit, LENGTH[ceo.addrs]]) DO
	  IF i # 0 THEN Put.Text[wh, ", "L]; A[addrs[i]]; ENDLOOP;
      Put.CR[wh];
      ce ← ceo.next;
      ENDLOOP;
    END;

  Locks: PROCEDURE =
    BEGIN
    limit: CARDINAL = 10;
    lock: Lock.Lock;
    lo: Lock.LockObject;
    IF ~GetThings[] THEN RETURN;
    lock ← Read[gateVector.locks];
    IF lock = NIL THEN BEGIN Put.Line[wh, "Nothing is locked."L]; RETURN; END;
    Put.Line[wh, "Lock table:"L];
    Put.Line[wh, " Read W  Name"L];
    THROUGH [0..30) UNTIL lock = NIL DO
      CopyRead[to: @lo, from: lock, nwords: SIZE[Lock.LockObject]];
      D5[lo.useCount];
      Put.Text[wh, IF lo.write THEN " W  "L ELSE "    "L];
      ShowString[lo.name];
      Put.CR[wh];
      lock ← lo.next;
      ENDLOOP;
    END;

  seconds: LONG CARDINAL;

  Sla: PROCEDURE =
    BEGIN OPEN AltoSlaDefs;
    Counters: TYPE = ARRAY StatsDefs.StatCounterIndex OF LONG CARDINAL;
    p: LONG POINTER;
    activeLines: CARDINAL;
    hisLineTable: LONG POINTER TO ARRAY Line OF LineTableEntry;
    hisLineInfo: LONG POINTER TO ARRAY Line OF LineInfoBlock;
    hisRoutingTable: LONG POINTER TO ARRAY SlaHost OF RoutingTableEntry;
    hisCounters: Counters;
    IF ~GetThings[] THEN RETURN;
    LongCopyRead[
      to: @hisCounters, from: giantVector.statCounters, nwords: SIZE[Counters]];
    seconds ← hisCounters[statSeconds];
    p ← giantVector.slaThings;
    IF p = NIL THEN BEGIN Put.Line[wh, "No SLA Driver."L]; RETURN; END;
    p ← p + maxByte; -- skip CRC Table
    activeLines ← LongRead[p];
    hisLineTable ← p + 8;
    p ← 8 + p + maxLine*SIZE[LineTableEntry];
    hisRoutingTable ← p;
    p ← p + maxSlaHost*SIZE[RoutingTableEntry];
    hisLineInfo ← p;
    IF activeLines > maxLine THEN ERROR;
    PrintSlaStats[activeLines, hisLineInfo, hisRoutingTable];
    Put.CR[wh];
    END;

  PrintSlaStats: PUBLIC PROCEDURE [
    lines: CARDINAL, hisInfo: LONG POINTER, hisRouting: LONG POINTER] =
    BEGIN OPEN AltoSlaDefs;
    line: Line;
    host: SlaHost;
    info: ARRAY Line OF LineInfoBlock;
    lib: POINTER TO LineInfoBlock;
    routing: ARRAY SlaHost OF RoutingTableEntry;
    rte: POINTER TO RoutingTableEntry;
    k: CARDINAL;
    temp: LONG CARDINAL;
    Put.CR[wh];
    LongCopyRead[to: @info, from: hisInfo, nwords: lines*SIZE[LineInfoBlock]];
    LongCopyRead[
      to: @routing, from: hisRouting, nwords: maxSlaHost*SIZE[RoutingTableEntry]];
    Put.Line[
      wh,
      "SLA Line Statistics:
             ---Packets----    ------Bytes-----   ------Errors-----
Line To      Sent  Received      Sent  Received   CRC  Sync Control   State"L];
    FOR line IN [0..lines) DO
      lib ← @info[line];
      O3[line];
      -- find out who this line is connected to
      FOR host IN SlaHost DO
	rte ← @routing[host];
	IF rte.line = line AND rte.hops = 1 THEN BEGIN O4[host]; EXIT; END;
	REPEAT FINISHED => Put.Text[wh, "   ?"L];
	ENDLOOP;
      LD10[lib.packetsSent];
      LD10[lib.packetsRecv];
      LD10[lib.bytesSent];
      LD10[lib.bytesRecv];
      D6[lib.crcErrors];
      D6[lib.syncErrors];
      D6[lib.controlErrors];
      Put.Text[wh, "     "L];
      SELECT lib.state FROM
	up => Put.Line[wh, "Up"L];
	down => Put.Line[wh, "Down"L];
	loopedBack => Put.Line[wh, "Looped back"L];
	missing => Put.Line[wh, "Missing"L];
	ENDCASE => Put.Line[wh, "  ??"L];
      ENDLOOP;
    Put.Line[
      wh,
      "
     Hi/Lo Q   ------Hi-Q------        -------Bits/Sec-------
Line          Packets     Bytes        Hi      Sent  Received"L];
    FOR line IN [0..lines) DO
      lib ← @info[line];
      O3[line];
      D4[lib.hiPriQueue.length];
      D4[lib.lowPriQueue.length];
      LD10[lib.hiPacketsSent];
      LD10[lib.hiBytesSent];
      temp ← lib.hiBytesSent + overheadPerPacket*lib.hiPacketsSent;
      LD10[temp*8/seconds];
      temp ← lib.bytesSent + overheadPerPacket*lib.packetsSent;
      LD10[temp*8/seconds];
      temp ← lib.bytesRecv + overheadPerPacket*lib.packetsRecv;
      LD10[temp*8/seconds];
      Put.CR[wh];
      ENDLOOP;
    Put.Line[
      wh,
      "
Routing Table:
Host Line Hops    Host Line Hops    Host Line Hops    Host Line Hops"L];
    k ← 0;
    FOR host IN SlaHost DO
      rte ← @routing[host];
      IF rte.hops = longHop THEN LOOP;
      IF k # 0 THEN Put.Text[wh, "    "L];
      O4[host];
      D5[rte.line];
      D5[rte.hops];
      IF (k ← k + 1) = 4 THEN BEGIN Put.CR[wh]; k ← 0; END;
      ENDLOOP;
    IF k # 0 THEN Put.CR[wh];
    END;

  -- Interface to the debugger

  Read: PROCEDURE [p: POINTER] RETURNS [UNSPECIFIED] =
    BEGIN RETURN[DebugUsefulDefs.ShortREAD[p]]; END;

  LongRead: PROCEDURE [LONG POINTER] RETURNS [UNSPECIFIED] =
    DebugUsefulDefs.LongREAD;

  CopyRead: PROCEDURE [to: POINTER, from: POINTER, nwords: CARDINAL] =
    BEGIN DebugUsefulDefs.ShortCopyREAD[from: from, nwords: nwords, to: to]; END;

  LongCopyRead: PROCEDURE [to: POINTER, from: LONG POINTER, nwords: CARDINAL] =
    BEGIN DebugUsefulDefs.LongCopyREAD[from: from, nwords: nwords, to: to]; END;

  LongReadLong: PROCEDURE [from: LONG POINTER] RETURNS [x: LONG UNSPECIFIED] =
    BEGIN LongCopyRead[to: @x, from: from, nwords: 2]; END;

  -- Printout routines

  A: PROCEDURE [a: PupAddress] =
    BEGIN
    Put.Char[wh, ' ];
    Put.Octal[wh, a.net];
    Put.Char[wh, '#];
    Put.Octal[wh, a.host];
    Put.Char[wh, '#];
    IF a.socket.a # 0 THEN BEGIN Put.Octal[wh, a.socket.a]; Put.Char[wh, '|]; END;
    Put.Octal[wh, a.socket.b];
    END;

  D2: PROCEDURE [num: UNSPECIFIED] =
    BEGIN Put.Number[wh, num, [10, FALSE, TRUE, 2]]; END;

  D4: PROCEDURE [num: UNSPECIFIED] =
    BEGIN Put.Number[wh, num, [10, FALSE, TRUE, 4]]; END;

  D3: PROCEDURE [num: UNSPECIFIED] =
    BEGIN Put.Number[wh, num, [10, FALSE, TRUE, 3]]; END;

  D5: PROCEDURE [num: UNSPECIFIED] =
    BEGIN Put.Number[wh, num, [10, FALSE, TRUE, 5]]; END;

  D6: PROCEDURE [num: UNSPECIFIED] =
    BEGIN Put.Number[wh, num, [10, FALSE, TRUE, 6]]; END;

  LP: PROCEDURE [num: LONG POINTER] =
    BEGIN
    short: RECORD [p: POINTER, other: WORD] = LOOPHOLE[num];
    Put.Char[wh, IF short.other = 0 THEN '  ELSE '?];
    Put.Number[wh, short.p, [8, FALSE, TRUE, 6]];
    END;

  LD6: PROCEDURE [num: LONG CARDINAL] =
    BEGIN Put.LongNumber[wh, num, [10, FALSE, TRUE, 6]]; END;

  LD10: PROCEDURE [num: LONG CARDINAL] =
    BEGIN Put.LongNumber[wh, num, [10, FALSE, TRUE, 10]]; END;

  O3: PROCEDURE [num: UNSPECIFIED] =
    BEGIN Put.Number[wh, num, [8, FALSE, TRUE, 3]]; END;

  O4: PROCEDURE [num: UNSPECIFIED] =
    BEGIN Put.Number[wh, num, [8, FALSE, TRUE, 4]]; END;

  O6: PROCEDURE [num: UNSPECIFIED] =
    BEGIN Put.Number[wh, num, [8, FALSE, TRUE, 6]]; END;

  O7: PROCEDURE [num: UNSPECIFIED] =
    BEGIN Put.Number[wh, num, [8, FALSE, TRUE, 7]]; END;

  ShowString: PROCEDURE [s: STRING] =
    BEGIN
    temp: STRING = [204]; -- maxlength gets clobbered
    IF s # NIL THEN
      BEGIN
      CopyRead[to: temp, from: s, nwords: 100];
      IF temp.length > 200 THEN temp.length ← 25;
      Put.Text[wh, temp];
      END
    ELSE Put.Text[wh, "NIL"L];
    END;

  SetUp: PROCEDURE =
    BEGIN
    giantVector ← Storage.Node[SIZE[DriverDefs.GiantVector]];
    gateVector ← Storage.Node[SIZE[GateDefs.GateDebugRecord]];
    END;

  SetDown: PROCEDURE =
    BEGIN
    giantVector ← Storage.FreeNodeNil[giantVector];
    gateVector ← Storage.FreeNodeNil[gateVector];
    END;

  items: ARRAY [0..4] OF Menu.ItemObject ←
    [["All", DoInfo], ["SLA", DoInfo], ["Boot", DoInfo], ["Cache", DoInfo],
      ["Locks", DoInfo]];

  all: CARDINAL = 0;
  sla: CARDINAL = 1;
  boot: CARDINAL = 2;
  cache: CARDINAL = 3;
  locks: CARDINAL = 4;

  DoInfo: Menu.MCRType =
    BEGIN
    SetUp[];
    SELECT index FROM
      all => All[];
      sla => Sla[];
      boot => Boot[];
      cache => Cache[];
      locks => Locks[];
      ENDCASE;
    SetDown[];
    END;

  boss: POINTER TO FRAME[Boss] ← NIL;
  gate: POINTER TO FRAME[GateInit] ← NIL;
  nMods: CARDINAL = 2;
  LookForFrames: PROCEDURE =
    BEGIN
    moduleName: ARRAY [0..nMods) OF STRING ← ["Boss"L, "GateInit"L];
    basePtr: ARRAY [0..nMods) OF POINTER ← [@boss, @gate];
    keyString: STRING = [40];
    nFound: CARDINAL ← 0;

    CheckOneFrame: PROCEDURE [han: UNSPECIFIED] RETURNS [BOOLEAN] =
      BEGIN
      name: POINTER TO ARRAY [0..nMods) OF STRING = @moduleName;
      base: POINTER TO ARRAY [0..nMods) OF POINTER = @basePtr;
      key: STRING = keyString;

      key.length ← 0;
      DebugUsefulDefs.Name[name: key, gf: han];
      FOR i: CARDINAL IN [0..nMods) DO
	IF String.EquivalentStrings[key, name[i]] THEN
	  BEGIN
	  IF base[i]↑ = NIL THEN BEGIN base[i]↑ ← han; nFound ← nFound + 1 END
	  ELSE BEGIN Put.Text[NIL, "Duplicate: "L]; Put.Line[NIL, key]; END;
	  EXIT
	  END;
	ENDLOOP;
      RETURN[nFound = nMods];
      END;

    FOR i: CARDINAL IN [0..nMods) DO basePtr[i]↑ ← NIL; ENDLOOP;
    [] ← DebugUsefulDefs.Enumerate[CheckOneFrame];
    IF nFound # nMods THEN
      BEGIN
      FOR i: CARDINAL IN [0..nMods) DO
	IF basePtr[i]↑ = NIL THEN
	  BEGIN Put.Text[NIL, "Missing: "L]; Put.Line[NIL, moduleName[i]]; END;
	ENDLOOP;
      END;
    END;

  notifierItem: Event.Item ←
    [eventMask: Event.Masks[newSession], eventProc: Notify];
  Notify: Event.Notifier =
    BEGIN
    SELECT why FROM newSession => BEGIN boss ← NIL; gate ← NIL; END; ENDCASE;
    END;

  -- initialization

  Event.AddNotifier[@notifierItem];
  Menu.Instantiate[
    Menu.Create[DESCRIPTOR[items], "Gate"], UserInput.GetDefaultWindow[]];
  END.