【问题标题】:How to debug System.StackOverflowException without link to source code?如何在不链接到源代码的情况下调试 System.StackOverflowException?
【发布时间】:2013-01-07 13:29:21
【问题描述】:

最近,我经常遇到类型的错误

“'System.StackOverflowException' 类型的未处理异常 发生在未知模块中。”。

这发生在具有相当大代码库 (C# / XNA) 的游戏(我开发的)中。但通常只有在玩了几分钟后才会出现错误(而不是每次运行)。

不幸的是,Visual Studio 调试器似乎无法进一步定位问题,只是让我检查汇编代码而不参考我的源代码行。如何调试这样的错误?我猜想 Valgrind 之类的工具在 C# 中不可用。有没有更好的调试器可以告诉我问题在源代码中的位置?

应用以下建议答案中的步骤时可用的调用堆栈。它是:

ntdll.dll!_NtWaitForSingleObject@12()  + 0x15 bytes 
ntdll.dll!_NtWaitForSingleObject@12()  + 0x15 bytes 
KernelBase.dll!_WaitForSingleObjectEx@12()  + 0xcb bytes    
kernel32.dll!_WaitForSingleObjectExImplementation@12()  + 0x43 bytes    
clr.dll!CLREvent::CreateManualEvent()  - 0x15f3bb bytes 
clr.dll!CLREvent::CreateManualEvent()  - 0x15f37a bytes 
clr.dll!CLREvent::WaitEx()  + 0x47 bytes    
clr.dll!CLREvent::Wait()  + 0x19 bytes  
clr.dll!Thread::WaitSuspendEventsHelper()  + 0xa8 bytes 
clr.dll!Thread::WaitSuspendEvents()  + 0x17 bytes   
clr.dll!Thread::RareEnablePreemptiveGC()  + 0x181977 bytes  
clr.dll!Thread::RareDisablePreemptiveGC()  + 0x38e3 bytes   
clr.dll!Debugger::SendException()  + 0x12b bytes    
clr.dll!Debugger::LastChanceManagedException()  + 0x19f bytes   
clr.dll!NotifyDebuggerLastChance()  + 0x79 bytes    
clr.dll!WatsonLastChance()  + 0x166 bytes   
clr.dll!EEPolicy::HandleFatalStackOverflow()  + 0x189 bytes 
clr.dll!EEPolicy::HandleStackOverflow()  + 0xd8 bytes   
clr.dll!_COMPlusFrameHandler()  + 0xff302 bytes 
ntdll.dll!ExecuteHandler2@20()  + 0x26 bytes    
ntdll.dll!ExecuteHandler@20()  + 0x24 bytes 
ntdll.dll!_RtlDispatchException@8()  + 0xd3 bytes   
ntdll.dll!_KiUserExceptionDispatcher@8()  + 0xf bytes   
clr.dll!SystemNative::ArrayCopy()  + 0x19 bytes 
mscorlib.ni.dll!6ed326a2()  
Frames below may be incorrect and/or missing, no symbols loaded for mscorlib.ni.dll 

【问题讨论】:

  • 你连堆栈跟踪都没有吗?
  • 如果您有源代码访问权限,请附加调试器并在本地运行它,它将带您到引发异常的行
  • Stackoverflow 不是论坛
  • @trippino "localized" 和 "localized" 都是完全有效的拼写。你编辑的那部分是假的。不过,我把它改回来同样是假的。
  • 使用在 Debug 中而不是在 Release 中构建的代码,因为 Release 构建配置可能不会导出调试符号,并且不会让您看到生成溢出的代码。另外,您可能看不到异常来自哪里,因为它来自您引用的代码,来自外部程序集,您应该能够在调用堆栈中看到它。

标签: c# xna


【解决方案1】:

如果 ntdll.dll 发生崩溃,您需要它的符号,但我认为更有可能的是您传递了一些奇怪的垃圾,导致它崩溃。您是否在进行可能导致崩溃的 Windows API 调用?

另一个用户在这里提到的另一种可能性是,您可能正在某个地方进行递归调用,而堆栈已用完。如果对非托管代码进行调用,这将特别成问题:

  • 是否存在可能导致无限循环的逻辑条件?
  • 是否有任何构造函数会进行无意的递归调用?
  • 您的代码中是否有任何递归方法可能会卡住?

此外,在您继续寻找替代调试方法之前,您可能需要尝试几件事:

  1. 确保项目是在调试中构建的
  2. 检查 Visual Studio 设置以确保它在所有异常情况下都停止
  3. 如果您的项目设置中提供“仅我的代码”设置,请关闭它(这甚至会出现在 C# 项目中吗?)
  4. 开启混合模式调试/非托管调试
  5. 确保生成符号并将其存储在正确的位置 (*.pdb)
  6. 如果所有这些都失败了,您可以在系统事件查看器中四处寻找任何奇怪的错误

【讨论】:

  • 感谢您提供这些非常有用的提示。我会尽快尝试这些并报告。
  • 应用所有建议的设置后,我仍然无法获得导致代码问题的具体信息(我将很快解决)但我得到了一个不错的调用堆栈(见上面我的问题)。
  • 我可以确定问题是无意识的递归调用,导致在很少输入的代码分支中出现无限循环。我只能通过繁琐的“手动调试”来识别代码的错误部分(注释掉代码的不同部分,从而缩小问题范围)。有趣的是,即使在应用了所有建议的步骤(加载所有符号等)之后,Visual Studio 调试器也无法在我的源代码的错误部分中断并进入它。
【解决方案2】:

StackOverflowException 通常是由某个方法不断地调用自身引起的。

它在一段时间后发生的事实让我更加坚定:你面临着无限递归。

这种行为的一个非常简单的例子是:

void SomeMethod()
{
    SomeMethod(); // StackOverflowException
}

【讨论】:

  • 感谢您指出这一点。但是,无限递归会导致可重现且一致的错误。但是,我可以测试运行几个小时而没有问题的游戏。其他人在几分钟后崩溃(游戏中的每个人都在做同样的事情)。因此,我想这个问题更棘手。但我正在努力,我会尽快发布结果。
  • 情况并非如此;只有当您满足特定的逻辑条件时,您才能进入无限递归,这意味着您只会“有时”满足条件,从而解释不可预测的行为。话虽如此,听起来它可能还是别的东西......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-29
  • 2016-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多