From fc7aa50f2a96c3d5f1e426eaa4aaeac4f34bb477 Mon Sep 17 00:00:00 2001 From: John Pohovey Date: Wed, 18 Mar 2026 12:12:57 -0700 Subject: [PATCH 1/4] fix stale session state when loading a second (new) trace via file>open --- .../Profile/Data/RawProfileData.cs | 9 +++++++++ .../Profile/ETW/ETWProfileDataProvider.cs | 12 ++++++++++++ src/ProfileExplorerUI/MainWindowPanels.cs | 7 ++++++- src/ProfileExplorerUI/Panels/SectionPanel.xaml.cs | 5 +++++ .../Panels/SectionPanelPair.xaml.cs | 4 ++++ 5 files changed, 36 insertions(+), 1 deletion(-) 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/ETW/ETWProfileDataProvider.cs b/src/ProfileExplorerCore/Profile/ETW/ETWProfileDataProvider.cs index 575601cc..f6bf8457 100644 --- a/src/ProfileExplorerCore/Profile/ETW/ETWProfileDataProvider.cs +++ b/src/ProfileExplorerCore/Profile/ETW/ETWProfileDataProvider.cs @@ -69,6 +69,12 @@ 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.uniqueFrames_.Clear(); + ResolvedProfileStack.frameInstances_.Clear(); + ResolvedProfileStack.kernelFrameInstances_.Clear(); } public static async Task> @@ -425,6 +431,12 @@ private void CollectChunkSamples(List(end - start + 1); diff --git a/src/ProfileExplorerUI/MainWindowPanels.cs b/src/ProfileExplorerUI/MainWindowPanels.cs index 17ad816c..8c0d41de 100644 --- a/src/ProfileExplorerUI/MainWindowPanels.cs +++ b/src/ProfileExplorerUI/MainWindowPanels.cs @@ -1028,10 +1028,15 @@ private async Task SetupPanels() { } private async Task SetupSectionPanel() { + Trace.WriteLine($"SetupSectionPanel: MainSummary={SectionPanel.MainSummary?.ModuleName ?? "NULL"}, Docs={sessionState_.Documents.Count}"); + if (SectionPanel.MainSummary == null) { SectionPanel.CompilerInfo = compilerInfo_; SectionPanel.Session = this; + // Clear stale module summaries from any previous session. + SectionPanel.ClearModuleSummaries(); + foreach (var doc in sessionState_.Documents) { if (doc != sessionState_.MainDocument && doc != sessionState_.DiffDocument) { @@ -1040,7 +1045,7 @@ private async Task SetupSectionPanel() { } } - await SectionPanel.SetMainSummary(sessionState_.MainDocument.Summary); + await SectionPanel.SetMainSummary(sessionState_.MainDocument.Summary, updateFunctionList: true); SectionPanel.MainTitle = sessionState_.MainDocument.ModuleName; SectionPanel.OnSessionStart(); } 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; From d07a49bfd0f056b2279c960ee425d1894a5c10fc Mon Sep 17 00:00:00 2001 From: John Pohovey Date: Thu, 19 Mar 2026 11:35:07 -0700 Subject: [PATCH 2/4] better clear() encapsulation --- .../Profile/Data/ResolvedProfileStack.cs | 10 ++++++++++ .../Profile/ETW/ETWProfileDataProvider.cs | 4 +--- 2 files changed, 11 insertions(+), 3 deletions(-) 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 f6bf8457..b46beebf 100644 --- a/src/ProfileExplorerCore/Profile/ETW/ETWProfileDataProvider.cs +++ b/src/ProfileExplorerCore/Profile/ETW/ETWProfileDataProvider.cs @@ -72,9 +72,7 @@ public ETWProfileDataProvider() { // Clear static caches from any previous trace load to prevent // stale frame/function references leaking across sessions. - ResolvedProfileStack.uniqueFrames_.Clear(); - ResolvedProfileStack.frameInstances_.Clear(); - ResolvedProfileStack.kernelFrameInstances_.Clear(); + ResolvedProfileStack.ResetCaches(); } public static async Task> From c1e898ef35da114b1fb657809e70a35719ed1c6d Mon Sep 17 00:00:00 2001 From: John Pohovey Date: Thu, 19 Mar 2026 11:39:25 -0700 Subject: [PATCH 3/4] remove debug trace logging --- src/ProfileExplorerUI/MainWindowPanels.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ProfileExplorerUI/MainWindowPanels.cs b/src/ProfileExplorerUI/MainWindowPanels.cs index 8c0d41de..82226c1f 100644 --- a/src/ProfileExplorerUI/MainWindowPanels.cs +++ b/src/ProfileExplorerUI/MainWindowPanels.cs @@ -1028,19 +1028,17 @@ private async Task SetupPanels() { } private async Task SetupSectionPanel() { - Trace.WriteLine($"SetupSectionPanel: MainSummary={SectionPanel.MainSummary?.ModuleName ?? "NULL"}, Docs={sessionState_.Documents.Count}"); - if (SectionPanel.MainSummary == null) { SectionPanel.CompilerInfo = compilerInfo_; SectionPanel.Session = this; - // Clear stale module summaries from any previous session. + // 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); } } From 32572697413baed2d8e59135ad36a42e854df192 Mon Sep 17 00:00:00 2001 From: John Pohovey Date: Thu, 19 Mar 2026 15:46:00 -0700 Subject: [PATCH 4/4] dedup some load work --- src/ProfileExplorerUI/MainWindowPanels.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProfileExplorerUI/MainWindowPanels.cs b/src/ProfileExplorerUI/MainWindowPanels.cs index 82226c1f..4ee9144b 100644 --- a/src/ProfileExplorerUI/MainWindowPanels.cs +++ b/src/ProfileExplorerUI/MainWindowPanels.cs @@ -1043,7 +1043,7 @@ private async Task SetupSectionPanel() { } } - await SectionPanel.SetMainSummary(sessionState_.MainDocument.Summary, updateFunctionList: true); + await SectionPanel.SetMainSummary(sessionState_.MainDocument.Summary); SectionPanel.MainTitle = sessionState_.MainDocument.ModuleName; SectionPanel.OnSessionStart(); }