如何在 .NET 中调试 stackoverflowexception

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4733878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 15:09:18  来源:igfitidea点击:

How to debug a stackoverflowexception in .NET

.netdebugging

提问by Christo

Scenario:

设想:

I've just been on a roll and implemented a bunch of code, but when I execute it I get thrown a StackOverflowException? The StackOverflowException doesn't have a stacktrace so I'm stuck. I know why a stack overflow might occur, but to fix it I need to know where it's root is.

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

All I'm getting is: An unhandled exception of type 'System.StackOverflowException' occurred in tag-you're-it.dll

我得到的只是:在 tag-you're-it.dll 中发生了类型为“System.StackOverflowException”的未处理异常

Options:

选项:

  1. Scan through all the changes and try to pin point the problem. (could be slow)
  2. Use a debugger and step through till you find the problem. (probably better than 1.)
  3. Use a profile and look for the most called methods.
  4. ?
  1. 浏览所有更改并尝试找出问题所在。(可能很慢)
  2. 使用调试器并逐步执行,直到找到问题为止。(可能比 1 好。)
  3. 使用配置文件并查找调用次数最多的方法。
  4. ?

PS:

PS:

This is a hypothetical situation (although not too uncommon) and therefore no code is available.

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

采纳答案by BatteryBackupUnit

WinDbg can get the job done, including even getting a sensible (clr) stack trace. You'll need to get WinDbgunless you've already installed it with Visual Studio or Windows SDK. Note: "WinDbg Preview" with the new GUI has worked fine for me.

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

I suggest to start your process from WinDbg, but of course you can also attach it to a running process if this suits you better.

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

Note: Right after starting the process, the CLR is not loaded, and .loadby SOS.dll clrwill fail ("unable to find module 'clr'). You have to wait for the CLR to be loaded. To stop execution once that happens perform:

注意:在启动进程后,CLR 没有被加载,并且.loadby SOS.dll clr会失败(“无法找到模块‘clr’)。你必须等待 CLR 被加载。一旦发生这种情况就停止执行,请执行:

  • sxe ld clr
  • sxe ld clr

Once the CLR is loaded you'll have to perform the following steps to break on StackOverflowException(enter in the command window / line):

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

  • .loadby SOS.dll clr(not.loadby sos clr—this can lead to the extension being loaded twice)
  • !stoponexception -create System.StackOverflowException
  • g(continues debugging)
  • .loadby SOS.dll clr不是.loadby sos clr——这会导致扩展被加载两次)
  • !stoponexception -create System.StackOverflowException
  • g(继续调试)

trigger the StackOverflowException / wait for it to happen

触发 StackOverflowException / 等待它发生

  • !clrstack(will print the stacktrace)
  • !clrstack(将打印堆栈跟踪)

Notable sources:

值得注意的来源:

回答by Neil Barnwell

This is almost always due to recursion. Either a method calling itself, or a method calling a method that calls it back and so on.

这几乎总是由于递归。要么是调用自身的方法,要么是调用回调它的方法的方法,依此类推。

To find it:

要找到它:

  • UPDATED: I didn't realise, but apparently you can't get the stack trace for a StackOverflowException(I guess something to do with not being able to catch one, either). However there are ways to get a dump as mentioned here.
  • ReSharper will show methods that call themselves (it puts a little green circle in the sidebar for recursive calls) though it won't catch recursion where two or more methods are involved.
  • Use a tool like ANTS Profiler to see which methods are called the most times.
  • Keep an eye out for events that fire that might call code that means the same event fires again, causing a loop.
  • 更新:我没有意识到,但显然您无法获得 a 的堆栈跟踪StackOverflowException(我想也与无法捕获一个有关)。但是,有一些方法可以获得这里提到的转储。
  • ReSharper 将显示调用自身的方法(它在侧栏中为递归调用放置一个小绿色圆圈),尽管它不会捕获涉及两个或多个方法的递归。
  • 使用 ANTS Profiler 之类的工具查看调用次数最多的方法。
  • 密切关注可能调用代码的事件,这意味着同一事件再次触发,从而导致循环。

Occasionally you'll get typos like this, too:

有时你也会遇到这样的拼写错误:

private string name;

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

Which is one reason why I personally now prefer to use automatic properties.

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

回答by Johan

Go to Debug, exceptions and check the thrown checkbox at 'Common Language Runtime Exceptions'. Now when you cause the stackoverflow exception, the debugger will stop (eventually) and show you the call stack.

转到调试,异常并选中“公共语言运行时异常”中抛出的复选框。现在,当您导致 stackoverflow 异常时,调试器将(最终)停止并显示调用堆栈。

回答by Borja

You can execute the program on debug mode and pause it. On the current callstack you can see that there are a method or a group of method that appears several times, these are the problematic methods. Put a break point on this method and look what its calling itself all the time.

您可以在调试模式下执行程序并暂停它。在当前的调用栈上可以看到有一个方法或者一组方法出现了好几次,这些就是有问题的方法。在这个方法上放置一个断点,看看它一直在调用什么。

回答by Andrey Stukalin

The ProcDumputility has helped us to debug the issue as described in details here. Steps:

ProcDump工具帮助我们为调试问题在这里详细描述。脚步:

  1. Download the tool
  2. Run the process, note its ID
  3. Attach the debugger by running procdump -accepteula -e 1 -f C00000FD.STACK_OVERFLOW -g -ma <process ID> d:\home\DebugTools\Dumps(the directory must exist)
  4. Make the exception to happen and the procdump will make you a dump.
  5. Open the dump file in the Visual Studio. For my sample app right after opening the dump file the VS has highlighted the line on which the SO happened.
  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 的行。

We can use the same technique on Azure by enabling the CrashDiagnoserextension, like described here. Basically it performs the same steps as above. The dump file it generates could be downloaded and opened within the Visual Studio.

我们可以通过启用CrashDiagnoser扩展在 Azure 上使用相同的技术,如此处所述。基本上它执行与上面相同的步骤。它生成的转储文件可以在 Visual Studio 中下载和打开。

回答by jazza1000

Just wanted to add to the answer about using WinDbg, with what I found for debugging a dotnet coreapplication

只是想添加有关使用 WinDbg 的答案,以及我在调试dotnet 核心应用程序时发现的内容

  1. make sure you have windbg installed
  2. start your application via command line with dotnet run
  3. attach to the running process with windbg
  4. from the command line enter .loadby sos coreclr(this should detect the version of .net core that you are using, but if not you can use .load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.5\soswhere 2.05 is the version of .netcore you are using
  5. commands are now available by entering !help
  6. Use !dsoto get a dump of the stack
  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. 使用!dso获取堆栈转储

In my case that told me exactly where the stackoverflow exception was occurring

在我的情况下,它告诉我 stackoverflow 异常发生的确切位置

回答by Jas

I suspect if the stack size of the thread that caused the stack overflow is larger than some threshold that Visual Studio debugger can track, then the call stack is unavailable.

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

A workaround is to spawn a thread who's stack size is smaller than the default stack size, thus Visual Studio debugger can track the call stack.

一种解决方法是生成一个堆栈大小小于默认堆栈大小的线程,因此 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.

回答by Daniel Earwicker

At the method that is the "entry point" to the operation that fails, put a breakpoint. Step through the code and watch for occurrences of the same sequence of method calls happening over and over in an identical pattern so the call stack gets deeper and deeper.

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

As soon as you notice that, put a breakpoint at the current location, wherever that is. Continue execution (F5 in Visual Studio) - if you're on the right track then the debugger will stop very quickly at the same location, and the call stack will be even deeper.

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

Now you have a "live" stack frame you can examine, in order to figure out how to ensure that this recursion will properly terminate.

现在您有一个可以检查的“实时”堆栈帧,以找出如何确保此递归正确终止。

回答by PPC-Coder

If you have the code and are able to run your program from Visual Studio it should break in the debugger (if first-chance exceptions are enabled) when it encounters the System.StackOverflowException. From there you can examine the call stack and see which calls are blowing the stack. enter image description here

如果您有代码并且能够从 Visual Studio 运行您的程序,它应该会在遇到 System.StackOverflowException 时在调试器中中断(如果启用了第一次机会异常)。从那里您可以检查调用堆栈并查看哪些调用正在破坏堆栈。在此处输入图片说明

I've confirmed that this works for Visual Studio 2010 and Visual C# 2010 Express.

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

回答by Matthew Czarnek

Personally, I like to narrow it down as much as possible to a certain section of code. For example, I just had one. The odd thing was it was only happening on the machine I couldn't directly debug.

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

I had two threads running in parallel, so I stopped one from running(or you could unparallelize it).

我有两个并行运行的线程,所以我停止了一个运行(或者你可以让它无与伦比)。

Then I went through my functions and added like print out functions, such as: Just as function starts:

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

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

Just before function returns:

就在函数返回之前:

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

Where GetCurrentMethod is defined as:

其中 GetCurrentMethod 定义为:

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

Then I run it, maybe I don't add to all functions, but enough to narrow down where in the code it's happening. Then within that section, add more.

然后我运行它,也许我没有添加到所有函数中,但足以缩小它在代码中发生的位置。然后在该部分中添加更多内容。

You could also add checkpoint as it runs through certain methods.

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

Then run it again, what you'll find is that the StackOverFlow exception will occur between those statements. Keep narrowing down till you find it.

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

Fairly simply and quick to find where it's happening this way.

以这种方式相当简单快速地找到它发生的地方。