Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preserving original StackTrace/LineNumbers in .NET Exceptions

Understanding the difference between throw ex and throw, why is the original StackTrace preserved in this example:

    static void Main(string[] args)
    {
        try
        {
            LongFaultyMethod();
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
        }
    }

    static void LongFaultyMethod()
    {
        try
        {
            int x = 20;
            SomethingThatThrowsException(x);
        }
        catch (Exception)
        {
            throw;
        }
    }

    static void SomethingThatThrowsException(int x)
    {
        int y = x / (x - x);
    }

But not in this one:

    static void Main(string[] args)
    {
        try
        {
            LongFaultyMethod();
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
        }
    }

    static void LongFaultyMethod()
    {
        try
        {
            int x = 20;
            int y = x / (x - 20);
        }
        catch (Exception)
        {
            throw;
        }
    }

The second scenario is producing the same output as throw ex would?

In both cases, one expects to see the line number where y is initialized.

like image 265
Sam Avatar asked Oct 18 '09 14:10

Sam


People also ask

Which of the following will preserve a stack trace?

Rule description To keep the original stack trace information with the exception, use the throw statement without specifying the exception.

What is Rethrowing an exception in C#?

Re-throwing an exception means calling the throw statement without an exception object, inside a catch block. It can only be used inside a catch block.

What is stack trace in exception?

A trace of the method calls is called a stack trace. The stack trace listing provides a way to follow the call stack to the line number in the method where the exception occurs. The StackTrace property returns the frames of the call stack that originate at the location where the exception was thrown.

How do I Rethrow exceptions in VB net?

And always use just "Throw", not "Throw ex". Throw will rethrow the handles exception keeping it's stack intact.


1 Answers

I'm not sure whether this limitation is within the C# language, the CLI, or the Microsoft implementation of these, but your second example is a case where an explicit call to Exception.InternalPreserveStackTrace is required as documented in the following post. Since this method is internal, it generally has to be called through reflection. The performance issues involved in this can be almost completely alleviated by creating an Action<Exception> for the call, as shown at the end of this answer.

Reference: Rethrowing exceptions and preserving the full call stack trace

Edit: After reexamining ECMA-335 Partition I §12.4.2 (Exception handling) and Partition III §4.24 (rethrow), I now believe that the behavior you are seeing is a semantic error in the CLR (Microsoft's implementation of the CLI). The only specific reference to the behavior is "A rethrow does not change the stack trace in the object." In the case described here, the rethrow is in fact altering the stack trace, making the PreserveStackTrace hack a workaround for a know CLR flaw.

static void LongFaultyMethod() 
{ 
    try 
    { 
        int x = 20; 
        int y = x / (x - 20); 
    } 
    catch (Exception ex) 
    { 
        PreserveStackTrace(ex); // <-- add this line
        throw; 
    } 
} 

PreserveStackTrace here is an optimization of the one from that blog entry:

private static readonly Action<Exception> _internalPreserveStackTrace =
    (Action<Exception>)Delegate.CreateDelegate(
        typeof(Action<Exception>),
        typeof(Exception).GetMethod(
            "InternalPreserveStackTrace",
            BindingFlags.Instance | BindingFlags.NonPublic));

public static void PreserveStackTrace(Exception e)
{
    _internalPreserveStackTrace(e);
}
like image 164
Sam Harwell Avatar answered Sep 27 '22 17:09

Sam Harwell