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..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,25 +110,20 @@ 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.StackIndex = parent; - sample.TimeRelativeMSec = time; - time++; - AddSample(sample); + AddSampleFromStack(stack, sample, ref time); } newCallStackFound = true; } } } + + // Handle the last sample if there are any remaining frames + if (stack.Count != 0) + { + AddSampleFromStack(stack, sample, ref time); + } + Interner.DoneInterning(); } #endregion