From a3f656eb28ffce71deccf8f893e62982c16ccde7 Mon Sep 17 00:00:00 2001 From: Brian Robbins Date: Mon, 17 Aug 2026 13:44:44 -0700 Subject: [PATCH 1/2] Improve stack names for generated JIT helpers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/TraceEvent/Parsers/ClrTraceEventParser.cs | 1 + src/TraceEvent/TraceLog.cs | 84 ++++++++++++++----- 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/TraceEvent/Parsers/ClrTraceEventParser.cs b/src/TraceEvent/Parsers/ClrTraceEventParser.cs index a905fa8bb..d49c5f3f9 100644 --- a/src/TraceEvent/Parsers/ClrTraceEventParser.cs +++ b/src/TraceEvent/Parsers/ClrTraceEventParser.cs @@ -11956,6 +11956,7 @@ public abstract class MethodLoadUnloadTraceDataBase : TraceEvent public bool IsDynamic { get { return (MethodFlags & MethodFlags.Dynamic) != 0; } } public bool IsGeneric { get { return (MethodFlags & MethodFlags.Generic) != 0; } } public bool IsJitted { get { return (MethodFlags & MethodFlags.Jitted) != 0; } } + public bool IsJitHelper { get { return (MethodFlags & MethodFlags.JitHelper) != 0; } } public OptimizationTier OptimizationTier { diff --git a/src/TraceEvent/TraceLog.cs b/src/TraceEvent/TraceLog.cs index f1036b304..298645128 100644 --- a/src/TraceEvent/TraceLog.cs +++ b/src/TraceEvent/TraceLog.cs @@ -346,8 +346,8 @@ private void SetupInitialRundownCallbacks(EventPipeEventSource rawEvents) TraceProcess process = processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC); process.InsertJITTEDMethod(data.MethodStartAddress, data.MethodSize, delegate () { - TraceManagedModule module = process.LoadedModules.GetOrCreateManagedModule(data.ModuleID, data.TimeStampQPC); - MethodIndex methodIndex = CodeAddresses.Methods.NewMethod(GetFullName(data), module.ModuleFile.ModuleFileIndex, data.MethodToken); + TraceModuleFile moduleFile = process.LoadedModules.GetOrCreateMethodModuleFile(data); + MethodIndex methodIndex = CodeAddresses.Methods.NewMethod(GetFullName(data), moduleFile.ModuleFileIndex, data.MethodToken); return new TraceProcess.MethodLookupInfo(data.MethodStartAddress, data.MethodSize, methodIndex); }); }; @@ -1363,7 +1363,7 @@ private unsafe void SetupCallbacks(TraceEventDispatcher rawEvents) eventCount = 0; // FIX NOW HACK, because Method and Module unload methods are missing. - jittedMethods = new List(); + methodLoadEvents = new List(); jsJittedMethods = new List(); sourceFilesByID = new Dictionary(); @@ -1684,17 +1684,17 @@ private unsafe void SetupCallbacks(TraceEventDispatcher rawEvents) bookKeepingEvent = true; } - if (data.IsJitted) + if (ShouldTrackMethodLoad(data.MethodFlags)) { TraceProcess process = processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC); process.InsertJITTEDMethod(data.MethodStartAddress, data.MethodSize, delegate () { - TraceManagedModule module = process.LoadedModules.GetOrCreateManagedModule(data.ModuleID, data.TimeStampQPC); - MethodIndex methodIndex = CodeAddresses.Methods.NewMethod(TraceLog.GetFullName(data), module.ModuleFile.ModuleFileIndex, data.MethodToken); + TraceModuleFile moduleFile = process.LoadedModules.GetOrCreateMethodModuleFile(data); + MethodIndex methodIndex = CodeAddresses.Methods.NewMethod(TraceLog.GetFullName(data), moduleFile.ModuleFileIndex, data.MethodToken); return new TraceProcess.MethodLookupInfo(data.MethodStartAddress, data.MethodSize, methodIndex); }); - jittedMethods.Add((MethodLoadUnloadVerboseTraceData)data.Clone()); + methodLoadEvents.Add((MethodLoadUnloadVerboseTraceData)data.Clone()); } }; rawEvents.Clr.MethodLoadVerbose += onMethodStart; @@ -2427,9 +2427,9 @@ private unsafe void CopyRawEvents(TraceEventDispatcher rawEvents, IStreamWriter userStackKeyToInfo = null; // TODO FIX NOW hack because unloadMethod not present - foreach (var jittedMethod in jittedMethods) + foreach (var methodLoadEvent in methodLoadEvents) { - codeAddresses.AddMethod(jittedMethod); + codeAddresses.AddMethod(methodLoadEvent); } foreach (var jsJittedMethod in jsJittedMethods) @@ -3531,22 +3531,36 @@ internal void DebugWarn(bool condition, string message, TraceEvent data) } internal static string GetFullName(MethodLoadUnloadVerboseTraceData data) { - string sig = data.MethodSignature; - int parens = sig.IndexOf('('); + return GetFullName(data.MethodFlags, data.MethodNamespace, data.MethodName, data.MethodSignature); + } + + private static string GetFullName(MethodFlags methodFlags, string methodNamespace, string methodName, string methodSignature) + { + if ((methodFlags & MethodFlags.JitHelper) != 0) + { + return methodName; + } + + int parens = methodSignature.IndexOf('('); string args; if (parens >= 0) { - args = sig.Substring(parens); + args = methodSignature.Substring(parens); } else { args = ""; } - string fullName = data.MethodNamespace + "." + data.MethodName + args; + string fullName = methodNamespace + "." + methodName + args; return fullName; } + private static bool ShouldTrackMethodLoad(MethodFlags methodFlags) + { + return (methodFlags & (MethodFlags.Jitted | MethodFlags.JitHelper)) != 0; + } + internal int FindPageIndex(long timeQPC) { int pageIndex; @@ -4202,8 +4216,8 @@ int IFastSerializableVersion.MinimumReaderVersion private bool noStack; // This event should never have a stack associated with it, so skip them if we every try to attach a stack. private TraceThread thread; // cache of the TraceThread for the current event. - // TODO FIX NOW remove the jittedMethods ones. - private List jittedMethods; + // TODO FIX NOW remove the methodLoadEvents ones. + private List methodLoadEvents; private List jsJittedMethods; private Dictionary sourceFilesByID; @@ -7225,6 +7239,29 @@ internal TraceManagedModule GetOrCreateManagedModule(long managedModuleID, long } return module; } + + internal TraceModuleFile GetOrCreateMethodModuleFile(MethodLoadUnloadVerboseTraceData data) + { + if (data.IsJitHelper) + { + return GetOrCreateJitHelperModuleFile(); + } + + return GetOrCreateManagedModule(data.ModuleID, data.TimeStampQPC).ModuleFile; + } + + private TraceModuleFile GetOrCreateJitHelperModuleFile() + { + if (jitHelperModuleFile == null) + { + // Stash process-private, dynamically generated runtime helpers under an invalid file path + // so they are clearly synthetic while still displaying cleanly in stacks. + string modulePath = "[GeneratedRuntimeHelpers:ProcessIndex=" + process.ProcessIndex + "]\\GeneratedRuntimeHelpers.dll"; + jitHelperModuleFile = process.Log.ModuleFiles.GetOrCreateModuleFile(modulePath, 0, false); + } + + return jitHelperModuleFile; + } /// /// Finds the index and module for an a given managed module ID. If not found, new module /// should be inserted at index + 1; @@ -7411,6 +7448,7 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() private TraceProcess process; private GrowableArray modules; // Contains unmanaged modules sorted by key + private TraceModuleFile jitHelperModuleFile; #endregion } @@ -8536,7 +8574,7 @@ internal void AddMethod(MethodLoadUnloadVerboseTraceData data) MethodIndex methodIndex = Microsoft.Diagnostics.Tracing.Etlx.MethodIndex.Invalid; ILMapIndex ilMap = ILMapIndex.Invalid; ModuleFileIndex moduleFileIndex = Microsoft.Diagnostics.Tracing.Etlx.ModuleFileIndex.Invalid; - TraceManagedModule module = null; + TraceModuleFile moduleFile = null; TraceProcess process = log.Processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC); ForAllUnresolvedCodeAddressesInRange(process, data.MethodStartAddress, data.MethodSize, true, delegate (ref CodeAddressInfo info) { @@ -8545,10 +8583,10 @@ internal void AddMethod(MethodLoadUnloadVerboseTraceData data) if (info.GetMethodIndex(this) == Microsoft.Diagnostics.Tracing.Etlx.MethodIndex.Invalid) { // Lazily create the method since many methods never have code samples in them. - if (module == null) + if (moduleFile == null) { - module = process.LoadedModules.GetOrCreateManagedModule(data.ModuleID, data.TimeStampQPC); - moduleFileIndex = module.ModuleFile.ModuleFileIndex; + moduleFile = process.LoadedModules.GetOrCreateMethodModuleFile(data); + moduleFileIndex = moduleFile.ModuleFileIndex; methodIndex = methods.NewMethod(TraceLog.GetFullName(data), moduleFileIndex, data.MethodToken); if (data.IsJitted) { @@ -10427,7 +10465,7 @@ internal void SetModuleFileName(TraceModuleFile moduleFile, string fileName) /// cache entry associated with 'nativePath' and 'moduleImageBase'. 'moduleImageBase' can be 0 for managed assemblies /// that were not loaded with LoadLibrary. /// - internal TraceModuleFile GetOrCreateModuleFile(string nativePath, Address imageBase) + internal TraceModuleFile GetOrCreateModuleFile(string nativePath, Address imageBase, bool normalizePath = true) { TraceModuleFile moduleFile = null; if (nativePath != null) @@ -10437,7 +10475,7 @@ internal TraceModuleFile GetOrCreateModuleFile(string nativePath, Address imageB if (moduleFile == null) { - moduleFile = new TraceModuleFile(nativePath, imageBase, (ModuleFileIndex)moduleFiles.Count); + moduleFile = new TraceModuleFile(nativePath, imageBase, (ModuleFileIndex)moduleFiles.Count, normalizePath); moduleFiles.Add(moduleFile); if (nativePath != null) { @@ -10784,11 +10822,11 @@ public override string ToString() "/>"; } #region Private - internal TraceModuleFile(string fileName, Address imageBase, ModuleFileIndex moduleFileIndex) + internal TraceModuleFile(string fileName, Address imageBase, ModuleFileIndex moduleFileIndex, bool normalizePath = true) { if (fileName != null) { - this.fileName = fileName.ToLowerInvariant(); // Normalize to lower case. + this.fileName = normalizePath ? fileName.ToLowerInvariant() : fileName; } this.imageBase = imageBase; From bd09d57ab3d4a2612a34d31555cad2a9a3f02032 Mon Sep 17 00:00:00 2001 From: Brian Robbins Date: Mon, 17 Aug 2026 13:53:16 -0700 Subject: [PATCH 2/2] Simplify generated helper module path handling Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/TraceEvent/TraceLog.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/TraceEvent/TraceLog.cs b/src/TraceEvent/TraceLog.cs index 298645128..0988ea73a 100644 --- a/src/TraceEvent/TraceLog.cs +++ b/src/TraceEvent/TraceLog.cs @@ -7256,8 +7256,8 @@ private TraceModuleFile GetOrCreateJitHelperModuleFile() { // Stash process-private, dynamically generated runtime helpers under an invalid file path // so they are clearly synthetic while still displaying cleanly in stacks. - string modulePath = "[GeneratedRuntimeHelpers:ProcessIndex=" + process.ProcessIndex + "]\\GeneratedRuntimeHelpers.dll"; - jitHelperModuleFile = process.Log.ModuleFiles.GetOrCreateModuleFile(modulePath, 0, false); + string modulePath = "[generatedruntimehelpers:processindex=" + process.ProcessIndex + "]\\generatedruntimehelpers.dll"; + jitHelperModuleFile = process.Log.ModuleFiles.GetOrCreateModuleFile(modulePath, 0); } return jitHelperModuleFile; @@ -10465,7 +10465,7 @@ internal void SetModuleFileName(TraceModuleFile moduleFile, string fileName) /// cache entry associated with 'nativePath' and 'moduleImageBase'. 'moduleImageBase' can be 0 for managed assemblies /// that were not loaded with LoadLibrary. /// - internal TraceModuleFile GetOrCreateModuleFile(string nativePath, Address imageBase, bool normalizePath = true) + internal TraceModuleFile GetOrCreateModuleFile(string nativePath, Address imageBase) { TraceModuleFile moduleFile = null; if (nativePath != null) @@ -10475,7 +10475,7 @@ internal TraceModuleFile GetOrCreateModuleFile(string nativePath, Address imageB if (moduleFile == null) { - moduleFile = new TraceModuleFile(nativePath, imageBase, (ModuleFileIndex)moduleFiles.Count, normalizePath); + moduleFile = new TraceModuleFile(nativePath, imageBase, (ModuleFileIndex)moduleFiles.Count); moduleFiles.Add(moduleFile); if (nativePath != null) { @@ -10822,11 +10822,11 @@ public override string ToString() "/>"; } #region Private - internal TraceModuleFile(string fileName, Address imageBase, ModuleFileIndex moduleFileIndex, bool normalizePath = true) + internal TraceModuleFile(string fileName, Address imageBase, ModuleFileIndex moduleFileIndex) { if (fileName != null) { - this.fileName = normalizePath ? fileName.ToLowerInvariant() : fileName; + this.fileName = fileName.ToLowerInvariant(); // Normalize to lower case. } this.imageBase = imageBase;