17

There has been discussion here before about the correct way to rethrow an exception. This question, instead, is about how to get useful behavior from Visual Studio when using rethrow.

Consider this code:

   static void foo() {
        throw new Exception("boo!");
    }

    static void Main(string[] args) {
        try {
            foo();
        } catch (Exception x) {
            // do some stuff
            throw;
        }
    }

The exception that comes out has the correct stack trace, showing foo() as the source of the exception. However, the GUI Call Stack window only shows Main, whereas I was expecting it to show the exception's call stack, all the way to foo.

When there is no rethrow, I can use the GUI to very quickly navigate the call stack to see what call caused the exception and how we got there.

With the rethrow I'd like to be able to do the same thing. Instead, the call stack that the GUI shows is of no use to me. I have to copy the exception details to the clipboard, paste it to Notepad, and then manually navigate to whichever function of the call stack I'm interested in.

By the way, I get the same behavior if I add [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] or if I change the catch to just catch (Exception).

My question is: given that the code I have uses rethrow, can someone suggest a convenient way to navigate the call stack associated with the exception? I'm using Visual Studio 2010.

Athari
  • 33,702
  • 16
  • 105
  • 146
redtuna
  • 4,586
  • 21
  • 35
  • Why are you rethrowing the exception? Is there more code in the `catch` clause that you didn't include here? Otherwise I think your solution is to simply not catch the exception. – Robert Harvey Dec 04 '10 at 01:14
  • 1
    Robert, this is a minimal snippet to illustrate the problem. The actual code does some work before rethrowing. – redtuna Dec 04 '10 at 01:19

4 Answers4

11

The debugger breaks at the throw in Main because that exception is unhandled. By default, the debugger will only break on unhandled exceptions. Once you've stopped at Main, the call stack for the original exception from foo is present in the exception, but all of the other context has been lost (e.g. locals, stack/memory state).

It sounds like you want the debugger to break on the throw in foo, so you should tell the debugger to break on first-chance exceptions:

  1. Debug » Exceptions... (Ctrl+Alt+E)
  2. Check "Thrown" for the exception types you care about (in this case, Commange Language Runtime Exceptions)
  3. Click OK
  4. Start debugging

In this case, the debugger will break immediately when foo throws an exception. Now, you can examine the stack, locals, etc., in the context of the original exception. If you continue execution (F5), the debugger will break again on the rethrow in Main.

Taking another approach, if you're running VS2010 Ultimate, you can also use IntelliTrace to "debug backwards" to see parameters, threads, and variables at the time of the exception. See this MSDN article for details. (Full disclosure: I work on a team closely related to IntelliTrace).

Chris Schmich
  • 29,128
  • 5
  • 77
  • 94
  • 1
    Thanks, that can help in some cases indeed. The problem is that in my case, there are lots of exceptions thrown about over time, and I'm only interested in the few that will be rethrown. Looking at every thrown exception would be more work that copy/pasting the call stack as I'm doing now. I was hoping for an easy way to navigate the exception's call stack, even if some of the associated state is lost. – redtuna Dec 04 '10 at 01:28
  • 1
    @redtuna: see the edit at the end of my post (might be useful if you're using VS2010 Ultimate). Also, are the thrown exceptions all of the same type? If not, you can specify the types of the exceptions you don't care about in the Exceptions dialog, and then uncheck the "Thrown" box to prevent the debugger from breaking on them. If the exceptions are all of the same type, then yes, it's much more difficult. – Chris Schmich Dec 04 '10 at 01:33
  • 2
    IntelliTrace looks mighty impressive! It looks like that's the closest we got to our goal with the tools I have here, so I'm accepting your answer (even though Athari's answer might be better for some other people). – redtuna Dec 04 '10 at 01:44
8

If you use ReSharper, you can copy exception stacktrace to clipboard, then choose in the menu: ReSharper > Tools > Browse Stack Trace (Ctrl+E,T). It will show stacktrace with clickable locations, so you'll be able to quickly navigate.


(source: jetbrains.com)

This feature is also very useful while digging through logs from users (if stacktraces of exceptions are logged).

Community
  • 1
  • 1
Athari
  • 33,702
  • 16
  • 105
  • 146
  • Nice! I don't have resharper, but that's an awesome feature. It sounds like it would speed things up nicely for me. – redtuna Dec 04 '10 at 01:40
  • 2
    @redtuna ReSharper is a must-have for C# developers. It speeds up a lot of tasks multiple times. – Athari Dec 04 '10 at 01:45
1

Not that you should re-throw but here's a blog post about how to preserve the stack trace, essentially it boils down to this:

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

...
catch (Exception ex)
{
  // do something
  // ...
  PreserveStackTrace(ex);
  throw;
}
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • 2
    Thanks, but that doesn't solve my particular problem. This code, as the blog post explains, helps to get the correct stack trace in the exception for the special case where the original exception and the catch are in the same method. In my case, the exception holds the correct stack trace: the problem is that I don't know how to use the GUI to navigate that stack trace. – redtuna Dec 04 '10 at 01:24
0

Mike Stall has given a great and simple solution to your problem:

Mark the methods where you rethrow the exception with the attribute [DebuggerNonUserCode]

The IDE will consider this is not your code and will not break the debugger in such place, and instead will look further in the stack, showing the next rethrow or the initial exception place.

(if the next rethrow is also annoying, mark it as [DebuggerNonUserCode] as well, etc...)

Wizou
  • 1,336
  • 13
  • 24