Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Await with .NET 4.0: meaningful stack traces

Tags:

I have a C# console application project using .NET 4.0, with the Microsoft.Bcl.Async package installed. I use this code:

internal class Program
{
    private static void Main(string[] args)
    {
        Foo().Wait();
    }

    static void Log(Exception ex)
    {

    }

    private static async Task Foo()
    {
        try
        {
            await DoSomething();
        }
        catch (Exception ex)
        {
            Log(ex);
        }
    }

    private static async Task DoSomething()
    {
        throw new DivideByZeroException();
    }
}

If I put a breakpoint inside of the Log method, I get my DivideByZero exception, but the stack trace I see is:

at Microsoft.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at Microsoft.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccess(Task task)
at Microsoft.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
at Microsoft.Runtime.CompilerServices.TaskAwaiter.GetResult()
at AsyncStacks.Program.<Foo>d__0.MoveNext() in p:\Sandbox\AsyncStacks\AsyncStacks\Program.cs:line 25

This stack trace is next to useless as it doesn't tell me where the exception was actually thrown.

If I change my project to target .NET 4.5, I get a more useful exception:

at AsyncStacks.Program.<DoSomething>d__3.MoveNext() in p:\Sandbox\AsyncStacks\AsyncStacks\Program.cs:line 35
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at AsyncStacks.Program.<Foo>d__0.MoveNext() in p:\Sandbox\AsyncStacks\AsyncStacks\Program.cs:line 25

How can I get a useful stack trace from a .NET 4.0 project when using await?

Update

The "old" AsyncTargetingPack does throw a much better stack trace. The issue seems to have been introduced in the "new" Microsoft.Bcl.Async.

like image 666
Paul Stovell Avatar asked Jul 29 '13 10:07

Paul Stovell


1 Answers

Changing Foo to this appears to get a better result:

private static async Task Foo()
{
    await DoSomething().ContinueWith(t => Log(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
}

Update: However, the downside is you have to use the same .ContinueWith every time you use the await keyword. I.e., if you await something that also awaits, you need to .ContinueWith that too.

like image 67
Paul Stovell Avatar answered Sep 20 '22 23:09

Paul Stovell