-- File: PerfCommands.mesa   last edited by
-- Sandman on September 19, 1980  9:25 AM
-- Karlton on Jun 25, 1980 2:59 AM
-- Karlton on September 19, 1980  2:02 PM

DIRECTORY
  BP USING [BBHandle, BytePC, FindBB, FindBBNum, FindModBB, FindUserBB],
  DebugUsefulDefs USING [ShortREAD],
  Format USING [NumberFormat],
  MachineDefs USING [
    BBHandle, BytePC, ByteToRealPC, RealPC, RealToBytePC, UBBPointer],
  PerfCommonOps USING [
    AverageTime, logSW, Number, WriteConvertedTicksToMs, WriteLongNumber,
    WritePercent, WriteTime],
  PerfOps USING [
    DeleteHist, GetConfigAndModuleName, GetHistBase, GetLegTable, GetNodeTable,
    GetPCR, NoContext, PostError, process, PutMessage, WriteLegTableHeader,
    WriteNodeTableHeader],
  PerfPrivate USING [
    AllNodes, HistBase, HistIndex, Histogram, Leg, LegIndex, LegTab, LongNumber,
    MaxLegs, MaxNodes, Node, NodeID, NodeIndex, NodeTab, NoLegSlot, NullHist,
    NullID, NullNode, Number, OverFlowNumber, PCR, SubLegIndex, SubNodeIndex],
  PerfStructures USING [IndexToHandle, NullPsbHandle, PsbIndex],
  Put USING [Char, CR, Decimal, Line, Number, Text],
  SDDefs USING [sBreakBlock, SD],
  String USING [InvalidNumber, StringToOctal],
  UserInput USING [userAbort],
  Window USING [Handle];

PerfCommands: PROGRAM
  IMPORTS
    BP, DebugUsefulDefs, MachineDefs, PerfOps, Put, UserInput, String,
    PerfCommonOps, PerfStructures
  EXPORTS PerfOps =PUBLIC

  BEGIN OPEN PerfCommonOps, PerfOps, PerfPrivate;

  handle: Window.Handle ← logSW;

  SetProcess: PROCEDURE =
    BEGIN OPEN PerfStructures;
    pCR: PCR ← GetPCR[write];
    index: PsbIndex;
    IF process = NIL OR process.length = 0 THEN {
      pCR.process ← NullPsbHandle; Put.Line[handle, "All processes now tracked"L]}
    ELSE
      BEGIN
      index ← String.StringToOctal[
	process ! String.InvalidNumber => GOTO badProcess];
      Put.Text[handle, "Track process: "L];
      Put.Line[handle, process];
      pCR.process ← IndexToHandle[index];
      EXITS badProcess => PostError[badProcess];
      END;
    END;

  ClearTables: PROCEDURE =
    BEGIN
    i: CARDINAL;
    pCR: PCR ← GetPCR[write];
    nodeTable: POINTER TO NodeTab ← GetNodeTable[write];
    legTable: POINTER TO LegTab ← GetLegTable[write];
    pCR.measuringNow ← FALSE;
    pCR.perfTime ← 0;
    pCR.totalTime ← 0;
    pCR.totalBreaks ← 0;
    pCR.process ← PerfStructures.NullPsbHandle;
    pCR.nextLeg ← 0;
    pCR.nextNode ← 0;
    pCR.histFree ← FIRST[HistIndex];
    pCR.lastID ← NullNode;
    FOR i IN SubNodeIndex DO
      nodeTable[i] ← Node[
	id: NullID, hitsLow: 0, hitsHigh: 0, overflowed: FALSE, hist: NullHist];
      ENDLOOP;
    FOR i IN SubLegIndex DO
      legTable[i] ←
	[start: 0, from: NullNode, to: NullNode, owner: NIL, hitsLow: 0,
	  hitsHigh: 0, sum: 0, lock: FALSE, overflowed: FALSE, someIgnored: FALSE,
	  hist: NullHist];
      ENDLOOP;
    Put.Line[handle, "Tables initialized"L];
    END;


  ZeroCounts: PROCEDURE =
    BEGIN
    pCR: PCR ← GetPCR[write];
    nodeTable: POINTER TO NodeTab ← GetNodeTable[write];
    legTable: POINTER TO LegTab ← GetLegTable[write];
    histBase: HistBase;
    node: POINTER TO Node;
    leg: POINTER TO Leg;
    i: CARDINAL;
    pCR.measuringNow ← FALSE;
    pCR.perfTime ← 0;
    pCR.totalTime ← 0;
    pCR.totalBreaks ← 0;
    pCR.lastID ← NullNode;
    FOR i IN SubNodeIndex DO
      node ← @nodeTable[i];
      node.hitsLow ← 0;
      node.hitsHigh ← 0;
      node.overflowed ← FALSE;
      IF node.hist # NullHist THEN
	BEGIN histBase ← GetHistBase[write]; ZeroHist[@histBase[node.hist]]; END;
      ENDLOOP;
    FOR i IN SubLegIndex DO
      leg ← @legTable[i];
      leg.owner ← NIL;
      leg.hitsLow ← 0;
      leg.hitsHigh ← 0;
      leg.someIgnored ← FALSE;
      leg.overflowed ← FALSE;
      leg.sum ← 0;
      IF leg.hist # NullHist THEN
	BEGIN histBase ← GetHistBase[write]; ZeroHist[@histBase[leg.hist]]; END;
      ENDLOOP;
    Put.Line[handle, "Tables zeroed"L];
    END;

  ZeroHist: PROCEDURE [hist: POINTER TO Histogram] =
    BEGIN
    i: CARDINAL;
    hist.count ← 0;
    hist.overflow ← 0;
    hist.underflow ← 0;
    hist.sum ← 0;
    FOR i IN [0..hist.nBuckets) DO hist.buckets[i] ← 0; ENDLOOP;
    RETURN
    END;


  DeleteLeg: PROCEDURE [index: LegIndex] =
    BEGIN
    pCR: PCR ← GetPCR[write];
    legTable: POINTER TO LegTab ← GetLegTable[write];
    leg: POINTER TO Leg = @legTable[index];
    IF index >= pCR.nextLeg THEN BEGIN PostError[badLeg]; RETURN END;
    IF leg.hist # NullHist THEN DeleteHist[leg.hist];
    leg↑ ←
      [start: 0, from: NullNode, to: NullNode, owner: NIL, hitsLow: 0,
	hitsHigh: 0, sum: 0, lock: FALSE, overflowed: FALSE, someIgnored: FALSE,
	hist: NullHist];
    IF index = pCR.nextLeg - 1 THEN pCR.nextLeg ← pCR.nextLeg - 1;
    Put.Text[handle, "Leg "L];
    Put.Decimal[handle, index];
    Put.Line[handle, " deleted"L];
    END;


  AddLeg: PROCEDURE [from, to: NodeIndex] =
    BEGIN
    pCR: PCR ← GetPCR[write];
    nodeTable: POINTER TO NodeTab ← GetNodeTable[write];
    legTable: POINTER TO LegTab ← GetLegTable[write];
    FindLegSlot: PROCEDURE RETURNS [i: LegIndex] =
      BEGIN
      FOR i IN [0..pCR.nextLeg) DO
	IF ~legTable[i].lock AND legTable[i].from = NullNode THEN RETURN[i];
	ENDLOOP;
      IF pCR.nextLeg < MaxLegs THEN
	BEGIN i ← pCR.nextLeg; pCR.nextLeg ← pCR.nextLeg + 1; RETURN; END;
      RETURN[NoLegSlot];
      END;
    NotInLegTab: PROCEDURE [start, end: CARDINAL] RETURNS [BOOLEAN] =
      BEGIN
      i: CARDINAL;
      FOR i IN SubLegIndex DO
	IF legTable[i].from = start AND legTable[i].to = end THEN RETURN[FALSE];
	ENDLOOP;
      RETURN[TRUE];
      END;
    CreateLeg: PROCEDURE [from, to: NodeIndex] =
      BEGIN
      leg: LegIndex;
      IF from IN [0..pCR.nextNode) AND to IN [0..pCR.nextNode) THEN
	IF NotInLegTab[from, to] THEN
	  BEGIN
	  IF (leg ← FindLegSlot[]) # NoLegSlot THEN
	    BEGIN
	    legTable[leg] ←
	      [start: 0, from: from, to: to, owner: NIL, hitsLow: 0, hitsHigh: 0,
		sum: 0, overflowed: FALSE, someIgnored: FALSE, lock: TRUE,
		hist: NullHist];
	    Put.Text[handle, "Leg from "L];
	    Put.Decimal[handle, NodeIDToBpNum[nodeTable[from].id]];
	    Put.Text[handle, " to "L];
	    Put.Decimal[handle, NodeIDToBpNum[nodeTable[to].id]];
	    Put.Line[handle, " added"L];
	    END
	  ELSE PostError[noLegRoom];
	  END
	ELSE NULL
      ELSE PostError[badNode];
      RETURN
      END;
    i, j: CARDINAL;
    IF from # AllNodes THEN
      IF to # AllNodes THEN CreateLeg[from, to]
      ELSE FOR i IN [0..pCR.nextNode) DO CreateLeg[from, i]; ENDLOOP
    ELSE
      IF to # AllNodes THEN
	FOR i IN [0..pCR.nextNode) DO CreateLeg[i, to]; ENDLOOP
      ELSE
	FOR i IN [0..pCR.nextNode) DO
	  FOR j IN [0..pCR.nextNode) DO CreateLeg[i, j]; ENDLOOP ENDLOOP;
    RETURN
    END;


  PrintTables: PROCEDURE =
    BEGIN
    PrintTotals[];
    IF UserInput.userAbort THEN
      BEGIN PutMessage[aborted]; Put.CR[handle]; RETURN END;
    PrintNodeTable[];
    PrintLegTable[];
    RETURN
    END;


  PrintTotals: PROCEDURE =
    BEGIN
    s: STRING ← [20];
    pCR: PCR ← GetPCR[read];
    Put.CR[handle];
    PutMessage[totalTime];
    WriteConvertedTicksToMs[pCR.totalTime, pCR.pulseConversion, 15];
    Put.CR[handle];
    PutMessage[elapsedTime];
    WriteConvertedTicksToMs[pCR.totalTime - pCR.perfTime, pCR.pulseConversion, 15];
    Put.CR[handle];
    PutMessage[totalOverhead];
    WriteConvertedTicksToMs[pCR.perfTime, pCR.pulseConversion, 15];
    Put.CR[handle];
    PutMessage[nBreaks];
    WriteLongNumber[pCR.totalBreaks, 15];
    Put.CR[handle];
    PutMessage[avgOverhead];
    WriteTime[AverageTime[pCR.perfTime, pCR.totalBreaks, pCR.pulseConversion], 15];
    Put.CR[handle];
    PutMessage[percentInMont];
    WritePercent[pCR.perfTime, pCR.totalTime, 15];
    Put.CR[handle];
    END;

  NumberFormat: TYPE = Format.NumberFormat;
  ID: NumberFormat = [base: 10, zerofill: FALSE, unsigned: TRUE, columns: 3];
  PC: NumberFormat = [base: 8, zerofill: FALSE, unsigned: TRUE, columns: 9];
  Frame: NumberFormat = [base: 8, zerofill: FALSE, unsigned: TRUE, columns: 7];

  PrintNodeTable: PROCEDURE =
    BEGIN OPEN String;
    pCR: PCR ← GetPCR[read];
    nodeTable: POINTER TO NodeTab ← GetNodeTable[read];
    node: POINTER TO Node;
    i: CARDINAL;
    config: STRING ← [40];
    module: STRING ← [40];
    IF UserInput.userAbort THEN
      BEGIN PutMessage[aborted]; Put.CR[handle]; RETURN END;
    Put.CR[handle];
    WriteNodeTableHeader[];
    node ← @nodeTable[0];
    FOR i IN SubNodeIndex DO
      IF i < pCR.nextNode OR (node.hitsLow # 0 AND node.hitsHigh # 0) THEN
	BEGIN
	IF UserInput.userAbort THEN
	  BEGIN PutMessage[aborted]; Put.CR[handle]; RETURN END;
	Put.Number[handle, NodeIDToBpNum[node.id], ID];
	Put.Char[handle, IF node.hist = NullHist THEN '  ELSE '*];
	Put.Number[handle, node.id.frame, Frame];
	Put.Number[handle, MachineDefs.RealToBytePC[node.id.pc], PC];
	WriteNodeHits[node, 11];
	Put.Char[handle, ' ];
	IF i < pCR.nextNode THEN
	  BEGIN
	  module.length ← config.length ← 0;
	  GetConfigAndModuleName[
	    node.id.frame, config, module ! NoContext => GOTO noContext];
	  PrintString[config, 8];
	  Put.Char[handle, ' ];
	  PrintString[module, 8];
	  Put.CR[handle];
	  END;
	EXITS noContext => PostError[noContext];
	END;
      node ← node + SIZE[Node];
      ENDLOOP;
    END;

  PrintString: PROCEDURE [s: STRING, length: CARDINAL] =
    BEGIN
    i, l: CARDINAL;
    l ← MIN[length, s.length];
    FOR i IN [0..l) DO Put.Char[handle, s[i]]; ENDLOOP;
    FOR i IN [l..length) DO Put.Char[handle, ' ]; ENDLOOP;
    RETURN
    END;

  WriteNodeHits: PROCEDURE [node: POINTER TO Node, columns: CARDINAL] =
    BEGIN
    num: Number;
    num ←
      IF node.overflowed THEN OverFlowNumber
      ELSE LOOPHOLE[LongNumber[
	num[lowbits: node.hitsLow, highbits: node.hitsHigh]]];
    WriteLongNumber[num, columns];
    Put.Char[handle, IF node.overflowed THEN '* ELSE ' ];
    RETURN
    END;


  PrintLegTable: PROCEDURE =
    BEGIN OPEN String;
    i: CARDINAL;
    s: STRING ← [100];
    pCR: PCR ← GetPCR[read];
    nodeTable: POINTER TO NodeTab ← GetNodeTable[read];
    legTable: POINTER TO LegTab ← GetLegTable[read];
    leg: POINTER TO Leg;
    Put.CR[handle];
    IF UserInput.userAbort THEN
      BEGIN PutMessage[aborted]; Put.CR[handle]; RETURN END;
    WriteLegTableHeader[];
    leg ← @legTable[0];
    FOR i IN SubLegIndex DO
      IF (i < pCR.nextLeg AND leg.from # NullNode) OR
	(leg.hitsLow # 0 AND leg.hitsHigh # 0 AND ~leg.overflowed) THEN
	BEGIN
	IF UserInput.userAbort THEN
	  BEGIN PutMessage[aborted]; Put.CR[handle]; RETURN END;
	Put.Number[handle, i, ID];
	Put.Char[handle, IF leg.hist = NullHist THEN '  ELSE '*];
	Put.Char[handle, ' ];
	Put.Number[handle, NodeIDToBpNum[nodeTable[leg.from].id], ID];
	Put.Text[handle, " ->"L];
	Put.Number[handle, NodeIDToBpNum[nodeTable[leg.to].id], ID];
	WriteLegHits[leg, 13];
	WriteConvertedTicksToMs[leg.sum, pCR.pulseConversion, 14];
	WriteTime[AverageLegTime[leg], 16];
	WritePercent[leg.sum, pCR.totalTime - pCR.perfTime, 8];
	Put.CR[handle];
	END;
      leg ← leg + SIZE[Leg];
      ENDLOOP;
    END;

  AverageLegTime: PROCEDURE [leg: POINTER TO Leg] RETURNS [Number] =
    BEGIN
    RETURN[
      AverageTime[
	time: leg.sum,
	count: LOOPHOLE[LongNumber[num[lowbits: leg.hitsLow, highbits: leg.hitsHigh]]],
	conversion: GetPCR[read].pulseConversion]];
    END;

  WriteLegHits: PROCEDURE [leg: POINTER TO Leg, columns: CARDINAL] =
    BEGIN
    num: Number;
    num ←
      IF leg.overflowed THEN OverFlowNumber
      ELSE LOOPHOLE[LongNumber[
	num[lowbits: leg.hitsLow, highbits: leg.hitsHigh]]];
    WriteLongNumber[num, columns];
    Put.Text[
      handle,
      SELECT TRUE FROM
	leg.overflowed AND leg.someIgnored => "*~"L,
	leg.someIgnored => "~ "L,
	leg.overflowed => "* "L,
	ENDCASE => "  "L];
    RETURN
    END;

  CollectNodes: PUBLIC PROCEDURE =
    BEGIN OPEN DebugUsefulDefs;
    pCR: PCR ← GetPCR[read];
    nodeTable: POINTER TO NodeTab ← GetNodeTable[read];
    i, index: CARDINAL;
    ubb: MachineDefs.UBBPointer;
    bba: MachineDefs.BBHandle = ShortREAD[@SDDefs.SD[SDDefs.sBreakBlock]];
    id: NodeID;
    FOR i IN [0..ShortREAD[@bba.length]) DO
      ubb ← @bba.blocks[i];
      id ← [frame: ShortREAD[@ubb.frame], pc: ShortREAD[@ubb.pc]];
      IF FindIndex[id, nodeTable, pCR] = MaxNodes THEN
	IF pCR.nextNode < MaxNodes THEN
	  BEGIN
	  [] ← GetPCR[write];
	  [] ← GetNodeTable[write];
	  index ← pCR.nextNode;
	  pCR.nextNode ← pCR.nextNode + 1;
	  nodeTable[index] ←
	    [id: id, hist: NullHist, overflowed: FALSE, hitsLow: 0, hitsHigh: 0];
	  END
	ELSE PutMessage[tooSmall];
      ENDLOOP;
    Put.Line[handle, "Collected nodes"L];
    END;

  FindIndex: PROCEDURE [id: NodeID, table: POINTER TO NodeTab, pCR: PCR]
    RETURNS [node: NodeIndex] =
    BEGIN
    IF id = NullID THEN RETURN[NullNode];
    FOR node IN [0..pCR.nextNode) DO IF table[node].id = id THEN RETURN; ENDLOOP;
    RETURN[NullNode]
    END;

  FillWithSpacesAnd0Length: PROCEDURE [s: STRING] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..s.maxlength) DO s[i] ← ' ; ENDLOOP;
    s.length ← 0;
    RETURN
    END;

  NodeIDToBpNum: PROCEDURE [id: NodeID] RETURNS [CARDINAL] =
    BEGIN
    pc: BP.BytePC = MachineDefs.RealToBytePC[id.pc];
    bb: BP.BBHandle ← BP.FindBB[id.frame, pc];
    IF bb = NIL THEN bb ← BP.FindModBB[id.frame, pc];
    RETURN[IF bb = NIL THEN 0 ELSE bb.num];
    END;

  BpNumToNodeID: PROCEDURE [num: CARDINAL] RETURNS [id: NodeID] =
    BEGIN
    bb: BP.BBHandle ← BP.FindBBNum[num];
    i: CARDINAL;
    IF bb = NIL THEN RETURN[NullID];
    i ← BP.FindUserBB[bb.gf, bb.pc];
    IF i = LAST[CARDINAL] THEN RETURN[NullID];
    RETURN[[MachineDefs.ByteToRealPC[bb.pc], bb.gf]];
    END;

  END.