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

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

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

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

注:不是在寻找一个debugging工具,旨在解决一个特定的问题,如gflags,pageheap,内存validation,净化等。我正在寻找释放和稳定的工具来跟踪或重放在指令级别。

如果遇到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; } 

当我启动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: } 

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

某些程序假定8字节对齐,并且使用/ unaligned参数停止正常工作。 微软Internet Explorer就是这样一个程序。

我发现了一个解决方案:使用VMware Workstation和Visual Studio 2010进行“重放调试”。设置它需要很长时间,但是您可以使用Visual Studio C ++调试器来及时调试。 以下是演示重放调试如何工作的视频: http : //blogs.vmware.com/workstation/2010/01/replay-debugging-try-it-today.html 。

该解决方案的一个缺点是,VMware似乎已经停止在最新的VMware版本中进行重播调试。 此外,只有某些处理器类型似乎支持重放。 我还没有找到任何支持处理器的全面列表。 我测试了我的三台电脑上的重播功能:重放功能在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 ,但使用“Debug”配置而不是“Release”)。

  • 将主机的“Debugging Tools for Windows”目录下的“gflags.exe”复制到虚拟机上,在虚拟机上运行gflags.exe,在“系统注册表选项”下选择“禁止内核堆栈分页”,按OK。 重启虚拟机。

  • 将待测应用程序的所有EXE和DLL文件复制到虚拟机,并确保可以启动应用程序并重现问题。

  • 关闭虚拟机并创建快照(通过VMware Workstation中的上下文菜单项“Take Snapshot”)。

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

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

    主机名称替换PC的名字。

  • (仅用于远程调试:)为VM创建手动录制。 即登录到虚拟机的操作系统,开始录制(通过上下文菜单“录制”),运行被测试的应用程序并执行必要的操作来重现问题。 然后停止并保存录音。

  • 启动Visual Studio并进入“VMware |选项|重播VM |常规中的调试”,并设置以下值:

    • “本地或远程”必须设置为“本地”进行本地调试,或者“远程”进行远程调试。
    • 必须将“虚拟机”设置为虚拟机的.vmx文件的路径。
    • “远程机器密码”必须设置为上面使用的密码(仅用于远程调试)。
    • “录制到重播”必须设置为以前使用VMware创建的录制名称。
    • “主机可执行文件搜索路径”必须设置为一个目录,您可以在其中保存受测试应用程序所需的DLL,并且Visual Studio需要这些DLL才能显示正确的堆栈跟踪。

    按“应用”。

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

    • “用于记录的基本快照”:先前创建的快照的名称。

    按“确定”。

  • (用于本地调试:)在Visual Studio中,选择“VMware |创建录制以播放”; 这将重新启动虚拟机。 登录到VM,运行测试中的应用程序并执行重现问题所需的操作。 然后停止并保存录音。

  • 选择“VMware |启动重播调试”。 VMware现在会自动重新启动虚拟机和待测应用程序,并重播记录的操作。 等到应用程序崩溃; Visual Studio调试器会自动变为活动状态。

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

  • 一旦VMware重放虚拟机到你的断点,你可以使用“Step Over”和“Step Into”从断点处前进,即重放记录的事件历史记录,直到找到原因你的应用程序崩溃了。

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

当程序运行的时候,我会附加WinDbg,并在崩溃或异常时调试中断时执行一个小型转储:

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

我会启用gflags为您的应用程序,无论是从WinDbg的命令行:

 !gflag +ust 

记得删除后,这个标志!

然后你可以运行一个自动的exepction分析:

 !analyze -v 

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

 ~* kb 

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

 ~xs 

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

 .ecxr 

如何从catch块中恢复调用堆栈有一个很好的链接:http://blogs.msdn.com/b/slavao/archive/2005/01/30/363428.aspx也是这样的: http:// blogs.msdn.com/b/jmstall/archive/2005/01/18/355697.aspx

这里最重要的事情是用windbg连接你应该能够检查所有线程和调用栈的状态,你也可以在visual studio中打开minidump: http : //msdn.microsoft.com/en-us/ library / windows / desktop / ee416349%28v = vs.85%29.aspx#Analysis_of_a_minidump如果您偏好visual studio进行导航,您可以在windbg中打开相同的转储,使用其工具进行分析,使用visual studio导航代码。 希望这可以帮助。

gdb是否提供开箱即用的功能?

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

另外,设置自己的日志应用程序很简单,它可以输出任意数量的数据,并可以通过命令行参数激活。

您现在可以设置它来处理您正在进行的崩溃,或者只是为了覆盖基础知识,然后在修复错误或添加新功能时对其进行扩展。 好处是你能够准确地捕获你认为有用的数据,甚至可以指定日志级别以避免被噪音淹没?

如何使用BMC的AppSight?

我们在以前的公司使用过它(对不起,我花了一段时间记住名字),它被用来研究崩溃等ISTR你跑它,然后运行你的软件,它记录了所有发生在日志文件您可以稍后查看。

它绝对在Windows上工作,因为这是我用它。

这可能是你在找什么?

不完全确定这是否是你想要的,但是'u'将会拆除当前线程上当前IP寄存器的最后一条指令。 这会告诉你最后一个运行的指令,通过反编译的代码,你可以正确地找出不同寄存器的值。 在大多数情况下,这是一个缓慢而艰难的过程,但是它几乎可以100%地准确地发现刚刚发生的事情(除了一些奇怪的硬件问题,或者真正奇怪的代码问题)。 过去我曾经使用这种方法来弄清为什么当我没有源代码的时候,某些东西被清除了。

如果你检查windbg的帮助文件,你会发现更多的信息。