-- File: CommDebug.mesa, Last Edit: HGM  March 20, 1981  5:15 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 [], -- Needed by Put.Number
  Menu USING [ItemObject, MCRType, Create, Instantiate],
  UserInput USING [GetDefaultWindow],
  Put USING [Char, CR, Line, LongNumber, Number, Octal, Text],
  String USING [EquivalentStrings],
  Storage USING [Node, Free, FreeNodeNil],

  Boss USING [bigBoy],
  BufferMgr USING [accessHandleChainHead, systemBufferQueue],
  StatsDefs USING [StatCounterIndex],
  PupRouterDefs USING [
    PupRouterSocket, PupRouterSocketObject, RoutingTableEntry,
    RoutingTableObject],
  PupDefs USING [defaultNumberOfNetworks],
  DriverDefs USING [GiantVector, NetworkObject],
  SpecialSystem USING [HostNumber, NetworkAddress],
  BufferDefs USING [
    Buffer, BufferAccessHandle, BufferAccessObject, BufferObject, OisBufferObject,
    PupAddress, Queue];

CommDebug: PROGRAM
  IMPORTS DebugUsefulDefs, Event, Menu, UserInput, Put, String, Storage
  SHARES Boss, BufferMgr, DriverDefs, BufferDefs =
  BEGIN OPEN BufferDefs;

  giantVector: POINTER TO DriverDefs.GiantVector ← NIL;
  freeQueue: Queue;
  -- our copies of the real thing
  routingTable: POINTER TO ARRAY [0..PupDefs.defaultNumberOfNetworks] OF
    PupRouterDefs.RoutingTableObject ← NIL;
  routingTableLength: CARDINAL;
  wordsPerBuffer: CARDINAL;

  GetThings: PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    IF boss = NIL THEN
      BEGIN
      Put.Line[NIL, "Scanning GFT...."L];
      LookForFrames[];
      IF boss = NIL THEN
	BEGIN Put.Line[NIL, "**** Can't find Boss!"L]; RETURN[FALSE]; END;
      IF bufferMgr = NIL THEN
	BEGIN Put.Line[NIL, "**** Can't find BufferMgr!"L]; RETURN[FALSE]; END;
      END;
    Put.CR[NIL];
    CopyRead[
      to: giantVector, from: @boss.bigBoy, nwords: SIZE[DriverDefs.GiantVector]];
    routingTableLength ← LENGTH[giantVector.pupRoutingTable];
    freeQueue ← ReadLong[@bufferMgr.systemBufferQueue];
    IF routingTableLength > LENGTH[routingTable↑] THEN
      BEGIN
      Put.Line[NIL, "routingTable is huge - truncating."L];
      routingTableLength ← LENGTH[routingTable↑];
      END;
    LongCopyRead[
      to: routingTable, from: BASE[giantVector.pupRoutingTable],
      nwords: routingTableLength*SIZE[PupRouterDefs.RoutingTableObject]];
    wordsPerBuffer ← giantVector.wordsPerBuffer;
    RETURN[TRUE];
    END;

  PupRoute: PROCEDURE =
    BEGIN
    IF ~GetThings[] THEN RETURN;
    Put.Text[NIL, "

Pup Network Routing Table"L];
    Put.Line[NIL, "
  ix  hp  tm  rt    network net hst"L];
    FOR i: CARDINAL IN [0..routingTableLength) DO
      rte: PupRouterDefs.RoutingTableEntry;
      network: DriverDefs.NetworkObject;
      rte ← @routingTable[i];
      IF rte.network # NIL THEN
	BEGIN
	O4[i];
	D4[rte.hop];
	D4[rte.time];
	O4[rte.route];
	Put.Text[NIL, "  "L];
	O9L[rte.network];
	LongCopyRead[
	  to: @network, from: rte.network,
	  nwords: SIZE[DriverDefs.NetworkObject]];
	O4[network.netNumber.b];
	O4[network.hostNumber];
	Put.Line[NIL, ""L];
	END;
      ENDLOOP;
    END;

  PupSockets: PROCEDURE =
    BEGIN
    so: PupRouterDefs.PupRouterSocketObject;
    IF ~GetThings[] THEN RETURN;
    Put.Line[NIL, "

Pup Socket Table
ix    loc  ready  IQ    local,    remote"L];
    LongCopyRead[
      to: @so.next, from: giantVector.firstPupSocket,
      nwords: SIZE[PupRouterDefs.PupRouterSocket]];
    FOR i: CARDINAL IN [0..25) UNTIL so.next = NIL DO
      OPEN so;
      D2[i];
      O9L[next];
      LongCopyRead[
	to: @so, from: next, nwords: SIZE[PupRouterDefs.PupRouterSocketObject]];
      O7[LOOPHOLE[@ready, POINTER]↑]; -- first word of CONDITION is process handle
      D4[input.length];
      Put.Text[NIL, "  "L];
      PrintPupAddress[local];
      Put.Char[NIL, ',];
      PrintPupAddress[remote];
      Put.Line[NIL, ""L];
      ENDLOOP;
    END;

  EnumerateBuffers: PROCEDURE [proc: PROCEDURE [Buffer, CARDINAL]] =
    BEGIN
    bah: BufferAccessHandle ← ReadLong[@bufferMgr.accessHandleChainHead];
    baho: BufferAccessObject;
    i: CARDINAL ← 0;
    UNTIL bah = NIL DO
      b: Buffer;
      LongCopyRead[to: @baho, from: bah, nwords: SIZE[BufferAccessObject]];
      b ← baho.firstBuffer;
      IF b # NIL THEN
	BEGIN
	FOR k: CARDINAL IN [0..MIN[baho.total, 100]) DO
	  proc[b, i];
	  i ← i + 1;
	  IF i > 100 THEN RETURN;
	  b ← b + baho.wordsPerBuffer;
	  ENDLOOP;
	END;
      bah ← baho.next;
      ENDLOOP;
    END;

  Buffers: PROCEDURE =
    BEGIN
    IF ~GetThings[] THEN RETURN;
    Put.Line[NIL, "

Buffers
   n      loc    queue  reque  owner     next f type  dev"L];
    EnumerateBuffers[ShowBuffer];
    END;

  ShowBuffer: PROCEDURE [b: Buffer, n: CARDINAL] =
    BEGIN
    bo: pup BufferObject;
    D4[n];
    LongCopyRead[to: @bo, from: b, nwords: SIZE[pup BufferObject]];
    O9L[b];
    Q[bo.queue];
    O7[bo.requeueProcedure];
    O7[bo.debug];
    C[bo.next];
    SELECT bo.bufFunc FROM
      send => Put.Text[NIL, " s"L];
      receive => Put.Text[NIL, " r"L];
      systemUse => Put.Text[NIL, "  "L];
      ENDCASE => Put.Text[NIL, " ?"L];
    SELECT bo.type FROM
      raw => Put.Text[NIL, "  raw"L];
      pup => Put.Text[NIL, "  pup"L];
      ois => Put.Text[NIL, "  ois"L];
      ENDCASE => Put.Text[NIL, "  ???"L];
    SELECT bo.device FROM
      ethernet => Put.Text[NIL, "   EN"L];
      sla => Put.Text[NIL, "  sla"L];
      arpanet => Put.Text[NIL, "   AN"L];
      packetradio => Put.Text[NIL, "   PR"L];
      unknown => Put.Text[NIL, "  unk"L];
      ENDCASE => Put.Text[NIL, "  ???"L];
    Put.Line[NIL, ""L];
    END;

  PacketsAsPups: PROCEDURE =
    BEGIN
    IF ~GetThings[] THEN RETURN;
    Put.Line[NIL, "

Packets as Pups
   n len  PT       pupID    dest ← source"L];
    EnumerateBuffers[ShowPacketsAsPups];
    END;

  ShowPacketsAsPups: PROCEDURE [b: Buffer, n: CARDINAL] =
    BEGIN
    bo: pup BufferObject;
    D4[n];
    LongCopyRead[to: @bo, from: b, nwords: SIZE[pup BufferObject]];
    D4[bo.pupLength];
    SELECT bo.pupType FROM
      error => Put.Text[NIL, " ERR"L];
      rfc => Put.Text[NIL, " RFC"L];
      abort => Put.Text[NIL, "  AB"L];
      end => Put.Text[NIL, " END"L];
      endRep => Put.Text[NIL, " ERP"L];
      data => Put.Text[NIL, "   D"L];
      aData => Put.Text[NIL, "  aD"L];
      ack => Put.Text[NIL, " ACK"L];
      mark => Put.Text[NIL, "  MK"L];
      aMark => Put.Text[NIL, "  aM"L];
      gatewayRequest => Put.Text[NIL, "  GR"L];
      gatewayInfo => Put.Text[NIL, "  GI"L];
      request => Put.Text[NIL, " req"L];
      result => Put.Text[NIL, " res"L];
      unsolicited => Put.Text[NIL, " uns"L];
      custodian => Put.Text[NIL, " cus"L];
      sync => Put.Text[NIL, " syn"L];
      pineAck => Put.Text[NIL, "  pA"L];
      noop => Put.Text[NIL, " nop"L];
      ENDCASE => O4[bo.pupType];
    Put.Text[NIL, "   "L];
    O7[bo.pupID.a];
    O7[bo.pupID.b];
    Put.Text[NIL, "   "L];
    PrintPupAddress[bo.dest];
    Put.Text[NIL, " ←"L];
    PrintPupAddress[bo.source];
    Put.Line[NIL, ""L];
    END;

  PacketsAsOiscp: PROCEDURE =
    BEGIN
    IF ~GetThings[] THEN RETURN;
    Put.Line[NIL, "

Packets as Oiscp
 len  PT       pupID    dest ← source"L];
    EnumerateBuffers[ShowPacketsAsOiscp];
    END;

  ShowPacketsAsOiscp: PROCEDURE [b: Buffer, n: CARDINAL] =
    BEGIN
    bo: OisBufferObject;
    D4[n];
    LongCopyRead[to: @bo, from: b, nwords: SIZE[ois BufferObject]];
    D4[bo.ois.pktLength];
    SELECT bo.ois.transCntlAndPktTp.packetType FROM
      routingInformation => Put.Text[NIL, " RTI"L];
      echo => Put.Text[NIL, " Eco"L];
      error => Put.Text[NIL, " Err"L];
      packetExchange => Put.Text[NIL, "  PE"L];
      sequencedPacket => Put.Text[NIL, " SPP"L];
      ENDCASE => O4[bo.ois.transCntlAndPktTp.packetType];
    Put.Text[NIL, "   "L];
    PrintOiscpAddress[bo.ois.destination];
    Put.Text[NIL, " ←"L];
    PrintOiscpAddress[bo.ois.source];
    Put.Line[NIL, ""L];
    END;

  BufferChain: PROCEDURE =
    BEGIN
    bah: BufferAccessHandle;
    IF ~GetThings[] THEN RETURN;
    Put.Line[NIL, "
Buffer Control Blocks
 ru su  r  s  t  v    first     next"L];
    bah ← ReadLong[@bufferMgr.accessHandleChainHead];
    FOR i: CARDINAL IN [0..100) UNTIL bah = NIL DO
      baho: BufferAccessObject;
      LongCopyRead[to: @baho, from: bah, nwords: SIZE[BufferAccessObject]];
      D3[baho.receiveInUse];
      D3[baho.sendInUse];
      D3[baho.receive];
      D3[baho.send];
      D3[baho.total];
      D3[baho.reserve];
      O9L[baho.firstBuffer];
      O9L[baho.next];
      IF baho.madeForSystem THEN Put.Text[NIL, ", System"L];
      IF ~baho.active THEN Put.Text[NIL, ", Not Active"L];
      Put.Line[NIL, ""L];
      bah ← baho.next;
      ENDLOOP;
    END;

  All: PROCEDURE =
    BEGIN
    IF ~GetThings[] THEN RETURN;
    BufferChain[];
    PupRoute[];
    PupSockets[];
    Buffers[];
    PacketsAsPups[];
    PacketsAsOiscp[];
    END;

  -- Interface to the debugger

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

  ReadLong: PROCEDURE [from: POINTER] RETURNS [x: LONG UNSPECIFIED] =
    BEGIN CopyRead[to: @x, from: from, nwords: 2]; 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;

  -- Print out Statistics

  Counters: TYPE = ARRAY StatsDefs.StatCounterIndex OF LONG INTEGER;
  Strings: TYPE = ARRAY StatsDefs.StatCounterIndex OF STRING;

  Stats: PROCEDURE =
    BEGIN
    hisCounters: Counters;
    hisStrings: Strings;
    s: StatsDefs.StatCounterIndex;
    temp: STRING = [204]; -- maxlength gets clobbered
    IF ~GetThings[] THEN RETURN;
    LongCopyRead[
      to: @hisCounters, from: giantVector.statCounters, nwords: SIZE[Counters]];
    LongCopyRead[
      to: @hisStrings, from: giantVector.statStrings, nwords: SIZE[Strings]];
    FOR s IN StatsDefs.StatCounterIndex DO
      IF hisCounters[s] = 0 THEN LOOP;
      LD10[hisCounters[s]];
      Put.Char[NIL, ' ];
      IF hisStrings[s] # NIL THEN
	BEGIN
	CopyRead[to: temp, from: hisStrings[s], nwords: 100];
	Put.Line[NIL, temp];
	END
      ELSE Put.CR[NIL];
      ENDLOOP;
    END;

  -- get a block of info from the "right" address space

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

  PrintOiscpAddress: PROCEDURE [a: SpecialSystem.NetworkAddress] =
    BEGIN
    Put.Char[NIL, ' ];
    IF a.net.a # 0 THEN BEGIN Put.Octal[NIL, a.net.a]; Put.Char[NIL, '|]; END;
    Put.Octal[NIL, a.net.b];
    Put.Char[NIL, '#];
    PrintHostNumber[a.host];
    Put.Char[NIL, '#];
    Put.Octal[NIL, a.socket];
    END;

  PrintHostNumber: PROCEDURE [host: SpecialSystem.HostNumber] =
    BEGIN
    WITH h: host SELECT FROM
      physical =>
	BEGIN
	IF h.a # 0 THEN BEGIN Put.Octal[NIL, h.a]; Put.Char[NIL, '|]; END;
	IF h.a # 0 OR h.b # 0 THEN
	  BEGIN Put.Octal[NIL, h.b]; Put.Char[NIL, '|]; END;
	Put.Octal[NIL, h.c];
	END;
      multicast =>
	BEGIN
	Put.Char[NIL, 'M];
	IF h.a # 0 THEN BEGIN Put.Octal[NIL, h.a]; Put.Char[NIL, '|]; END;
	IF h.a # 0 OR h.b # 0 THEN
	  BEGIN Put.Octal[NIL, h.b]; Put.Char[NIL, '|]; END;
	Put.Octal[NIL, h.c];
	END;
      ENDCASE => ERROR;
    END;

  C: PROCEDURE [b: Buffer] =
    BEGIN
    IF b = NIL THEN Put.Text[NIL, "      NIL"L] ELSE O9L[b];
    END;

  -- Printout routines

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

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

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

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

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

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

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

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

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

  O9L: PROCEDURE [num: LONG UNSPECIFIED] =
    BEGIN Put.LongNumber[NIL, num, [8, FALSE, TRUE, 9]]; END;

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

  Q: PROCEDURE [q: Queue] =
    BEGIN
    SELECT q FROM
      NIL => Put.Text[NIL, "     none"L];
      freeQueue => Put.Text[NIL, "     free"L];
      ENDCASE => O9L[q];
    END;

  SetUp: PROCEDURE =
    BEGIN
    giantVector ← Storage.Node[SIZE[DriverDefs.GiantVector]];
    routingTable ← Storage.Node[
      (PupDefs.defaultNumberOfNetworks + 1)*SIZE[
	PupRouterDefs.RoutingTableObject]];
    END;

  SetDown: PROCEDURE =
    BEGIN
    IF giantVector # NIL THEN giantVector ← Storage.FreeNodeNil[giantVector];
    IF routingTable # NIL THEN routingTable ← Storage.FreeNodeNil[routingTable];
    END;

  items: ARRAY [0..8) OF Menu.ItemObject ←
    [["All", DoInfo], ["BufferChain", DoInfo], ["PupRoute", DoInfo],
      ["PupSockets", DoInfo], ["Buffers", DoInfo], ["Pups", DoInfo],
      ["Oiscp", DoInfo], ["Statistics", DoInfo]];

  all: CARDINAL = 0;
  bufferChain: CARDINAL = 1;
  pupRoute: CARDINAL = 2;
  pupSockets: CARDINAL = 3;
  buffers: CARDINAL = 4;
  pups: CARDINAL = 5;
  oiscp: CARDINAL = 6;
  stats: CARDINAL = 7;

  DoInfo: Menu.MCRType =
    BEGIN
    SetUp[];
    SELECT index FROM
      all => All[];
      bufferChain => BufferChain[];
      pupRoute => PupRoute[];
      pupSockets => PupSockets[];
      pups => PacketsAsPups[];
      oiscp => PacketsAsOiscp[];
      buffers => Buffers[];
      stats => Stats[];
      ENDCASE => ERROR;
    SetDown[];
    END;

  boss: POINTER TO FRAME[Boss] ← NIL;
  bufferMgr: POINTER TO FRAME[BufferMgr] ← NIL;
  nMods: CARDINAL = 2;
  LookForFrames: PROCEDURE =
    BEGIN
    moduleName: ARRAY [0..nMods) OF STRING ← ["Boss"L, "BufferMgr"L];
    basePtr: ARRAY [0..nMods) OF POINTER ← [@boss, @bufferMgr];
    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; END; ENDCASE; END;

  -- initialization

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