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 }