1 //
2 // Copyright (c) 2010-2025 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.IO;
10 using System.Linq;
11 using System.Text;
12 using Antmicro.Renode.Debugging;
13 using Antmicro.Renode.Exceptions;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Peripherals.Bus;
16 using Antmicro.Renode.Utilities.Binding;
17 using Antmicro.Renode.Peripherals.CPU.GuestProfiling;
18 
19 namespace Antmicro.Renode.Peripherals.CPU
20 {
21     public abstract partial class TranslationCPU
22     {
EnableProfiler(ProfilerType type, string filename, bool flushInstantly = false, bool enableMultipleTracks = true, long? fileSizeLimit = null, int? maximumNestedContexts = null)23         public void EnableProfiler(ProfilerType type, string filename, bool flushInstantly = false, bool enableMultipleTracks = true, long? fileSizeLimit = null, int? maximumNestedContexts = null)
24         {
25             // Remove the old profiler if it was enabled
26             if(profiler != null)
27             {
28                 ConfigureProfilerInterruptHooks(false);
29                 profiler.Dispose();
30             }
31 
32             try
33             {
34                 File.WriteAllText(filename, string.Empty);
35             }
36             catch(Exception e)
37             {
38                 throw new RecoverableException($"There was an error when preparing the profiler output file {filename}: {e.Message}");
39             }
40 
41             switch(type)
42             {
43                 case ProfilerType.CollapsedStack:
44                     profiler = new CollapsedStackProfiler(this, filename, flushInstantly, fileSizeLimit, maximumNestedContexts);
45                     break;
46                 case ProfilerType.Perfetto:
47                     profiler = new PerfettoProfiler(this, filename, flushInstantly, enableMultipleTracks, fileSizeLimit, maximumNestedContexts);
48                     break;
49                 default:
50                     throw new RecoverableException($"{type} is not a valid profiler output type");
51             }
52 
53             ConfigureProfilerInterruptHooks(true);
54             TlibEnableGuestProfiler(1);
55         }
56 
EnableProfilerCollapsedStack(string filename, bool flushInstantly = false, long? fileSizeLimit = null, int? maximumNestedContexts = null)57         public void EnableProfilerCollapsedStack(string filename, bool flushInstantly = false, long? fileSizeLimit = null, int? maximumNestedContexts = null)
58         {
59             // CollapsedStack format doesn't support multiple tracks
60             EnableProfiler(ProfilerType.CollapsedStack, filename, flushInstantly, false, fileSizeLimit, maximumNestedContexts);
61         }
62 
EnableProfilerPerfetto(string filename, bool flushInstantly = false, bool enableMultipleTracks = true, long? fileSizeLimit = null, int? maximumNestedContexts = null)63         public void EnableProfilerPerfetto(string filename, bool flushInstantly = false, bool enableMultipleTracks = true, long? fileSizeLimit = null, int? maximumNestedContexts = null)
64         {
65             EnableProfiler(ProfilerType.Perfetto, filename, flushInstantly, enableMultipleTracks, fileSizeLimit, maximumNestedContexts);
66         }
67 
DisableProfiler()68         public void DisableProfiler()
69         {
70             if(profiler == null)
71             {
72                 throw new RecoverableException("The profiler is not enabled on this core");
73             }
74 
75             profiler.Dispose();
76             ConfigureProfilerInterruptHooks(false);
77             TlibEnableGuestProfiler(0);
78             profiler = null;
79         }
80 
FlushProfiler()81         public void FlushProfiler()
82         {
83             if(profiler == null)
84             {
85                 throw new RecoverableException("The profiler is not enabled on this core");
86             }
87 
88             profiler.FlushBuffer();
89         }
90 
GetProfilerStack()91         public string GetProfilerStack()
92         {
93             if(profiler == null)
94             {
95                 throw new RecoverableException("The profiler is not enabled on this core");
96             }
97 
98             return profiler.GetCurrentStack();
99         }
100 
101         public enum ProfilerType
102         {
103             CollapsedStack,
104             Perfetto
105         }
106 
107         public BaseProfiler Profiler => profiler;
108 
109         [Export]
OnStackChange(ulong currentAddress, ulong returnAddress, ulong instructionsCount, int isFrameAdd)110         protected void OnStackChange(ulong currentAddress, ulong returnAddress, ulong instructionsCount, int isFrameAdd)
111         {
112             // It is possible to execute stack change announcement from a currently executed translation block
113             // even after disabling the profiler and flushing the translation cache
114             if(profiler == null)
115             {
116                 return;
117             }
118             if(isFrameAdd != 0)
119             {
120                 profiler.StackFrameAdd(currentAddress, returnAddress, instructionsCount);
121             }
122             else
123             {
124                 profiler.StackFramePop(currentAddress, returnAddress, instructionsCount);
125             }
126         }
127 
128         [Export]
OnContextChange(ulong threadId)129         protected void OnContextChange(ulong threadId)
130         {
131             profiler.OnContextChange(threadId);
132         }
133 
ConfigureProfilerInterruptHooks(bool enabled)134         private void ConfigureProfilerInterruptHooks(bool enabled)
135         {
136             if(enabled)
137             {
138                 this.AddHookAtInterruptBegin(profiler.InterruptEnter);
139                 this.AddHookAtInterruptEnd(profiler.InterruptExit);
140             }
141             else
142             {
143                 this.RemoveHookAtInterruptBegin(profiler.InterruptEnter);
144                 this.RemoveHookAtInterruptEnd(profiler.InterruptExit);
145             }
146         }
147 
148         private BaseProfiler profiler;
149 
150 #pragma warning disable 649
151         [Import]
152         private Action<int> TlibEnableGuestProfiler;
153 #pragma warning restore 649
154     }
155 }
156