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 Antmicro.Migrant; 10 using Antmicro.Renode.Exceptions; 11 using Antmicro.Renode.Logging; 12 13 namespace Antmicro.Renode.Peripherals.CPU.GuestProfiling 14 { 15 [Transient] 16 public abstract class BaseProfiler : IDisposable 17 { BaseProfiler(TranslationCPU cpu, bool flushInstantly, int? maximumNestedContexts)18 public BaseProfiler(TranslationCPU cpu, bool flushInstantly, int? maximumNestedContexts) 19 { 20 this.cpu = cpu; 21 this.flushInstantly = flushInstantly; 22 23 if(maximumNestedContexts <= 0) { 24 throw new ConstructionException("Optional maximumNestedContexts parameter must be a non-zero, positive integer."); 25 } 26 this.maximumNestedContexts = maximumNestedContexts; 27 28 isFirstFrame = true; 29 bufferLock = new Object(); 30 wholeExecution = new Dictionary<ulong, ProfilerContext>(); 31 wholeExecution.Add(currentContextId, new ProfilerContext()); 32 } 33 Dispose()34 public virtual void Dispose() 35 { 36 FlushBuffer(); 37 currentStack.Clear(); 38 currentContext.Clear(); 39 wholeExecution.Clear(); 40 } 41 StackFrameAdd(ulong currentAddress, ulong returnAddress, ulong instructionsCount)42 public abstract void StackFrameAdd(ulong currentAddress, ulong returnAddress, ulong instructionsCount); StackFramePop(ulong currentAddress, ulong returnAddress, ulong instructionsCount)43 public abstract void StackFramePop(ulong currentAddress, ulong returnAddress, ulong instructionsCount); OnContextChange(ulong newContextId)44 public abstract void OnContextChange(ulong newContextId); InterruptEnter(ulong interruptIndex)45 public abstract void InterruptEnter(ulong interruptIndex); InterruptExit(ulong interruptIndex)46 public abstract void InterruptExit(ulong interruptIndex); FlushBuffer()47 public abstract void FlushBuffer(); 48 GetCurrentStack()49 public virtual string GetCurrentStack() 50 { 51 throw new RecoverableException($"Functionality is not supported by the currently selected profiler"); 52 } 53 GetSymbolName(ulong address)54 protected string GetSymbolName(ulong address) 55 { 56 if(!cpu.Bus.TryFindSymbolAt(address, out var name, out var _, cpu)) 57 { 58 // Symbol not found - return address must serve as a symbol 59 name = $"0x{address:X}"; 60 } 61 return name; 62 } 63 PushCurrentContextSafe()64 protected void PushCurrentContextSafe() 65 { 66 if(maximumNestedContexts.HasValue && currentContext.Count >= maximumNestedContexts) 67 { 68 cpu.Log(LogLevel.Warning, "Profiler: maximum nested contexts exceeded, disabling profiler"); 69 cpu.DisableProfiler(); 70 return; 71 } 72 currentContext.PushCurrentStack(); 73 } 74 75 protected readonly TranslationCPU cpu; 76 protected readonly bool flushInstantly; 77 protected readonly Object bufferLock; 78 // Keeps track of all of the created contexts. Key is the id of the thread 79 protected readonly Dictionary<ulong, ProfilerContext> wholeExecution; 80 81 protected ProfilerContext currentContext => wholeExecution[currentContextId]; 82 protected Stack<string> currentStack => currentContext.CurrentStack; 83 protected bool isFirstFrame; 84 protected ulong currentContextId; 85 86 private readonly int? maximumNestedContexts; 87 88 protected class ProfilerContext 89 { ProfilerContext()90 public ProfilerContext() 91 { 92 context = new Stack<Stack<string>>(); 93 currentStack = new Stack<string>(); 94 } 95 PopCurrentStack()96 public void PopCurrentStack() 97 { 98 if(context.Count == 0) 99 { 100 currentStack = new Stack<string>(); 101 } 102 else 103 { 104 currentStack = context.Pop(); 105 } 106 } 107 PushCurrentStack()108 public void PushCurrentStack() 109 { 110 context.Push(currentStack); 111 currentStack = new Stack<string>(); 112 } 113 Clear()114 public void Clear() 115 { 116 context.Clear(); 117 } 118 119 public int Count => context.Count; 120 public Stack<string> CurrentStack => currentStack; 121 122 // We need to keep a stack of stacks as each context has its main execution and (potentially nested) interrupts 123 private readonly Stack<Stack<string>> context; 124 private Stack<string> currentStack; 125 } 126 } 127 } 128