├── .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 | [![NuGet](https://img.shields.io/nuget/v/AsyncFriendlyStackTrace.svg)](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 | } --------------------------------------------------------------------------------