-- PerfBreakHandler.Mesa; edited by Sandman on September 4, 1980  3:30 PM  

DIRECTORY
  ControlDefs USING [
    FieldDescriptor, FrameHandle, StateVector, TraceNext, TrapStatus],
  CoreSwapDefs USING [ExternalStateVector, PuntInfo, UBBPointer, UserBreakBlock],
  FrameOps USING [GetReturnLink, MyLocalFrame, SetReturnFrame],
  ImageDefs USING [AbortMesa],
  Inline USING [BITSHIFT, LongNumber],
  KeyDefs USING [Keys],
  Mopcodes USING [zRFS],
  PerfPrivate USING [
    Histogram, Leg, LegTab, MaxLegs, MaxNodes, Node, NodeID, NodeTab, NullHist,
    NullNode, Number, PerfControlRecord],
  ProcessDefs USING [DisableInterrupts, EnableInterrupts],
  ProcessOps USING [CurrentPSB],
  TimingDefs USING [Fudge, HiLo, Machine, MaxTick, Pair, ReadTime, RealTime],
  TrapOps USING [ReadXTS, WriteXTS];

PerfBreakHandler: PROGRAM
  IMPORTS FrameOps, ImageDefs, Inline, ProcessDefs, TimingDefs, TrapOps
  SHARES ProcessDefs =

  BEGIN OPEN ControlDefs, PerfPrivate;

  WBPort: PORT [POINTER TO CoreSwapDefs.ExternalStateVector];
  ReadField: PROCEDURE [POINTER, FieldDescriptor] RETURNS [UNSPECIFIED] = MACHINE
    CODE BEGIN Mopcodes.zRFS END;

  BreakBlock: TYPE = RECORD [
    count: CARDINAL, blocks: ARRAY [0..MaxNodes) OF CoreSwapDefs.UserBreakBlock];

  perfRecord: PerfControlRecord;
  breakBlock: BreakBlock;
  machine: TimingDefs.Machine;
  fudge: TimingDefs.Fudge;
  activeFudge: TimingDefs.Pair;
  currentState: TimingDefs.HiLo;
  nodeTab: POINTER TO NodeTab;
  legTab: POINTER TO LegTab;

  GetFudge: PROCEDURE RETURNS [val: CARDINAL] = INLINE
    BEGIN
    val ← activeFudge.value;
    IF (activeFudge.factor ← activeFudge.factor - 1) = 0 THEN
      BEGIN
      currentState ← SELECT currentState FROM hi => lo, ENDCASE => hi;
      activeFudge ← fudge[currentState];
      END;
    RETURN
    END;

  MonitorBreaks: PROCEDURE =
    BEGIN OPEN Inline;
    state: StateVector;
    frame: FrameHandle;
    esv: CoreSwapDefs.ExternalStateVector;
    xferTrapStatus: TrapStatus;
    ubb: CoreSwapDefs.UBBPointer;
    pCR: POINTER TO PerfControlRecord;
    locL: POINTER;
    fd: FieldDescriptor;
    id: CARDINAL;
    value: CARDINAL;
    word: INTEGER;
    longval: LongNumber;
    hist: POINTER TO Histogram;
    timeSpent: LongNumber;
    timeOnEntry: Number;
    lastExit: Number;
    lastEntry: Number;
    lastPerfEntry: Number;
    lastOverhead: Number;
    lastLegTime: Number;
    breakType: {perf, normal, other};
    stillMustDoLastLeg: BOOLEAN;
    i: CARDINAL;
    free: BOOLEAN;
    node: POINTER TO Node;
    leg: POINTER TO Leg;
    enterTime, exitTime: TimingDefs.RealTime;
    nodeID: NodeID;
    state ← STATE;
    pCR ← @perfRecord;
    pCR.self ← FrameOps.MyLocalFrame[];
    state.dest ← FrameOps.GetReturnLink[];
    ProcessDefs.DisableInterrupts[];
    ProcessDefs.DisableInterrupts[];
    DO
      OPEN pCR;
      xferTrapStatus ← TrapOps.ReadXTS[];
      IF xferTrapStatus.state = on THEN TrapOps.WriteXTS[TraceNext];
      lastEntry ← timeOnEntry;
      exitTime ← TimingDefs.ReadTime[];
      ProcessDefs.EnableInterrupts[];
      ProcessDefs.EnableInterrupts[];
      TRANSFER WITH state;
      ProcessDefs.DisableInterrupts[];
      ProcessDefs.DisableInterrupts[];
      state ← STATE;
      enterTime ← TimingDefs.ReadTime[];
      FrameOps.SetReturnFrame[state.dest ← frame ← state.source];
      state.source ← FrameOps.MyLocalFrame[];
      frame.pc ← [IF frame.pc < 0 THEN -frame.pc ELSE (1 - frame.pc)];

      -- Fixup time
      timeOnEntry ←
	LONG[
	  WITH enterTime.low SELECT machine FROM altoI => low, altoII => low,
	  d0 => low, ENDCASE => 0] + BITSHIFT[enterTime.high.whole, 10] +
	  LOOPHOLE[LongNumber[num[highbits: enterTime.high.high, lowbits: 0]],
	  LONG CARDINAL];

      lastExit ←
	LONG[
	  WITH exitTime.low SELECT machine FROM altoI => low, altoII => low,
	  d0 => low, ENDCASE => 0] + BITSHIFT[exitTime.high.whole, 10] +
	  LOOPHOLE[LongNumber[num[highbits: exitTime.high.high, lowbits: 0]], LONG
	  CARDINAL];

      i ← GetFudge[];
      timeSpent.lc ← timeOnEntry - lastExit;
      IF lastExit > timeOnEntry THEN
	timeSpent.lc ← timeSpent.lc + TimingDefs.MaxTick;
      IF timeSpent.lc > i THEN lastExit ← lastExit + i
      ELSE lastExit ← lastExit + timeSpent.lc;


      IF ~measuringNow THEN
	BEGIN lastEntry ← lastExit; lastPerfEntry ← timeOnEntry; END;
      lastOverhead ← lastExit - lastEntry;
      IF lastEntry > lastExit THEN
	lastOverhead ← lastOverhead + TimingDefs.MaxTick;
      lastPerfEntry ← lastPerfEntry + lastOverhead;
      IF lastCall = perf THEN
	BEGIN
	perfTime ← perfTime + lastOverhead;
	totalTime ← totalTime + lastOverhead;
	END
      ELSE
	FOR leg ← @legTab[0], leg + SIZE[Leg] UNTIL leg = @legTab[nextLeg] DO
	  IF leg.owner # NIL THEN leg.start ← leg.start + lastOverhead; ENDLOOP;
      nodeID ← [pc: frame.pc, frame: frame.accesslink];
      breakType ← normal;
      FOR i IN [0..breakBlock.count) DO
	ubb ← @breakBlock.blocks[i];
	IF nodeID = [frame: ubb.frame, pc: ubb.pc] THEN
	  BEGIN
	  state.instbyte ← ubb.inst;
	  node ← @nodeTab[0];
	  IF process # NIL AND process # ProcessOps.CurrentPSB↑ THEN
	    BEGIN breakType ← other; EXIT END;
	  FOR id IN [0..nextNode) DO
	    IF nodeID = node.id THEN GOTO foundEntry;
	    node ← node + SIZE[Node];
	    ENDLOOP;
	  IF nextNode < MaxNodes THEN
	    BEGIN
	    id ← nextNode;
	    nextNode ← nextNode + 1;
	    node↑ ←
	      [id: nodeID, hitsLow: 0, hitsHigh: 0, overflowed: FALSE,
		hist: NullHist];
	    GO TO foundEntry;
	    END
	  ELSE BEGIN breakType ← other; EXIT END;
	  END;
	REPEAT
	  foundEntry =>
	    BEGIN
	    breakType ← perf;
	    IF (node.hitsLow ← node.hitsLow + 1) = 0 THEN
	      IF (node.hitsHigh ← node.hitsHigh + 1) = 0 THEN
		node.overflowed ← TRUE;
	    IF node.hist # NullHist AND ~ubb.counterL THEN
	      BEGIN
	      locL ←
		IF ubb.localL THEN frame + LOOPHOLE[ubb.ptrL, CARDINAL]
		ELSE ubb.ptrL;
	      fd ← [offset: 0, posn: ubb.posnL, size: ubb.sizeL];
	      value ← ReadField[locL, fd];
	      hist ← @histBase[node.hist];
	      hist.count ← hist.count + 1;
	      hist.sum ← hist.sum + value;
	      IF LOOPHOLE[hist.base, LongNumber].lowbits > value THEN
		hist.underflow ← hist.underflow + 1
	      ELSE
		BEGIN
		value ←
		  (value - LOOPHOLE[hist.base, LongNumber].lowbits)/hist.scale;
		IF hist.class = log THEN
		  IF (word ← value) # 0 THEN
		    FOR value DECREASING IN [0..15] DO
		      IF word < 0 THEN EXIT; word ← word*2; ENDLOOP;
		IF value < hist.nBuckets THEN
		  hist.buckets[value] ← hist.buckets[value] + 1
		ELSE hist.overflow ← hist.overflow + 1;
		END;
	      END;
	    END;
	ENDLOOP;


      IF breakType = perf THEN
	BEGIN
	lastLegTime ← timeOnEntry - lastPerfEntry;
	IF lastPerfEntry > timeOnEntry THEN
	  lastLegTime ← lastLegTime + TimingDefs.MaxTick;
	totalTime ← totalTime + lastLegTime;
	IF trackLeg # none THEN
	  BEGIN
	  stillMustDoLastLeg ← TRUE;
	  FOR leg ← @legTab[0], leg + SIZE[Leg] UNTIL leg = @legTab[nextLeg] DO
	    IF leg.owner # NIL THEN
	      BEGIN
	      leg.start ← leg.start + lastOverhead;
	      IF id = leg.to THEN
		BEGIN
		IF leg.owner # ProcessOps.CurrentPSB↑ THEN leg.someIgnored ← TRUE
		ELSE
		  BEGIN
		  leg.owner ← NIL;
		  IF lastID = leg.from THEN stillMustDoLastLeg ← FALSE;
		  IF (leg.hitsLow ← leg.hitsLow + 1) = 0 THEN
		    IF (leg.hitsHigh ← leg.hitsHigh + 1) = 0 THEN
		      leg.overflowed ← TRUE;
		  timeSpent.lc ← timeOnEntry - leg.start;
		  IF leg.start > timeOnEntry THEN
		    timeSpent.lc ← timeSpent.lc + TimingDefs.MaxTick;
		  leg.sum ← leg.sum + timeSpent.lc;
		  IF leg.hist # NullHist THEN
		    BEGIN
		    hist ← @histBase[leg.hist];
		    hist.count ← hist.count + 1;
		    hist.sum ← hist.sum + timeSpent.lc;
		    IF hist.base > timeSpent.lc THEN
		      hist.underflow ← hist.underflow + 1
		    ELSE
		      BEGIN
		      timeSpent.lc ← timeSpent.lc - hist.base;
		      longval.highbits ← BITSHIFT[
			timeSpent.highbits, -hist.scale];
		      longval.lowbits ←
			BITSHIFT[timeSpent.lowbits, -hist.scale] + BITSHIFT[
			  timeSpent.highbits, 16 - hist.scale];
		      IF hist.class = log THEN
			IF (word ← longval.highbits) # 0 THEN
			  FOR value DECREASING IN [16..31] DO
			    IF word < 0 THEN EXIT; word ← word*2; ENDLOOP
			ELSE
			  IF (word ← longval.lowbits) # 0 THEN
			    FOR value DECREASING IN [0..15] DO
			      IF word < 0 THEN EXIT; word ← word*2; ENDLOOP
			  ELSE value ← 0
		      ELSE
			value ←
			  IF longval.highbits # 0 THEN hist.nBuckets
			  ELSE longval.lowbits;
		      IF value < hist.nBuckets THEN
			hist.buckets[value] ← hist.buckets[value] + 1
		      ELSE hist.overflow ← hist.overflow + 1;
		      END;
		    END;
		  END;
		END
	      ELSE IF trackLeg = successor THEN leg.owner ← NIL;
	      END;
	    IF id = leg.from THEN
	      BEGIN
	      leg.start ← timeOnEntry;
	      IF leg.owner # NIL THEN leg.someIgnored ← TRUE;
	      leg.owner ← ProcessOps.CurrentPSB↑;
	      END;
	    ENDLOOP;
	  free ← FALSE;
	  IF addLeg = successor AND stillMustDoLastLeg AND measuringNow THEN
	    BEGIN
	    FOR leg ← @legTab[0], leg + SIZE[Leg] UNTIL leg = @legTab[nextLeg] DO
	      IF ~leg.lock AND leg.from = NullNode THEN
		BEGIN free ← TRUE; EXIT; END;
	      ENDLOOP;
	    IF ~free AND nextLeg < MaxLegs THEN
	      BEGIN
	      leg ← @legTab[nextLeg];
	      nextLeg ← nextLeg + 1;
	      free ← TRUE
	      END;
	    IF free THEN
	      BEGIN
	      leg↑ ←
		[start: timeOnEntry, from: lastID, to: id, lock: FALSE,
		  someIgnored: FALSE, owner: NIL, hitsLow: 1, hitsHigh: 0,
		  sum: lastLegTime, hist: NullHist, overflowed: FALSE];
	      IF id = lastID THEN leg.owner ← ProcessOps.CurrentPSB↑;
	      stillMustDoLastLeg ← FALSE;
	      END;
	    END;
	  END;
	END;
      SELECT breakType FROM
	perf =>
	  BEGIN
	  lastID ← id;
	  measuringNow ← TRUE;
	  totalBreaks ← totalBreaks + 1;
	  lastPerfEntry ← timeOnEntry;
	  lastCall ← perf;
	  END;
	normal =>
	  BEGIN
	  esv ← CoreSwapDefs.PuntInfo↑.puntESV;
	  esv.state ← @state;
	  esv.reason ← worrybreak;
	  DO
	    WBPort[@esv]; --  now subtract out time spent in the debugging world
	    lastCall ← normal;
	    SELECT esv.reason FROM
	      proceed => EXIT;
	      kill => ImageDefs.AbortMesa[];
	      showscreen => UNTIL KeyDefs.Keys.Spare3 = down DO NULL ENDLOOP;
	      ENDCASE;
	    esv.reason ← return;
	    ENDLOOP;
	  END;
	ENDCASE => lastCall ← normal;

      ENDLOOP;
    END;

  END...