exception call stack truncated without any re-throwing

随声附和 提交于 2019-12-04 16:25:43

问题


I have an unusual case where I have a very simple Exception getting thrown and caught in the same method. It isn’t re-thrown (the usual kind of problem naïve programmers have). And yet its StackFrame contains only one the current method. Here’s what it looks like:

   at (my class).MyMethod() in C:\(my file path and line)

In reality there are probably 30 methods leading up to this in the VS2010 debugger's call stack, going across half a dozen different assemblies. It seems impossible for all that to have been optimized out. Moreover, this code is built in debug mode, without optimizations, for .NET 4. I even have (based on http://msdn.microsoft.com/en-us/library/9dd8z24x.aspx) .ini files (including one named [app].vshost.ini) in the same folder containing:

[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0

Also, the method calls are not at the end of methods, so tail-recursion optimization seems further unlikely.

As to how it is called: there are no uses of reflection on the call stack, no Invoke() or BeginInvoke() of any kind. This is just a long chain of calls from a button click. The click handler is about 10 calls down the call stack. Beneath that you have the usual WndProc, NativeWindow.Callback, native/managed transitions, and message loop. This is ultimately inside a ShowDialog() call which is run from a C# EXE assembly.

Now, I found that I can construct instances of the StackTrace class in my catch handler, and if I pass the Exception object, the call stack is also short. If instead I just call new StackTrace() with no arguments, it yields a complete call stack.

I’ve used Reflector in an attempt to debug into the internals of the Exception class getting thrown and its call stack constructed, but I couldn’t set breakpoints in Exception or in StackTrace. I could set them in Environment.GetStackTrace() and this method (which Exception calls) does not appear to get called during the construction and throwing process, but I don’t know if the debugger is really working properly. (This method does get triggered for some other things though, so I'm not sure what to make of it.)

Here’s an excerpt of the method:

private void MyMethod()
{
    ...               
    try
    {
        throw new ApplicationException("Test failure");
    }
    catch (Exception e)
    {
        StackTrace stackTrace1 = new StackTrace(e);
        StackTrace stackTrace2 = new StackTrace(e, false);
        StackTrace stackTrace3 = new StackTrace(e, true);
        StackTrace stackTrace4 = new StackTrace();
        string STs = stackTrace1.ToString() + "\n---\n"
            + stackTrace2.ToString() + "\n---\n"
            + stackTrace3.ToString() + "\n---\n"
            + stackTrace4.ToString();
        Log(EventSeverity.Debug, STs);
        ...
        }
    }

It’s really pretty simple: Throw exception, catch and log it.

I get the same results either in the debugger or when running standalone—a one-line call stack. And I know I have seen this problem elsewhere in our code base. Previously I had assumed it was due to re-throwing exceptions, but in a lot of cases it we log right inside the initial catch block. I’m quite baffled and all the web searching I’ve done hasn’t produce anything.


This is a little too much to add as a comment to the answer provided, but here's some more information:

I now see that this behavior is discussed at http://dotnetthoughts.wordpress.com/2007/10/27/where-did-my-exception-occur/ and that it is actually described at http://msdn.microsoft.com/en-us/library/system.exception.stacktrace.aspx (though I think one could easily miss what they're saying there).

So I guess my "solution" will be a little hit-or-miss. We have a central method we usually call to format exceptions. Inside that method, I'll create a new StackTrace() both with and without the Exception object. Then I'll look for the method that is at the bottom of the Exception's stack trace, and display everything beneath that in the new StackTrace(), indicating it was called by that series of calls.

The down side of course is that if this method isn't used, the information won't be there. But I had to expect some kind of code change somewhere.


回答1:


When an exception is thrown, only a partial stack trace will be used in the Exception.StackTrace property. The stack only shows calls up until the method that is catching the exception. To get the full stack (as you have noted) you should create a new StackTrace() object.

I can't find any links on it at the moment but I believe the stack trace is built by walking up the stack while throwing the exception. Once the exception reaches a catch block, the stack stops being compiled. Therefore, you only get a partial stack.

Typically, a catch block is not concerned with who called it, but where the exception is originating from.



来源:https://stackoverflow.com/questions/5301535/exception-call-stack-truncated-without-any-re-throwing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!