diff --git a/src/ProfileExplorerCore/Profile/Data/RawProfileData.cs b/src/ProfileExplorerCore/Profile/Data/RawProfileData.cs index 8229ef04..bb8ee111 100644 --- a/src/ProfileExplorerCore/Profile/Data/RawProfileData.cs +++ b/src/ProfileExplorerCore/Profile/Data/RawProfileData.cs @@ -26,6 +26,15 @@ public class RawProfileData : IDisposable { private static ProfileImage lastIpImage_; [ThreadStatic] private static IpToImageCache globalIpImageCache_; + + /// + /// Clears thread-local caches on the current thread + /// + public static void ClearThreadLocalCaches() { + ipImageCache_ = null; + lastIpImage_ = null; + globalIpImageCache_ = null; + } [ProtoMember(1)] private List samples_; [ProtoMember(2)] diff --git a/src/ProfileExplorerCore/Profile/Data/ResolvedProfileStack.cs b/src/ProfileExplorerCore/Profile/Data/ResolvedProfileStack.cs index aff87dc2..39ed2472 100644 --- a/src/ProfileExplorerCore/Profile/Data/ResolvedProfileStack.cs +++ b/src/ProfileExplorerCore/Profile/Data/ResolvedProfileStack.cs @@ -86,6 +86,16 @@ public sealed class ResolvedProfileStack { public static ConcurrentDictionary frameInstances_ = new(); public static ConcurrentDictionary kernelFrameInstances_ = new(); + /// + /// Clears all static frame caches. Must be called before processing a new trace + /// to prevent stale frame references from a previous session. + /// + public static void ResetCaches() { + uniqueFrames_.Clear(); + frameInstances_.Clear(); + kernelFrameInstances_.Clear(); + } + public ResolvedProfileStack(int frameCount, ProfileContext context) { StackFrames = new List(frameCount); Context = context; diff --git a/src/ProfileExplorerCore/Profile/ETW/ETWProfileDataProvider.cs b/src/ProfileExplorerCore/Profile/ETW/ETWProfileDataProvider.cs index 575601cc..b46beebf 100644 --- a/src/ProfileExplorerCore/Profile/ETW/ETWProfileDataProvider.cs +++ b/src/ProfileExplorerCore/Profile/ETW/ETWProfileDataProvider.cs @@ -69,6 +69,10 @@ public ETWProfileDataProvider() { for (int i = 0; i < imageLocks_.Length; i++) { imageLocks_[i] = new object(); } + + // Clear static caches from any previous trace load to prevent + // stale frame/function references leaking across sessions. + ResolvedProfileStack.ResetCaches(); } public static async Task> @@ -425,6 +429,12 @@ private void CollectChunkSamples(List(end - start + 1); diff --git a/src/ProfileExplorerUI/MainWindowPanels.cs b/src/ProfileExplorerUI/MainWindowPanels.cs index 17ad816c..4ee9144b 100644 --- a/src/ProfileExplorerUI/MainWindowPanels.cs +++ b/src/ProfileExplorerUI/MainWindowPanels.cs @@ -1032,10 +1032,13 @@ private async Task SetupSectionPanel() { SectionPanel.CompilerInfo = compilerInfo_; SectionPanel.Session = this; + // Clear stale module summaries from any previous session + // before adding the new ones. + SectionPanel.ClearModuleSummaries(); + foreach (var doc in sessionState_.Documents) { if (doc != sessionState_.MainDocument && doc != sessionState_.DiffDocument) { - // Add optional modules, usually used for profiling. SectionPanel.AddModuleSummary(doc.Summary); } } diff --git a/src/ProfileExplorerUI/Panels/SectionPanel.xaml.cs b/src/ProfileExplorerUI/Panels/SectionPanel.xaml.cs index a2de56f6..2d79d2e2 100644 --- a/src/ProfileExplorerUI/Panels/SectionPanel.xaml.cs +++ b/src/ProfileExplorerUI/Panels/SectionPanel.xaml.cs @@ -1252,6 +1252,11 @@ public void AddModuleSummary(IRTextSummary summary) { } } + public void ClearModuleSummaries() { + moduleSummaries_.Clear(); + sectionExtensionComputed_ = false; + } + public bool HasSummary(IRTextSummary summary) { return summary == summary_ || moduleSummaries_.Contains(summary); diff --git a/src/ProfileExplorerUI/Panels/SectionPanelPair.xaml.cs b/src/ProfileExplorerUI/Panels/SectionPanelPair.xaml.cs index e5ad93a1..26a3a5b2 100644 --- a/src/ProfileExplorerUI/Panels/SectionPanelPair.xaml.cs +++ b/src/ProfileExplorerUI/Panels/SectionPanelPair.xaml.cs @@ -151,6 +151,10 @@ public void AddModuleSummary(IRTextSummary summary) { MainPanel.AddModuleSummary(summary); } + public void ClearModuleSummaries() { + MainPanel.ClearModuleSummaries(); + } + public async Task RefreshDocumentsDiffs() { if (MainPanel.CurrentFunction == null) { return;