I rethrow an exception with "throw;", but the stacktrace is incorrect:
static void Main(string[] args) {
try {
try {
throw new Exception("Test"); //Line 12
}
catch (Exception ex) {
throw; //Line 15
}
}
catch (Exception ex) {
System.Diagnostics.Debug.Write(ex.ToString());
}
Console.ReadKey();
}
The right stacktrace should be:
System.Exception: Test at ConsoleApplication1.Program.Main(String[] args) in Program.cs:Line 12
But I get:
System.Exception: Test at ConsoleApplication1.Program.Main(String[] args) in Program.cs:Line 15
But line 15 is the position of the "throw;". I have tested this with .NET 3.5.
In simple terms, a stack trace is a list of the method calls that the application was in the middle of when an Exception was thrown. Simple Example. With the example given in the question, we can determine exactly where the exception was thrown in the application.
Analyze external stack tracesFrom the main menu, select Code | Analyze Stack Trace or Thread Dump. In the Analyze Stack Trace dialog that opens, paste the external stack trace or thread dump into the Put a stacktrace here: text area. Click OK. The stack trace is displayed in the Run tool window.
Rule description To keep the original stack trace information with the exception, use the throw statement without specifying the exception.
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.
It's something that can be considered as expected.
Modifying stack trace is usual case if you specify throw ex;
, FxCop will than notify you that stack is modified. In case you make throw;
, no warning is generated, but still, the trace will be modified.
So unfortunately for now it's the best not to catch the ex or throw it as an inner one.
I think it should be considered as a Windows impact or smth like that - edited.
Jeff Richter describes this situation in more detail in his "CLR via C#":
The following code throws the same exception object that it caught and causes the CLR to reset its starting point for the exception:
private void SomeMethod() { try { ... } catch (Exception e) { ... throw e; // CLR thinks this is where exception originated. // FxCop reports this as an error } }
In contrast, if you re-throw an exception object by using the throw keyword by itself, the CLR doesn’t reset the stack’s starting point. The following code re-throws the same exception object that it caught, causing the CLR to not reset its starting point for the exception:
private void SomeMethod() { try { ... } catch (Exception e) { ... throw; // This has no effect on where the CLR thinks the exception // originated. FxCop does NOT report this as an error } }
In fact, the only difference between these two code fragments is what the CLR thinks is the original location where the exception was thrown. Unfortunately, when you throw or rethrow an exception, Windows does reset the stack’s starting point. So if the exception becomes unhandled, the stack location that gets reported to Windows Error Reporting is the location of the last throw or re-throw, even though the CLR knows the stack location where the original exception was thrown. This is unfortunate because it makes debugging applications that have failed in the field much more difficult. Some developers have found this so intolerable that they have chosen a different way to implement their code to ensure that the stack trace truly reflects the location where an exception was originally thrown:
private void SomeMethod() { Boolean trySucceeds = false; try { ... trySucceeds = true; } finally { if (!trySucceeds) { /* catch code goes in here */ } } }
This is a well known limitation in the Windows version of the CLR. It uses Windows' built-in support for exception handling (SEH). Problem is, it is stack frame based and a method has only one stack frame. You can easily solve the problem by moving the inner try/catch block into another helper method, thus creating another stack frame. Another consequence of this limitation is that the JIT compiler won't inline any method that contains a try statement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With