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