1 // 2 // Copyright (c) 2010-2018 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.UserInterface.Commands; 10 using Antmicro.Renode.UserInterface; 11 using AntShell.Commands; 12 using Antmicro.Renode.UserInterface.Tokenizer; 13 using Antmicro.Renode.Peripherals.CPU; 14 using Antmicro.Renode.Debug; 15 using Antmicro.Renode.Core; 16 using System.Collections.Generic; 17 using Antmicro.Renode.Exceptions; 18 using Antmicro.Renode.Utilities; 19 using System.Linq; 20 using Antmicro.Renode.Plugins.TracePlugin.Handlers; 21 using Dynamitey; 22 23 namespace Antmicro.Renode.Plugins.TracePlugin 24 { 25 public class TraceCommand : Command 26 { 27 PrintHelp(ICommandInteraction writer)28 public override void PrintHelp(ICommandInteraction writer) 29 { 30 base.PrintHelp(writer); 31 writer.WriteLine(); 32 writer.WriteLine("Usage:"); 33 writer.WriteLine("- to trace only function call with a registered handler:"); 34 writer.WriteLine("{0} {1} cpuName \"functionName\"".FormatWith(Name, TraceEnableCommand)); 35 writer.WriteLine(); 36 writer.WriteLine("- to trace function call and returned value with a registered handler:"); 37 writer.WriteLine("{0} {1} cpuName \"functionName\" true".FormatWith(Name, TraceEnableCommand)); 38 writer.WriteLine(); 39 writer.WriteLine("- to trace function call without a handler, with or without return value:"); 40 writer.WriteLine("{0} {1} cpuName \"functionName\" [true|false] [number of parameters]".FormatWith(Name, TraceEnableCommand)); 41 writer.WriteLine(); 42 writer.WriteLine("- to trace function call without a handler, with or without return value, with specified variable types:"); 43 writer.WriteLine("{0} {1} cpuName \"functionName\" [true|false] [list of parameter types]".FormatWith(Name, TraceEnableCommand)); 44 writer.WriteLine("(Note, that if return value is expected, the last parameter type must relate to return value."); 45 writer.WriteLine(); 46 writer.WriteLine("- to disable tracing of a function:"); 47 writer.WriteLine("{0} {1} cpuName \"functionName\"".FormatWith(Name, TraceDisableCommand)); 48 writer.WriteLine(); 49 writer.WriteLine("Handlers available for functions:"); 50 writer.WriteLine(handlers.Keys.Select(x => "- " + x).Stringify("\r\n")); 51 writer.WriteLine(); 52 writer.WriteLine("Possible values for parameter types:"); 53 writer.WriteLine(Enum.GetNames(typeof(FunctionCallParameterType)).Where(x => !x.Contains("Array")).Select(x => "- " + x).Stringify("\r\n")); 54 } 55 56 [Runnable] Run(ICommandInteraction writer, [Values(TraceEnableCommand, TraceDisableCommand)] LiteralToken enable, LiteralToken cpuToken, StringToken functionName)57 public void Run(ICommandInteraction writer, [Values(TraceEnableCommand, TraceDisableCommand)] LiteralToken enable, LiteralToken cpuToken, StringToken functionName) 58 { 59 if(enable.Value == TraceEnableCommand) 60 { 61 Execute(writer, cpuToken, functionName.Value, false, null); 62 } 63 else 64 { 65 var cpu = (Arm)monitor.ConvertValueOrThrowRecoverable(cpuToken.Value, typeof(Arm)); 66 var cpuTracer = EnsureTracer(cpu); 67 cpuTracer.RemoveTracing(functionName.Value); 68 } 69 } 70 71 [Runnable] Run(ICommandInteraction writer, [Values(TraceEnableCommand)] LiteralToken enable, LiteralToken cpuToken, StringToken functionName, BooleanToken traceReturn)72 public void Run(ICommandInteraction writer, [Values(TraceEnableCommand)] LiteralToken enable, LiteralToken cpuToken, StringToken functionName, BooleanToken traceReturn) 73 { 74 Execute(writer, cpuToken, functionName.Value, traceReturn.Value, null); 75 } 76 77 [Runnable] Run(ICommandInteraction writer, [Values(TraceEnableCommand)] LiteralToken enable, LiteralToken cpuToken, StringToken functionName, BooleanToken traceReturn, DecimalIntegerToken numberOfParameters)78 public void Run(ICommandInteraction writer, [Values(TraceEnableCommand)] LiteralToken enable, LiteralToken cpuToken, StringToken functionName, BooleanToken traceReturn, DecimalIntegerToken numberOfParameters) 79 { 80 Execute(writer, cpuToken, functionName.Value, traceReturn.Value, (int)numberOfParameters.Value); 81 } 82 83 [Runnable] Run(ICommandInteraction writer, [Values(TraceEnableCommand)] LiteralToken enable, LiteralToken cpuToken, StringToken functionName, BooleanToken traceReturn, params LiteralToken[] types)84 public void Run(ICommandInteraction writer, [Values(TraceEnableCommand)] LiteralToken enable, LiteralToken cpuToken, StringToken functionName, BooleanToken traceReturn, params LiteralToken[] types) 85 { 86 var cpu = (Arm)monitor.ConvertValueOrThrowRecoverable(cpuToken.Value, typeof(Arm)); 87 var cpuTracer = EnsureTracer(cpu); 88 var handler = new DefaultFunctionHandler(cpu); 89 var paramList = new List<FunctionCallParameter>(); 90 foreach(var parameter in types) 91 { 92 FunctionCallParameterType paramType; 93 if(!Enum.TryParse(parameter.Value, out paramType)) 94 { 95 throw new RecoverableException("{0} is not a proper parameter type.".FormatWith(parameter.Value)); 96 } 97 paramList.Add(new FunctionCallParameter{ Type = paramType }); 98 } 99 handler.CallParameters = paramList.Take(paramList.Count - (traceReturn.Value ? 1 : 0)); 100 handler.ReturnParameter = traceReturn.Value ? paramList.Last() : (FunctionCallParameter?)null; 101 if(traceReturn.Value) 102 { 103 cpuTracer.TraceFunction(functionName.Value, handler.CallParameters, handler.CallHandler, handler.ReturnParameter, handler.ReturnHandler); 104 } 105 else 106 { 107 cpuTracer.TraceFunction(functionName.Value, handler.CallParameters, handler.CallHandler); 108 } 109 110 } 111 RegisterFunctionName(string function, Type callbackType)112 public void RegisterFunctionName(string function, Type callbackType) 113 { 114 if(handlers.ContainsKey(function)) 115 { 116 throw new RecoverableException("Function \"{0}\" already registered.".FormatWith(function)); 117 } 118 handlers[function] = callbackType; 119 } 120 TraceCommand(Monitor monitor)121 public TraceCommand(Monitor monitor) : base(monitor, "trace", "Hooks up watches for some interesting methods.") 122 { 123 124 handlers = new Dictionary<string, Type> 125 { 126 { "printk", typeof(PrintfHandler) }, 127 { "printf", typeof(PrintfHandler) } 128 }; 129 } 130 FindTracerName(Arm cpu)131 private string FindTracerName(Arm cpu) 132 { 133 string cpuName; 134 if(!cpu.Bus.Machine.TryGetAnyName(cpu, out cpuName)) 135 { 136 throw new Exception("This should never have happened!"); 137 } 138 return "{0}.{1}-{2}".FormatWith(EmulationManager.Instance.CurrentEmulation[cpu.Bus.Machine], cpuName, TracerName); 139 } 140 Execute(ICommandInteraction writer, LiteralToken cpuToken, String functionName, bool traceReturn, int? numberOfParameters)141 private void Execute(ICommandInteraction writer, LiteralToken cpuToken, String functionName, bool traceReturn, int? numberOfParameters) 142 { 143 144 var cpu = (Arm)monitor.ConvertValueOrThrowRecoverable(cpuToken.Value, typeof(Arm)); 145 146 var cpuTracer = EnsureTracer(cpu); 147 Type handlerType; 148 IFunctionHandler handler; 149 if(!handlers.TryGetValue(functionName, out handlerType)) 150 { 151 if(numberOfParameters.HasValue) 152 { 153 var paramList = new List<FunctionCallParameter>(); 154 for(var i = 0; i < numberOfParameters; ++i) 155 { 156 paramList.Add(new FunctionCallParameter{ Type = FunctionCallParameterType.UInt32 }); 157 } 158 FunctionCallParameter? returnParameter = null; 159 if(traceReturn) 160 { 161 returnParameter = new FunctionCallParameter{ Type = FunctionCallParameterType.UInt32 }; 162 } 163 var defHandler = new DefaultFunctionHandler(cpu); 164 defHandler.CallParameters = paramList; 165 defHandler.ReturnParameter = returnParameter; 166 handler = defHandler; 167 } 168 else 169 { 170 throw new RecoverableException("Handler for {0} not register. You must provide numberOfParameters to use default handler.".FormatWith(functionName)); 171 } 172 } 173 else 174 { 175 handler = Dynamic.InvokeConstructor(handlerType, cpu); 176 } 177 if(traceReturn) 178 { 179 cpuTracer.TraceFunction(functionName, handler.CallParameters, handler.CallHandler, handler.ReturnParameter, handler.ReturnHandler); 180 } 181 else 182 { 183 cpuTracer.TraceFunction(functionName, handler.CallParameters, handler.CallHandler); 184 } 185 } 186 EnsureTracer(Arm cpu)187 private CPUTracer EnsureTracer(Arm cpu) 188 { 189 CPUTracer tracer; 190 var tracerName = FindTracerName(cpu); 191 if(!EmulationManager.Instance.CurrentEmulation.TryGetFromBag(tracerName, out tracer)) 192 { 193 cpu.CreateCPUTracer(tracerName); 194 } 195 if(!EmulationManager.Instance.CurrentEmulation.TryGetFromBag(tracerName, out tracer)) 196 { 197 throw new RecoverableException("Could not initialize CPUTracer."); 198 } 199 return tracer; 200 } 201 202 private readonly Dictionary<String, Type> handlers; 203 204 private const string TracerName = "tracealyzerTracer"; 205 private const string TraceEnableCommand = "enable"; 206 private const string TraceDisableCommand = "disable"; 207 } 208 }