From 7b5820297bddca98fb82e87e5a01a3380f011001 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Mon, 29 Jun 2026 18:04:16 +0300 Subject: [PATCH] [clr-interp] Add support for logging interpreter IR ranges to perf map Tested that it works correctly when passing the following env vars to the runtime: ``` export DOTNET_PerfMapEnabled=1 export DOTNET_PerfMapStubGranularity=2 ``` --- src/coreclr/vm/perfmap.cpp | 41 ++++++++++++++++++++++++++++++++++++++ src/coreclr/vm/perfmap.h | 5 +++++ src/coreclr/vm/prestub.cpp | 16 +++++++++++++-- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index 248488d1383dc2..6b11db5d85b585 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -416,6 +416,47 @@ void PerfMap::LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode) EX_CATCH{} EX_END_CATCH } +#ifdef FEATURE_INTERPRETER +// Log an interpreter IR bytecode range to the perfmap. +// This allows symbolication from perf scripts, when the interpreter IP is allocated to a fixed +// register in InterpExecMethod +void PerfMap::LogInterpreterMethod(MethodDesc * pMethod, PCODE irAddress, size_t irSize) +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + PRECONDITION(pMethod != nullptr); + PRECONDITION(irAddress != nullptr); + PRECONDITION(irSize > 0); + } CONTRACTL_END; + + if (!s_enabled) + { + return; + } + + EX_TRY + { + SString name; + pMethod->GetFullMethodInfo(name); + + SString line; + line.Printf(FMT_CODE_ADDR " %x [Interpreter] %s\n", irAddress, irSize, name.GetUTF8()); + + { + CrstHolder ch(&(s_csPerfMap)); + + if (s_Current != nullptr) + { + s_Current->WriteLine(line); + } + } + } + EX_CATCH{} EX_END_CATCH +} +#endif // FEATURE_INTERPRETER + // Log a set of stub to the map. void PerfMap::LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, size_t codeSize, PerfMapStubType stubAllocationType) { diff --git a/src/coreclr/vm/perfmap.h b/src/coreclr/vm/perfmap.h index c748c95bd67f40..17b1e645bf7efc 100644 --- a/src/coreclr/vm/perfmap.h +++ b/src/coreclr/vm/perfmap.h @@ -148,6 +148,11 @@ class PerfMap // Log a pre-compiled method to the map. static void LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode); +#ifdef FEATURE_INTERPRETER + // Log an interpreter IR bytecode range to the perfmap + static void LogInterpreterMethod(MethodDesc * pMethod, PCODE irAddress, size_t irSize); +#endif + // Log a set of stub to the map. static void LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, size_t codeSize, PerfMapStubType stubAllocationType); diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 4cbd3847c2e266..119789e2dd97e1 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -878,8 +878,20 @@ PCODE MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, J #endif // PROFILING_SUPPORTED #ifdef FEATURE_PERFMAP - // Save the JIT'd method information so that perf can resolve JIT'd call frames. - PerfMap::LogJITCompiledMethod(this, pCode, sizeOfCode, pConfig); +#if defined(FEATURE_INTERPRETER) + if (isInterpreterCode) + { + InterpreterPrecode* pPrecode = InterpreterPrecode::FromEntryPoint(pCode); + InterpByteCodeStart* interpreterCode = (InterpByteCodeStart*)pPrecode->GetData()->ByteCodeAddr; + PCODE irAddress = PINSTRToPCODE((TADDR)interpreterCode); + PerfMap::LogInterpreterMethod(this, irAddress, sizeOfCode); + } + else +#endif // FEATURE_INTERPRETER + { + // Save the JIT'd method information so that perf can resolve JIT'd call frames. + PerfMap::LogJITCompiledMethod(this, pCode, sizeOfCode, pConfig); + } #endif // The notification will only occur if someone has registered for this method.