DIRECTORY DragOpsCross, DragOpsCrossUtils, HandCodingUtil, IO, LizardCache, LizardHeart, LizardToolOutput, Real USING [RoundLI], Rope, SparseMemory; 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[entry.regL]]; PutAsCard[out, ", rtnPC: %w\n", entry.pc]; 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, "\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, "\n instBufferCycles: %g", pStats.instBufferCycles]; PutF1I[out, ", returnInterlockCycles: %g", pStats.returnInterlockCycles]; 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; 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, "\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, "\n instBufferCycles: %g", new.pStats.instBufferCycles - old.pStats.instBufferCycles]; PutF1I[out, ", returnInterlockCycles: %g", new.pStats.returnInterlockCycles - old.pStats.returnInterlockCycles]; 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.RoundLI[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 c 1985 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) November 15, 1985 4:20:12 pm 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. Κ ‚˜codešœ™Kšœ Οmœ1™˜>—K˜—Kšœ.˜.KšœK˜KKšœ5˜5Kšœ/˜/˜Kšœ'™'Kšœ žœ'˜7Kšœ žœ%˜4Kšœ6˜6Kšœ0˜0Kšœ0˜0Kšœ)˜)Kšœ>˜>Kšœ/˜/Kšœ5˜5Kšœ.˜.K˜—Kšœ5˜5KšœC˜CKšœI˜IKšœ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˜šœ™Kšœ'˜'Kšœ$˜$šžœ žœ˜Kšœ<˜˜>—K˜—Kšœ.˜.Kšœ1˜1Kšœ.˜.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šœe˜eKšœp˜pKšœ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šœ<˜