Skip to content

Commit

Permalink
Skip repeats and IValueTaskSource.GetResult (#132)
Browse files Browse the repository at this point in the history
* Skip repeats and IValueTaskSource.GetResult

* Add RecursionTests

* Varibale counts

* More flexible
  • Loading branch information
benaadams authored Jan 3, 2021
1 parent c502ee9 commit b3e8977
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 17 deletions.
14 changes: 14 additions & 0 deletions src/Ben.Demystifier/EnhancedStackFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ public class EnhancedStackFrame : StackFrame

public StackFrame StackFrame { get; }

public bool IsRecursive
{
get => MethodInfo.RecurseCount > 0;
internal set => MethodInfo.RecurseCount++;
}

public ResolvedMethod MethodInfo { get; }

internal EnhancedStackFrame(StackFrame stackFrame, ResolvedMethod methodInfo, string fileName, int lineNumber, int colNumber)
Expand All @@ -26,6 +32,14 @@ internal EnhancedStackFrame(StackFrame stackFrame, ResolvedMethod methodInfo, st
_colNumber = colNumber;
}

internal bool IsEquivalent(ResolvedMethod methodInfo, string fileName, int lineNumber, int colNumber)
{
return _lineNumber == lineNumber &&
_colNumber == colNumber &&
_fileName == fileName &&
MethodInfo.IsSequentialEquivalent(methodInfo);
}

/// <summary>
/// Gets the column number in the file that contains the code that is executing.
/// This information is typically extracted from the debugging symbols for the executable.
Expand Down
41 changes: 30 additions & 11 deletions src/Ben.Demystifier/EnhancedStackTrace.Frames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ private static List<EnhancedStackFrame> GetFrames(StackTrace stackTrace)
return frames;
}

using (var portablePdbReader = new PortablePdbReader())
EnhancedStackFrame lastFrame = null;
PortablePdbReader portablePdbReader = null;
try
{

for (var i = 0; i < stackFrames.Length; i++)
{
var frame = stackFrames[i];
Expand All @@ -66,17 +67,32 @@ private static List<EnhancedStackFrame> GetFrames(StackTrace stackTrace)
{
// .NET Framework and older versions of mono don't support portable PDBs
// so we read it manually to get file name and line information
portablePdbReader.PopulateStackFrame(frame, method, frame.GetILOffset(), out fileName, out row, out column);
(portablePdbReader ??= new PortablePdbReader()).PopulateStackFrame(frame, method, frame.GetILOffset(), out fileName, out row, out column);
}

var stackFrame = new EnhancedStackFrame(frame, GetMethodDisplayString(method), fileName, row, column);


frames.Add(stackFrame);
var resolvedMethod = GetMethodDisplayString(method);
if (lastFrame?.IsEquivalent(resolvedMethod, fileName, row, column) ?? false)
{
lastFrame.IsRecursive = true;
}
else
{
var stackFrame = new EnhancedStackFrame(frame, resolvedMethod, fileName, row, column);
frames.Add(stackFrame);
lastFrame = stackFrame;
}
}
}
finally
{
if (portablePdbReader is not null)
{
portablePdbReader.Dispose();
}

return frames;
}

return frames;
}

public static ResolvedMethod GetMethodDisplayString(MethodBase originMethod)
Expand Down Expand Up @@ -684,6 +700,10 @@ private static bool ShowInStackTrace(MethodBase method)
{
return false;
}
if (method.Name.StartsWith("System.Threading.Tasks.Sources.IValueTaskSource") && method.Name.EndsWith(".GetResult"))
{
return false;
}
if (type == typeof(Task) || type.DeclaringType == typeof(Task))
{
if (method.Name.Contains(".cctor"))
Expand All @@ -700,7 +720,6 @@ private static bool ShowInStackTrace(MethodBase method)
case "InnerInvoke":
case "ExecuteEntryUnsafe":
case "ExecuteFromThreadPool":
case "s_ecCallback":
return false;
}
}
Expand Down Expand Up @@ -829,7 +848,7 @@ static MethodInfo[] GetDeclaredMethods(Type type) =>
return false;
}

foreach (MethodInfo candidateMethod in methods)
foreach (var candidateMethod in methods)
{
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(inherit: false);
// ReSharper disable once ConditionIsAlwaysTrueOrFalse - Taken from CoreFX
Expand All @@ -839,7 +858,7 @@ static MethodInfo[] GetDeclaredMethods(Type type) =>
}

bool foundAttribute = false, foundIteratorAttribute = false;
foreach (StateMachineAttribute asma in attributes)
foreach (var asma in attributes)
{
if (asma.StateMachineType == declaringType)
{
Expand Down
18 changes: 18 additions & 0 deletions src/Ben.Demystifier/ResolvedMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ public class ResolvedMethod
public EnumerableIList<ResolvedParameter> Parameters { get; set; }

public EnumerableIList<ResolvedParameter> SubMethodParameters { get; set; }
public int RecurseCount { get; internal set; }

internal bool IsSequentialEquivalent(ResolvedMethod obj)
{
return
IsAsync == obj.IsAsync &&
DeclaringType == obj.DeclaringType &&
Name == obj.Name &&
IsLambda == obj.IsLambda &&
Ordinal == obj.Ordinal &&
GenericArguments == obj.GenericArguments &&
SubMethod == obj.SubMethod;
}

public override string ToString() => Append(new StringBuilder()).ToString();

Expand Down Expand Up @@ -140,6 +153,11 @@ internal StringBuilder Append(StringBuilder builder)
}
}

if (RecurseCount > 0)
{
builder.Append($" x {RecurseCount + 1:0}");
}

return builder;
}

Expand Down
10 changes: 4 additions & 6 deletions test/Ben.Demystifier.Test/AsyncEnumerableTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
Expand Down Expand Up @@ -33,16 +34,13 @@ public async Task DemystifiesAsyncEnumerable()
var stackTrace = demystifiedException.ToString();
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
var trace = string.Join("", stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => Regex.Replace(s, " x [0-9]+", " x N"))
.Skip(1)
.ToArray());
var expected = string.Join("", new[] {
" at async IAsyncEnumerable<int> Ben.Demystifier.Test.AsyncEnumerableTests.Throw(CancellationToken cancellationToken)+MoveNext()",
" at async IAsyncEnumerable<int> Ben.Demystifier.Test.AsyncEnumerableTests.Throw(CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult(short token)",
" at async IAsyncEnumerable<long> Ben.Demystifier.Test.AsyncEnumerableTests.Start(CancellationToken cancellationToken)+MoveNext()",
" at async IAsyncEnumerable<long> Ben.Demystifier.Test.AsyncEnumerableTests.Start(CancellationToken cancellationToken)+MoveNext()",
" at async IAsyncEnumerable<long> Ben.Demystifier.Test.AsyncEnumerableTests.Start(CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult(short token)",
" at async Task Ben.Demystifier.Test.AsyncEnumerableTests.DemystifiesAsyncEnumerable()",
" at async Task Ben.Demystifier.Test.AsyncEnumerableTests.DemystifiesAsyncEnumerable()"
" at async IAsyncEnumerable<long> Ben.Demystifier.Test.AsyncEnumerableTests.Start(CancellationToken cancellationToken)+MoveNext() x N",
" at async Task Ben.Demystifier.Test.AsyncEnumerableTests.DemystifiesAsyncEnumerable() x N"
});
Assert.Equal(expected, trace);
}
Expand Down
74 changes: 74 additions & 0 deletions test/Ben.Demystifier.Test/RecursionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Xunit;

namespace Ben.Demystifier.Test
{
public class RecursionTests
{
[Fact]
public async Task DemystifiesAsyncRecursion()
{
Exception demystifiedException = null;

try
{
await RecurseAsync(10);
}
catch (Exception ex)
{
demystifiedException = ex.Demystify();
}

// Assert
var stackTrace = demystifiedException.ToString();
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
var traces = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => Regex.Replace(s, " x [0-9]+", " x N"))
.Skip(1)
.ToArray();

Assert.Contains(" at async Task<int> Ben.Demystifier.Test.RecursionTests.RecurseAsync(int depth) x N", traces);
}

[Fact]
public void DemystifiesRecursion()
{
Exception demystifiedException = null;

try
{
Recurse(10);
}
catch (Exception ex)
{
demystifiedException = ex.Demystify();
}

// Assert
var stackTrace = demystifiedException.ToString();
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
var traces = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => Regex.Replace(s, " x [0-9]+", " x N"))
.Skip(1)
.ToArray();

Assert.Contains(" at int Ben.Demystifier.Test.RecursionTests.Recurse(int depth) x N", traces);
}

async Task<int> RecurseAsync(int depth)
{
if (depth > 0) await RecurseAsync(depth - 1);
throw new InvalidOperationException();
}

int Recurse(int depth)
{
if (depth > 0) Recurse(depth - 1);
throw new InvalidOperationException();
}
}
}

0 comments on commit b3e8977

Please sign in to comment.