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.IO;
9 using System.Text;
10 using System.Collections.Generic;
11 using Antmicro.Renode.Utilities;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.CPU.Disassembler;
14 
15 namespace Antmicro.Renode.Peripherals.CPU
16 {
17     public class TraceTextWriter : TraceWriter
18     {
19         public static IReadOnlyList<TraceFormat> SupportedFormats = new List<TraceFormat>
20         {
21             TraceFormat.PC,
22             TraceFormat.Opcode,
23             TraceFormat.PCAndOpcode,
24             TraceFormat.Disassembly
25         }.AsReadOnly();
26 
TraceTextWriter(TranslationCPU cpu, string path, TraceFormat format, bool compress)27         public TraceTextWriter(TranslationCPU cpu, string path, TraceFormat format, bool compress)
28             : base(cpu, path, format, compress)
29         {
30             disassemblyCache = new LRUCache<uint, Disassembler.DisassemblyResult>(CacheSize);
31             stringBuilder = new StringBuilder();
32             textWriter = new StreamWriter(stream, Encoding.ASCII);
33         }
34 
Write(ExecutionTracer.Block block)35         public override void Write(ExecutionTracer.Block block)
36         {
37             var pc = block.FirstInstructionPC;
38             var pcVirtual = block.FirstInstructionVirtualPC;
39             var counter = 0;
40             var hasAdditionalData = block.AdditionalDataInTheBlock.TryDequeue(out var nextAdditionalData);
41 
42             while(counter < (int)block.InstructionsCount)
43             {
44                 if(!TryReadAndDisassembleInstruction(pc, block.DisassemblyFlags, out var result))
45                 {
46                     stringBuilder.AppendFormat("Couldn't disassemble opcode at PC 0x{0:X}\n", pc);
47                     break;
48                 }
49                 else
50                 {
51                     switch(format)
52                     {
53                         case TraceFormat.PC:
54                             stringBuilder.AppendFormat("0x{0:X}\n", result.PC);
55                             break;
56 
57                         case TraceFormat.Opcode:
58                             stringBuilder.AppendFormat("0x{0}\n", result.OpcodeString.ToUpper());
59                             break;
60 
61                         case TraceFormat.PCAndOpcode:
62                             stringBuilder.AppendFormat("0x{0:X}: 0x{1}\n", result.PC, result.OpcodeString.ToUpper());
63                             break;
64 
65                         case TraceFormat.Disassembly:
66                             var symbol = AttachedCPU.Bus.FindSymbolAt(pc, AttachedCPU);
67                             var disassembly = result.ToString().Replace("\t", " ");
68                             if(symbol != null)
69                             {
70                                 stringBuilder.AppendFormat("{0, -60} [{1}]\n", disassembly, symbol);
71                             }
72                             else
73                             {
74                                 stringBuilder.AppendFormat("{0}\n", disassembly);
75                             }
76                             break;
77                     }
78                     while(hasAdditionalData && (nextAdditionalData.PC == pcVirtual))
79                     {
80                         stringBuilder.AppendFormat("{0}\n", nextAdditionalData.GetStringRepresentation());
81                         hasAdditionalData = block.AdditionalDataInTheBlock.TryDequeue(out nextAdditionalData);
82                     }
83                     pc += (ulong)result.OpcodeSize;
84                     pcVirtual += (ulong)result.OpcodeSize;
85                     counter++;
86                 }
87                 FlushIfNecessary();
88             }
89         }
90 
FlushBuffer()91         public override void FlushBuffer()
92         {
93             textWriter.Write(stringBuilder);
94             textWriter.Flush();
95             stream.Flush();
96             stringBuilder.Clear();
97         }
98 
Dispose(bool disposing)99         protected override void Dispose(bool disposing)
100         {
101             if(disposed)
102             {
103                 return;
104             }
105 
106             if(disposing)
107             {
108                 FlushBuffer();
109                 textWriter?.Dispose();
110                 stream?.Dispose();
111             }
112             disposed = true;
113         }
114 
FlushIfNecessary()115         private void FlushIfNecessary()
116         {
117             if(stringBuilder.Length > BufferFlushLevel)
118             {
119                 FlushBuffer();
120             }
121         }
122 
123         private bool disposed;
124 
125         private readonly TextWriter textWriter;
126         private readonly StringBuilder stringBuilder;
127 
128         private const int CacheSize = 100000;
129         private const int BufferFlushLevel = 1000000;
130     }
131 }
132