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

9 c++ windows debugging windbg

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

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

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

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

Ser*_*kov 9

如果您面对another thread overwriting memory of the crashing thread它,使用gflags(GFlags和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;    
}
Run Code Online (Sandbox Code Playgroud)

当我运行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: }
Run Code Online (Sandbox Code Playgroud)


更新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;

}
Run Code Online (Sandbox Code Playgroud)

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: }
Run Code Online (Sandbox Code Playgroud)

不幸的是,/ unaligned选项可能导致程序无法正常工作(如何使用Pageheap.exe):

有些程序会对8字节对齐进行假设,并且它们会使用/ unaligned参数停止正常工作.Microsoft Internet Explorer就是这样一个程序.


小智 5

我找到了一个解决方案:使用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版本中重新考虑并重新引入重放调试,因为这确实为调试增加了新的维度.

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

在下面的描述中,"本地调试"表示Visual Studio和VMware安装在同一台PC上."远程调试"意味着Visual Studio和VMware安装在不同的PC上.

  • 在主机系统上安装Visual Studio 2010 SP1.

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

  • 在主机系统上,安装"Windows调试工具".

  • 安装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,但使用"调试"配置而不是"发布").

  • 将"gflags.exe"从主机的"Windows调试工具"目录复制到VM,在VM上运行gflags.exe,在"系统注册表"选项卡下选择"禁用内核堆栈分页",然后按"确定".重新启动VM.

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

  • 关闭VM并创建快照(通过VMware Workstation中的上下文菜单项"Take Snapshot").

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

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

    主机名替换为PC的名称.

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

  • 启动Visual Studio并转到"VMware |选项|在VM中重播调试|常规",并设置以下值:

    • 必须将"本地或远程"设置为"本地"以进行本地调试,或将"远程"设置为远程调试.
    • 必须将"虚拟机"设置为VM的.vmx文件的路径.
    • 必须将"Remote Machione Passcode"设置为您上面使用的密码(仅用于远程调试).
    • "录制到重播"必须设置为您之前使用VMware创建的录制名称.
    • 必须将"Host Executable Search Path"设置为一个目录,在该目录中保存被测试应用程序所需的DLL,Visual Studio需要这些DLL才能显示正确的堆栈跟踪.

    按"应用".

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

    • "用于录制的基本快照":先前创建的快照的名称.

    按"确定".

  • (对于本地调试:)在Visual Studio中,选择"VMware | Create Recording for Replay"; 这会重新启动VM.登录到VM,运行测试中的应用程序并执行重现问题所需的操作.然后停止并保存录音.

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

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

  • 一旦VMware将VM重播到您的断点,您就可以使用"Step Over"和"Step Into"从断点开始前进,即重放记录的事件历史记录,直到您找到可以确定原因的位置为止你的申请崩溃了.

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