1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System; 9 using System.Text; 10 using System.Collections.Generic; 11 using Antmicro.Renode.Utilities; 12 using Antmicro.Renode.Logging; 13 14 namespace Antmicro.Renode.Peripherals.CPU 15 { 16 public class TraceBinaryWriter : TraceWriter 17 { 18 public static IReadOnlyList<TraceFormat> SupportedFormats = new List<TraceFormat> 19 { 20 TraceFormat.PC, 21 TraceFormat.Opcode, 22 TraceFormat.PCAndOpcode 23 }.AsReadOnly(); 24 TraceBinaryWriter(TranslationCPU cpu, string path, TraceFormat format, bool compress)25 public TraceBinaryWriter(TranslationCPU cpu, string path, TraceFormat format, bool compress) 26 : base(cpu, path, format, compress) 27 { 28 this.pcWidth = this.format == TraceFormat.Opcode ? 0 : (int)(cpu.PC.Bits + 7) / 8; 29 30 cache = new LRUCache<uint, byte[]>(CacheSize); 31 buffer = new byte[BufferSize]; 32 } 33 WriteHeader()34 public override void WriteHeader() 35 { 36 stream.Write(Encoding.ASCII.GetBytes(FormatSignature), 0, Encoding.ASCII.GetByteCount(FormatSignature)); 37 stream.WriteByte(FormatVersion); 38 stream.WriteByte((byte)pcWidth); 39 stream.WriteByte((byte)(IncludeOpcode ? 1 : 0)); 40 if(IncludeOpcode) 41 { 42 LLVMArchitectureMapping.GetTripleAndModelKey(AttachedCPU, 0, out var triple, out var model); 43 var tripleAndModelString = $"{triple} {model}"; 44 usesThumbFlag = tripleAndModelString.Contains("armv7a"); 45 var byteCount = Encoding.ASCII.GetByteCount(tripleAndModelString); 46 47 stream.WriteByte((byte)(usesThumbFlag ? 1 : 0)); 48 stream.WriteByte((byte)byteCount); 49 stream.Write(Encoding.ASCII.GetBytes(tripleAndModelString), 0, byteCount); 50 } 51 } 52 Write(ExecutionTracer.Block block)53 public override void Write(ExecutionTracer.Block block) 54 { 55 var pc = block.FirstInstructionPC; 56 var pcVirtual = block.FirstInstructionVirtualPC; 57 var counter = 0u; 58 59 var hasAdditionalData = block.AdditionalDataInTheBlock.TryDequeue(out var insnAdditionalData); 60 61 if(usesThumbFlag) 62 { 63 // if the CPU supports thumb, we need to mark translation blocks 64 // with a flag and length of the block, that is needed to properly disassemble the trace 65 var isThumb = block.DisassemblyFlags > 0; 66 WriteByteToBuffer((byte)(isThumb ? 1 : 0)); 67 WriteInstructionsCountToBuffer(block.InstructionsCount); 68 } 69 70 while(counter < block.InstructionsCount) 71 { 72 if(!TryReadAndDecodeInstruction(pc, block.DisassemblyFlags, out var opcode)) 73 { 74 break; 75 } 76 77 if(IncludePC) 78 { 79 WritePCToBuffer(pc); 80 } 81 if(IncludeOpcode) 82 { 83 WriteByteToBuffer((byte)opcode.Length); 84 WriteBytesToBuffer(opcode); 85 } 86 while(hasAdditionalData && insnAdditionalData.PC == pcVirtual) 87 { 88 WriteByteToBuffer((byte)insnAdditionalData.Type); 89 WriteBytesToBuffer(insnAdditionalData.GetBinaryRepresentation()); 90 hasAdditionalData = block.AdditionalDataInTheBlock.TryDequeue(out insnAdditionalData); 91 } 92 WriteByteToBuffer((byte)AdditionalDataType.None); 93 94 pc += (ulong)opcode.Length; 95 pcVirtual += (ulong)opcode.Length; 96 counter++; 97 98 if(bufferPosition >= BufferFlushLevel) 99 { 100 FlushBuffer(); 101 } 102 } 103 } 104 105 private bool usesThumbFlag; 106 107 private bool IncludeOpcode => this.format != TraceFormat.PC; 108 109 private bool IncludePC => this.format != TraceFormat.Opcode; 110 WriteBytesToBuffer(byte[] data)111 private void WriteBytesToBuffer(byte[] data) 112 { 113 Buffer.BlockCopy(data, 0, buffer, bufferPosition, data.Length); 114 bufferPosition += data.Length; 115 } 116 WriteByteToBuffer(byte data)117 private void WriteByteToBuffer(byte data) 118 { 119 buffer[bufferPosition] = data; 120 bufferPosition += 1; 121 } 122 WritePCToBuffer(ulong pc)123 private void WritePCToBuffer(ulong pc) 124 { 125 BitHelper.GetBytesFromValue(buffer, bufferPosition, pc, pcWidth, true); 126 bufferPosition += pcWidth; 127 } 128 WriteInstructionsCountToBuffer(ulong count)129 private void WriteInstructionsCountToBuffer(ulong count) 130 { 131 BitHelper.GetBytesFromValue(buffer, bufferPosition, count, 8, true); 132 bufferPosition += 8; 133 } 134 FlushBuffer()135 public override void FlushBuffer() 136 { 137 stream.Write(buffer, 0, bufferPosition); 138 stream.Flush(); 139 bufferPosition = 0; 140 } 141 TryReadAndDecodeInstruction(ulong pc, uint flags, out byte[] opcode)142 private bool TryReadAndDecodeInstruction(ulong pc, uint flags, out byte[] opcode) 143 { 144 // here we read only 4-bytes as it should cover most cases 145 var key = AttachedCPU.Bus.ReadDoubleWord(pc, context: AttachedCPU); 146 if(!cache.TryGetValue(key, out opcode)) 147 { 148 // here we are prepared for longer opcodes 149 var mem = AttachedCPU.Bus.ReadBytes(pc, MaxOpcodeBytes, context: AttachedCPU); 150 if(!AttachedCPU.Disassembler.TryDecodeInstruction(pc, mem, flags, out var decodedOpcode)) 151 { 152 opcode = new byte[0] { }; 153 // mark this as an invalid opcode 154 cache.Add(key, opcode); 155 } 156 else 157 { 158 opcode = decodedOpcode; 159 if(opcode.Length <= 4) 160 { 161 cache.Add(key, opcode); 162 } 163 } 164 } 165 166 if(opcode.Length == 0) 167 { 168 AttachedCPU.Log(LogLevel.Warning, "ExecutionTracer: Couldn't disassemble opcode at PC 0x{0:X}\n", pc); 169 return false; 170 } 171 172 return true; 173 } 174 175 private int bufferPosition; 176 177 private readonly LRUCache<uint, byte[]> cache; 178 179 private readonly byte[] buffer; 180 private readonly int pcWidth; 181 182 private const string FormatSignature = "ReTrace"; 183 private const byte FormatVersion = 4; 184 185 private const int CacheSize = 100000; 186 private const int BufferSize = 10000; 187 private const int BufferFlushLevel = 9000; 188 } 189 } 190