├── .vscode
└── settings.json
├── src
├── AsyncFriendlyStackTrace.Test
│ ├── IExample.cs
│ ├── Example4_GoodSerialization.cs
│ ├── AsyncFriendlyStackTrace.Test.csproj
│ ├── Example1.cs
│ ├── Example2_Wait.cs
│ ├── Example3_BadSerialization.cs
│ └── Program.cs
├── AsyncFriendlyStackTrace
│ ├── EnvironmentUtil.cs
│ ├── AsyncFriendlyStackTrace.csproj
│ ├── ReflectionUtil.cs
│ ├── ExceptionExtensions.cs
│ └── StackTraceExtensions.cs
└── AsyncFriendlyStackTrace.sln
├── LICENSE
├── docs
├── Example1.md
├── Example2_Wait.md
├── Example4_GoodSerialization.md
└── Example3_BadSerialization.md
├── README.md
└── .gitignore
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "dotnet.defaultSolution": "src\\AsyncFriendlyStackTrace.sln"
3 | }
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace.Test/IExample.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace AsyncFriendlyStackTrace.Test
4 | {
5 | internal interface IExample
6 | {
7 | Task Run();
8 | }
9 | }
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace.Test/Example4_GoodSerialization.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AsyncFriendlyStackTrace.Test
4 | {
5 | internal class Example4_GoodSerialization : Example3_BadSerialization
6 | {
7 | protected override void PrepareException(Exception exception)
8 | {
9 | exception.PrepareForAsyncSerialization();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace.Test/AsyncFriendlyStackTrace.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48;net6.0
5 | Exe
6 | CS0618
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace/EnvironmentUtil.cs:
--------------------------------------------------------------------------------
1 | #if !NET6_0_OR_GREATER
2 | using System;
3 |
4 | namespace AsyncFriendlyStackTrace
5 | {
6 | internal static class EnvironmentUtil
7 | {
8 | // see http://www.mono-project.com/docs/gui/winforms/porting-winforms-applications/#runtime-conditionals
9 | private static readonly Lazy _runningOnMono = new Lazy(() => Type.GetType("Mono.Runtime") != null);
10 |
11 | internal static bool IsRunningOnMono()
12 | {
13 | return _runningOnMono.Value;
14 | }
15 | }
16 | }
17 | #endif
18 |
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace.Test/Example1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace AsyncFriendlyStackTrace.Test
5 | {
6 | internal class Example1 : IExample
7 | {
8 | public async Task Run()
9 | {
10 | await A();
11 | }
12 |
13 | private async Task A()
14 | {
15 | await B();
16 | }
17 |
18 | private async Task B()
19 | {
20 | await C();
21 | }
22 |
23 | private async Task C()
24 | {
25 | await Task.Yield();
26 | throw new Exception("Crash! Boom! Bang!");
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace.Test/Example2_Wait.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace AsyncFriendlyStackTrace.Test
5 | {
6 | internal class Example2_Wait : IExample
7 | {
8 | public async Task Run()
9 | {
10 | await A();
11 | }
12 |
13 | private async Task A()
14 | {
15 | B().Wait();
16 | await Task.Yield();
17 | }
18 |
19 | private async Task B()
20 | {
21 | await C();
22 | }
23 |
24 | private async Task C()
25 | {
26 | await Task.Yield();
27 | throw new Exception("Crash! Boom! Bang!");
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace/AsyncFriendlyStackTrace.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Async-friendly format for stack traces and exceptions (not needed in .NET 6+)
5 | 1.7.0
6 | Eli Arbel
7 | netstandard2.0;net6.0
8 | async;await;stack track;stacktrace;exception
9 | MIT
10 | true
11 | true
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Eli Arbel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace/ReflectionUtil.cs:
--------------------------------------------------------------------------------
1 | #if !NET6_0_OR_GREATER
2 | using System;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Reflection;
6 |
7 | namespace AsyncFriendlyStackTrace
8 | {
9 | internal static class ReflectionUtil
10 | {
11 | ///
12 | /// Allows accessing private fields efficiently.
13 | ///
14 | /// Type of the field's owner.
15 | /// Type of the field.
16 | /// The field name.
17 | /// A delegate field accessor.
18 | internal static Func GenerateGetField(string fieldName)
19 | {
20 | var param = Expression.Parameter(typeof(TOwner));
21 | return Expression.Lambda>(Expression.Field(param, fieldName), param).Compile();
22 | }
23 |
24 | internal static bool HasField(string field)
25 | {
26 | return typeof(T).GetRuntimeFields().Any(x => x.Name == field);
27 | }
28 | }
29 | }
30 | #endif
31 |
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace.Test/Example3_BadSerialization.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Runtime.Serialization;
4 | using System.Threading.Tasks;
5 |
6 | namespace AsyncFriendlyStackTrace.Test
7 | {
8 | internal class Example3_BadSerialization : IExample
9 | {
10 | public async Task Run()
11 | {
12 | try
13 | {
14 | await new Example1().Run();
15 | }
16 | catch (Exception e)
17 | {
18 | PrepareException(e);
19 | var serializedException = SerializeAndDeserializeException(e);
20 | throw new AggregateException(serializedException);
21 | }
22 | }
23 |
24 | protected virtual void PrepareException(Exception exception)
25 | {
26 | // do nothing
27 | }
28 |
29 | private static Exception SerializeAndDeserializeException(Exception e)
30 | {
31 | using (var stream = new MemoryStream())
32 | {
33 | // The caveat of using PrepareForAsyncSerialization is that it adds items
34 | // to the exception's Data dictionary, which can't be serialized by default using DCS.
35 | // Thus you must specify the dictionary's (internal) type as a known type...
36 | var serializer = new DataContractSerializer(e.GetType(), new[] { e.Data.GetType() });
37 | serializer.WriteObject(stream, e);
38 | stream.Position = 0;
39 | return (Exception)serializer.ReadObject(stream);
40 | }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26403.7
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncFriendlyStackTrace", "AsyncFriendlyStackTrace\AsyncFriendlyStackTrace.csproj", "{2A4A7E6D-05DE-40FC-B77E-9BF388599EC0}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncFriendlyStackTrace.Test", "AsyncFriendlyStackTrace.Test\AsyncFriendlyStackTrace.Test.csproj", "{B2436320-65BE-44A3-9E08-C3CB78BF5D34}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {2A4A7E6D-05DE-40FC-B77E-9BF388599EC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {2A4A7E6D-05DE-40FC-B77E-9BF388599EC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {2A4A7E6D-05DE-40FC-B77E-9BF388599EC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {2A4A7E6D-05DE-40FC-B77E-9BF388599EC0}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {B2436320-65BE-44A3-9E08-C3CB78BF5D34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {B2436320-65BE-44A3-9E08-C3CB78BF5D34}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {B2436320-65BE-44A3-9E08-C3CB78BF5D34}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {B2436320-65BE-44A3-9E08-C3CB78BF5D34}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace.Test/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 |
5 | namespace AsyncFriendlyStackTrace.Test
6 | {
7 | public class Program
8 | {
9 | private static bool _outputToFile;
10 |
11 | public static void Main(string[] args)
12 | {
13 | _outputToFile = args.Any(arg => string.Equals(arg, "/file", StringComparison.OrdinalIgnoreCase));
14 |
15 | Run();
16 | Run();
17 | Run();
18 | Run();
19 | }
20 |
21 | private static void Run()
22 | where TExample : IExample, new()
23 | {
24 | if (_outputToFile)
25 | {
26 | using (var writer = File.CreateText($"{typeof (TExample).Name}.md"))
27 | {
28 | Run(writer);
29 | }
30 | }
31 | else
32 | {
33 | Run(Console.Out);
34 | }
35 | }
36 |
37 | private static void Run(TextWriter writer)
38 | where TExample : IExample, new()
39 | {
40 | var name = typeof(TExample).Name;
41 | writer.WriteLine(name);
42 | writer.WriteLine(new string('=', name.Length));
43 | try
44 | {
45 | new TExample().Run().GetAwaiter().GetResult();
46 | }
47 | catch (Exception e)
48 | {
49 | writer.WriteLine("OLD");
50 | writer.WriteLine("---");
51 | writer.WriteLine("```");
52 | writer.WriteLine(e.ToString());
53 | writer.WriteLine("```");
54 | writer.WriteLine();
55 |
56 | writer.WriteLine("NEW");
57 | writer.WriteLine("---");
58 | writer.WriteLine("```");
59 | writer.WriteLine(e.ToAsyncString());
60 | writer.WriteLine("```");
61 | }
62 | writer.WriteLine();
63 | writer.WriteLine();
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/docs/Example1.md:
--------------------------------------------------------------------------------
1 | Example1
2 | ========
3 | OLD
4 | ---
5 | ```
6 | System.Exception: Crash! Boom! Bang!
7 | at AsyncFriendlyStackTrace.Test.Example1.d__3.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
8 | --- End of stack trace from previous location where exception was thrown ---
9 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
10 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
11 | at AsyncFriendlyStackTrace.Test.Example1.d__2.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
12 | --- End of stack trace from previous location where exception was thrown ---
13 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
14 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
15 | at AsyncFriendlyStackTrace.Test.Example1.d__1.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
16 | --- End of stack trace from previous location where exception was thrown ---
17 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
18 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
19 | at AsyncFriendlyStackTrace.Test.Example1.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
20 | --- End of stack trace from previous location where exception was thrown ---
21 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
22 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
23 | at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
24 | ```
25 |
26 | NEW
27 | ---
28 | ```
29 | System.Exception: Crash! Boom! Bang!
30 | at async AsyncFriendlyStackTrace.Test.Example1.C(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
31 | at async AsyncFriendlyStackTrace.Test.Example1.B(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
32 | at async AsyncFriendlyStackTrace.Test.Example1.A(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
33 | at async AsyncFriendlyStackTrace.Test.Example1.Run(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
34 | at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
35 | ```
36 |
37 |
38 |
--------------------------------------------------------------------------------
/docs/Example2_Wait.md:
--------------------------------------------------------------------------------
1 | Example2_Wait
2 | =============
3 | OLD
4 | ---
5 | ```
6 | System.AggregateException: One or more errors occurred. ---> System.Exception: Crash! Boom! Bang!
7 | at AsyncFriendlyStackTrace.Test.Example2_Wait.d__3.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 27
8 | --- End of stack trace from previous location where exception was thrown ---
9 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
10 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
11 | at AsyncFriendlyStackTrace.Test.Example2_Wait.d__2.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 21
12 | --- End of inner exception stack trace ---
13 | at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
14 | at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
15 | at AsyncFriendlyStackTrace.Test.Example2_Wait.d__1.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 0
16 | --- End of stack trace from previous location where exception was thrown ---
17 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
18 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
19 | at AsyncFriendlyStackTrace.Test.Example2_Wait.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 10
20 | --- End of stack trace from previous location where exception was thrown ---
21 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
22 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
23 | at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
24 | ---> (Inner Exception #0) System.Exception: Crash! Boom! Bang!
25 | at AsyncFriendlyStackTrace.Test.Example2_Wait.d__3.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 27
26 | --- End of stack trace from previous location where exception was thrown ---
27 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
28 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
29 | at AsyncFriendlyStackTrace.Test.Example2_Wait.d__2.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 21<---
30 |
31 | ```
32 |
33 | NEW
34 | ---
35 | ```
36 | System.AggregateException: One or more errors occurred. ---> Crash! Boom! Bang!
37 | at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
38 | at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
39 | at async AsyncFriendlyStackTrace.Test.Example2_Wait.A(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 0
40 | at async AsyncFriendlyStackTrace.Test.Example2_Wait.Run(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 10
41 | at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
42 | ---> (Inner Exception #0) System.Exception: Crash! Boom! Bang!
43 | at async AsyncFriendlyStackTrace.Test.Example2_Wait.C(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 27
44 | at async AsyncFriendlyStackTrace.Test.Example2_Wait.B(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example2_Wait.cs:line 21<---
45 |
46 | ```
47 |
48 |
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Async-Friendly Stack Trace
2 |
3 | ## Archived - no longer needed on .NET 5+
4 | Note I have released version `1.7.0` of this package for .NET 6+ that basically calls `StackTrace.ToString()` and `Exception.ToString()` and adds `Obsolete` attributes, to alert users it is no longer needed.
5 |
6 | Async-friendly format for stack traces and exceptions.
7 |
8 | [](https://www.nuget.org/packages/AsyncFriendlyStackTrace/)
9 |
10 | Also check out Ben's [Demystifier](https://github.com/benaadams/Ben.Demystifier/) which resolves async, iterators, tuples, location functions and more.
11 |
12 | ```
13 | System.Exception: Crash! Boom! Bang!
14 | at async AsyncFriendlyStackTrace.Test.Example1.C(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
15 | at async AsyncFriendlyStackTrace.Test.Example1.B(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
16 | at async AsyncFriendlyStackTrace.Test.Example1.A(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
17 | at async AsyncFriendlyStackTrace.Test.Example1.Run(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
18 | at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
19 | ```
20 |
21 | ## Install
22 |
23 | ```powershell
24 | Install-Package AsyncFriendlyStackTrace
25 | ```
26 |
27 | ## Usage
28 |
29 | To format exceptions, use the extension methods in `ExceptionExtensions`:
30 |
31 | ```csharp
32 |
33 | exception.ToAsyncString();
34 |
35 | ```
36 |
37 | This produces an async-friendly format, as you can see in the examples below. There is also special handling for `AggregateException`s and `ReflectionTypeLoadException` (which can contain multiple inner exceptions).
38 |
39 | The main formatting work is done by the `StackTraceExtensions.ToAsyncString` extension method. The async-friendly formatting is archieved by:
40 | * Skipping all awaiter frames (all methods in types implementing `INotifyCompletion`) and `ExceptionDispatchInfo` frames.
41 | * Inferring the original method name from the async state machine class (`IAsyncStateMachine`)
42 | and removing the "MoveNext" - currently only for C#.
43 | * Adding the "async" prefix after "at" on each line for async invocations.
44 | * Appending "(?)" to the method signature to indicate that parameter information is missing.
45 | * Removing the "End of stack trace from previous location..." text.
46 |
47 | ## Example outputs
48 |
49 | In all the examples, OLD refrers to `ToString()` output, while NEW is `ToAsyncString()`.
50 |
51 | * [Example 1](docs/Example1.md) ([code](src/AsyncFriendlyStackTrace.Test/Example1.cs)): A simple 3 async method chain.
52 | * [Example 2](docs/Example2_Wait.md) ([code](src/AsyncFriendlyStackTrace.Test/Example2_Wait.cs)): Async invocations with a synchronous `Wait()` in the middle, causing an `AggregateException`.
53 | * [Example 3](docs/Example3_BadSerialization.md) ([code](src/AsyncFriendlyStackTrace.Test/Example3_BadSerialization.cs)): Bad Serialization - When exception is serialized and deserialized, its stack trace is saved as string. So we can't reformat the stack trace. The "new" stack trace is still a bit shorter due to an improved `AggregateException` formatting (the first inner exception isn't repeated twice).
54 | * [Example 4](docs/Example4_GoodSerialization.md) ([code](src/AsyncFriendlyStackTrace.Test/Example4_GoodSerialization.cs)): Good Serialization - We use the `PrepareForAsyncSerialization` *before* serializing the exception. This saves the async-friendly stack trace *as a string* in the `Data` dictionary of the exception. This has two downsides:
55 | * The serialized data will now contain both stack trace formats.
56 | * When using the `DataContractSerializer`, you must include `exception.Data.GetType()` as a known type. This is because its concrete type (`ListDictionaryInternal`) is internal.
57 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | # NUNIT
34 | *.VisualState.xml
35 | TestResult.xml
36 |
37 | # Build Results of an ATL Project
38 | [Dd]ebugPS/
39 | [Rr]eleasePS/
40 | dlldata.c
41 |
42 | # DNX
43 | project.lock.json
44 | artifacts/
45 |
46 | *_i.c
47 | *_p.c
48 | *_i.h
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.pch
53 | *.pdb
54 | *.pgc
55 | *.pgd
56 | *.rsp
57 | *.sbr
58 | *.tlb
59 | *.tli
60 | *.tlh
61 | *.tmp
62 | *.tmp_proj
63 | *.log
64 | *.vspscc
65 | *.vssscc
66 | .builds
67 | *.pidb
68 | *.svclog
69 | *.scc
70 |
71 | # Chutzpah Test files
72 | _Chutzpah*
73 |
74 | # Visual C++ cache files
75 | ipch/
76 | *.aps
77 | *.ncb
78 | *.opendb
79 | *.opensdf
80 | *.sdf
81 | *.cachefile
82 |
83 | # Visual Studio profiler
84 | *.psess
85 | *.vsp
86 | *.vspx
87 | *.sap
88 |
89 | # TFS 2012 Local Workspace
90 | $tf/
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 | *.DotSettings.user
99 |
100 | # JustCode is a .NET coding add-in
101 | .JustCode
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | _NCrunch_*
111 | .*crunch*.local.xml
112 | nCrunchTemp_*
113 |
114 | # MightyMoose
115 | *.mm.*
116 | AutoTest.Net/
117 |
118 | # Web workbench (sass)
119 | .sass-cache/
120 |
121 | # Installshield output folder
122 | [Ee]xpress/
123 |
124 | # DocProject is a documentation generator add-in
125 | DocProject/buildhelp/
126 | DocProject/Help/*.HxT
127 | DocProject/Help/*.HxC
128 | DocProject/Help/*.hhc
129 | DocProject/Help/*.hhk
130 | DocProject/Help/*.hhp
131 | DocProject/Help/Html2
132 | DocProject/Help/html
133 |
134 | # Click-Once directory
135 | publish/
136 |
137 | # Publish Web Output
138 | *.[Pp]ublish.xml
139 | *.azurePubxml
140 | # TODO: Comment the next line if you want to checkin your web deploy settings
141 | # but database connection strings (with potential passwords) will be unencrypted
142 | *.pubxml
143 | *.publishproj
144 |
145 | # NuGet Packages
146 | *.nupkg
147 | # The packages folder can be ignored because of Package Restore
148 | **/packages/*
149 | # except build/, which is used as an MSBuild target.
150 | !**/packages/build/
151 | # Uncomment if necessary however generally it will be regenerated when needed
152 | #!**/packages/repositories.config
153 | # NuGet v3's project.json files produces more ignoreable files
154 | *.nuget.props
155 | *.nuget.targets
156 |
157 | # Microsoft Azure Build Output
158 | csx/
159 | *.build.csdef
160 |
161 | # Microsoft Azure Emulator
162 | ecf/
163 | rcf/
164 |
165 | # Microsoft Azure ApplicationInsights config file
166 | ApplicationInsights.config
167 |
168 | # Windows Store app package directory
169 | AppPackages/
170 | BundleArtifacts/
171 |
172 | # Visual Studio cache files
173 | # files ending in .cache can be ignored
174 | *.[Cc]ache
175 | # but keep track of directories ending in .cache
176 | !*.[Cc]ache/
177 |
178 | # Others
179 | ClientBin/
180 | ~$*
181 | *~
182 | *.dbmdl
183 | *.dbproj.schemaview
184 | *.pfx
185 | *.publishsettings
186 | node_modules/
187 | orleans.codegen.cs
188 |
189 | # RIA/Silverlight projects
190 | Generated_Code/
191 |
192 | # Backup & report files from converting an old project file
193 | # to a newer Visual Studio version. Backup files are not needed,
194 | # because we have git ;-)
195 | _UpgradeReport_Files/
196 | Backup*/
197 | UpgradeLog*.XML
198 | UpgradeLog*.htm
199 |
200 | # SQL Server files
201 | *.mdf
202 | *.ldf
203 |
204 | # Business Intelligence projects
205 | *.rdl.data
206 | *.bim.layout
207 | *.bim_*.settings
208 |
209 | # Microsoft Fakes
210 | FakesAssemblies/
211 |
212 | # GhostDoc plugin setting file
213 | *.GhostDoc.xml
214 |
215 | # Node.js Tools for Visual Studio
216 | .ntvs_analysis.dat
217 |
218 | # Visual Studio 6 build log
219 | *.plg
220 |
221 | # Visual Studio 6 workspace options file
222 | *.opt
223 |
224 | # Visual Studio LightSwitch build output
225 | **/*.HTMLClient/GeneratedArtifacts
226 | **/*.DesktopClient/GeneratedArtifacts
227 | **/*.DesktopClient/ModelManifest.xml
228 | **/*.Server/GeneratedArtifacts
229 | **/*.Server/ModelManifest.xml
230 | _Pvt_Extensions
231 |
232 | # Paket dependency manager
233 | .paket/paket.exe
234 |
235 | # FAKE - F# Make
236 | .fake/
237 |
--------------------------------------------------------------------------------
/docs/Example4_GoodSerialization.md:
--------------------------------------------------------------------------------
1 | Example4_GoodSerialization
2 | ==========================
3 | OLD
4 | ---
5 | ```
6 | System.AggregateException: One or more errors occurred. ---> System.Exception: Crash! Boom! Bang!
7 | at AsyncFriendlyStackTrace.Test.Example1.d__3.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
8 | --- End of stack trace from previous location where exception was thrown ---
9 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
10 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
11 | at AsyncFriendlyStackTrace.Test.Example1.d__2.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
12 | --- End of stack trace from previous location where exception was thrown ---
13 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
14 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
15 | at AsyncFriendlyStackTrace.Test.Example1.d__1.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
16 | --- End of stack trace from previous location where exception was thrown ---
17 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
18 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
19 | at AsyncFriendlyStackTrace.Test.Example1.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
20 | --- End of stack trace from previous location where exception was thrown ---
21 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
22 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
23 | at AsyncFriendlyStackTrace.Test.Example3_BadSerialization.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 14
24 | --- End of inner exception stack trace ---
25 | at AsyncFriendlyStackTrace.Test.Example3_BadSerialization.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 20
26 | --- End of stack trace from previous location where exception was thrown ---
27 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
28 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
29 | at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
30 | ---> (Inner Exception #0) System.Exception: Crash! Boom! Bang!
31 | at AsyncFriendlyStackTrace.Test.Example1.d__3.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
32 | --- End of stack trace from previous location where exception was thrown ---
33 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
34 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
35 | at AsyncFriendlyStackTrace.Test.Example1.d__2.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
36 | --- End of stack trace from previous location where exception was thrown ---
37 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
38 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
39 | at AsyncFriendlyStackTrace.Test.Example1.d__1.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
40 | --- End of stack trace from previous location where exception was thrown ---
41 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
42 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
43 | at AsyncFriendlyStackTrace.Test.Example1.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
44 | --- End of stack trace from previous location where exception was thrown ---
45 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
46 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
47 | at AsyncFriendlyStackTrace.Test.Example3_BadSerialization.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 14<---
48 |
49 | ```
50 |
51 | NEW
52 | ---
53 | ```
54 | System.AggregateException: One or more errors occurred. ---> Crash! Boom! Bang!
55 | at async AsyncFriendlyStackTrace.Test.Example3_BadSerialization.Run(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 20
56 | at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
57 | ---> (Inner Exception #0) System.Exception: Crash! Boom! Bang!
58 | at async AsyncFriendlyStackTrace.Test.Example1.C(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
59 | at async AsyncFriendlyStackTrace.Test.Example1.B(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
60 | at async AsyncFriendlyStackTrace.Test.Example1.A(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
61 | at async AsyncFriendlyStackTrace.Test.Example1.Run(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
62 | at async AsyncFriendlyStackTrace.Test.Example3_BadSerialization.Run(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 14<---
63 |
64 | ```
65 |
66 |
67 |
--------------------------------------------------------------------------------
/docs/Example3_BadSerialization.md:
--------------------------------------------------------------------------------
1 | Example3_BadSerialization
2 | =========================
3 | OLD
4 | ---
5 | ```
6 | System.AggregateException: One or more errors occurred. ---> System.Exception: Crash! Boom! Bang!
7 | at AsyncFriendlyStackTrace.Test.Example1.d__3.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
8 | --- End of stack trace from previous location where exception was thrown ---
9 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
10 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
11 | at AsyncFriendlyStackTrace.Test.Example1.d__2.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
12 | --- End of stack trace from previous location where exception was thrown ---
13 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
14 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
15 | at AsyncFriendlyStackTrace.Test.Example1.d__1.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
16 | --- End of stack trace from previous location where exception was thrown ---
17 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
18 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
19 | at AsyncFriendlyStackTrace.Test.Example1.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
20 | --- End of stack trace from previous location where exception was thrown ---
21 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
22 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
23 | at AsyncFriendlyStackTrace.Test.Example3_BadSerialization.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 14
24 | --- End of inner exception stack trace ---
25 | at AsyncFriendlyStackTrace.Test.Example3_BadSerialization.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 20
26 | --- End of stack trace from previous location where exception was thrown ---
27 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
28 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
29 | at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
30 | ---> (Inner Exception #0) System.Exception: Crash! Boom! Bang!
31 | at AsyncFriendlyStackTrace.Test.Example1.d__3.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
32 | --- End of stack trace from previous location where exception was thrown ---
33 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
34 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
35 | at AsyncFriendlyStackTrace.Test.Example1.d__2.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
36 | --- End of stack trace from previous location where exception was thrown ---
37 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
38 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
39 | at AsyncFriendlyStackTrace.Test.Example1.d__1.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
40 | --- End of stack trace from previous location where exception was thrown ---
41 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
42 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
43 | at AsyncFriendlyStackTrace.Test.Example1.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
44 | --- End of stack trace from previous location where exception was thrown ---
45 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
46 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
47 | at AsyncFriendlyStackTrace.Test.Example3_BadSerialization.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 14<---
48 |
49 | ```
50 |
51 | NEW
52 | ---
53 | ```
54 | System.AggregateException: One or more errors occurred. ---> Crash! Boom! Bang!
55 | at async AsyncFriendlyStackTrace.Test.Example3_BadSerialization.Run(?) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 20
56 | at AsyncFriendlyStackTrace.Test.Program.Run[TExample](TextWriter writer) in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Program.cs:line 45
57 | ---> (Inner Exception #0) System.Exception: Crash! Boom! Bang!
58 | at AsyncFriendlyStackTrace.Test.Example1.d__3.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 26
59 | --- End of stack trace from previous location where exception was thrown ---
60 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
61 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
62 | at AsyncFriendlyStackTrace.Test.Example1.d__2.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 20
63 | --- End of stack trace from previous location where exception was thrown ---
64 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
65 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
66 | at AsyncFriendlyStackTrace.Test.Example1.d__1.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 15
67 | --- End of stack trace from previous location where exception was thrown ---
68 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
69 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
70 | at AsyncFriendlyStackTrace.Test.Example1.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example1.cs:line 10
71 | --- End of stack trace from previous location where exception was thrown ---
72 | at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
73 | at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
74 | at AsyncFriendlyStackTrace.Test.Example3_BadSerialization.d__0.MoveNext() in C:\Source\Repos\AsyncFriendlyStackTrace\src\AsyncFriendlyStackTrace.Test\Example3_BadSerialization.cs:line 14<---
75 |
76 | ```
77 |
78 |
79 |
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace/ExceptionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Globalization;
5 | using System.Reflection;
6 |
7 | namespace AsyncFriendlyStackTrace
8 | {
9 | ///
10 | /// Provides extension methods for .
11 | ///
12 | public static class ExceptionExtensions
13 | {
14 | #if NET6_0_OR_GREATER
15 |
16 | ///
17 | /// Gets an async-friendly string using . On .NET 6+ just calls .
18 | ///
19 | [Obsolete("This package is no longer needed. Use Exception.ToString() instead.")]
20 | public static string ToAsyncString(this Exception exception) => exception.ToString();
21 |
22 | ///
23 | /// Prepares an for serialization by including the async-friendly stack trace. On .NET 6+ is a no-op.
24 | ///
25 | [Obsolete("This package is no longer needed.")]
26 | public static void PrepareForAsyncSerialization(this Exception exception) {}
27 |
28 | #else
29 |
30 | private const string EndOfInnerExceptionStack = "--- End of inner exception stack trace ---";
31 | private const string AggregateExceptionFormatString = "{0}{1}---> (Inner Exception #{2}) {3}{4}{5}";
32 | private const string AsyncStackTraceExceptionData = "AsyncFriendlyStackTrace";
33 |
34 | private static readonly Lazy StackTraceFieldNameGuesser = new Lazy(
35 | () => EnvironmentUtil.IsRunningOnMono()
36 | && ReflectionUtil.HasField("stack_trace") ? "stack_trace" : "_stackTraceString");
37 |
38 | private static Func GetStackTraceString =>
39 | ReflectionUtil.GenerateGetField(StackTraceFieldNameGuesser.Value);
40 |
41 | private static readonly Func GetRemoteStackTraceString =
42 | ReflectionUtil.GenerateGetField("_remoteStackTraceString");
43 |
44 | ///
45 | /// Gets an async-friendly string using .
46 | /// Includes special handling for s.
47 | ///
48 | /// The exception to format.
49 | /// An async-friendly string representation of an .
50 | public static string ToAsyncString(this Exception exception)
51 | {
52 | if (exception == null) throw new ArgumentNullException(nameof(exception));
53 |
54 | var innerExceptions = GetInnerExceptions(exception);
55 | if (innerExceptions != null)
56 | {
57 | return ToAsyncAggregateString(exception, innerExceptions);
58 | }
59 |
60 | return ToAsyncStringCore(exception, includeMessageOnly: false);
61 | }
62 |
63 | ///
64 | /// Prepares an for serialization by including the async-friendly
65 | /// stack trace as additional .
66 | /// Note that both the original and the new stack traces will be serialized.
67 | /// This method operates recursively on all inner exceptions,
68 | /// including ones in an .
69 | ///
70 | /// The exception.
71 | public static void PrepareForAsyncSerialization(this Exception exception)
72 | {
73 | if (exception == null) throw new ArgumentNullException(nameof(exception));
74 |
75 | if (exception.Data[AsyncStackTraceExceptionData] != null ||
76 | GetStackTraceString(exception) != null)
77 | return;
78 |
79 | exception.Data[AsyncStackTraceExceptionData] = GetAsyncStackTrace(exception);
80 |
81 | var innerExceptions = GetInnerExceptions(exception);
82 | if (innerExceptions != null)
83 | {
84 | foreach (var innerException in innerExceptions)
85 | {
86 | innerException.PrepareForAsyncSerialization();
87 | }
88 | }
89 | else
90 | {
91 | exception.InnerException?.PrepareForAsyncSerialization();
92 | }
93 | }
94 |
95 | private static IList GetInnerExceptions(Exception exception)
96 | {
97 | if (exception is AggregateException aggregateException)
98 | {
99 | return aggregateException.InnerExceptions;
100 | }
101 |
102 | if (exception is ReflectionTypeLoadException reflectionTypeLoadException)
103 | {
104 | return reflectionTypeLoadException.LoaderExceptions;
105 | }
106 |
107 | return null;
108 | }
109 |
110 | private static string ToAsyncAggregateString(Exception exception, IList inner)
111 | {
112 | var s = ToAsyncStringCore(exception, includeMessageOnly: true);
113 | for (var i = 0; i < inner.Count; i++)
114 | {
115 | s = string.Format(CultureInfo.InvariantCulture, AggregateExceptionFormatString, s,
116 | Environment.NewLine, i, inner[i].ToAsyncString(), "<---", Environment.NewLine);
117 | }
118 | return s;
119 | }
120 |
121 | private static string ToAsyncStringCore(Exception exception, bool includeMessageOnly)
122 | {
123 | var message = exception.Message;
124 | var className = exception.GetType().ToString();
125 | var s = message.Length <= 0 ? className : className + ": " + message;
126 |
127 | var innerException = exception.InnerException;
128 | if (innerException != null)
129 | {
130 | if (includeMessageOnly)
131 | {
132 | do
133 | {
134 | s += " ---> " + innerException.Message;
135 | innerException = innerException.InnerException;
136 | } while (innerException != null);
137 | }
138 | else
139 | {
140 | s += " ---> " + innerException.ToAsyncString() + Environment.NewLine +
141 | " " + EndOfInnerExceptionStack;
142 | }
143 | }
144 |
145 | s += Environment.NewLine + GetAsyncStackTrace(exception);
146 |
147 | return s;
148 | }
149 |
150 | private static string GetAsyncStackTrace(Exception exception)
151 | {
152 | var stackTrace = exception.Data[AsyncStackTraceExceptionData] ??
153 | GetStackTraceString(exception) ??
154 | new StackTrace(exception, true).ToAsyncString();
155 | var remoteStackTrace = GetRemoteStackTraceString(exception);
156 | return remoteStackTrace + stackTrace;
157 | }
158 |
159 | #endif
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/AsyncFriendlyStackTrace/StackTraceExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Globalization;
4 | using System.Reflection;
5 | using System.Runtime.CompilerServices;
6 | using System.Runtime.ExceptionServices;
7 | using System.Security;
8 | using System.Text;
9 |
10 | namespace AsyncFriendlyStackTrace
11 | {
12 | ///
13 | /// Provides extension methods for .
14 | ///
15 | public static class StackTraceExtensions
16 | {
17 | #if NET6_0_OR_GREATER
18 |
19 | ///
20 | /// Produces an async-friendly readable representation of the stack trace. On .NET 6+ just calls .
21 | ///
22 | [Obsolete("This package is no longer needed. Use StackTrace.ToString() instead.")]
23 | public static string ToAsyncString(this StackTrace stackTrace) => stackTrace.ToString();
24 |
25 | #else
26 |
27 | private const string AtString = "at";
28 | private const string LineFormat = "in {0}:line {1}";
29 | private const string AsyncMethodPrefix = "async ";
30 |
31 | ///
32 | /// Produces an async-friendly readable representation of the stack trace.
33 | ///
34 | ///
35 | /// The async-friendly formatting is archieved by:
36 | /// * Skipping all awaiter frames (all methods in types implementing ).
37 | /// * Inferring the original method name from the async state machine class ()
38 | /// and removing the "MoveNext" - currently only for C#.
39 | /// * Adding the "async" prefix after "at" on each line for async invocations.
40 | /// * Appending "(?)" to the method signature to indicate that parameter information is missing.
41 | /// * Removing the "End of stack trace from previous location..." text.
42 | ///
43 | /// The stack trace.
44 | /// An async-friendly readable representation of the stack trace.
45 | public static string ToAsyncString(this StackTrace stackTrace)
46 | {
47 | if (stackTrace == null) throw new ArgumentNullException(nameof(stackTrace));
48 | var stackFrames = stackTrace.GetFrames();
49 | if (stackFrames == null) return string.Empty;
50 |
51 | var displayFilenames = true;
52 | var firstFrame = true;
53 | var stringBuilder = new StringBuilder(255);
54 |
55 | foreach (var frame in stackFrames)
56 | {
57 | var method = frame.GetMethod();
58 |
59 | if (method == null) continue;
60 | var declaringType = method.DeclaringType?.GetTypeInfo();
61 | // skip awaiters
62 | if (declaringType != null &&
63 | (typeof(INotifyCompletion).GetTypeInfo().IsAssignableFrom(declaringType) ||
64 | method.DeclaringType == typeof(ExceptionDispatchInfo)))
65 | {
66 | continue;
67 | }
68 |
69 | if (firstFrame)
70 | {
71 | firstFrame = false;
72 | }
73 | else
74 | {
75 | stringBuilder.Append(Environment.NewLine);
76 | }
77 | stringBuilder.AppendFormat(CultureInfo.InvariantCulture, " {0} ", AtString);
78 |
79 | var isAsync = FormatMethodName(stringBuilder, declaringType);
80 | if (!isAsync)
81 | {
82 | stringBuilder.Append(method.Name);
83 | if (method is MethodInfo methodInfo && methodInfo.IsGenericMethod)
84 | {
85 | FormatGenericArguments(stringBuilder, methodInfo.GetGenericArguments());
86 | }
87 | }
88 | else if (declaringType?.IsGenericType == true)
89 | {
90 | FormatGenericArguments(stringBuilder, declaringType.GenericTypeArguments);
91 | }
92 | stringBuilder.Append("(");
93 | if (isAsync)
94 | {
95 | stringBuilder.Append("?");
96 | }
97 | else
98 | {
99 | FormatParameters(stringBuilder, method);
100 | }
101 | stringBuilder.Append(")");
102 | displayFilenames = FormatFileName(displayFilenames, frame, stringBuilder);
103 | }
104 | return stringBuilder.ToString();
105 | }
106 |
107 | private static bool FormatMethodName(StringBuilder stringBuilder, TypeInfo declaringType)
108 | {
109 | if (declaringType == null) return false;
110 | var isAsync = false;
111 | var fullName = declaringType.FullName.Replace('+', '.');
112 | if (typeof(IAsyncStateMachine).GetTypeInfo().IsAssignableFrom(declaringType))
113 | {
114 | isAsync = true;
115 | stringBuilder.Append(AsyncMethodPrefix);
116 | var start = fullName.LastIndexOf('<');
117 | var end = fullName.LastIndexOf('>');
118 | if (start >= 0 && end >= 0)
119 | {
120 | stringBuilder.Append(fullName.Remove(start, 1).Substring(0, end - 1));
121 | }
122 | else
123 | {
124 | stringBuilder.Append(fullName);
125 | }
126 | }
127 | else
128 | {
129 | stringBuilder.Append(fullName);
130 | stringBuilder.Append(".");
131 | }
132 | return isAsync;
133 | }
134 |
135 | private static bool FormatFileName(bool displayFilenames, StackFrame frame, StringBuilder stringBuilder)
136 | {
137 | if (displayFilenames && frame.GetILOffset() != -1)
138 | {
139 | string text = null;
140 | try
141 | {
142 | text = frame.GetFileName();
143 | }
144 | catch (NotSupportedException)
145 | {
146 | displayFilenames = false;
147 | }
148 | catch (SecurityException)
149 | {
150 | displayFilenames = false;
151 | }
152 | if (text != null)
153 | {
154 | stringBuilder.Append(' ');
155 | stringBuilder.AppendFormat(CultureInfo.InvariantCulture, LineFormat, text, frame.GetFileLineNumber());
156 | }
157 | }
158 | return displayFilenames;
159 | }
160 |
161 | private static void FormatParameters(StringBuilder stringBuilder, MethodBase method)
162 | {
163 | var parameters = method.GetParameters();
164 | var firstParam = true;
165 | foreach (var t in parameters)
166 | {
167 | if (!firstParam)
168 | {
169 | stringBuilder.Append(", ");
170 | }
171 | else
172 | {
173 | firstParam = false;
174 | }
175 | var typeName = t.ParameterType?.Name ?? "";
176 | stringBuilder.Append(typeName + " " + t.Name);
177 | }
178 | }
179 |
180 | private static void FormatGenericArguments(StringBuilder stringBuilder, Type[] genericArguments)
181 | {
182 | stringBuilder.Append("[");
183 | var k = 0;
184 | var firstTypeParam = true;
185 | while (k < genericArguments.Length)
186 | {
187 | if (!firstTypeParam)
188 | {
189 | stringBuilder.Append(",");
190 | }
191 | else
192 | {
193 | firstTypeParam = false;
194 | }
195 | stringBuilder.Append(genericArguments[k].Name);
196 | k++;
197 | }
198 | stringBuilder.Append("]");
199 | }
200 | #endif
201 | }
202 | }
--------------------------------------------------------------------------------