60

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.

John
  • 6,693
  • 3
  • 51
  • 90
  • 5
    I'm not aware of any facilities like this. .NET 4.5 does support ETW tracing with enough detail that you could build an event listener to construct a stack trace like this. But that would be a lot of work and I don't think SL5 would support it anyway. – Stephen Cleary Dec 14 '12 at 19:02
  • 1
    No, it wouldn't. But thanks for the comment, a negative is information also. – John Dec 14 '12 at 19:11
  • 13
    Take a look at this: http://msdn.microsoft.com/en-us/magazine/jj891052.aspx. – Toni Petrina Feb 08 '13 at 09:59
  • I wrote up a "manual" solution [on my blog](http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html). You do have to add code to each method you want to include, though. :( – Stephen Cleary Apr 18 '13 at 11:06
  • Toni: please add your comment as answer. – Stu Apr 20 '13 at 23:35
  • Update: I released a [NuGet package (described on my blog)](http://blog.stephencleary.com/2013/05/announcement-async-diagnostics.html) that uses PostSharp to inject tracing automatically. So getting a good causality stack should be a lot simpler now. Unfortunately, SL is not supported, but if you can duplicate the error in a desktop app you could trace it easily. – Stephen Cleary Jun 03 '13 at 02:16
  • 1
    Not directly a solution, but [here's a hack](http://stackoverflow.com/a/28633192/868227) to get original method given async method. – Jacek Gorgoń Feb 20 '15 at 16:56
  • John, what did you end up doing? I'm SL5 user myself.. – katit Mar 12 '15 at 22:24
  • @katit Sorry, didn't see your comment. But I don't really have a good answer for sl, which I still use - I usually set breakpoints and use logging so that I can reconstruct the semantic stack. – John Apr 09 '15 at 14:18
  • That's bad :( If error during debug - no problem. I'm more concerned about those I get in my logs and can't tell what caused it.. – katit Apr 14 '15 at 19:15
  • I suggest trying [Demystifier](https://github.com/benaadams/Ben.Demystifier) which can create much more detailed stack traces for you. – Jason Evans May 12 '21 at 14:41

1 Answers1

7

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

Further Reading: Debugging Asynchronous Code in Visual Studio 2013 - Call Stack enhancements.

KyleMit
  • 30,350
  • 66
  • 462
  • 664
John
  • 6,693
  • 3
  • 51
  • 90