Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# exceptions and rethrow #7106

Closed
saxenark opened this issue Dec 8, 2016 · 7 comments
Closed

C# exceptions and rethrow #7106

saxenark opened this issue Dec 8, 2016 · 7 comments
Labels
question Answer questions and provide assistance, not an issue with source code or documentation.

Comments

@saxenark
Copy link

saxenark commented Dec 8, 2016

In the program below, I expected to have the line "throw new Exception("A")" also reported when I dump the exception object in Main. Unfortunately, this information is being lost even though I am doing a rethrow using throw;. Two questions:

a) Why is this behavior when throw; is supposed to preserve call stack?
b) What is the recommended way to handle this across different versions of C# and .NET?

using System;

namespace DBConnect
{
    class Program
    {
        static void f()
        {
            try
            {
                g();
            }
            catch(Exception e)
            {
                e.ToString();
                //throw new Exception("f", e);
                throw;
            }
        }

        static void g()
        {
            try
            {
                throw new Exception("A");   // this line does not show up in call trace
            }
            catch(Exception e)
            {
                e.ToString();
                //throw new Exception("g", e);
                throw;
            }
        }

        static void Main(string[] args)
        {
            try
            {
                f();
            }
            catch(Exception e)
            {
                Console.WriteLine(e.StackTrace);
                Console.WriteLine(e.ToString());
            }
        }
    }
}

@mikedn
Copy link
Contributor

mikedn commented Dec 8, 2016

a) Why is this behavior when throw; is supposed to preserve call stack?

Well, throw; (rethrow in IL) is supposed to preserve the original call stack but it also adds itself to the call stack. But in your g() method throw and rethrow are in the same method. The line number information is recorded per stack frame and there's only one stack frame for a method. So rethrow practically overwrites the line number information that throw generates.

b) What is the recommended way to handle this across different versions of C# and .NET?

I'm not aware of a way to avoid this. Besides, this is not specific to any particular version of C# and .NET, to the best of my knowledge all behave like this since day 1.

Personally I avoid this kind of code whenever possible. Not necessarily because line number information is lost because I find it weird to throw an exception and then catch and rethrow it just below. But of course, it's not always possible to avoid this kind of code in a reasonable fashion.

@danmoseley
Copy link
Member

Does ExceptionDispatchInfo help - example dotnet/corefx#14249

@mikedn
Copy link
Contributor

mikedn commented Dec 8, 2016

Hmm, looks like it could help, it doesn't produce the exact same stack trace that a "proper" rethrow would produce but at least it contains both locations:

        static void g()
        {
            try
            {
                throw new Exception("A");   // line 26
            }
            catch (Exception e)
            {
                e.ToString();
                ExceptionDispatchInfo.Capture(e).Throw();  // line 31
            }
        }

generates

   at DBConnect.Program.g() in c:\users\mike\Desktop\Temp\Projects\ConsoleApp4\Program.cs:line 26
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at DBConnect.Program.g() in c:\users\mike\Desktop\Temp\Projects\ConsoleApp4\Program.cs:line 31
   at DBConnect.Program.f() in c:\users\mike\Desktop\Temp\Projects\ConsoleApp4\Program.cs:line 18
   at DBConnect.Program.Main(String[] args) in c:\users\mike\Desktop\Temp\Projects\ConsoleApp4\Program.cs:line 39

@saxenark
Copy link
Author

saxenark commented Dec 9, 2016

I think I finally found the issue documented very clearly in MSDN

The common language runtime (CLR) updates the stack trace whenever an exception is thrown in application code (by using the throw keyword). If the exception was rethrown in a method that is different than the method where it was originally thrown, the stack trace contains both the location in the method where the exception was originally thrown, and the location in the method where the exception was rethrown. If the exception is thrown, and later rethrown, in the same method, the stack trace only contains the location where the exception was rethrown and does not include the location where the exception was originally thrown.

I just wish it was part of the throw; msdn documentation so that it is easier to co-relate.

@gkhanna79
Copy link
Member

@saxenark Can this be closed?

@saxenark
Copy link
Author

Yes, please close this. Thanks.

@danmoseley
Copy link
Member

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 27, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
question Answer questions and provide assistance, not an issue with source code or documentation.
Projects
None yet
Development

No branches or pull requests

4 participants