From 5f8c844b7af4e5ea6beb1f404a0245b0cf403ccf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:17:13 +0000 Subject: [PATCH 1/3] Initial plan From 8a944635b6ba795ae6c1d892a399d0ec8e28c932 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:24:25 +0000 Subject: [PATCH 2/3] Fix last sample dropped in cdbstack and set sample metric Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com> --- .../DebuggerStackSourceTests.cs | 78 +++++++++++++++++++ .../OtherSources/DebuggerStackSource.cs | 20 +++++ 2 files changed, 98 insertions(+) create mode 100644 src/PerfView.Tests/DebuggerStackSourceTests.cs diff --git a/src/PerfView.Tests/DebuggerStackSourceTests.cs b/src/PerfView.Tests/DebuggerStackSourceTests.cs new file mode 100644 index 000000000..4fda66467 --- /dev/null +++ b/src/PerfView.Tests/DebuggerStackSourceTests.cs @@ -0,0 +1,78 @@ +using Diagnostics.Tracing.StackSources; +using Microsoft.Diagnostics.Tracing.Stacks; +using System.IO; +using Xunit; + +namespace PerfViewTests +{ + public class DebuggerStackSourceTests + { + [Fact] + public void TestLastSampleIsNotDropped() + { + // Create a sample cdbstack file with two samples + var cdbStackContent = @"Call Site +coreclr!JIT_MonEnterWorker_Portable +System_Windows_ni!MS.Internal.ManagedPeerTable.TryGetManagedPeer(IntPtr, Boolean, System.Object ByRef) +Call Site +kernel32!BaseThreadInitThunk +ntdll!RtlUserThreadStart"; + + DebuggerStackSource stackSource; + using (var reader = new StringReader(cdbStackContent)) + { + stackSource = new DebuggerStackSource(reader); + } + + // Count the samples + int sampleCount = 0; + stackSource.ForEach(sample => sampleCount++); + + // We should have 2 samples, but the bug causes only 1 to be added + Assert.Equal(2, sampleCount); + } + + [Fact] + public void TestSingleSampleIsAdded() + { + // Create a sample cdbstack file with a single sample (no subsequent "Call Site") + var cdbStackContent = @"Call Site +coreclr!JIT_MonEnterWorker_Portable +System_Windows_ni!MS.Internal.ManagedPeerTable.TryGetManagedPeer(IntPtr, Boolean, System.Object ByRef)"; + + DebuggerStackSource stackSource; + using (var reader = new StringReader(cdbStackContent)) + { + stackSource = new DebuggerStackSource(reader); + } + + // Count the samples + int sampleCount = 0; + stackSource.ForEach(sample => sampleCount++); + + // We should have 1 sample + Assert.Equal(1, sampleCount); + } + + [Fact] + public void TestSampleMetricIsSet() + { + // Create a sample cdbstack file with one sample + var cdbStackContent = @"Call Site +coreclr!JIT_MonEnterWorker_Portable +System_Windows_ni!MS.Internal.ManagedPeerTable.TryGetManagedPeer(IntPtr, Boolean, System.Object ByRef)"; + + DebuggerStackSource stackSource; + using (var reader = new StringReader(cdbStackContent)) + { + stackSource = new DebuggerStackSource(reader); + } + + // Check that metric is set to 1 for each sample + stackSource.ForEach(sample => + { + Assert.Equal(1, sample.Metric); + }); + } + } +} diff --git a/src/PerfView/OtherSources/DebuggerStackSource.cs b/src/PerfView/OtherSources/DebuggerStackSource.cs index dacaa4835..795f9a98f 100644 --- a/src/PerfView/OtherSources/DebuggerStackSource.cs +++ b/src/PerfView/OtherSources/DebuggerStackSource.cs @@ -102,6 +102,7 @@ private void Read(TextReader reader) stack.Clear(); + sample.Metric = 1; sample.StackIndex = parent; sample.TimeRelativeMSec = time; time++; @@ -112,6 +113,25 @@ private void Read(TextReader reader) } } } + + // Handle the last sample if there are any remaining frames + if (stack.Count != 0) + { + StackSourceCallStackIndex parent = StackSourceCallStackIndex.Invalid; + for (int i = stack.Count - 1; i >= 0; --i) + { + parent = Interner.CallStackIntern(stack[i].frame, parent); + } + + stack.Clear(); + + sample.Metric = 1; + sample.StackIndex = parent; + sample.TimeRelativeMSec = time; + time++; + AddSample(sample); + } + Interner.DoneInterning(); } #endregion From 3e85b23149015da43fda86367b20c426eb2399fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:36:00 +0000 Subject: [PATCH 3/3] Refactor: Extract AddSampleFromStack helper method to eliminate duplication Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com> --- .../OtherSources/DebuggerStackSource.cs | 46 ++++++++----------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/src/PerfView/OtherSources/DebuggerStackSource.cs b/src/PerfView/OtherSources/DebuggerStackSource.cs index 795f9a98f..8e67a4935 100644 --- a/src/PerfView/OtherSources/DebuggerStackSource.cs +++ b/src/PerfView/OtherSources/DebuggerStackSource.cs @@ -30,6 +30,23 @@ private struct DebuggerCallStackFrame public StackSourceFrameIndex frame; } + private void AddSampleFromStack(GrowableArray stack, StackSourceSample sample, ref float time) + { + StackSourceCallStackIndex parent = StackSourceCallStackIndex.Invalid; + for (int i = stack.Count - 1; i >= 0; --i) + { + parent = Interner.CallStackIntern(stack[i].frame, parent); + } + + stack.Clear(); + + sample.Metric = 1; + sample.StackIndex = parent; + sample.TimeRelativeMSec = time; + time++; + AddSample(sample); + } + private void Read(TextReader reader) { var framePattern = new Regex(@"\b(\w+?)\!(\S\(?[\S\s]*\)?)"); @@ -93,20 +110,7 @@ private void Read(TextReader reader) // clear the stack if (stack.Count != 0) { - - StackSourceCallStackIndex parent = StackSourceCallStackIndex.Invalid; - for (int i = stack.Count - 1; i >= 0; --i) - { - parent = Interner.CallStackIntern(stack[i].frame, parent); - } - - stack.Clear(); - - sample.Metric = 1; - sample.StackIndex = parent; - sample.TimeRelativeMSec = time; - time++; - AddSample(sample); + AddSampleFromStack(stack, sample, ref time); } newCallStackFound = true; @@ -117,19 +121,7 @@ private void Read(TextReader reader) // Handle the last sample if there are any remaining frames if (stack.Count != 0) { - StackSourceCallStackIndex parent = StackSourceCallStackIndex.Invalid; - for (int i = stack.Count - 1; i >= 0; --i) - { - parent = Interner.CallStackIntern(stack[i].frame, parent); - } - - stack.Clear(); - - sample.Metric = 1; - sample.StackIndex = parent; - sample.TimeRelativeMSec = time; - time++; - AddSample(sample); + AddSampleFromStack(stack, sample, ref time); } Interner.DoneInterning();