Jon-Eric
Jon-Eric

Reputation: 17295

Find where rethrown exception was originally thrown using Visual Studio C# debugger?

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?

Upvotes: 8

Views: 14137

Answers (5)

Alebo
Alebo

Reputation: 327

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

Upvotes: 1

Jon-Eric
Jon-Eric

Reputation: 17295

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

Upvotes: 7

Dennis
Dennis

Reputation: 20571

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();
}

Upvotes: 1

supercat
supercat

Reputation: 81347

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.

Upvotes: 0

Rob Smyth
Rob Smyth

Reputation: 1858

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

Upvotes: 10

Related Questions