8

The usual advice when rethrowing an exception is to use a throw; statement so the original stack trace is preserved. (Example)

However, when I try this simple example, the Visual Studio debugger does not show the original stack trace.

namespace ExceptionTest
{
    class Program
    {
        static void ThrowException()
        {
            throw new System.Exception();  // The line that I WANT the debugger to show.
        }

        static void Main(string[] args)
        {
            try
            {
                ThrowException();
            }
            catch (System.Exception)
            {
                System.Console.WriteLine("An exception was thrown.");

                throw;  // The line that the debugger ACTUALLY shows.
            }
        }
    }
}

How can I use the debugger to find the original source of the exception?

Community
  • 1
  • 1
Jon-Eric
  • 16,977
  • 9
  • 65
  • 97
  • possible duplicate of [C# rethrow an exception: how to get the exception stack in the IDE?](http://stackoverflow.com/questions/4351333/c-sharp-rethrow-an-exception-how-to-get-the-exception-stack-in-the-ide) – Cody Gray - on strike Feb 09 '12 at 20:55

5 Answers5

10

Your best option is to ask Visual Studio to break on the original exception rather than navigate back to it from the stack trace. To do this:

1) Click on the 'Debug' menu item 2) Click 'Exceptions...' 3) Select 'Common Language Runtime Exceptions' - 'Thrown'

With this approach you may get more than you really wanted if there are many exceptions being thrown. You can filter which exceptions it breaks on by expanding the tree list.

See image:

enter image description here

Rob Smyth
  • 1,768
  • 11
  • 19
  • This applies even when you don't use `throw;` (and instead do something like `throw ex;`), right? So what is the advantage of using `throw;` instead of `throw ex;`? – Jon-Eric Feb 09 '12 at 20:50
  • The Call Stack window will not contain the point where the exception occurred. The Exception.StackTrace property contains the preserved stack trace. Follow the steps in this answer or read the StackTrace property of the exception to determine where the exception originated. – JamieSee Feb 09 '12 at 20:59
  • @Jon-Eric If you `throw ex;` instead of `throw;` the StackTrace property on the Exception won't have the preserved stack trace. Always rethrow with `throw;` or by wrapping the exception, i.e. `throw new ApplicationException("My exception explanation.", ex);`. – JamieSee Feb 09 '12 at 21:03
  • 1
    @James Thanks. That makes sense. I wish the visual debugger would better help me leverage the StackTrace property. I found [this has been explained before](http://stackoverflow.com/q/4351333/99377). – Jon-Eric Feb 09 '12 at 21:09
  • I use Ctrl + Alt + E to get here. I switch back and forth with this on / off multiple times a day. – tsells Feb 10 '12 at 02:07
  • On Visual Studio 2019 the menus have shifted slightly so you have to go to Debug menu item > Windows > Exception Settings. Ctrl + Alt + E still works. – Caltor Jan 13 '22 at 12:38
7

If you're running Visual Studio 2010 Ultimate, use IntelliTrace.

It keeps a record of all exceptions thrown and allows you to "debug back in time" to see parameters, threads, and variables at the time of each throw.

(Taken from Chris Schmich's answer to a similar question.)

Community
  • 1
  • 1
Jon-Eric
  • 16,977
  • 9
  • 65
  • 97
1

You can use the DebuggerNonUserCode Attribute.

See http://blogs.msdn.com/b/jmstall/archive/2007/02/12/making-catch-rethrow-more-debuggable.aspx

The example becomes like this:

namespace ExceptionTest
{
    class Program
    {
        static void ThrowException()
        {
            throw new System.Exception();  // The line that I WANT the debugger to show.
        }

        [DebuggerNonUserCode()]
        static void Main(string[] args)
        {
            try
            {
                ThrowException();
            }
            catch (System.Exception)
            {
                System.Console.WriteLine("An exception was thrown.");

                throw;  // The line that the debugger ACTUALLY shows.
            }
        }
    }
}
Alebo
  • 327
  • 2
  • 7
1

The best solution that I've found is write the Exception callstack to the Debug.Console and then let the built-in code line parser in Visual Studio to provide the navigation.

I found it really useful when dealing with unhandled exceptions on the AppDomain and WPF Dispatcher as Visual Studio always breaks too late.

Based from an article on Code Project, I have modified it that it outputs to the Console as a single block of text - rather than line-by-line - which was necessary I have logging also writing to the Console.

Usage

public void ReportException(Exception exception)
{
    if (Debugger.IsAttached)
    {
        DebugHelper.PrintExceptionToConsole(exception);
        Debugger.Break();
    }

    // ...

}

Source

public static class DebugHelper
{
    // Original idea taken from the CodeProject article 
    // http://www.codeproject.com/Articles/21400/Navigating-Exception-Backtraces-in-Visual-Studio

    private static readonly string StarSeparator = new String('*', 80);
    private static readonly string DashSeparator = new String('-', 80);
    private const string TabString = "   ";

    /// <summary>
    /// Prints the exception using a format recognized by the Visual Studio console parser.
    /// Allows for quick navigation of exception call stack.
    /// </summary>
    /// <param name="exception">The exception.</param>
    public static void PrintExceptionToConsole(Exception exception)
    {
        using (var indentedTextWriter = new IndentedTextWriter(Console.Out, TabString))
        {                
            var indentLevel = 0;
            while (exception != null)
            {
                indentedTextWriter.Indent = indentLevel;
                indentedTextWriter.Write(FormatExceptionForDebugLineParser(exception));
                exception = exception.InnerException;
                indentLevel++;
            }
        }
    }

    private static string FormatExceptionForDebugLineParser(Exception exception)
    {
        StringBuilder result = new StringBuilder();

        result.AppendLine(StarSeparator);
        result.AppendLineFormat("  {0}: \"{1}\"", exception.GetType().Name, exception.Message);
        result.AppendLine(DashSeparator);

        // Split lines into method info and filename / line number
        string[] lines = exception.StackTrace.Split(new string[] { " at " }, StringSplitOptions.RemoveEmptyEntries)
                                                .Select(x => x.Trim())
                                                .Where(x => !String.IsNullOrEmpty(x))
                                                .ToArray();

        foreach (var line in lines)
        {
            string[] parts = line.Split(new string[] { " in " }, StringSplitOptions.RemoveEmptyEntries);
            string methodInfo = parts[0];
            if (parts.Length == 2)
            {
                string[] subparts = parts[1].Split(new string[] { ":line " }, StringSplitOptions.RemoveEmptyEntries);
                result.AppendLineFormat("  {0}({1},1): {2}", subparts[0], Int32.Parse(subparts[1]), methodInfo);
            }
            else
                result.AppendLineFormat("  {0}", methodInfo);
        }

        result.AppendLine(StarSeparator);

        return result.ToString();
    }

}

To use the above, as is, you will also need the Extension Method below and add the System.CodeDom.Compiler namespace for IndentedTextWriter.

Extension Method

/// <summary>
/// Appends the string returned by processing a composite format string followed by the default line terminator.
/// </summary>
/// <param name="sb">The StringBuilder.</param>
/// <param name="format">The format.</param>
/// <param name="args">The args.</param>
public static void AppendLineFormat(this StringBuilder sb, string format, params object[] args)
{
    sb.AppendFormat(format, args);
    sb.AppendLine();
}
Dennis
  • 20,275
  • 4
  • 64
  • 80
0

Incidentally, in vb.net, one can use exception filters in some situations like this where one knows one isn't really interested in catching an exception--just finding out that it happened. If the code were written in vb.net and used a filter to capture the exception (probably doing the output itself within the 'finally' block) there wouldn't be a "catch and rethrow"--the cursor would jump to the source of the original exception (as a bonus, vs would interrupt the program flow before any stack unwinding took place). Note that one can opt to have vs trap every time a particular exception is thrown, whether or not it will be caught, but sometimes one is only interested in some of the places an exception is thrown, rather than all of them.

supercat
  • 77,689
  • 9
  • 166
  • 211