异常调用堆栈被截断,没有任何重新抛出 [英] exception call stack truncated without any re-throwing

查看:237
本文介绍了异常调用堆栈被截断,没有任何重新抛出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个异常的情况,我有一个非常简单的异常被抛出并被捕获在同一个方法。 不重新抛出(程序员天生的常见问题)。而它的StackFrame只包含一个当前的方法。这是它的样子:

  at(我的类).MyMethod()在C:\(我的文件路径和行)

实际上,在VS2010调试器的调用堆栈中,大概有30种方法可以引导,六个不同的组合。所有这些都被优化了似乎是不可能的。此外,此代码内置于调试模式,无优化,适用于.NET 4.我甚至(基于 http://msdn.microsoft.com/en-us/library/9dd8z24x.aspx ).ini文件(包括一个名为[app] .vshost.ini的文件)在同一文件夹中包含:

  [.Net Framework调试控件] 
GenerateTrackingInfo = 1
AllowOptimize = 0

另外,方法调用不在方法的末尾,所以尾递归优化似乎更不可能。



关于它的调用方式:在调用堆栈中没有使用反射,没有任何Invoke()或BeginInvoke()。这只是一个从按钮点击的长串呼叫。点击处理程序在调用堆栈下约10个调用。在下面你有通常的WndProc,NativeWindow.Callback,本机/托管转换和消息循环。这最终在一个从C#EXE程序集中运行的ShowDialog()调用中。



现在,我发现我可以在我的catch处理程序中构造StackTrace类的实例,如果我传递异常对象,调用堆栈也很短。如果我只是调用新的StackTrace()没有参数,它会产生一个完整的调用堆栈。



我已经使用Reflector来尝试调试内部异常类被抛出并调用栈构造,但是我无法在Exception或StackTrace中设置断点。我可以在Environment.GetStackTrace()中设置它们,并且在构造和抛出过程中,这种方法(Exception调用)似乎没有被调用,但是我不知道调试器是否真的正常工作。 (此方法确实会触发一些其他的事情,所以我不知道该怎么做。)



这是一个摘录的方法:

  private void MyMethod()
{
...
try
{
throw new ApplicationException(Test failure);
}
catch(异常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()+\\\
--- \\\

+ stackTrace2.ToString()+\\\
--- \\\

+ stackTrace3.ToString()+\\\
--- \\\

+ stackTrace4.ToString();
Log(EventSeverity.Debug,STs);
...
}
}

真的很简单:抛出异常,捕获并记录它。



我在调试器中或运行独立的单行调用堆栈时获得相同的结果。我知道我在代码库中的其他地方看到这个问题。以前我以为这是因为重新抛出异常,但是在很多情况下,我们记录在初始catch块的内部。我很困惑,所做的所有网页搜索都没有产生任何东西。






这是一个小小的太多的添加作为对提供的答案的评论,但这里有一些更多的信息:



我现在看到,这个行为讨论在
http://dotnetthoughts.wordpress.com/2007/10/27/ where-did-my-exception-occurred / ,它实际上是在 http://msdn.microsoft.com/en-us/library/system.exception.stacktrace.aspx (虽然我认为可以轻易地想念他们在说什么)。 / p>

所以我想我的解决方案将会有点命中。我们通常称之为格式化异常的中心方法。在这个方法中,我将创建一个新的StackTrace(),不管是否有异常对象。然后,我将寻找位于Exception堆栈跟踪底部的方法,并在新的StackTrace()中显示所有的内容,表示它被该系列调用所调用。



当然的缺点是如果不使用这种方法,信息就不会存在。但是我不得不期待某种代码的改变。

解决方案

当抛出异常时,只有部分堆栈跟踪将用于 Exception.StackTrace 属性。堆栈只显示调用,直到捕获异常的方法。要获得完整的堆栈(如您所指出的),您应该创建一个新的StackTrace()对象。



I目前找不到任何链接,但我相信堆栈跟踪是通过在抛出异常的同时走上栈来构建的。一旦异常到达catch块,堆栈将停止编译。因此,您只能获得部分堆栈。



通常,catch块不关心谁调用它,但异常来自哪里。


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.

解决方案

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.

这篇关于异常调用堆栈被截断,没有任何重新抛出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆