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
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:
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.