为什么Minidumps不能提供良好的调用堆栈?

Tod*_*Tod 17 debugging minidump callstack dbghelp

多年来我在很多游戏项目中都使用过minidumps,他们似乎有大约50%的机会拥有一个有效的调用堆栈.我该怎么做才能让他们有更好的调用堆栈?

我已经尝试将最新的dbghelp.dll放在exe目录中.这似乎有些帮助.

Visual Studio 2008或2010是否更好?(我还在VS 2005).

我使用的代码看起来像这个样本.

Chr*_*ine 25

为提高转储中调用堆栈的准确性,您可以做的一件事是使用Visual Studio以外的调试器 - 特别是使用WinDbg或使用dbgeng.dll中的"Windows Debugger"调试引擎的其他工具(相反)到Visual Studio使用的"Visual Studio Debugger"调试引擎.

根据我们的经验,WinDbg在从Visual Studio生成无法使用或非常不准确的调用堆栈的相同转储中生成良好的调用堆栈方面100%可靠.据我所知,在未处理异常是崩溃源的情况下,WinDbg会自动执行重构/恢复异常调用堆栈棘手过程,但Visual Studio却没有(或不能?).两个调试器使用不同的启发式来解释堆栈

WinDbg起初可能令人生畏,所以这里是我的快速指南,介绍如何让它更容易,甚至避免直接使用它.

一个纯粹致命的指南,以提取良好的callstacks

这些是从"最快/最简单"到"最慢/最神秘的解释"的排序.

  1. 最简单的选择:使用Microsoft的DbgDiag

    这是一个鲜为人知的工具,可以自动对常见问题进行大量分析,而且非常简单,可以给非程序员甚至客户.它快速且几乎万无一失,并且已成为我快速分析传入故障转储的"转到"工具.

    • 启动"DebugDiag Analysis"应用程序
    • 选择主页面上的"CrashHangAnalysis"复选框
    • 将转储拖放到主页面上的"数据文件"窗格中
    • 点击"开始分析"


    几秒钟到几分钟后,它会吐出一个漂亮的.mhtml文件,其中包含对问题的分析,有关所有相关线程的信息,完整的调用堆栈等.所有超链接和易于使用.

    DebugDiag甚至可以自动执行一些更复杂的分析,这些分析可能在WinDbg中很痛苦(比如跟踪应用程序中350个线程中的哪一个导致死锁).

    注意:出于安全原因,Chrome不会下载或打开.mhtml文件,因此您必须在Internet Explorer或Microsoft Edge中打开才能使用它.这很烦人,我已经向DebugDiag团队(dbgdiag@microsoft.com)提交了一个请求,要求将格式更改为纯HTML

  2. 中间选项:安装WinDbg作为Visual Studio的备用调试引擎

    • 如果尚未安装Visual Studio,请安装它.这需要在下一步之前完成.
    • 安装Windows驱动程序工具包(WDK)
    • 启动Visual Studio,(这部分很重要!)使用新的"File - > Open - > Crash Dump ..."选项打开转储.这将使用Windows调试器调试故障转储(如果您在Visual Studio上拖放转储或使用标准的"文件 - >打开 - >文件..."选项打开转储,它将调试它使用旧的Visual Studio调试引擎...所以小心使用正确的选项).
    • 您现在应该能够看到正确的调用堆栈并使用Visual Studio GUI进行导航,尽管有些工作方式不同(监视窗口需要使用不熟悉的WinDbg语法,线程ID不同等).注意:Visual Studio UI可能非常缓慢,特别是如果涉及许多线程并且'线程'或'并行堆栈'窗口是打开的.
  3. 硬核选项:直接使用WinDbg

    • 启动WinDbg.exe
    • 将转储拖放到WinDbg窗口中
    • 键入!analyze -v,然后按Enter.经过一段时间后,WinDbg会吐出一个崩溃调用堆栈,并且还会估计出问题的根源.如果你正在分析一个死锁,你可以试试!analyze -v -hang,WinDbg会经常向你展示所涉及的依赖链.


    此时您可能拥有所需的所有信息!但是,如果您希望在Visual Studio调试器中检查进程状态,则可以执行以下附加步骤:

    • 在Visual Studio中打开故障转储
    • 在callstack窗口中单击鼠标右键,然后选择"Go to Disassembly"
    • 将WinDbg输出callstack顶行的十六进制地址粘贴到Disassembly窗口的"Address"栏中,然后按Enter键.你现在正处于崩溃的位置,看着反汇编的代码.
    • 在反汇编窗口中单击鼠标右键,然后选择"转到源代码"以转到该位置的源代码.现在,您正在查看崩溃站点的源代码.

注意:以上所有内容都要求配置正确的符号服务器路径,否则您将无法解析调用堆栈中的符号.我建议设置_NT_SYMBOL_PATH环境变量,以便它可以自动用于Visual Studio,WinDbg和DebugDiag.


Cra*_*rks 6

你的callstack缺少什么?你有一堆地址没有解析为有效的函数名称(即0x8732ae00而不是CFoo:Bar())?如果是这样,那么您需要将.PDB放在调试器可以找到它们的位置,或者设置符号服务器并在"模块"窗格的右键单击上下文菜单中设置"符号路径".

每当有人检查新的Perforce更改列表时,我们会存储每个二进制文件中的每个.PDB,这样当转储从办公室内的任何人或零售的任何客户回来时,我们都会得到与他们所在游戏版本相对应的.PDB.运行.设置符号服务器和路径后,我所要做的就是双击.mdmp,每次都可以使用.

或者你有一个似乎只有一个功能的调用堆栈?比如,0x8538cf00在堆栈中没有任何其他东西?如果是这样,那么你的崩溃实际上是堆栈本身被破坏了.如果后交链中的返回地址已被覆盖,则调试器自然无法解析它们.

有时您也会发现实际发出minidump的线程不是引发导致崩溃的异常的线程.在Threads窗口中查看其中一个其他线程是否包含违规代码.

如果您正在调试"发布"版本 - 也就是说,编译了所有优化标志的编译器 - 您将不得不忍受调试器在查找局部变量和其他一些数据时遇到的问题.这是因为启用优化意味着允许编译器将数据保存在寄存器上,折叠计算,并且通常会执行各种阻止数据实际写入堆栈的事情.如果这是您的问题,那么您需要打开反汇编窗口并手动追踪数据,或重建调试二进制文件并重现问题,您可以在其中查看它.