1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using Antmicro.Renode.Peripherals.CPU; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Logging; 12 using System.Collections.Generic; 13 using System.Linq; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Peripherals.Bus; 16 using System.Text; 17 using Antmicro.Renode.Utilities; 18 19 namespace Antmicro.Renode.Debug 20 { 21 public static class CPUTracerExtensions 22 { CreateCPUTracer(this Arm cpu, string name)23 public static void CreateCPUTracer(this Arm cpu, string name) 24 { 25 EmulationManager.Instance.CurrentEmulation.AddOrUpdateInBag(name, new CPUTracer(cpu)); 26 } 27 } 28 29 public class CPUTracer : IExternal 30 { CPUTracer(Arm cpu)31 public CPUTracer(Arm cpu) 32 { 33 this.cpu = cpu; 34 this.bus = cpu.Bus; 35 } 36 TraceFunction(string name, IEnumerable<FunctionCallParameter> parameters, Action<TranslationCPU, ulong, string, IEnumerable<object>> callback, FunctionCallParameter? returnParameter = null, Action<TranslationCPU, ulong, string, IEnumerable<object>> returnCallback = null)37 public void TraceFunction(string name, IEnumerable<FunctionCallParameter> parameters, Action<TranslationCPU, ulong, string, IEnumerable<object>> callback, 38 FunctionCallParameter? returnParameter = null, Action<TranslationCPU, ulong, string, IEnumerable<object>> returnCallback = null) 39 { 40 if(registeredCallbacks.ContainsKey(name)) 41 { 42 throw new RecoverableException("Function {0} is already being traced.".FormatWith(name)); 43 } 44 cpu.Log(LogLevel.Info, "Going to trace function '{0}'.", name); 45 46 Symbol symbol; 47 try 48 { 49 var address = cpu.Bus.GetSymbolAddress(name); 50 symbol = cpu.Bus.GetLookup(cpu).GetSymbolByAddress(address); 51 } 52 catch(RecoverableException) 53 { 54 cpu.Log(LogLevel.Warning, "Symbol {0} not found, exiting.", name); 55 throw; 56 } 57 58 var traceInfo = new TraceInfo(); 59 traceInfo.Begin = symbol.Start.RawValue; 60 traceInfo.BeginCallback = (cpu, pc) => EvaluateTraceCallback(pc, name, parameters, callback); 61 62 cpu.AddHook(traceInfo.Begin, traceInfo.BeginCallback); 63 if(returnCallback != null && returnParameter.HasValue) 64 { 65 traceInfo.HasEnd = true; 66 traceInfo.End = symbol.End.RawValue - (symbol.IsThumbSymbol ? 2 : 4UL); 67 traceInfo.EndCallback = (cpu, pc) => EvaluateTraceCallback(pc, name, new[] { returnParameter.Value }, returnCallback); 68 cpu.Log(LogLevel.Debug, "Address is @ 0x{0:X}, end is @ 0x{1:X}.", traceInfo.Begin, traceInfo.End); 69 cpu.AddHook(traceInfo.End, traceInfo.EndCallback); 70 } 71 else 72 { 73 cpu.Log(LogLevel.Debug, "Address is @ 0x{0:X}, end is not traced.", traceInfo.Begin); 74 } 75 76 registeredCallbacks[name] = traceInfo; 77 } 78 RemoveTracing(string name)79 public void RemoveTracing(string name) 80 { 81 TraceInfo traceInfo; 82 if(registeredCallbacks.TryGetValue(name, out traceInfo)) 83 { 84 cpu.Log(LogLevel.Info, "Removing trace from function '{0}'.", name); 85 cpu.RemoveHook(traceInfo.Begin, traceInfo.BeginCallback); 86 if(traceInfo.HasEnd) 87 { 88 cpu.RemoveHook(traceInfo.End, traceInfo.EndCallback); 89 } 90 } 91 else 92 { 93 cpu.Log(LogLevel.Warning, "Hook on function {0} not found, not removing.", name); 94 } 95 } 96 EvaluateTraceCallback(ulong pc, string name, IEnumerable<FunctionCallParameter> parameters, Action<TranslationCPU, ulong, string, List<object>> callback)97 private void EvaluateTraceCallback(ulong pc, string name, IEnumerable<FunctionCallParameter> parameters, Action<TranslationCPU, ulong, string, List<object>> callback) 98 { 99 var regs = new List<object>(); 100 var paramList = parameters.ToList(); 101 //works only for 0-4 parameters! 102 for(int i = 0; i < Math.Min(paramList.Count, 4); i++) 103 { 104 regs.Add(TranslateParameter(cpu.R[i], paramList[i])); 105 } 106 if(paramList.Count > 4) 107 { 108 var offset = 0u; 109 var sp = cpu.SP; 110 for(int i = 4; i < paramList.Count; ++i) 111 { 112 regs.Add(TranslateParameter(cpu.Bus.ReadDoubleWord(sp + offset), paramList[i])); 113 offset += 4; //does not support longer data types! 114 } 115 } 116 callback(cpu, pc, name, regs); 117 } 118 TranslateParameter(ulong value, FunctionCallParameter parameter)119 private object TranslateParameter(ulong value, FunctionCallParameter parameter) 120 { 121 var parameterType = parameter.Type; 122 var size = parameter.NumberOfElements; 123 switch(parameterType) 124 { 125 case FunctionCallParameterType.Ignore: 126 return null; 127 case FunctionCallParameterType.Int64: 128 return unchecked((long)value); 129 case FunctionCallParameterType.UInt64: 130 return value; 131 case FunctionCallParameterType.Byte: 132 return (byte)value; 133 case FunctionCallParameterType.Int32: 134 return unchecked((int)value); 135 case FunctionCallParameterType.UInt32: 136 return (uint)value; 137 case FunctionCallParameterType.Int16: 138 return unchecked((short)value); 139 case FunctionCallParameterType.UInt16: 140 return (ushort)value; 141 case FunctionCallParameterType.String: 142 var done = false; 143 var resultString = new StringBuilder(); 144 while(!done) 145 { 146 var readBytes = bus.ReadBytes(value, SizeOfStringBatch, context: cpu); 147 for(var i = 0; i < readBytes.Length; ++i) 148 { 149 var currentByte = readBytes[i]; 150 if(currentByte == 0) 151 { 152 done = true; 153 break; 154 } 155 if(currentByte >= 32 && currentByte < 127) 156 { 157 resultString.Append(Convert.ToChar(currentByte)); 158 } 159 } 160 value += SizeOfStringBatch; 161 } 162 return resultString.ToString(); 163 case FunctionCallParameterType.ByteArray: 164 return bus.ReadBytes(value, size, context: cpu); 165 case FunctionCallParameterType.Int32Array: 166 { 167 var intResult = new int[size]; 168 var byteLength = size * sizeof(int); 169 Buffer.BlockCopy(bus.ReadBytes(value, byteLength, context: cpu), 0, intResult, 0, byteLength); 170 return intResult; 171 } 172 case FunctionCallParameterType.UInt32Array: 173 { 174 var uintResult = new uint[size]; 175 var byteLength = size * sizeof(uint); 176 Buffer.BlockCopy(bus.ReadBytes(value, byteLength, context: cpu), 0, uintResult, 0, byteLength); 177 return uintResult; 178 } 179 default: 180 throw new ArgumentOutOfRangeException(); 181 } 182 } 183 184 private readonly Dictionary<string, TraceInfo> registeredCallbacks = new Dictionary<string, TraceInfo>(); 185 private readonly Arm cpu; 186 private readonly IBusController bus; 187 188 private const int SizeOfStringBatch = 100; 189 190 private struct TraceInfo 191 { 192 public ulong Begin; 193 public bool HasEnd; 194 public ulong End; 195 public Action<ICpuSupportingGdb, ulong> BeginCallback; 196 public Action<ICpuSupportingGdb, ulong> EndCallback; 197 } 198 } 199 } 200