Reputation: 17295
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
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
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
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
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
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:
Upvotes: 10