Fabrice's weblog

Tools and Source

News

My .NET Toolbox
An error occured. See the script errors signaled by your web browser.
No tools selected yet
.NET tools by SharpToolbox.com

Read sample chapters or buy LINQ in Action now!
Our LINQ book is also available on AMAZON

.NET jobs

Emplois .NET

Tuneo

ASP.NET Hosting transatlantys

Contact

Me

Others

Selected content

Archives

Rethrowing exceptions and preserving the full call stack trace

Did you know that depending on the way you rethrow exceptions you may lose important information? There are already several blog posts that explain and demonstrate the difference between throw and throw ex. I'm realizing only now that none of the two solutions yields the complete call stack trace information!

Let's see what the problem is and I'll show you the real solution.

I'll use the following method to generate an exception:

private static void BadWork()
{
  int i = 0;
  int j = 12 / i; // Line 10: DivideByZeroException
  int k = j + 1;
}

Let's consider what happens if we call BadWork and rethrow the exception with throw ex as follows:

try
{
  BadWork();
}
catch (Exception ex)
{
  // do something
  // ...
  throw ex; // Line 24
}

Here is the call stack trace that we get in this case:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowEx() in Program.cs:line 24
   at Program.Main(String[] args) in Program.cs:line 88

Line 24 is where throw ex is, not where the exception was thrown.

Let's now replace throw ex by throw:

try
{
  BadWork();
}
catch
{
  // do something
  // ...
  throw; // Line 38
}


This time, here is the call stack trace:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.BadWork() in Program.cs:line 10
   at Program.WithThrow() in Program.cs:line 38
   at Program.Main(String[] args) in Program.cs:line 89

As you can see, we get one additional stack frame this time. Line 10 is where the exception was thrown, which is important information because this is the only information that identifies where the exception actually happened.

This shows that it's better to use throw rather than throw ex if you want the full stack trace information to be preserved. However, there are cases where throw is not enough. In the following example, throw does not preserve the full stack trace:

try
{
  int i = 0;
  int j = 12 / i; // Line 47
  int k = j + 1;
}
catch
{
  // do something
  // ...
  throw; // Line 54
}

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowIncomplete() in Program.cs:line 54
   at Program.Main(String[] args) in Program.cs:line 106

This time, you can see that information is lost again. Line 54 is where throw is, not where the exception was thrown.

To preserve the full call stack information, you need to use the following method:

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

This method can be used as follows: 

try
{
  int i = 0;
  int j = 12 / i; // Line 78
  int k = j + 1;
}
catch (Exception ex)
{
  // do something
  // ...
  PreserveStackTrace(ex);
  throw; // Line 86
}

Here is the new call stack information you get with the above code:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowAndStackTracePreservation() in Program.cs:line 78
   at Program.WithThrowAndStackTracePreservation() in Program.cs:line 86
   at Program.Main(String[] args) in Program.cs:line 110

Here is the call stack information you get with throw ex and a call to PreserveStackTrace:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.BadWork() in Program.cs:line 10
   at Program.WithThrowExAndStackTracePreservation() in Program.cs:line 62
   at Program.WithThrowExAndStackTracePreservation() in Program.cs:line 69
   at Program.Main(String[] args) in Program.cs:line 109

Here we get the full call stack information. Lines 78 and 10 are where the exceptions were thrown. To my knowledge, this is the only way to get complete call stack information in your logs. Without it, it may be difficult to hunt down some bugs.
It's worth noting that if you call PreserveStackTrace, then you can use throw or throw ex and you'll equally get the full stack trace information.

I found this useful trick on Chris Taylor's blog. If you want to use this with .NET 1, you should refer to Chris' post because it seems that the InternalPreserveStackTrace method didn't exist before .NET 2.0.

The complete source code is attached to this post.

Attachment: Program.cs.txt
Posted: Jan 02 2008, 01:14 PM by Fabrice Marguerie | with 18 comment(s)
Filed under: , ,

Comments

Fabrice Marguerie said:

Nikola, when you throw a new exception with the inner exception specified, you lose the type of the original exception.

Throwing a new exception of type Exception or a more specific type can be useful when done on purpose. But if you always throw Exception, you are losing something. The trick I show here is useful when you want to let the original exception flow, without creating a new one, which would mean something different.

Let's consider the following example:

try

{

 ...

 try

 {

   // Code that throws a DivideByZeroException

 }

 catch (Exception x)

 {

   ...

   throw new Exception("message", x);

 }

}

catch (DivideByZeroException)

{

 // NEVER EXECUTED because all exceptions have been encapsulated into new instances of the Exception class

}

# January 2, 2008 11:26 AM

Jose Arturo Cano said:

Hi, I'm working in vb.net

If I use:

Try

 ....

Catch ex As Exception

 PreserveStackTrace(ex);

 Throw ex // Line 86

End Try

The stack is not preserved.

It works only if I use:

Try

 ....

Catch ex As Reflection.TargetInvocationException

 PreserveStackTrace(ex);

 Throw ex // Line 86

End Try

The problem is that "Reflection.TargetInvocationException" does not catch all the exceptions, only the ones thrown by reflected methods.

Does anyone knows a way for this to work catching all the exceptions?

# May 21, 2008 12:04 PM

John said:

To Fabrice Marguerie:

Fabrice you do not actually lose the type. When you use innerException or rethrow an exception, it still knows what type of exception it is.

I sometimes wish people would be sure of their facts before posting!

# October 30, 2008 6:50 PM

Fabrice Marguerie said:

John, I guess that the text you refer to is "when you throw a new exception with the inner exception specified, you lose the type of the original exception."

What I mean by this is that the type of the new exception is indeed different than the original exception. At least in Nikola's example. Of course, the type of the inner exception is unchanged. But when you throw a new exception of a different type, you cannot catch this exception in a catch statement based on the original type. That's why it's better to rethrow the original exception if that's what you want. That's not always what you want and nesting exceptions using innerException is perfectly fine and useful in some cases.

I'm sure of my facts on this.

# October 30, 2008 7:30 PM

Matthew said:

Hell yah, thanks for the article.  I was trying to accomplish something similar by writing to _stackTraceString of innerexception using reflection but that wasn't working.  This bit of code is exactly what I needed. (and gives a better result)

# November 17, 2008 1:47 AM

Someone said:

That's exactly what I was looking for.

Thanks!

# December 8, 2008 11:52 AM

Rizwan Khalid said:

I believe the use of inner exception is much safer than any other method but there are some ground rules.

Only catch an exception in an inner function when you have some useful information to add:

try{

}

catch(exception e){

 //Log some info regarding the state e.g. current

 //data

 throw new exception('my custom message', e)

}

That way stack is only increased when you need local information otherwise the exception can just be allowed to pass through to a parent function

# March 26, 2009 8:37 AM

John (different one) said:

Supposing that I always catch "Exception" and I'm not terribly concerned about the type, then using Throw("msg", ex) with the inner exception specified, WILL retain the original stack trace, correct?

# April 22, 2009 5:51 PM

Fabrice Marguerie said:

Mr Different John,

I haven't checked that, but I would say yes, assuming that Throw("msg", ex) is the equivalent of the following C# code:

throw new Exception("msg", ex);

Fabrice

# April 22, 2009 8:26 PM

farrukh said:

Dear Fabrice,

First of all thanks for such a nice article. It really provided me what i was looking for.

Secondly you are right

Throw("msg", ex)

is the equivalent of

throw new Exception("msg", ex);

# June 11, 2009 3:24 AM

Fabrice Marguerie said:

Thanks farrukh

# June 11, 2009 5:19 AM

GokuDaMaster said:

Fabrice,

Thanks for the post.  Would this slight modification be ok?

public static void RethrowException(Exception ex)

       {

           MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",

                                                                        BindingFlags.Instance | BindingFlags.NonPublic);

           preserveStackTrace.Invoke(ex, null);

           throw ex;

       }

# July 8, 2009 3:01 PM

Fabrice Marguerie said:

GokuDaMaster, why would one want to do this?

Why would you want to have the stack trace show the RethrowException method as the exception source??

# July 11, 2009 12:44 PM

swingnchad1 said:

It's more for convenience and readability I guess.  The stack trace would still show the function that called RethrowException.  

As long as the same function didn't call RethrowException multiple times it would be easy to find the problem line, otherwise using your version would be better.

# July 13, 2009 5:40 PM

atconway said:

There was an older comment from 'Jose' about having some issues in VB.NET.  I took the directly converted C# -> VB.NET code and tested it.  It worked as descibed with no issues. I had code with and without the call to the Sub, and my exception log would show the proper line number with stack trace or omit it as excpected. This is a well explained entry on this topic, thank you.  The VB.NET code I used is below:

   Private Shared Sub PreserveStackTrace(ByVal exObj As Exception)

       Dim preserveStackTrace As MethodInfo = GetType(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance Or BindingFlags.NonPublic)

       preserveStackTrace.Invoke(exObj, Nothing)

   End Sub

# November 10, 2009 3:33 PM

Yogesh said:

Thank you.

You make my day.

# June 30, 2010 4:30 AM

Fabrice Marguerie said:

Mikhail, thanks a lot for sharing the results of your study.

Personally, I consider that performance is not a big issue because exceptions should remain exceptional, as you write at the end of your comment.

To answer your question, what's expensive is reflection.

This can be largely improved by caching the results of the call to typeof(Exception).GetMethod(...) (in a static field). This is a common practice to reduce the cost of accessing members via reflection.

I haven't tested this, but I invite you to include this option in your benchmark.

# December 13, 2010 5:52 AM