Debugging exceptions in a Async/Await (Call Stack)

Patrick picture Patrick · Apr 9, 2013 · Viewed 8.7k times · Source

I use the Async/Await to free my UI-Thread and accomplish multithreading. Now I have a problem when I hit a exception. The Call Stack of my Async parts allways starts with ThreadPoolWorkQue.Dipatch(), which doesn't help me very much.

I found a MSDN-Article Andrew Stasyuk. Async Causality Chain Tracking about it but as I understand it, its not a ready to use solution.

What is the best/easiest way to debug if you use multithreading with Async/Await?

Answer

Stephen Cleary picture Stephen Cleary · Apr 9, 2013

The article you found does a good job of explaining why call stacks don't work the way most of us think they do. Technically, the call stack only tells us where the code is returning to after the current method. In other words, the call stack is "where the code is going", not "where the code came from".

Interestingly, the article does mention a solution in passing, but doesn't expound on it. I have a blog post that goes explains the CallContext solution in detail. Essentially, you use the logical call context to create your own "diagnostic context".

I like the CallContext solution better than the solution presented in the article because it does work will all forms of async code (including fork/join code like Task.WhenAll).

This is the best solution I know of (other than doing something really complex like hooking into the profiling API). Caveats of the CallContext approach:

  • It only works on .NET 4.5 full. No support for Windows Store apps, .NET 4.0, etc.
  • You do have to "instrument" your code manually. There's no way AFAIK to inject it automatically.
  • Exceptions don't capture the logical call context automatically. So this solution works fine if you're breaking into the debugger when exceptions are thrown, but it's not as useful if you're just catching the exceptions in another place and logging them.

The code (depends on the immutable collections NuGet library):

public static class MyStack
{
    private static readonly string name = Guid.NewGuid().ToString("N");

    private static ImmutableStack<string> CurrentContext
    {
        get
        {
            var ret = CallContext.LogicalGetData(name) as ImmutableStack<string>;
            return ret ?? ImmutableStack.Create<string>();
        }

        set
        {
            CallContext.LogicalSetData(name, value);
        }
    }

    public static IDisposable Push([CallerMemberName] string context = "")
    {
        CurrentContext = CurrentContext.Push(context);
        return new PopWhenDisposed();
    }

    private static void Pop()
    {
        CurrentContext = CurrentContext.Pop();
    }

    private sealed class PopWhenDisposed : IDisposable
    {
        private bool disposed;

        public void Dispose()
        {
            if (disposed)
                return;
            Pop();
            disposed = true;
        }
    }

    // Keep this in your watch window.
    public static string CurrentStack
    {
        get
        {
            return string.Join(" ", CurrentContext.Reverse());
        }
    }
}

Usage:

static async Task SomeWorkAsync()
{
    using (MyStack.Push()) // Pushes "SomeWorkAsync"
    {
        ...
    }
}

Update: I released a NuGet package (described on my blog) that uses PostSharp to inject the pushes and pops automatically. So getting a good trace should be a lot simpler now.