如何记录或重播在崩溃之前立即执行的行或指令

时间:2012-06-06 10:11:45

标签: c++ windows debugging windbg

通常我必须在Windows上调试崩溃的C ++程序,我可以重现崩溃,但很难确定代码中的哪些指令序列导致崩溃(例如,另一个线程覆盖崩溃线程的内存)。在这种情况下,即使是调用堆栈也无济于事。通常我会通过注释掉源代码的部分来缩小崩溃原因,但这非常繁琐。

有没有人知道Windows的工具可以报告或重播在崩溃前立即在所有线程中执行的最后几个源代码行或机器代码指令?即类似于gdb的反向调试功能或类似Mutek的BugTrapper(不再可用)。我正在寻找一个发布且稳定的工具(我知道SoftwareVerify的'Bug Validator'和Hexray的IDA Pro 6.3 Trace Replayer,两者都在封闭测试程序中。)

我已经尝试过的是WinDbg跟踪命令wtta @$ra,但这两个命令的缺点是它们会在几秒钟后自动停止。我需要在崩溃发生之前运行的跟踪命令,并跟踪正在运行的程序的所有线程。

注意:正在寻找旨在解决特定问题的调试工具,例如gflags,pageheap,Memory Validator,Purify等。我在寻找在指令级别发布和稳定的工具来跟踪或重放。

6 个答案:

答案 0 :(得分:9)

如果您面对another thread overwriting memory of the crashing thread,使用gflagsGFlags and PageHeap )非常有用。它不是告诉你在崩溃之前执行的几行,而是告诉你算法已经覆盖正确分配的内存块的地方。

首先激活此类检查:

gflags /p /enable your_app.exe /full
gflags /p /enable your_app.exe /full /backwards

检查您是否已正确激活
gflags /p

运行应用程序并收集转储文件

然后禁用gflags检查:

gflags /p /disable your_app.exe

<小时/> 更新1

It does not immediately detect problems like *p = 0; where p is an invalid pointer
至少检测到一些问题。
例如:

#include <stdio.h>
int main(int argc, char *argv[])
{
  int *p = new int;
  printf("1) p=%p\n",p);
  *p = 1;
  delete p;
  printf("2) p=%p\n",p);
  *p = 2;
  printf("Done\n");
  return 0;    
}

当我启用gflags运行时,我得到一个转储文件并正确识别问题:

STACK_TEXT:  
0018ff44 00401215 00000001 03e5dfb8 03dfdf48 mem_alloc_3!main+0x5b [c:\src\tests\test.cpp\mem_alloc\mem_alloc\mem_alloc.3.cpp @ 11]
0018ff88 75f8339a 7efde000 0018ffd4 77bb9ef2 mem_alloc_3!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 586]
0018ff94 77bb9ef2 7efde000 2558d82c 00000000 kernel32!BaseThreadInitThunk+0xe
0018ffd4 77bb9ec5 004013bc 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70
0018ffec 00000000 004013bc 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b


STACK_COMMAND:  ~0s; .ecxr ; kb

FAULTING_SOURCE_CODE:  
     7:   printf("1) p=%p\n",p);
     8:   *p = 1;
     9:   delete p;
    10:   printf("2) p=%p\n",p);
>   11:   *p = 2;
    12:   printf("Done\n");
    13:   return 0;
    14: 
    15: }

<小时/> 更新2

@fmunkert的另一个例子:

#include <stdio.h>

int main()
{

        int *p = new int;  
        printf("1) p=%p\n",p);  
        *p = 1;  
        p++;
        printf("2) p=%p\n",p);
        *p =  2;   // <==== Illegal memory access
        printf("Done\n");  
        return 0;

}

gflags /p /enable mem_alloc.3.exe /full /unaligned

STACK_TEXT:  
0018ff44 00401205 00000001 0505ffbe 04ffdf44 mem_alloc_3!main+0x52 [c:\src\tests\test.cpp\mem_alloc\mem_alloc\mem_alloc.3.cpp @ 12]
0018ff88 75f8339a 7efde000 0018ffd4 77bb9ef2 mem_alloc_3!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 586]
0018ff94 77bb9ef2 7efde000 2577c47c 00000000 kernel32!BaseThreadInitThunk+0xe
0018ffd4 77bb9ec5 004013ac 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70
0018ffec 00000000 004013ac 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b


STACK_COMMAND:  ~0s; .ecxr ; kb

FAULTING_SOURCE_CODE:  
     8:         printf("1) p=%p\n",p);  
     9:         *p = 1;  
    10:         p++;
    11:         printf("2) p=%p\n",p);
>   12:         *p =  2;   // <==== Illegal memory access
    13:         printf("Done\n");  
    14:         return 0;
    15: 
    16: }

不幸 / unaligned 选项可能会导致程序无法正常运行(How to use Pageheap.exe):

  

有些程序会对8字节对齐进行假设,然后停止   使用/ unaligned参数正确工作。微软互联网   资源管理器就是这样一个程序。

答案 1 :(得分:5)

我找到了一个解决方案:&#34;重播调试&#34;使用VMware Workstation和Visual Studio 2010.设置它需要花费大量时间,但您可以获得可以及时调试的Visual Studio C ++调试器。这是一段视频,演示了重播调试的工作原理:http://blogs.vmware.com/workstation/2010/01/replay-debugging-try-it-today.html

该解决方案的一个缺点是VMware似乎已停止在最新的VMware版本中进行重播调试。此外,只有某些处理器类型似乎支持重放。我还没有找到任何受支持的处理器的完整列表;我在我的三台PC上测试了重放功能:重放不适用于Core i7 200;重播适用于Core2 6700和Core2 Q9650。

我真的希望VMware在未来的VMware Workstation版本中重新考虑并重新引入重播调试,因为这确实为调试增加了新的维度。

对于那些感兴趣的人,这里有一个描述如何为重放调试设置环境:

在下面的描述中,&#34;本地调试&#34;意味着Visual Studio和VMware安装在同一台PC上。 &#34;远程调试&#34;意味着Visual Studio和VMware安装在不同的PC上。

  • 在主机系统上安装带有SP1的Visual Studio 2010。

  • 确保已将Visual Studio配置为使用Microsoft的符号服务器。 (在&#34;工具|选项|调试|符号&#34;)下。

  • 在主机系统上,安装&#34; Windows调试工具&#34;。

  • 安装VMware Workstation 7.1。 (版本8.0不再包含重播调试功能)。这也将在Visual Studio中安装一个插件。

  • 使用Windows XP SP3在VMware上安装虚拟机(VM)。

  • 如果要测试的应用程序是调试版本,请在VM上安装Visual Studio调试DLL。 (有关如何执行此操作的说明,请参阅http://msdn.microsoft.com/en-us/library/dd293568.aspx,但使用&#34;调试&#34;配置而不是&#34;发布&#34;)。

  • 复制&#34; gflags.exe&#34;来自主持人&#34; Windows调试工具&#34;到VM的目录,在VM上运行gflags.exe,选择&#34;禁用内核栈的分页&#34;在&#34;系统注册表选项卡&#34;然后按OK。重新启动VM。

  • 将受测试应用程序的所有EXE和DLL文件复制到VM,并确保您可以启动该应用程序并重现该问题。

  • 关闭虚拟机并创建快照(通过上下文菜单项&#34;在VMware Workstation中拍摄快照&#34;

  • (仅用于远程调试:)在Visual Studio PC上启动以下命令并输入任意密码:

    C:\ Program Files \ VMware \ VMware Workstation \ Visual Studio集成调试器\ dclProxy.exe 主机名

    主机名替换为PC的名称。

  • (仅用于远程调试:)为VM手动创建录像。即登录VM的操作系统,开始录制(通过上下文菜单&#34;记录&#34;),运行测试中的应用程序并执行重现问题所需的操作。然后停止并保存录音。

  • 启动Visual Studio并转到&#34; VMware |选项|重播VM中的调试|常规&#34;,并设置以下值:

    • &#34;本地或远程&#34;必须设置为&#34; Local&#34;用于本地调试或远程&#34;远程&#34;用于远程调试。
    • &#34;虚拟机&#34;必须设置为VM的.vmx文件的路径。
    • &#34; Remote Machione Passcode&#34;必须设置为您上面使用的密码(仅用于远程调试)。
    • &#34;录制重播&#34;必须设置为先前使用VMware创建的录制名称。
    • &#34;主机可执行搜索路径&#34;必须设置为一个目录,在该目录中保存被测试的应用程序所需的DLL,Visual Studio需要这些DLL才能显示正确的堆栈跟踪。

    按&#34;应用&#34;。

  • 转到&#34; VMware |选项|重播VM中的调试|预录事件&#34;,并设置以下值:

    • &#34;用于录制的基本快照&#34;:先前创建的快照的名称。

    按&#34;确定&#34;。

  • (对于本地调试:)在Visual Studio中,选择&#34; VMware |为重播创建录音&#34 ;;这会重新启动VM。登录到VM,运行测试中的应用程序并执行重现问题所需的操作。然后停止并保存录音。

  • 选择&#34; VMware |开始重播调试&#34;。 VMware现在会自动重新启动VM和正在测试的应用程序,并重放记录的操作。等到应用程序崩溃;然后,Visual Studio调试器将自动变为活动状态。

  • 在Visual Studio调试器中,将断点设置为您认为应用程序在崩溃之前所处的位置。然后,选择&#34; VMware |反向继续&#34;。调试器现在向断点运行向后。此操作可能需要一些时间,因为VM将重新启动并重播,直到达到断点。 (您可以通过在记录方案时在崩溃发生前几秒添加快照来加快此操作。您可以在重放调试期间添加其他快照。)

  • 一旦VMware将VM重播到您的断点,您就可以使用&#34; Step Over&#34;和&#34;踏入&#34;从断点开始前进,即重播录制的事件历史记录,直到找到应用程序崩溃的原因为止。

更多信息:http://www.replaydebugging.com/

答案 2 :(得分:2)

我会在程序运行时附加WinDbg,并在崩溃或异常时调试破坏时执行minidump:

.dump /ma c:\mem.dmp // c:\mem.dmp could be any other location you desire

我会为你的应用启用gflags,或者从WinDbg中的命令行开始:

!gflag +ust

请记得在!!

后删除此标志

然后你可以进行自动化的exepction分析:

!analyze -v

这可能会告诉你它认为导致崩溃的原因,你可以转储所有线程的调用堆栈:

~* kb

如果看到任何可疑的东西,你可以切换线程并进一步检查:

~x s

您可以检查异常上下文记录:

.ecxr

如何从catch块恢复调用堆栈有一个很好的链接:http://blogs.msdn.com/b/slavao/archive/2005/01/30/363428.aspx以及:{ {3}}

这里的主要内容是附加了windbg你应该能够检查所有线程和调用堆栈的状态,你也可以在visual studio中打开minidump:http://blogs.msdn.com/b/jmstall/archive/2005/01/18/355697.aspx如果你更喜欢visual studio for导航时,您可以在windbg中打开相同的转储以使用其工具进行分析,并使用visual studio来导航代码。希望这会有所帮助。

答案 3 :(得分:2)

如何使用BMC的AppSight?

我们在以前的公司使用过它(抱歉,我花了一段时间才记住这个名字),它用于研究崩溃等等ISTR你运行它然后运行你的软件它记录了所有发生的事情您可以稍后查看的日志文件。

它肯定适用于Windows,因为我使用它。

你可能正在寻找什么?

答案 4 :(得分:1)

gdb不提供开箱即用的功能吗?

我使用它已经有一段时间但是我记得它可以运行一个程序,直到它崩溃然后在调试器中为你重放这些步骤。

此外,设置您自己的日志记录应用程序可以直接输出您可以输出的任何数据量,并且可以通过命令行参数激活到exe?

您现在可以设置它来解决您遇到的崩溃问题或只是为了解决基础问题,然后在修复错误或添加新功能时对其进行扩展。优点是您可以准确捕获您认为有用的数据,甚至可以指定记录级别以避免被噪声淹没?

答案 5 :(得分:-1)

不完全确定这是否是您想要的,但是'u'将从当前线程的当前IP寄存器中反汇编最后一条指令。这将向您显示最后运行的指令,您通常可以通过支持它反汇编的代码来确定不同寄存器的值。在大多数情况下,这是一个缓慢而艰难的过程,但它给你几乎100%的准确性(除了一些奇怪的硬件问题,或者真正奇怪的代码问题)刚刚发生的事情。我过去曾使用过这种方法来弄清楚为什么在我没有源代码的情况下某些东西被淘汰了。

如果您查看windbg帮助文件,您将找到有关它的更多信息。