【问题标题】:Tools for tracing a C# crash when debugger exits with no call stack?调试器退出时没有调用堆栈时跟踪 C# 崩溃的工具?
【发布时间】:2018-01-24 17:15:20
【问题描述】:

我有一个大型、复杂的 C# GUI 应用程序,它以完全可重现的方式崩溃,但我无法轻松诊断崩溃的原因,因为不是以通常的方式使用调用堆栈破坏调试器,而是完全调试会话退出。

唯一的提示是输出窗口末尾有一条消息:STATUS_STACK_BUFFER_OVERRUN。

我煞费苦心地尝试在崩溃发生之前将断点放置在随机位置,试图逐渐让我的断点更接近问题发生的地方,但这种方法并没有让我快速到达任何地方。

我想知道是否有任何现有的工具可以像检测分析器一样工作,基本上观察和记录所有函数的进入和退出,这样当程序因堆栈损坏而崩溃时,仍然可以检查这个外部数据来确定上次执行的位置?

我不认为这是大多数面向性能的分析器都有的功能,因为他们更关心一个函数被调用了多少次以及调用了多长时间,但也许有一个工具可以准确地告诉我什么最后一个已知的运行代码是?

如果有办法在 Visual Studio 中解决/诊断此问题或使用其他技术,我愿意接受其他建议。

【问题讨论】:

  • 作为事后分析,我没有找到任何符合要求的工具,所以我用艰难的方式解决了这个问题——大量的猜测、二次猜测、打印语句和断点来找出在哪里它失败了。在我的情况下,原因是堆栈分配的缓冲区(本地声明的 4x4 矩阵数组)不够大,并且代码写入超出了缓冲区的末尾并破坏了堆栈的其余部分。

标签: c# visual-studio crash profiler


【解决方案1】:

您可以使用conditional breakpoint 来触发when the stack depth exceeds a certain value

(new StackTrace()).GetFrames().Length > 60

这里的诀窍是知道在哪里放置断点,因为它必须设置在递归循环中的某个地方。但是,如果您知道是什么触发了错误,您可能有足够的直觉选择一些战略性的地方进行检查。也可以使用排除的过程:如果断点没有被触发,就知道循环没有涉及到代码。

还请注意,条件断点的成本很高,并且会显着减慢正在调试的应用程序。如果您不反对在代码中乱扔调试语句,您可以call Debugger.Break() 而不是设置断点:

if (Debugger.IsAttached && (new StackTrace()).GetFrames().Length > 60)
    Debugger.Break();

【讨论】:

  • 虽然 STACK_BUFFER_OVERRUN 错误听起来像是由失控递归引起的,但我不确定是否会发生类似的事情。 (通常,当您通过递归太多次来破坏堆栈时,您仍然会在 Visual Studio 中看到调用堆栈)。看起来更像是一些随机的代码刚刚出现并破坏了堆栈,而不一定是深度调用堆栈。
  • @uglycoyote 除非您在同一进程中运行一些非托管代码,否则您不应该遭受堆栈/堆损坏的困扰。并不是说它不会发生,但托管环境非常强大,可以抵御这种情况。
  • 啊,是的,谢谢你指出来。事实上,问题发生在非托管代码上。我应该将其指定为问题描述的一部分。它是一个 C# GUI,在底层使用一些 C++/CLI 和非托管 C++ 来执行 3D 动画。
【解决方案2】:

STATUS_STACK_BUFFER_OVERRUN 意味着有人在非托管或互操作代码中破坏了堆栈变量的结尾或开头。

如果您在纯模式或混合模式下附加调试器,请转到“异常”窗口并添加代码为 0xc0000409(堆栈溢出)的 Win32 异常。如果触发错误,它应该会在调试器中中断。

【讨论】:

    【解决方案3】:

    您可以使用我的Runtime Flow 工具记录应用程序中的所有函数调用。崩溃后,您可以看到最后输入的函数是什么。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-28
      • 2011-06-16
      • 1970-01-01
      • 2016-02-19
      • 2012-06-18
      相关资源
      最近更新 更多