【问题标题】:Unable to get full user-mode stacktrace while kernel debugging in windbg在windbg中进行内核调试时无法获得完整的用户模式堆栈跟踪
【发布时间】:2016-08-03 15:59:22
【问题描述】:

我在 Windows 10 主机上有一台虚拟 Windows 7 x64 机器,我使用 windbg 10.0.10586.567 对其进行内核调试。我在上面运行我自己的应用程序,我有完整的源代码和私有符号。每当我闯入并询问应用程序线程的堆栈跟踪时,当我的应用程序的一个二进制文件被“命中”时,回溯总是停止。

例如,如果我闯入,切换到进程,并使用!thread [thread address] 1f 请求堆栈跟踪,我会得到类似这样的信息(注意最后一行的“早期”零返回地址):

fffff880`0534e870 fffff800`026d6992 nt!KiSwapContext+0x7a
fffff880`0534e9b0 fffff800`026d81a2 nt!KiCommitThreadWait+0x1d2
fffff880`0534ea40 fffff800`029c7a2e nt!KeDelayExecutionThread+0x186
fffff880`0534eab0 fffff800`026d08d3 nt!NtDelayExecution+0x59
fffff880`0534eae0 00000000`76e7165a nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`0534eae0)
00000000`00276708 000007fe`fcf91203 ntdll!NtDelayExecution+0xa
00000000`00276710 00000001`410e7dd9 KERNELBASE!SleepEx+0xab
00000000`002767b0 00000000`00000000 MyApp!MainMessageLoop+0x4b1 [d:\whatever\path\myapplication.cpp @ 3024]

这看起来与您在调试 x64 进程的用户模式转储(缺少展开数据)时丢失二进制文件非常相似,但在这种情况下,堆栈跟踪通常不会“突然”停止,而是它在那一点上误入歧途,并显示出虚假的价值。

我尝试过的一些额外信息/事情:

  • 我设置了正确的符号路径(Microsoft 符号服务器和主机上具有匹配 PDB 的本地文件夹,即使堆栈跟踪不需要后者)
  • 我在主机上设置了一个二进制路径 (.exepath),其中包含匹配的二进制文件(我对此非常确定;将二进制文件直接从客户机复制到主机)
  • 如果我在应用程序导出的 DLL 函数之一中设置断点,那么当调试器中断时,我会得到一个单行堆栈跟踪,如下所示:0000000000274b40 0000000000000000 MyAppDLL!SomeExportedFunction+0x32 [d:\whatever\path\myapplicationDLL.cpp @ 232]
  • 我几乎尝试了所有命令组合来获取堆栈跟踪(.process /i.process /r /p!process -1 7.reloads、.reload /users、.reload /f MyApp.exe!thread [address] 1f 等)没有成功
  • 也尝试了旧版本的 windbg (6.11.0001.404),结果相同
  • 还尝试在 Windows 8.1 上作为访客使用完全相同的二进制文件,结果相同
  • !sym noisy 输出(不相关行省略):

    0: kd>.process /i [address] 0: kd>g 0: kd>.reload /user 0: kd> !process -1 2 0: kd> !thread [address] 1f [...] DBGHELP: d:\symbolcache\MyApp.pdb\76931C5A6C284779AD2F916CA324617E1\MyApp.pdb already cached DBGHELP: MyApp - private symbols & lines [...]

  • lmvm MyApp 输出:

    [...] Loaded symbol image file: MyApp.exe Image path: C:\MyApp\MyApp.exe [...]

有什么想法吗?

【问题讨论】:

  • 也许.thread /p /r <addr>; kb ?
  • @MarcSherman 结果相同:(
  • 您是否在 GFlags (!gflag +ksl) 中启用了内核符号加载?这个名字有点误导。您需要让用户符号正常工作。
  • @conio 我在 Win 7 和 Win 8.1 上都试过了(在目标上也安装了 Windows SDK,可能有点矫枉过正),问题仍然存在。 !gflag 报告 ptg - Enable pool taggingksl - Enable loading of kernel debugger symbols 我错过了什么吗?
  • 你似乎做对了一切。您能否将.fnent 的输出添加到作为堆栈的一部分显示的函数上(在您提供的示例中为MyApp!MainMessageLoop)?此外,您可能想从CMKD extensions 尝试!stack

标签: windows debugging windbg stack-trace symbols


【解决方案1】:

我无意中发现了一个可以解决这个问题的链接器开关:/DEBUGTYPEPDATA 参数。如果您使用此开关链接您的二进制文件,展开信息将被复制到您的 PDB 中。

我用/DEBUGTYPE:CV,PDATA 重新编译/重新链接了有问题的应用程序(如果指定了/DEBUG/DEBUGTYPE:CV 是默认值,请参阅documentation),现在一切都像一个魅力,我总是得到完整的调用堆栈。

其中一个奇怪的方面是:windbg 愉快地使用在 PDB 中找到的展开数据,但忽略了映射二进制文件中的完全相同的数据(都在主机上)。

【讨论】:

  • 好东西!关于您对二进制文件的评论,您是否设置了.exepath
  • @conio 是的,我做到了。
【解决方案2】:

这不是问题的完美解决方案(或任何解决方案,有人可能会说),但我正在提供这个临时答案和解决方法。

您应该能够获得您想要的信息,尽管使用 dps @rsp L10 之类的格式不是很好。

在 x86-64 中,您没有 x86 ebp 链的并行,但返回地址仍在堆栈上。这些将为您提供堆栈中的函数,它们之间的值将是传递给函数的参数(以及堆栈上保存的寄存器等)。 A random example from Google(因为我现在不在我的 Windows 机器上):

0:017> dps @rsp
00000000`1bb0fbb8  00000000`00000020
00000000`1bb0fbc0  00000000`00000000
00000000`1bb0fbc8  00000000`008bc6c6 Dolphin!ReadDataFromFifoOnCPU+0xb6 [d:\sources\comex\source\core\videocommon\fifo.cpp @ 245]
00000000`1bb0fbd0  00000000`1ba0ffeb
00000000`1bb0fbd8  00000000`00000020
00000000`1bb0fbe0  00000000`00000020
00000000`1bb0fbe8  00000000`00000800
00000000`1bb0fbf0  00000000`1ba0ffeb
00000000`1bb0fbf8  00000000`008c2ff5 Dolphin!InterpretDisplayListPreprocess+0x45 [d:\sources\comex\source\core\videocommon\opcodedecoding.cpp @ 87]
00000000`1bb0fc00  00000000`00000000
00000000`1bb0fc08  00000000`008bc041 Dolphin!RunGpu+0x81 [d:\sources\comex\source\core\videocommon\fifo.cpp @ 389]
00000000`1bb0fc10  00000000`8064cbc0
00000000`1bb0fc18  00000000`1bb0fcc0
00000000`1bb0fc20  00000000`00000000
00000000`1bb0fc28  00000000`008c2dda Dolphin!OpcodeDecoder_Preprocess+0x14a [d:\sources\comex\source\core\videocommon\opcodedecoding.cpp @ 326]
00000000`1bb0fc30  00000000`8064cbe0

鉴于您有符号,返回地址很容易区分。

【讨论】:

    【解决方案3】:

    对于用户模式模块,展开数据是延迟加载的,因此除非有人需要,否则它不会被映射。不幸的是,内核调试器不会强制为用户映像提供信息,因此有时您会遇到这种行为。您可以通过转储 PE 标头 (!dh) 并检查异常目录的状态 (!pte imagename+offset) 来查看数据是否已映射。

    鉴于您拥有该应用,请尝试通过在您的应用中某处执行堆栈遍历 NOP 来强制信息常驻:

    PVOID stack[2];
    (VOID)CaptureStackBackTrace(0, 2, (PVOID*)&stack, NULL);
    

    这并不能保证整个目录都会存在,但通常已经足够了。

    【讨论】:

    • 映射和不分页一样吗?整个情况可以这样描述:展开数据(部分或全部)被分页,所以没有调用堆栈;即使可以从本地二进制文件(在主机上)获取展开数据,windbg 拒绝使用它。这就是我尝试过的:找到异常目录,并开始在分页页面中进行分页。一个一个地做真的很麻烦,而且我想检查的线程在这个过程中改变了状态。我只想看看我的进程如何与操作系统交互。还有其他方法吗?
    • 这很有趣。你能提供一个参考吗?即使没有从程序内部添加调用,.pagein 也不会做到这一点吗?
    • 联系您,我相信您可能对答案感兴趣。
    猜你喜欢
    • 2011-11-11
    • 1970-01-01
    • 2014-08-13
    • 2020-12-03
    • 2022-07-07
    • 1970-01-01
    • 2010-10-09
    • 2014-09-12
    • 1970-01-01
    相关资源
    最近更新 更多