如何在.NET中调试stackoverflowexception

时间:2011-01-19 09:59:35

标签: .net debugging

情境:

我刚刚开始使用并实现了一堆代码,但是当我执行它时,我会抛出一个StackOverflowException? StackOverflowException没有堆栈跟踪,所以我卡住了。我知道为什么会发生堆栈溢出,但要修复它我需要知道它的根目录是什么。

所有我得到的是:tag-you-it.dll中出现未处理的“System.StackOverflowException”类型异常

选项:

  1. 扫描所有更改并尝试确定问题所在位置。 (可能很慢)
  2. 使用调试器并逐步执行直至找到问题。 (可能比1好。)
  3. 使用配置文件并查找最常用的方法。
  4. PS:

    这是一种假设情况(尽管并不常见),因此没有可用的代码。

12 个答案:

答案 0 :(得分:16)

这几乎总是归因于递归。一个调用自身的方法,或一个调用一个方法来调用它的方法,等等。

找到它:

  • 更新:我没有意识到,但显然你无法获得StackOverflowException的堆栈跟踪(我猜想无法捕获一个跟踪,无论是)。但是有一些方法可以获得转储as mentioned here
  • ReSharper将显示调用自身的方法(它在侧边栏中放置一个绿色圆圈用于递归调用),但它不会捕获涉及两个或更多方法的递归。
  • 使用ANTS Profiler之类的工具查看哪些方法被调用次数最多。
  • 密切关注可能会调用代码的事件,这些代码意味着同一事件再次触发,从而导致循环。

偶尔你会得到这样的拼写错误:

private string name;

public string Name
{
    get { return Name; } // Ooops! This is recursive, all because of a typo...
}

这就是我个人现在更喜欢使用自动属性的原因之一。

答案 1 :(得分:11)

WinDbg可以完成工作,甚至包括获得合理的(clr)堆栈跟踪。 您需要get WinDbg,除非您已经使用Visual Studio或Windows SDK安装它。 注意:使用新GUI的“WinDbg预览”对我来说很好。

我建议您从WinDbg开始您的流程,但当然如果这更适合您,您也可以将其附加到正在运行的流程中。

注意:在启动进程后,CLR未加载,.loadby SOS.dll clr将失败(“无法找到模块'clr')。您必须等待加载CLR。停止执行一旦发生这种情况,执行:

  • sxe ld clr

加载CLR后,您必须执行以下步骤以中断SackOverflowException(在命令窗口/行中输入):

  • .loadby SOS.dll clr .loadby sos clr - 这可能会导致扩展程序被加载两次)
  • !stoponexception -create System.StackOverflowException
  • g(继续调试)

触发StackOverflowException /等待它发生

  • !clrstack(将打印堆栈跟踪)

值得注意的消息来源:

答案 2 :(得分:8)

转到Debug,exception并检查'Common Language Runtime Exceptions'中的抛出复选框。现在,当您导致stackoverflow异常时,调试器将停止(最终)并显示调用堆栈。

答案 3 :(得分:5)

您可以在调试模式下执行程序并暂停它。在当前的callstack上,你可以看到有一种方法或一组方法多次出现,这些都是有问题的方法。在这个方法上加上一个断点,看看它一直在调用它自己。

答案 4 :(得分:1)

在作为失败操作的“入口点”的方法中,放置一个断点。逐步执行代码并观察以相同模式反复发生的相同方法调用序列的出现,以便调用堆栈越来越深。

一旦你注意到,就把断点放在当前位置,无论在哪里。继续执行(Visual Studio中的F5) - 如果你在正确的轨道上,那么调试器将在同一位置快速停止,并且调用堆栈将更深。

现在你可以检查一个“实时”堆栈框架,以便弄清楚如何确保这种递归正确终止。

答案 5 :(得分:1)

ProcDump实用程序帮助我们将问题调试为described in details here。步骤:

  1. 下载工具
  2. 运行该过程,记下其ID
  3. 通过运行procdump -accepteula -e 1 -f C00000FD.STACK_OVERFLOW -g -ma <process ID> d:\home\DebugTools\Dumps(目录必须存在)来附加调试器
  4. 使异常发生,并且procdump将使您转储。
  5. 在Visual Studio中打开转储文件。对于我的示例应用程序,在打开转储文件后,VS突出显示了发生SO的行。

我们可以通过启用 CrashDiagnoser 扩展名(例如described here)在Azure上使用相同的技术。基本上,它执行与上述相同的步骤。它生成的转储文件可以在Visual Studio中下载并打开。

答案 6 :(得分:0)

如果您有代码并且能够从Visual Studio运行程序,它应该在遇到System.StackOverflowException时中断调试器(如果启用了第一次机会异常)。从那里你可以检查调用堆栈,看看哪些调用正在吹动堆栈。 enter image description here

我已经确认这适用于Visual Studio 2010和Visual C#2010 Express。

答案 7 :(得分:0)

就个人而言,我希望尽可能地将其缩小到某段代码。例如,我只有一个。奇怪的是它只发生在我无法直接调试的机器上。

我有两个并行运行的线程,所以我阻止了一个运行(或者你可以将它无法并行)。

然后我浏览了我的函数并添加了类似打印输出的函数,例如: 就像功能开始一样:

Console.WriteLine("<Enter method: {0}", DebuggingHelper.GetCurrentMethod());

在函数返回之前:

Console.WriteLine(">Exit method: {0}", DebuggingHelper.GetCurrentMethod());

将GetCurrentMethod定义为:

[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCurrentMethod()
{
    StackTrace st = new StackTrace();
    StackFrame sf = st.GetFrame(1);
    return sf.GetMethod().Name;
}

然后我运行它,也许我不添加所有功能,但足以缩小它在代码中发生的位置。然后在该部分中添加更多。

您还可以在运行某些方法时添加检查点。

然后再次运行它,你会发现在这些语句之间会发生StackOverFlow异常。继续缩小直到你找到它。

通过这种方式非常简单快速地找到它发生的位置。

答案 8 :(得分:0)

只是想添加有关使用WinDbg的答案,以及我发现的用于调试 dotnet核心应用程序的信息

  1. 确保已安装windbg
  2. 使用dotnet run通过命令行启动您的应用程序
  3. 使用windbg附加运行过程
  4. 从命令行输入.loadby sos coreclr(这将检测到您正在使用的.net core的版本,但是如果没有,则可以使用.load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.5\sos,其中2.05是您正在使用的.netcore的版本。
  5. 现在可以通过输入!help 使用
  6. 命令
  7. 使用!dso获取堆栈的转储

在我的情况下,我确切地知道了stackoverflow异常发生的位置

答案 9 :(得分:0)

我怀疑导致堆栈溢出的线程的堆栈大小是否大于Visual Studio调试器可以跟踪的某个阈值,那么调用堆栈不可用。

一种解决方法是生成一个堆栈大小小于默认堆栈大小的线程,因此Visual Studio调试器可以跟踪调用堆栈。

        (new Thread(delegate ()
        {
            ProduceAStackOverFlowHere() ;
        }, 256 * 1024)).Start();//Default size for 32 bit process is 1MB, 64 bit process is 4MB. So I'll set the size at 256KB.

答案 10 :(得分:0)

在 IKVM.MET 的开发过程中,我想出了一个技巧来调试静态和动态程序集中的堆栈溢出错误,因为 IKVM.NET 广泛使用了动态程序集。我将在 Visual Studio 调试器中运行可执行文件,当发生堆栈溢出时,Visual Studio 调试器将挂起该进程,而不是终止它。然后,我使用 dotnet-dump 获取核心转储,最后,我将终止进程以释放 RAM 并使用 dotnet-dump 分析核心转储。请注意,这仅适用于 .NET 核心应用程序。

创建核心转储的命令

dotnet-dump collect -p <Process ID here>

可以使用任务管理器获取进程 ID。 查看堆栈跟踪的命令:

dotnet-dump analyze <file name of dump> -c clrstack -all

How to use dotnet-dump, if you want to do further debugging

注意:这种调试技术有风险,因为该进程可能会被破坏,并且可能无法每次都运行。

答案 11 :(得分:-1)

查找调用自身的方法(或一种方法调用另一种方法,反之亦然)并检查它们。当您获得SO异常时,递归通常是主要的嫌疑。