1 //
2 // Copyright (c) 2010-2023 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.IO;
10 using System.IO.Compression;
11 using Antmicro.Renode.Exceptions;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.CPU.Disassembler;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.CPU
17 {
18     public abstract class TraceWriter : IDisposable
19     {
TraceWriter(TranslationCPU cpu, string path, TraceFormat format, bool compress)20         public TraceWriter(TranslationCPU cpu, string path, TraceFormat format, bool compress)
21         {
22             AttachedCPU = cpu;
23             this.format = format;
24 
25             try
26             {
27                 stream = File.Open(path, FileMode.CreateNew);
28                 if(compress)
29                 {
30                     stream = new GZipStream(stream, CompressionLevel.Fastest);
31                 }
32             }
33             catch(Exception e)
34             {
35                 throw new RecoverableException($"There was an error when preparing the execution trace output file {path}: {e.Message}");
36             }
37         }
38 
Write(ExecutionTracer.Block block)39         public abstract void Write(ExecutionTracer.Block block);
40 
WriteHeader()41         public virtual void WriteHeader() { }
42 
FlushBuffer()43         public virtual void FlushBuffer() { }
44 
45         public TranslationCPU AttachedCPU { get; }
46 
Dispose()47         public void Dispose() => Dispose(true);
48 
Dispose(bool disposing)49         protected virtual void Dispose(bool disposing)
50         {
51             if(disposed)
52             {
53                 return;
54             }
55 
56             if(disposing)
57             {
58                 FlushBuffer();
59                 stream?.Dispose();
60             }
61             disposed = true;
62         }
63 
TryReadAndDisassembleInstruction(ulong pc, uint flags, out DisassemblyResult result)64         protected bool TryReadAndDisassembleInstruction(ulong pc, uint flags, out DisassemblyResult result)
65         {
66             // here we read only 4-bytes as it should cover most cases
67             var key = AttachedCPU.Bus.ReadDoubleWord(pc, context: AttachedCPU);
68             if(!disassemblyCache.TryGetValue(key, out result))
69             {
70                 // here we are prepared for longer opcodes
71                 var mem = AttachedCPU.Bus.ReadBytes(pc, MaxOpcodeBytes, context: AttachedCPU);
72                 if(!AttachedCPU.Disassembler.TryDisassembleInstruction(pc, mem, flags, out result))
73                 {
74                     result = new DisassemblyResult();
75                     // mark this as an invalid opcode
76                     disassemblyCache.Add(key, result);
77                 }
78                 else
79                 {
80                     if(result.OpcodeSize <= 4)
81                     {
82                         disassemblyCache.Add(key, result);
83                     }
84                 }
85             }
86 
87             if(result.OpcodeSize == 0)
88             {
89                 AttachedCPU.Log(LogLevel.Warning, "ExecutionTracer: couldn't disassemble opcode at PC 0x{0:X}", pc);
90                 return false;
91             }
92 
93             result.PC = pc;
94             return true;
95         }
96 
97         protected readonly TraceFormat format;
98         protected readonly Stream stream;
99         protected LRUCache<uint, Disassembler.DisassemblyResult> disassemblyCache;
100 
101         protected const int MaxOpcodeBytes = 16;
102 
103         private bool disposed;
104     }
105 }
106