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