Incorrect Stacktrace by Rethrow

Incorrect stacktrace by rethrow

Throwing twice in the same method is probably a special case - I've not been able to create a stack trace where different lines in the same method follow each other. As the word says, a "stack trace" shows you the stack frames that an exception traversed. And there is only one stack frame per method call!

If you throw from another method, throw; will not remove the entry for Foo(), as expected:

  static void Main(string[] args)
{
try
{
Rethrower();
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
Console.ReadKey();
}

static void Rethrower()
{
try
{
Foo();
}
catch (Exception ex)
{
throw;
}

}

static void Foo()
{
throw new Exception("Test");
}

If you modify Rethrower() and replace throw; by throw ex;, the Foo() entry in the stack trace disappears. Again, that's the expected behavior.

Rethrow an Exception with correct line numbers in the stack trace

You do not lose original exception if you place it in an inner exception.

[WebMethod]
public void ExceptionTest()
{
try
{
throw new Exception("An Error Happened");
}
catch (Exception ex)
{
evlWebServiceLog.WriteEntry(ex.ToString(), EventLogEntryType.Error);
throw new Exception("Your message", ex);
}
}

throw; is said to not reset stack trace, but it does in certain circumstances

As Darin already point out the reduced stack trace is due to method inlining. However there is also the point of the line reference that is available in the stack trace not being equal.

I do not know the explanation behind this, but there is a way for you to keep all stacktrace information when rethrowing an exception. You need to throw a new exception and pass the one you catch as the inner exception. With this approach the merged stacktrace will contain the point of origin of the exception and also the point where the exception is rethrown.

I talked about this and presented full examples on different ways to rethrow exceptions in the following blog post:

.NET Exceptions – throw ex is evil but throw is not that innocent


Your comment motivated me for a quick research but the best I could find was this comment by Jonathan de Halleux on a blog post about catch and rethrow:

It also changes the line number in the stacktrace in the method that
rethrows (since the rethrow becomes the site of throw in that method).

This could be elaborated further, but it points to the fact that probably the throw site which will then be used to obtain line information is tracked at each method and the rethrow causes it to be overridden.

So even though the stacktrace is preserved when using throw instead of throw e the original throw site will be lost unless you wrap and throw a new exception.


Other things to try; since SO does not allow direct messages and the above comment was made by Peli you can try to tag this question with pex to get his attention and get him to follow up on that comment. :)

How to rethrow InnerException without losing stack trace in C#?

In .NET 4.5 there is now the ExceptionDispatchInfo class.

This lets you capture an exception and re-throw it without changing the stack-trace:

using ExceptionDispatchInfo = 
System.Runtime.ExceptionServices.ExceptionDispatchInfo;

try
{
task.Wait();
}
catch(AggregateException ex)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}

This works on any exception, not just AggregateException.

It was introduced due to the await C# language feature, which unwraps the inner exceptions from AggregateException instances in order to make the asynchronous language features more like the synchronous language features.

Rethrowing exceptions in Java without losing the stack trace

catch (WhateverException e) {
throw e;
}

will simply rethrow the exception you've caught (obviously the surrounding method has to permit this via its signature etc.). The exception will maintain the original stack trace.

How can I rethrow an Inner Exception while maintaining the stack trace generated so far?

It is possible to preserve the stack trace before rethrowing without reflection:

static void PreserveStackTrace (Exception e)
{
var ctx = new StreamingContext (StreamingContextStates.CrossAppDomain) ;
var mgr = new ObjectManager (null, ctx) ;
var si = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;

e.GetObjectData (si, ctx) ;
mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
mgr.DoFixups () ; // ObjectManager calls SetObjectData

// voila, e is unmodified save for _remoteStackTraceString
}

This wastes a lot of cycles compared to InternalPreserveStackTrace, but has the advantage of relying only on public functionality. Here are a couple of common usage patterns for stack-trace preserving functions:

// usage (A): cross-thread invoke, messaging, custom task schedulers etc.
catch (Exception e)
{
PreserveStackTrace (e) ;

// store exception to be re-thrown later,
// possibly in a different thread
operationResult.Exception = e ;
}

// usage (B): after calling MethodInfo.Invoke() and the like
catch (TargetInvocationException tiex)
{
PreserveStackTrace (tiex.InnerException) ;

// unwrap TargetInvocationException, so that typed catch clauses
// in library/3rd-party code can work correctly;
// new stack trace is appended to existing one
throw tiex.InnerException ;
}

rethrow exception preserving backtrace

With your script:

backtrace
quit

... you will only see a stack trace when the inferior is about to exit (or when you use a core file, like in your example, when it has already exited), because you have not told gdb to stop anywhere.

Another approach would be to use the gdb catch throw command, plus a bit of scripting. This way you could capture a stack trace at each throw. You could do it this way:

(gdb) catch throw
Catchpoint 1 (throw)
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
> silent
> backtrace
> continue
> end

This will stop at every throw and print a backtrace. However, you did want to not print stack traces from the fatal frame. For this, you could use a gdb convenience function and make the catchpoint conditional:

(gdb) cond 1 $_any_caller_matches("fatal", 10)

(The "10" is just a guess at how many frames may separate fatal from the C++ library innards that handle throwing.)



Related Topics



Leave a reply



Submit