-- File: FrameFinderAlto.mesa, Last Edit: HGM  January 24, 1981  12:31 PM

DIRECTORY
  ControlDefs USING [ControlLink, FrameHandle, GlobalFrameHandle, NullLink],
  PrincOps USING [
    AllocationVector, AllocationVectorSize, AV, AVItem, FrameVec, LastAVSlot],
  ProcessOps USING [FirstProcess, LastProcess],
  PSBDefs USING [PSB],
  Resident USING [SegListHead],

  DebugUsefulDefs USING [Enumerate, Name, ShortREAD, ShortCopyREAD],
  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, Decimal, Line, Number, Octal, Text],
  String USING [EquivalentStrings],
  Window USING [Handle];

FrameFinderAlto: PROGRAM
  IMPORTS DebugUsefulDefs, Event, Menu, UserInput, Put, String =
  BEGIN

  wh: Window.Handle ← NIL;
  AvIndex: TYPE = [0..PrincOps.AllocationVectorSize);

  All: PROCEDURE = BEGIN Free[]; InUse[]; Extra[]; END;


  Free: PROCEDURE =
    BEGIN
    myAV: PrincOps.AllocationVector;
    total: CARDINAL ← 0;
    Put.CR[wh];
    Put.Line[wh, "AV table statistics:"L];
    Put.Line[wh, "fsi  size   cnt  words"L];
    DebugUsefulDefs.ShortCopyREAD[
      to: @myAV, from: PrincOps.AV, nwords: SIZE[PrincOps.AllocationVector]];
    FOR fsi: AvIndex IN AvIndex DO
      n: CARDINAL ← 0;
      item: PrincOps.AVItem ← myAV[fsi];
      Put.Number[wh, fsi, [8, FALSE, TRUE, 3]];
      IF fsi <= PrincOps.LastAVSlot THEN
	Put.Number[wh, PrincOps.FrameVec[fsi], [10, FALSE, TRUE, 6]]
      ELSE Put.Text[wh, "      "L];
      DO
	SELECT item.tag FROM
	  frame => n ← n + 1;
	  empty => EXIT;
	  indirect => EXIT;
	  enable => EXIT;
	  ENDCASE => ERROR;
	IF item.tag # frame THEN EXIT;
	IF n > 10000 THEN ERROR;
	item ← DebugUsefulDefs.ShortREAD[item.frame];
	ENDLOOP;
      Put.Number[wh, n, [10, FALSE, TRUE, 6]];
      IF n # 0 AND fsi <= PrincOps.LastAVSlot THEN
	BEGIN
	words: CARDINAL ← n*(PrincOps.FrameVec[fsi] + 1);
	total ← total + words;
	Put.Number[wh, words, [10, FALSE, TRUE, 6]];
	END
      ELSE Put.Text[wh, "      "L];
      SELECT item.tag FROM
	frame => ERROR;
	empty => NULL;
	indirect =>
	  BEGIN
	  Put.Text[wh, " indirect to "L];
	  Put.Number[wh, item.fsi, [8, FALSE, TRUE, 0]];
	  END;
	enable => Put.Text[wh, "enable"L];
	ENDCASE => ERROR;
      Put.CR[wh];
      ENDLOOP;
    Put.Text[wh, "There are a total of "L];
    Put.Decimal[wh, total];
    Put.Line[wh, " words in use by (small) free frames."L];
    END;

  InUse: PROCEDURE =
    BEGIN
    this, last: POINTER TO PSBDefs.PSB;
    that: PSBDefs.PSB;
    hits: ARRAY AvIndex OF CARDINAL ← ALL[0];
    total: CARDINAL ← 0;
    this ← DebugUsefulDefs.ShortREAD[ProcessOps.FirstProcess];
    last ← DebugUsefulDefs.ShortREAD[ProcessOps.LastProcess];
    Put.CR[wh];
    Put.Line[wh, "Scanning active processes..."L];
    DO
      DebugUsefulDefs.ShortCopyREAD[
	to: @that, from: this, nwords: SIZE[PSBDefs.PSB]];
      IF that.state = alive THEN
	BEGIN
	frame: ControlDefs.FrameHandle ← that.frame;
	count: CARDINAL ← 0;
	Put.Octal[wh, this];
	Put.Text[wh, ": "L];
	DO
	  index: AvIndex;
	  link: ControlDefs.ControlLink;
	  IF count # 0 THEN Put.Text[wh, ", "L];
	  Put.Octal[wh, frame];
	  link.frame ← frame;
	  IF link.tag # frame OR link = ControlDefs.NullLink THEN EXIT;
	  count ← count + 1;
	  index ← DebugUsefulDefs.ShortREAD[frame - 1];
	  Put.Char[wh, '(];
	  Put.Number[wh, index, [8, FALSE, TRUE, 0]];
	  Put.Char[wh, ')];
	  IF index >= PrincOps.AllocationVectorSize THEN ERROR;
	  hits[index] ← hits[index] + 1;
	  link ← DebugUsefulDefs.ShortREAD[@frame.returnlink];
	  frame ← link.frame;
	  ENDLOOP;
	Put.CR[wh];
	END;
      IF this = last THEN EXIT;
      this ← this + SIZE[PSBDefs.PSB];
      ENDLOOP;
    Put.CR[wh];
    Put.Line[wh, "Active frame statistics:"L];
    Put.Line[wh, "fsi  size   cnt  words"L];
    FOR fsi: AvIndex IN AvIndex DO
      slot: CARDINAL ← hits[fsi];
      Put.Number[wh, fsi, [8, FALSE, TRUE, 3]];
      IF fsi <= PrincOps.LastAVSlot THEN
	Put.Number[wh, PrincOps.FrameVec[fsi], [10, FALSE, TRUE, 6]]
      ELSE Put.Text[wh, "      "L];
      Put.Number[wh, slot, [10, FALSE, TRUE, 6]];
      IF slot # 0 AND fsi <= PrincOps.LastAVSlot THEN
	BEGIN
	words: CARDINAL ← slot*(PrincOps.FrameVec[fsi] + 1);
	total ← total + words;
	Put.Number[wh, words, [10, FALSE, TRUE, 6]];
	END
      ELSE Put.Text[wh, "      "L];
      Put.CR[wh];
      ENDLOOP;
    Put.Text[wh, "There are a total of "L];
    Put.Decimal[wh, total];
    Put.Line[wh, " words in use by frames in active processes."L];
    END;

  Extra: PROCEDURE =
    BEGIN
    -- Can't get the Compiler to find Resident.SegHeader;
    SegHeader: TYPE = RECORD [seg: POINTER, link: POINTER TO SegHeader];
    count: CARDINAL ← 0;
    overflow: POINTER TO SegHeader;
    IF resident = NIL THEN
      BEGIN
      Put.Line[wh, "Looking for Resident."L];
      LookForFrames[];
      IF resident = NIL THEN
	BEGIN Put.Line[NIL, "**** Can't find Resident!"L]; RETURN; END;
      END;
    overflow ← DebugUsefulDefs.ShortREAD[@resident.SegListHead];
    UNTIL overflow = NIL DO
      IF count # 0 THEN Put.Text[wh, ", "];
      Put.Number[wh, overflow, [8, FALSE, TRUE, 6]];
      count ← count + 1;
      IF count > 1000 THEN ERROR;
      overflow ← DebugUsefulDefs.ShortREAD[@overflow.link];
      ENDLOOP;
    IF count # 0 THEN Put.CR[wh];
    Put.Text[wh, "There are  "L];
    Put.Decimal[wh, count];
    Put.Line[wh, " pages used by overflow frames."L];
    END;


  items: ARRAY [0..3] OF Menu.ItemObject ←
    [["All", DoInfo], ["Free", DoInfo], ["InUse", DoInfo], ["Extra", DoInfo]];

  all: CARDINAL = 0;
  free: CARDINAL = 1;
  inUse: CARDINAL = 2;
  extra: CARDINAL = 3;

  DoInfo: Menu.MCRType =
    BEGIN
    SELECT index FROM
      all => All[];
      free => Free[];
      inUse => InUse[];
      extra => Extra[];
      ENDCASE => ERROR;
    END;

  nMods: CARDINAL = 1;
  resident: POINTER TO FRAME[Resident] ← NIL;
  LookForFrames: PROCEDURE =
    BEGIN
    moduleName: ARRAY [0..nMods) OF STRING ← ["Resident"L];
    basePtr: ARRAY [0..nMods) OF POINTER ← [@resident];
    keyString: STRING = [40];
    nFound: CARDINAL ← 0;

    CheckOneFrame: PROCEDURE [han: ControlDefs.GlobalFrameHandle]
      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 => resident ← NIL; ENDCASE; END;

  -- initialization

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