Stack traces with async/await

John picture John · Dec 14, 2012 · Viewed 18.9k times · Source

It's clear why stack traces are affected with Microsoft's new programming paradigm. We now have a semantic stack and a couple of physical ones (my choice of words).

What I get to see is an exception's StackTrace property (and in the debugger) is the physical ones, concatenated:

private async Task CheckFooAndBar()
{
    var log = LogManager.GetLogger("Test");
    log.Info("CheckFooAndBar");
    try
    {
        await Foo();
    }
    catch (Exception ex)
    {
        log.Info("StackTrace of last exception: " + ex.StackTrace);
    }
    Console.ReadKey();
}
private async Task Foo()
{
    await Task.Factory.StartNew(() => Thread.Sleep(1000));
    await Bar();
    await Task.Factory.StartNew(() => Thread.Sleep(1000));
}
private async Task Bar()
{
    await Task.Factory.StartNew(() => Thread.Sleep(1000));
    throw new Exception();
    await Task.Factory.StartNew(() => Thread.Sleep(1000));
}

This gives:

StackTrace of last exception:    at NLogAsyncExceptionTestCase.Program.<Bar>d__d.MoveNext() in c:\Users\Jens\Documents\Visual Studio 2012\Projects\NLogAsyncExceptionTestCase\NLogAsyncExceptionTestCase.Console\Program.cs:line 53
--- 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 System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at NLogAsyncExceptionTestCase.Program.<Foo>d__8.MoveNext() in c:\Users\Jens\Documents\Visual Studio 2012\Projects\NLogAsyncExceptionTestCase\NLogAsyncExceptionTestCase.Console\Program.cs:line 44
--- 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 System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at NLogAsyncExceptionTestCase.Program.<CheckFooAndBar>d__0.MoveNext() in c:\Users\Jens\Documents\Visual Studio 2012\Projects\NLogAsyncExceptionTestCase\NLogAsyncExceptionTestCase.Console\Program.cs:line 30 

My question is: Is there a (convenient, standard) way to convert this to a proper backtrace in the semantic sense, such as:

CheckFooAndBar
Foo
Bar

Of course there could be a mixture of awaits and inline path fragments in the stack.

I tried looking at the stack as it is with .NET 4.5 and SL5 with the async targetting pack, but not yet with WinRT. The output is from .NET 4.5.

In SL5, which is what I mainly do, the situation is more problematic: You don't get line numbers in stack traces in Silverlight (even with elevated privileges), which makes the need for context more important.

Answer

John picture John · Dec 4, 2013

With Visual Studio 2013 and .NET 4.5.1, this problem appears to be addressed - and not only in .NET.

For more information, see this blog post.

Edit: link is dead, archived here