diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..5c919e1
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+
+github: [benaadams]
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..03ec545
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "nuget" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "daily"
diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml
new file mode 100644
index 0000000..0d5b3ea
--- /dev/null
+++ b/.github/workflows/pull-request.yaml
@@ -0,0 +1,41 @@
+name: Demystifier PR Build
+on: pull_request
+
+jobs:
+ build:
+ name: "Build for PR"
+ runs-on: ${{ matrix.os }}
+ env:
+ DOTNET_NOLOGO: true
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [windows-latest, ubuntu-18.04, macOS-latest]
+ config: [Debug, Release]
+ steps:
+ - name: Clone source
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Install .NET Core SDK 2.1
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: '2.1.x'
+
+ - name: Install .NET Core SDK 3.1
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: '3.1.x'
+
+ - name: Install .NET SDK 5.0
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: '5.0.x'
+
+ - name: Get .NET information
+ run: dotnet --info
+
+ - name: "Test"
+ run: dotnet test -c ${{ matrix.config }}
+
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 9f08f22..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-image: Visual Studio 2017
-
-branches:
- only:
- - master
-
-skip_branch_with_pr: true
-
-skip_tags: true
-
-skip_commits:
- files:
- - BUILDING.md
- - CONTRIBUTING.md
- - ISSUE_TEMPLATE.md
- - LICENCE
- - README.md
-
-nuget:
- disable_publish_on_pr: true
-
-build_script:
-- ps: .\build.ps1 -target appveyor
-
-test: off
-
-deploy: off
-
-artifacts:
-- path: artifacts/build
-- path: artifacts/packages
-- path: artifacts/test
diff --git a/sample/StackTrace/StackTrace.csproj b/sample/StackTrace/StackTrace.csproj
index 3e72966..8699af6 100644
--- a/sample/StackTrace/StackTrace.csproj
+++ b/sample/StackTrace/StackTrace.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp2.0
+ netcoreapp2.1
diff --git a/src/Ben.Demystifier/Ben.Demystifier.csproj b/src/Ben.Demystifier/Ben.Demystifier.csproj
index de26b84..22997fd 100644
--- a/src/Ben.Demystifier/Ben.Demystifier.csproj
+++ b/src/Ben.Demystifier/Ben.Demystifier.csproj
@@ -11,6 +11,7 @@
git
true
embedded
+ true
@@ -21,12 +22,13 @@
-
+
- 1.6.0
+ 5.0.0
-
+
+
diff --git a/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs b/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs
index 17cf67f..03fd069 100644
--- a/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs
+++ b/src/Ben.Demystifier/EnhancedStackTrace.Frames.cs
@@ -53,7 +53,7 @@ namespace System.Diagnostics
var method = frame.GetMethod();
// Always show last stackFrame
- if (!ShowInStackTrace(method) && i < stackFrames.Length - 1)
+ if (method != null && !ShowInStackTrace(method) && i < stackFrames.Length - 1)
{
continue;
}
@@ -62,7 +62,7 @@ namespace System.Diagnostics
var row = frame.GetFileLineNumber();
var column = frame.GetFileColumnNumber();
var ilOffset = frame.GetILOffset();
- if (string.IsNullOrEmpty(fileName) && ilOffset >= 0)
+ if (method != null && string.IsNullOrEmpty(fileName) && ilOffset >= 0)
{
// .NET Framework and older versions of mono don't support portable PDBs
// so we read it manually to get file name and line information
@@ -636,6 +636,18 @@ namespace System.Diagnostics
{
Debug.Assert(method != null);
+ // Since .NET 5:
+ // https://github.com/dotnet/runtime/blob/7c18d4d6488dab82124d475d1199def01d1d252c/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs#L348-L361
+ if ((method.MethodImplementationFlags & MethodImplAttributes.AggressiveInlining) != 0)
+ {
+ // Aggressive Inlines won't normally show in the StackTrace; however for Tier0 Jit and
+ // cross-assembly AoT/R2R these inlines will be blocked until Tier1 Jit re-Jits
+ // them when they will inline. We don't show them in the StackTrace to bring consistency
+ // between this first-pass asm and fully optimized asm.
+ return false;
+ }
+
+ // Since .NET Core 2:
if (StackTraceHiddenAttributeType != null)
{
// Don't show any methods marked with the StackTraceHiddenAttribute
@@ -653,6 +665,17 @@ namespace System.Diagnostics
return true;
}
+ // Since .NET Core 2:
+ if (StackTraceHiddenAttributeType != null)
+ {
+ // Don't show any methods marked with the StackTraceHiddenAttribute
+ // https://github.com/dotnet/coreclr/pull/14652
+ if (IsStackTraceHidden(type))
+ {
+ return false;
+ }
+ }
+
if (type == typeof(Task<>) && method.Name == "InnerInvoke")
{
return false;
@@ -661,8 +684,13 @@ namespace System.Diagnostics
{
return false;
}
- if (type == typeof(Task))
+ if (type == typeof(Task) || type.DeclaringType == typeof(Task))
{
+ if (method.Name.Contains(".cctor"))
+ {
+ return false;
+ }
+
switch (method.Name)
{
case "ExecuteWithThreadLocal":
@@ -670,15 +698,24 @@ namespace System.Diagnostics
case "ExecutionContextCallback":
case "ExecuteEntry":
case "InnerInvoke":
+ case "ExecuteEntryUnsafe":
+ case "ExecuteFromThreadPool":
+ case "s_ecCallback":
return false;
}
}
if (type == typeof(ExecutionContext))
{
+ if (method.Name.Contains(".cctor"))
+ {
+ return false;
+ }
+
switch (method.Name)
{
case "RunInternal":
case "Run":
+ case "RunFromThreadPoolDispatchLoop":
return false;
}
}
@@ -706,44 +743,33 @@ namespace System.Diagnostics
}
}
- if (StackTraceHiddenAttributeType != null)
+ // Fallbacks for runtime pre-StackTraceHiddenAttribute
+ if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw")
{
- // Don't show any types marked with the StackTraceHiddenAttribute
- // https://github.com/dotnet/coreclr/pull/14652
- if (IsStackTraceHidden(type))
+ return false;
+ }
+
+ if (type == typeof(TaskAwaiter) ||
+ type == typeof(TaskAwaiter<>) ||
+ type == typeof(ValueTaskAwaiter) ||
+ type == typeof(ValueTaskAwaiter<>) ||
+ type == typeof(ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter) ||
+ type == typeof(ConfiguredValueTaskAwaitable<>.ConfiguredValueTaskAwaiter) ||
+ type == typeof(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) ||
+ type == typeof(ConfiguredTaskAwaitable<>.ConfiguredTaskAwaiter))
+ {
+ switch (method.Name)
{
- return false;
+ case "HandleNonSuccessAndDebuggerNotification":
+ case "ThrowForNonSuccess":
+ case "ValidateEnd":
+ case "GetResult":
+ return false;
}
}
- else
+ else if (type.FullName == "System.ThrowHelper")
{
- // Fallbacks for runtime pre-StackTraceHiddenAttribute
- if (type == typeof(ExceptionDispatchInfo) && method.Name == "Throw")
- {
- return false;
- }
- else if (type == typeof(TaskAwaiter) ||
- type == typeof(TaskAwaiter<>) ||
- type == typeof(ValueTaskAwaiter) ||
- type == typeof(ValueTaskAwaiter<>) ||
- type == typeof(ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter) ||
- type == typeof(ConfiguredValueTaskAwaitable<>.ConfiguredValueTaskAwaiter) ||
- type == typeof(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) ||
- type == typeof(ConfiguredTaskAwaitable<>.ConfiguredTaskAwaiter))
- {
- switch (method.Name)
- {
- case "HandleNonSuccessAndDebuggerNotification":
- case "ThrowForNonSuccess":
- case "ValidateEnd":
- case "GetResult":
- return false;
- }
- }
- else if (type.FullName == "System.ThrowHelper")
- {
- return false;
- }
+ return false;
}
return true;
diff --git a/src/Ben.Demystifier/TypeNameHelper.cs b/src/Ben.Demystifier/TypeNameHelper.cs
index fe4bc8b..3450b63 100644
--- a/src/Ben.Demystifier/TypeNameHelper.cs
+++ b/src/Ben.Demystifier/TypeNameHelper.cs
@@ -69,9 +69,8 @@ namespace System.Diagnostics
}
var genericPartIndex = type.Name.IndexOf('`');
- Debug.Assert(genericPartIndex >= 0);
- return type.Name.Substring(0, genericPartIndex);
+ return (genericPartIndex >= 0) ? type.Name.Substring(0, genericPartIndex) : type.Name;
}
private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options)
diff --git a/test/Ben.Demystifier.Benchmarks/Ben.Demystifier.Benchmarks.csproj b/test/Ben.Demystifier.Benchmarks/Ben.Demystifier.Benchmarks.csproj
index b208351..6c761d2 100644
--- a/test/Ben.Demystifier.Benchmarks/Ben.Demystifier.Benchmarks.csproj
+++ b/test/Ben.Demystifier.Benchmarks/Ben.Demystifier.Benchmarks.csproj
@@ -1,11 +1,11 @@
-
+
- netcoreapp2.1;netcoreapp2.0;net462
+ netcoreapp2.1;netcoreapp3.1;net462;net5.0
Release
Exe
-
+
\ No newline at end of file
diff --git a/test/Ben.Demystifier.Benchmarks/Exceptions.cs b/test/Ben.Demystifier.Benchmarks/Exceptions.cs
index 12417a9..c41396e 100644
--- a/test/Ben.Demystifier.Benchmarks/Exceptions.cs
+++ b/test/Ben.Demystifier.Benchmarks/Exceptions.cs
@@ -2,11 +2,14 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Attributes.Jobs;
+using BenchmarkDotNet.Jobs;
namespace Ben.Demystifier.Benchmarks
{
- [ClrJob, CoreJob]
+ [SimpleJob(RuntimeMoniker.Net48)]
+ [SimpleJob(RuntimeMoniker.NetCoreApp21)]
+ [SimpleJob(RuntimeMoniker.NetCoreApp31)]
+ [SimpleJob(RuntimeMoniker.NetCoreApp50)]
[Config(typeof(Config))]
public class ExceptionTests
{
diff --git a/test/Ben.Demystifier.Benchmarks/Program.cs b/test/Ben.Demystifier.Benchmarks/Program.cs
index 4b8a7e4..ac893e7 100644
--- a/test/Ben.Demystifier.Benchmarks/Program.cs
+++ b/test/Ben.Demystifier.Benchmarks/Program.cs
@@ -43,6 +43,6 @@ namespace Ben.Demystifier.Benchmarks
internal class Config : ManualConfig
{
- public Config() => Add(new MemoryDiagnoser());
+ public Config() => AddDiagnoser(MemoryDiagnoser.Default);
}
}
diff --git a/test/Ben.Demystifier.Test/AggregateException.cs b/test/Ben.Demystifier.Test/AggregateException.cs
index 3c0034a..59fe292 100644
--- a/test/Ben.Demystifier.Test/AggregateException.cs
+++ b/test/Ben.Demystifier.Test/AggregateException.cs
@@ -37,14 +37,28 @@ namespace Ben.Demystifier.Test
var trace = string.Join("", stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
// Remove items that vary between test runners
.Where(s =>
- s != " at Task Ben.Demystifier.Test.DynamicCompilation.DoesNotPreventStackTrace()+() => { }" &&
- !s.Contains("System.Threading.Tasks.Task.WaitAll")
+ (s != " at Task Ben.Demystifier.Test.DynamicCompilation.DoesNotPreventStackTrace()+() => { }" &&
+ !s.Contains("System.Threading.Tasks.Task.WaitAll"))
)
.Skip(1)
.ToArray())
// Remove Full framework back arrow
.Replace("<---", "");
-
+#if NET5_0 || NETCOREAPP3_1
+ var expected = string.Join("", new[] {
+ " ---> System.ArgumentException: Value does not fall within the expected range.",
+ " at async Task Ben.Demystifier.Test.AggregateException.Throw1()",
+ " at async void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()+(?) => { }",
+ " --- End of inner exception stack trace ---",
+ " at void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()",
+ " ---> (Inner Exception #1) System.NullReferenceException: Object reference not set to an instance of an object.",
+ " at async Task Ben.Demystifier.Test.AggregateException.Throw2()",
+ " at async void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()+(?) => { }",
+ " ---> (Inner Exception #2) System.InvalidOperationException: Operation is not valid due to the current state of the object.",
+ " at async Task Ben.Demystifier.Test.AggregateException.Throw3()",
+ " at async void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()+(?) => { }"
+ });
+#else
var expected = string.Join("", new[] {
" at async Task Ben.Demystifier.Test.AggregateException.Throw1()",
" at async void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()+(?) => { }",
@@ -59,7 +73,7 @@ namespace Ben.Demystifier.Test
"---> (Inner Exception #2) System.InvalidOperationException: Operation is not valid due to the current state of the object.",
" at async Task Ben.Demystifier.Test.AggregateException.Throw3()",
" at async void Ben.Demystifier.Test.AggregateException.DemystifiesAggregateExceptions()+(?) => { }"});
-
+#endif
Assert.Equal(expected, trace);
}
diff --git a/test/Ben.Demystifier.Test/Ben.Demystifier.Test.csproj b/test/Ben.Demystifier.Test/Ben.Demystifier.Test.csproj
index bf3aa90..66837aa 100644
--- a/test/Ben.Demystifier.Test/Ben.Demystifier.Test.csproj
+++ b/test/Ben.Demystifier.Test/Ben.Demystifier.Test.csproj
@@ -1,16 +1,18 @@
- netcoreapp2.1;netcoreapp2.0;net46
+ netcoreapp2.1;netcoreapp3.1;net46;net5.0
+ netcoreapp2.1;netcoreapp3.1;net5.0
true
..\..\src\Ben.Demystifier\key.snk
false
-
-
-
+
+
+
+
diff --git a/test/Ben.Demystifier.Test/DynamicCompilation.cs b/test/Ben.Demystifier.Test/DynamicCompilation.cs
index 3731011..e9a7364 100644
--- a/test/Ben.Demystifier.Test/DynamicCompilation.cs
+++ b/test/Ben.Demystifier.Test/DynamicCompilation.cs
@@ -44,6 +44,7 @@ namespace Ben.Demystifier.Test
s != " at void System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, object state)" &&
s != " at Task Ben.Demystifier.Test.DynamicCompilation.DoesNotPreventStackTrace()+() => { }"
)
+ .Select(s => Regex.Replace(s, "lambda_method[0-9]+\\(", "lambda_method("))
.ToArray();
Assert.Equal(
diff --git a/test/Ben.Demystifier.Test/NonThrownException.cs b/test/Ben.Demystifier.Test/NonThrownException.cs
index ef5abf2..ef99cf2 100644
--- a/test/Ben.Demystifier.Test/NonThrownException.cs
+++ b/test/Ben.Demystifier.Test/NonThrownException.cs
@@ -31,13 +31,24 @@ namespace Ben.Demystifier.Test
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
var trace = stackTrace.Split(new[]{Environment.NewLine}, StringSplitOptions.None);
+#if NETCOREAPP3_1 || NET5_0
Assert.Equal(
new[] {
- "System.Exception: Exception of type 'System.Exception' was thrown. ---> System.Exception: Exception of type 'System.Exception' was thrown.",
+ "System.Exception: Exception of type 'System.Exception' was thrown.",
+ " ---> System.Exception: Exception of type 'System.Exception' was thrown.",
" at Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()+() => { }",
" at async Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()",
" --- End of inner exception stack trace ---"},
trace);
+#else
+ Assert.Equal(
+ new[] {
+ "System.Exception: Exception of type 'System.Exception' was thrown. ---> System.Exception: Exception of type 'System.Exception' was thrown.",
+ " at Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()+() => { }",
+ " at async Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()",
+ " --- End of inner exception stack trace ---"},
+ trace);
+#endif
// Act
try
@@ -54,6 +65,18 @@ namespace Ben.Demystifier.Test
stackTrace = LineEndingsHelper.RemoveLineEndings(stackTrace);
trace = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
+#if NETCOREAPP3_1 || NET5_0
+ Assert.Equal(
+ new[] {
+ "System.Exception: Exception of type 'System.Exception' was thrown.",
+ " ---> System.Exception: Exception of type 'System.Exception' was thrown.",
+ " at Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()+() => { }",
+ " at async Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()",
+ " --- End of inner exception stack trace ---",
+ " at async Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()"
+ },
+ trace);
+#else
Assert.Equal(
new[] {
"System.Exception: Exception of type 'System.Exception' was thrown. ---> System.Exception: Exception of type 'System.Exception' was thrown.",
@@ -63,6 +86,7 @@ namespace Ben.Demystifier.Test
" at async Task Ben.Demystifier.Test.NonThrownException.DoesNotPreventThrowStackTrace()"
},
trace);
+#endif
}
[Fact]
diff --git a/test/Ben.Demystifier.Test/TypeNameTests.cs b/test/Ben.Demystifier.Test/TypeNameTests.cs
new file mode 100644
index 0000000..837e8a1
--- /dev/null
+++ b/test/Ben.Demystifier.Test/TypeNameTests.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Diagnostics;
+using Xunit;
+
+namespace Ben.Demystifier.Test
+{
+ public class TypeNameTests
+ {
+ [Fact]
+ public void NestedGenericTypes()
+ {
+ try
+ {
+ Throw(new Generic<(int, string)>.Nested());
+ }
+ catch (Exception ex)
+ {
+ var text = ex.ToStringDemystified();
+ }
+ }
+
+ public void Throw(Generic<(int a, string b)>.Nested nested)
+ {
+ throw null;
+ }
+ }
+
+ public class Generic { public struct Nested { } }
+}
\ No newline at end of file
diff --git a/version.json b/version.json
index 19a4255..a50dbf4 100644
--- a/version.json
+++ b/version.json
@@ -1,5 +1,5 @@
{
- "version": "0.1.4",
+ "version": "0.2.0",
"publicReleaseRefSpec": [
"^refs/heads/master$", // we release out of master
"^refs/heads/dev$", // we release out of develop