DIRECTORY DragOpsCross USING [Byte, FourBytes, Inst, ProcessorRegister, Word, ZerosWord], DragOpsCrossUtils USING [AddDelta, BytePCToWordAddress, WordToCard, WordToInt], HandCodingUtil USING [GetInstArray, GetRegArray, NameArray, RegNameArray, ToStream], IO USING [PutF, PutFR, PutFR1, PutRope, STREAM], LizardCache USING [CacheBase, CacheBaseRep, CacheStats], LizardHeart USING [IFUStackEntry, LizardIFUStackSize, Processor, ProcessorStats], LizardToolOutput USING [InstCountTable, InstCountTableRep, LastInstMod, LastInstTable, StatSnapshot, TestAbort], Real USING [Round], Rope USING [ROPE, Substr], SparseMemory USING [Base, Fetch]; LizardToolOutputImpl: CEDAR PROGRAM IMPORTS DragOpsCrossUtils, HandCodingUtil, IO, Real, Rope, SparseMemory EXPORTS LizardToolOutput = BEGIN OPEN LizardToolOutput; Byte: TYPE = DragOpsCross.Byte; LizardIFUStackSize: NAT = LizardHeart.LizardIFUStackSize; Inst: TYPE = DragOpsCross.Inst; ProcessorRegister: TYPE = DragOpsCross.ProcessorRegister; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Word: TYPE = DragOpsCross.Word; ZerosWord: Word = DragOpsCross.ZerosWord; LocalInstStats: TYPE = REF LocalInstStatsRep; LocalInstStatsRep: TYPE = ARRAY Inst OF LocalInstStatsEntry; LocalInstStatsEntry: TYPE = RECORD [inst: Inst, count: INT]; ShowRegisters: PUBLIC PROC [out: STREAM, processor: LizardHeart.Processor, testAbort: TestAbort _ NIL] = { regOut: NAT _ 0; regNameArray: HandCodingUtil.RegNameArray = HandCodingUtil.GetRegArray[]; FOR reg: ProcessorRegister IN ProcessorRegister DO word: Word = processor.regs[reg]; IF testAbort # NIL AND testAbort[] THEN GO TO requestStopPrint; IF word = ZerosWord THEN LOOP; IF regOut MOD 4 = 0 THEN IO.PutRope[out, "\n"]; regOut _ regOut + 1; IO.PutF[out, " %5g ", [rope[regNameArray[reg]]]]; PutAsBoth[out, "%w (%d)", word]; ENDLOOP; IO.PutRope[out, "\n"]; {youngest: NAT _ processor.youngest; eldest: NAT = processor.eldest; IF (youngest+1) MOD LizardIFUStackSize # eldest THEN { IO.PutRope[out, "\nIFU stack (youngest first)\n"]; DO entry: LizardHeart.IFUStackEntry = processor.ifuStack[youngest]; IF testAbort # NIL AND testAbort[] THEN GO TO requestStopPrint; IO.PutRope[out, " regL: "]; IO.PutRope[out, regNameArray[VAL[entry.status.lBase]]]; PutAsCard[out, ", rtnPC: %w", entry.pc]; IF entry.status.userMode THEN IO.PutRope[out, " (user)"] ELSE IO.PutRope[out, " (kernel)"]; IF entry.status.trapsEnabled THEN IO.PutRope[out, " (traps enabled)"] ELSE IO.PutRope[out, " (traps disabled)"]; IO.PutRope[out, "\n"]; IF youngest = eldest THEN EXIT; youngest _ (youngest + (LizardIFUStackSize-1)) MOD LizardIFUStackSize; ENDLOOP; } ELSE IO.PutRope[out, "\nIFU stack empty\n"]; }; EXITS requestStopPrint => { IO.PutRope[out, "\nPrinting stopped by request.\n\n"]; }; }; ShowRing: PUBLIC PROC [out: STREAM, ring: LastInstTable, mem: SparseMemory.Base, howMany: [0..LastInstMod] _ LastInstMod, testAbort: TestAbort _ NIL] = { regNameArray: HandCodingUtil.RegNameArray = HandCodingUtil.GetRegArray[]; instNameArray: HandCodingUtil.NameArray = HandCodingUtil.GetInstArray[]; which: CARDINAL _ ring.last; PutF1I[out, "\nLast %g instructions (most recent, first printed):\n", LastInstMod]; THROUGH [0..LastInstMod) DO pc: Word = ring.pcArray[which]; inst: DragOpsCross.Inst; rest: Word; IF pc = ZerosWord THEN EXIT; IF testAbort # NIL AND testAbort[] THEN GO TO requestStopPrint; PutAsCard[out, " pc: %w => ", pc]; [inst, rest] _ GetInstAndRest[mem, pc]; HandCodingUtil.ToStream[out, inst, rest, pc]; IO.PutRope[out, "\n"]; which _ (which-1) MOD LastInstMod; ENDLOOP; EXITS requestStopPrint => { IO.PutRope[out, "\nPrinting stopped by request.\n\n"]; }; }; ShowStats: PUBLIC PROC [out: STREAM, processor: LizardHeart.Processor, instStats: InstCountTable, testAbort: TestAbort _ NIL] = { pStats: LizardHeart.ProcessorStats _ processor.stats; instNameArray: HandCodingUtil.NameArray = HandCodingUtil.GetInstArray[]; instOut: NAT _ 0; cycles: INT _ pStats.cycles; insts: INT _ pStats.instructions; bytes: INT _ pStats.instBytes; rejects: INT _ processor.ifuCache.stats.rejectCycles+processor.euCache.stats.rejectCycles; instructionsSoFar: INT _ 0; invTotal: REAL _ IF pStats.instructions > 0 THEN 100.0 / pStats.instructions ELSE 0.0; PutF1I[out, "\ninstructions: %g", insts]; PutF1I[out, ", cycles: %g", cycles]; IF insts > 0 THEN { PutReal1[out, "\n bytes/inst: %g", (bytes*1.0)/insts, 2]; PutReal1[out, ", cycles/inst: %g", (cycles*1.0)/insts, 2]; IF rejects > 0 THEN PutReal1[out, ", cycles/reject: %g", (cycles*1.0)/rejects, 2]; }; PutF1I[out, "\n instBytesUsed: %g", bytes]; PutF1I[out, ", instBytesFlushed: %g", processor.instBuffer.bytesDiscarded]; PutF1I[out, ", forcedEmpty: %g", processor.instBuffer.forcedEmpty]; PutF1I[out, "\n euFetches: %g", pStats.euFetches]; PutF1I[out, ", euStores: %g", pStats.euStores]; { goodPredict: INT _ pStats.fallThruGood+pStats.jumpGood; badPredict: INT _ pStats.fallThruBad+pStats.jumpBad; PutF1I[out, "\n goodPredictions: %g", goodPredict]; PutF1I[out, ", badPredictions: %g", badPredict]; PutF1I[out, ", uncond jumps: %g", pStats.jumps]; PutF1I[out, ", calls: %g", pStats.calls]; PutF1I[out, "\n (fallThruGood: %g", pStats.fallThruGood]; PutF1I[out, ", jumpGood: %g", pStats.jumpGood]; PutF1I[out, ", fallThruBad: %g", pStats.fallThruBad]; PutF1I[out, ", jumpBad: %g)", pStats.jumpBad]; }; PutF1I[out, "\n stackOver: %g", pStats.stackOver]; PutF1I[out, ", regBusyCycles: %g", pStats.regBusyCycles]; PutF1I[out, "\n instBufferCycles: %g", pStats.instBufferCycles]; PutF1I[out, ", returnInterlockCycles: %g", pStats.returnInterlockCycles]; PutF1I[out, "\n lookaheadProbes: %g", pStats.lookaheadProbes]; PutF1I[out, ", lookaheadRejectCycles: %g", pStats.lookaheadRejects]; IF testAbort # NIL AND testAbort[] THEN GO TO requestStopPrint; IO.PutRope[out, "\n\nIFU cache - "]; DumpCache[out, processor.ifuCache]; IO.PutRope[out, "\nEU cache - "]; DumpCache[out, processor.euCache]; IO.PutRope[out, "\n"]; ShowInstStats[out, instStats, testAbort]; IO.PutRope[out, "\n"]; EXITS requestStopPrint => { IO.PutRope[out, "\nPrinting stopped by request.\n\n"]; }; }; ShowBiasedStats: PUBLIC PROC [out: STREAM, old, new: StatSnapshot, testAbort:TestAbort] = { instOut: NAT _ 0; cycles: INT _ new.pStats.cycles - old.pStats.cycles; insts: INT _ new.pStats.instructions - old.pStats.instructions; bytes: INT _ new.pStats.instBytes - old.pStats.instBytes; ifuRejects: INT _ new.ifuStats.rejectCycles - old.ifuStats.rejectCycles; euRejects: INT _ new.euStats.rejectCycles - old.euStats.rejectCycles; rejects: INT _ ifuRejects + euRejects; euFetches: INT _ new.pStats.euFetches - old.pStats.euFetches; euStores: INT _ new.pStats.euStores - old.pStats.euStores; instructionsSoFar: INT _ 0; invTotal: REAL _ IF insts > 0 THEN 100.0 / insts ELSE 0.0; discarded: INT _ new.discarded - old.discarded; forcedEmpty: INT _ new.forcedEmpty - old.forcedEmpty; PutF1I[out, "instructions: %g", insts]; PutF1I[out, ", cycles: %g", cycles]; IF insts > 0 THEN { PutReal1[out, "\n bytes/inst: %g", (bytes*1.0)/insts, 2]; PutReal1[out, ", cycles/inst: %g", (cycles*1.0)/insts, 2]; IF rejects > 0 THEN PutReal1[out, ", cycles/reject: %g", (cycles*1.0)/rejects, 2]; }; PutF1I[out, "\n instBytesUsed: %g", bytes]; PutF1I[out, ", instBytesFlushed: %g", discarded]; PutF1I[out, ", forcedEmpty: %g", forcedEmpty]; PutF1I[out, "\n euFetches: %g", euFetches]; PutF1I[out, ", euStores: %g", euStores]; { fallThruGood: INT _ new.pStats.fallThruGood - old.pStats.fallThruGood; jumpGood: INT _ new.pStats.jumpGood - old.pStats.jumpGood; fallThruBad: INT _ new.pStats.fallThruBad - old.pStats.fallThruBad; jumpBad: INT _ new.pStats.jumpBad - old.pStats.jumpBad; goodPredict: INT _ fallThruGood+jumpGood; badPredict: INT _ fallThruBad+jumpBad; PutF1I[out, "\n goodPredictions: %g", goodPredict]; PutF1I[out, ", badPredictions: %g", badPredict]; PutF1I[out, ", uncond jumps: %g", new.pStats.jumps - old.pStats.jumps]; PutF1I[out, ", calls: %g", new.pStats.calls - old.pStats.calls]; PutF1I[out, "\n (fallThruGood: %g", fallThruGood]; PutF1I[out, ", jumpGood: %g", jumpGood]; PutF1I[out, ", fallThruBad: %g", fallThruBad]; PutF1I[out, ", jumpBad: %g)", jumpBad]; }; PutF1I[out, "\n stackOver: %g", new.pStats.stackOver - old.pStats.stackOver]; PutF1I[out, ", regBusyCycles: %g", new.pStats.regBusyCycles - old.pStats.regBusyCycles]; PutF1I[out, "\n instBufferCycles: %g", new.pStats.instBufferCycles - old.pStats.instBufferCycles]; PutF1I[out, ", returnInterlockCycles: %g", new.pStats.returnInterlockCycles - old.pStats.returnInterlockCycles]; PutF1I[out, "\n lookaheadProbes: %g", new.pStats.lookaheadProbes - old.pStats.lookaheadProbes]; PutF1I[out, ", lookaheadRejectCycles: %g", new.pStats.lookaheadRejects - old.pStats.lookaheadRejects]; IF testAbort # NIL AND testAbort[] THEN GO TO requestStopPrint; IO.PutRope[out, "\n\nIFU cache - "]; ShowBiasedCache[out, old.ifuStats, new.ifuStats]; IO.PutRope[out, "\nEU cache - "]; ShowBiasedCache[out, old.euStats, new.euStats]; IO.PutRope[out, "\n"]; { biasedInstTable: InstCountTable _ NEW[InstCountTableRep]; FOR inst: Inst IN Inst DO biasedInstTable[inst] _ new.iStats[inst] - old.iStats[inst]; ENDLOOP; ShowInstStats[out, biasedInstTable, testAbort]; }; IO.PutRope[out, "\n"]; EXITS requestStopPrint => { IO.PutRope[out, "\nPrinting stopped by request.\n\n"]; }; }; PutAsBoth: PROC [out: STREAM, format: ROPE, word: Word] = { IO.PutF[out, format, [cardinal[DragOpsCrossUtils.WordToCard[word]]], [integer[DragOpsCrossUtils.WordToInt[word]]]]; }; PutAsCard: PROC [out: STREAM, format: ROPE, word: Word] = { IO.PutF[out, format, [cardinal[DragOpsCrossUtils.WordToCard[word]]]]; }; PutF1I: PROC [out: STREAM, format: ROPE, int: INT] = { IO.PutF[out, format, [integer[int]]]; }; PutReal1: PROC [out: STREAM, format: ROPE, real: REAL, fractionPlaces: [0..4] _ 1] = { fact: NAT _ SELECT fractionPlaces FROM 0 => 1, 1 => 10, 2 => 100, 3 => 1000, 4 => 10000, ENDCASE => ERROR; fix: INT _ Real.Round[real*fact]; fract: NAT _ fix MOD fact; form: ROPE _ "%g.%g"; fix _ fix / fact; SELECT fractionPlaces FROM 0 => {PutF1I[out, format, fix]; RETURN}; 1 => form _ "%g.%01g"; 2 => form _ "%g.%02g"; 3 => form _ "%g.%03g"; 4 => form _ "%g.%04g"; ENDCASE => ERROR; IO.PutF[out, format, [rope[IO.PutFR[form, [integer[fix]], [integer[fract]] ]]]]; }; DumpCache: PROC [out: STREAM, cache: LizardCache.CacheBase] = { probes: INT _ cache.stats.probes; PutF1I[out, "probes: %g", probes]; PutF1I[out, ", misses: %g", cache.stats.misses]; PutF1I[out, ", mapMisses: %g", cache.stats.mapMisses]; PutF1I[out, ", dirtyWrites: %g", cache.stats.dirtyWrites]; PutF1I[out, ", rejectCycles: %g", cache.stats.rejectCycles]; IF probes > 0 THEN { PutReal1[out, "\n miss rate: %%%g", (cache.stats.misses*100.0)/probes, 2]; PutReal1[out, ", reject cycles/probe: %g", (cache.stats.rejectCycles*1.0)/probes, 2]; }; }; GetInstAndRest: PROC [mem: SparseMemory.Base, pc: Word] RETURNS [inst: Inst, rest: Word] = { lastAddr: Word _ ZerosWord; bytes: DragOpsCross.FourBytes _ LOOPHOLE[ZerosWord]; instLen: NAT; word: Word; FetchByte: PROC [offset: NAT] RETURNS [Byte] = { wordAddr: Word; byteIndex: [0..3]; [wordAddr, byteIndex] _ DragOpsCrossUtils.BytePCToWordAddress[[DragOpsCrossUtils.AddDelta[offset, pc]]]; IF lastAddr # wordAddr THEN word _ SparseMemory.Fetch[mem, lastAddr _ wordAddr]; RETURN [LOOPHOLE[word, DragOpsCross.FourBytes][byteIndex]]; }; inst _ LOOPHOLE[FetchByte[0]]; instLen _ LOOPHOLE[inst, CARDINAL] / 64; IF instLen = 0 THEN instLen _ IF LOOPHOLE[inst, CARDINAL] < 40B THEN 1 ELSE 5; SELECT instLen FROM 2 => bytes[3] _ FetchByte[1]; 3 => { bytes[2] _ FetchByte[1]; bytes[3] _ FetchByte[2]; }; 5 => { bytes[0] _ FetchByte[1]; bytes[1] _ FetchByte[2]; bytes[2] _ FetchByte[3]; bytes[3] _ FetchByte[4]; }; ENDCASE; RETURN [inst, LOOPHOLE[bytes]]; }; ShowInstStats: PROC [out: IO.STREAM, instStats: InstCountTable, testAbort: TestAbort] = { SortInstStats: PROC [instStats: InstCountTable] RETURNS [LocalInstStats] = { nonZero: NAT _ 0; zeroEntry: LocalInstStatsEntry _ [VAL[0], 0]; local: LocalInstStats _ NEW[LocalInstStatsRep _ ALL[zeroEntry] ]; FOR inst: Inst IN Inst DO count: INT = instStats[inst]; IF count # 0 THEN { j: NAT _ nonZero; FOR i: NAT DECREASING IN [0..nonZero) DO IF local[VAL[i]].count < count THEN { local[VAL[i+1]] _ local[VAL[i]]; j _ i; }; ENDLOOP; local[VAL[j]] _ [inst, count]; nonZero _ nonZero + 1; }; ENDLOOP; RETURN [local]; }; instNameArray: HandCodingUtil.NameArray = HandCodingUtil.GetInstArray[]; total: INT _ 0; invTotal: REAL; instructionsSoFar: INT _ 0; instOut: NAT _ 0; local: LocalInstStats _ SortInstStats[instStats]; FOR inst: Inst IN Inst DO total _ total + instStats[inst]; ENDLOOP; invTotal _ IF total > 0 THEN 100.0 / total ELSE 0.0; FOR inst: Inst IN Inst DO entry: LocalInstStatsEntry _ local[inst]; count: INT = entry.count; cumPerc: REAL _ (instructionsSoFar _ instructionsSoFar + count) * invTotal; cumPercRope: ROPE _ Rope.Substr[IO.PutFR1["%f4.1", [real[cumPerc]] ], 0, 4]; IF testAbort # NIL AND testAbort[] THEN GO TO requestStopPrint; IF count = 0 THEN EXIT; IF instOut MOD 4 = 0 THEN IO.PutRope[out, "\n"]; instOut _ instOut + 1; IO.PutF[out, "%7g (%4g%%): %-7g", [integer[count]], [rope[cumPercRope]], [rope[instNameArray[entry.inst]]] ]; ENDLOOP; EXITS requestStopPrint => { IO.PutRope[out, "\nPrinting stopped by request.\n\n"]; }; }; ShowBiasedCache: PROC [out: IO.STREAM, old, new: LizardCache.CacheStats] = { biasedCache: LizardCache.CacheBase _ NEW[LizardCache.CacheBaseRep]; biasedCache.stats.probes _ new.probes - old.probes; biasedCache.stats.misses _ new.misses - old.misses; biasedCache.stats.mapMisses _ new.mapMisses - old.mapMisses; biasedCache.stats.dirtyWrites _ new.dirtyWrites - old.dirtyWrites; biasedCache.stats.rejectCycles _ new.rejectCycles - old.rejectCycles; DumpCache[out, biasedCache]; }; END. ΚLizardToolOutputImpl.mesa Copyright Σ 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) March 19, 1987 10:59:08 am PST McCreight, January 8, 1986 12:19:22 pm PST Peter Kessler November 11, 1985 3:05:59 pm PST Exported procedures Also show the IFU stack Dump the last N instructions Dump the processor statistics detail of jumps, calls, and predictions Dump the cache statistics Dump the instruction statistics Dump the processor statistics detail of jumps, calls, and predictions Dump the cache statistics Dump the instruction statistics Internal procedures assumes that %g is used in the format A slow but simple routine to fetch instructions and the rest given the PC. Κ7˜codešœ™KšœH™HK™2K™*K™.K™šΟk ˜ Kšœ œ=˜OKšœœ8˜OKšœœ@˜TKšœœ œ˜0Kšœ œ'˜8Kšœ œ@˜QKšœœZ˜pKšœœ ˜Kšœœœ ˜Kšœ œ˜!——headšœœ˜#Kšœœœ˜GKšœ˜Kšœ œ˜K˜Kšœœ˜Kšœœ"˜9Kšœœ˜Kšœœ"˜9Kšœœœ˜Kšœœœœ˜šœœ˜Kšœ)˜)K˜—Kšœœœ˜-Kšœœœœ˜˜>—K˜—Kšœ.˜.KšœK˜KKšœC˜CKšœ5˜5Kšœ/˜/˜Kšœ'™'Kšœ œ'˜7Kšœ œ%˜4Kšœ6˜6Kšœ0˜0Kšœ0˜0Kšœ)˜)Kšœ>˜>Kšœ/˜/Kšœ5˜5Kšœ.˜.K˜—Kšœ5˜5Kšœ9˜9KšœC˜CKšœI˜IKšœA˜AKšœD˜D—K™Kš œ œœ œœœ˜?K˜šœ™Kšœ"˜$Kšœ#˜#Kšœ˜!Kšœ"˜"Kšœ˜—K˜šœ™Kšœ)˜)—K˜Kšœ˜šœ˜Kšœ4˜6K˜—K˜—K˜šžœ œœ2˜[Kšœ œ˜Kšœœ)˜4Kšœœ5˜?Kšœœ/˜9Kšœ œ9˜HKšœ œ7˜EKšœ œ˜&Kšœ œ/˜=Kšœ œ-˜:Kšœœ˜Kš œ œœ œœ˜:Kšœ œ!˜/Kšœ œ%˜5K˜šœ™Kšœ'˜'Kšœ$˜$šœ œ˜Kšœ<˜˜>—K˜—Kšœ.˜.Kšœ1˜1Kšœ.˜.Kšœ.˜.Kšœ(˜(˜Kšœ'™'Kšœœ5˜FKšœ œ-˜:Kšœ œ3˜CKšœ œ+˜7Kšœ œ˜)Kšœ œ˜&Kšœ6˜6Kšœ0˜0KšœG˜GKšœ@˜@Kšœ7˜7Kšœ(˜(Kšœ.˜.Kšœ'˜'K˜—KšœP˜PKšœX˜XKšœe˜eKšœp˜pKšœb˜bKšœf˜f—K™Kš œ œœ œœœ˜?K˜šœ™Kšœ"˜$Kšœ1˜1Kšœ˜!Kšœ/˜/Kšœ˜—K˜šœ™˜Kšœ"œ˜9šœ œ˜Kšœ<˜˜KKšœ œœ*˜LKš œ œœ œœœ˜?Kšœ œœ˜Kšœ œœœ˜0Kšœ˜Kšœk˜mKšœ˜—š˜šœ˜Kšœ4˜6K˜——K˜K˜—šžœœœœ'˜LKšœ%œ˜CKšœ3˜3Kšœ3˜3Kšœ<˜