有没有编译选项可以使x64版本崩溃转储更易于使用?

5
每当我获取我的应用程序的x64发布版本的崩溃转储时,我发现很少能够查看本地变量,这使得修复某些问题变得困难或不可能。在x86中,我通常可以查看所有本地变量而没有任何问题。
在发布版本中有任何编译器选项可以让我在崩溃转储中查看本地变量吗?显然,我不想关闭优化,但也许有一些方法可以强制它保存本地变量并带来较小的性能影响吗?

你有考虑过切换到其他编译器,比如GCC或者Clang/LLVM吗?你可以使用带有调试信息和优化选项的编译器进行编译(比如使用g++或者clang并加上-O2 -g参数)。 - Basile Starynkevitch
本地变量往往会被优化掉,所以一般来说,你不能“既要馅饼又要吃饼”。 - Paul R
1
不要期望发布/优化版本能够方便调试。x86_64拥有更多的寄存器,因此编译器不需要将太多变量溢出到内存中,这可能是你没有看到值的原因之一。 - phuclv
始终生成调试版本并将该调试版本共享给遇到问题的客户,并希望客户能够提供更好的转储信息。 - magicandre1981
如果您正在使用VC++,请使用/Zo选项。它可以更好地跟踪本地变量。虽然不是完美的(优化过程会丢失一些信息),但比某些评论所声称的要好。详细信息请参阅: https://randomascii.wordpress.com/2013/09/11/debugging-optimized-codenew-in-visual-studio-2012/ - Bruce Dawson
显示剩余2条评论
1个回答

10

您提到了几个原因,解释了为什么您无法查看局部变量:

#1 - 这是一个发布版本。

当启用某些优化时,编译器可以执行一些使查看局部变量更加困难的操作:

  1. 它可以内联函数。当这种情况发生时,没有被优化掉的函数的局部变量会与调用堆栈帧混合在一起。
  2. 它可以通过使用称为帧指针省略的技巧来释放寄存器,并在函数调用中节省几个时钟周期。
  3. 为了节省堆栈空间,编译器可以重复使用函数体中先前保存变量的位置,以保存稍后在函数中使用的另一个变量。这意味着您在函数中的位置决定了实际上能够看到哪些局部变量。

#2 - 这是一个x64版本。

MSVC对于64位代码使用新的调用约定,称为x64调用约定。前4个函数参数存储在寄存器中,而不是堆栈上。这意味着即使您正在查看一个堆栈帧,您也不会看到其中一些参数,如果寄存器已被重新用于其他变量,则可能无法恢复它们。


那么现在怎么办?

既然我们知道了为什么您会遇到这样的困难,让我们看看您可以采取哪些措施来解决上述问题。以上所有问题都不是致命问题,但它们共同使得对局部变量的查看变得更加困难。

  1. 关闭一些优化。 您可以尝试使用启用了某些不会对调试造成太大影响的优化级别的发布版本构建。您可能希望从上面提到的与堆栈帧 (/Oy 和 /Ob) 相关的优化开始尝试。然后,您需要希望在关闭这些优化的情况下仍然能够重现该问题。此外,根据您内部政策和与客户的合同,您可能需要在向客户发送非官方版本之前请律师参与--这可能不是世界上最有趣的事情。

  2. 生成更好的符号文件。 VS2012 及更高版本具有一个新的编译器开关,/d2Zi+ in VS2012/Zo in VS2013, 当启用优化时会生成更好的调试信息。这使得调试优化代码与 GCC/Clang 相当。即使在 VS2012 中未记录该选项,但我仍然认为它相当安全,因为我没有看到生成的代码有任何差异——只有符号文件不同。您甚至可以使用 .symopt+ 0x40 强制 windbg 使用您的新符号文件,这使得您有更多机会从已有的转储文件中获取更多信息。

  3. 使用Windbg扩展来进行繁重的工作。其他StackOverflow答案中,我提到了一个称为CMKD的工具,它救过我几次。它可以尝试重构以寄存器传递的x64调用约定中传递的参数等内容。虽然不是百分之百可靠,但这可能是找回参数的最佳希望。

无论如何,我希望我的胡言乱语能对您的调试有所帮助。


CMKD似乎正在发挥作用 :) 我也会尝试使用/Zo选项 - paulm
1
你应该添加/Zo以获得更好的本地变量跟踪。它在使用windbg进行调试时特别有效(它显示内联),但它也适用于VC++。有一些注意事项-我的博客文章涵盖了它们: https://randomascii.wordpress.com/2013/09/11/debugging-optimized-codenew-in-visual-studio-2012/ - Bruce Dawson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接