如何在没有有用的调用堆栈的情况下调试难以重现的崩溃?

Dav*_*vid 15 delphi crash callstack c++builder

我在软件中遇到了一个奇怪的崩溃,我在调试它时遇到了很多麻烦,因此我正在寻求如何解决它的建议.

崩溃是读取NULL指针的访问冲突:

$ 00CF0041的第一次机会异常.异常类$ C0000005,消息'访问冲突位于0x00cf0041:读取地址0x00000000'.

它只发生'有时' - 我还没有设法找出任何押韵或理由,但是,当时 - 并且只在主线程中.当它发生时,调用堆栈包含一个不正确的条目:

用一行调用堆栈,Classes :: TList :: Get,地址0x00cf0041

对于主线程,它应该显示一个充满其他项目的大堆栈.

此时,所有其他线程都处于非活动状态(主要是坐在WaitForSingleObject或类似的功能.)我只看到这个崩溃发生在主线程中.它始终具有一个条目的相同调用堆栈,在同一地址的相同方法中.此方法可能相关也可能不相关 - 我们在应用程序中使用VCL.不过,我的赌注是,某些东西(可能是很久以前)正在破坏堆栈,而崩溃的地址实际上是随机的.请注意,它在几个版本中的地址相同 - 但它可能不是真正随机的.

这是我尝试过的:

  • 试图在某一点可靠地再现它.我没有发现任何东西每次都会重现它,以及偶尔做或不做的一些事情,没有明显的理由.这些并不是"狭隘"的足以将其缩小到特定代码段的行为.它可能与时间有关,但在IDE中断的时候,其他线程通常什么都不做.我不能排除线程问题,但认为这不太可能.
  • 使用额外的调试语句构建(额外的调试信息,额外的断言等).这样做之后,崩溃永远不会发生.
  • 在启用Codeguard的情况下构建.执行此操作后,崩溃永远不会发生,Codeguard没有显示错误.

我的问题:

1.如何找到导致崩溃的代码?我怎么做相当于走回堆栈?

2.对于如何追踪此次崩溃的原因,您有什么一般性的建议?

我正在使用Embarcadero RAD Studio 2010(该项目主要包含C++ Builder代码和少量Delphi.)

编辑:我想我应该添加实际导致这个的东西.有一个线程调用ReadDirectoryChangesW然后,使用GetOverlappedResult,等待事件继续并对更改执行某些操作.事件也发出信号,以便在设置状态标志后终止线程.问题是当线程退出时它从未调用过CancelIO.因此,Windows仍在跟踪更改,并且可能仍然在目录更改时写入缓冲区,即使缓冲区,重叠的结构和事件不再存在(也没有创建它们的线程上下文.)CancelIO调用时,没有更多的崩溃.

Bar*_*lly 15

即使IDE提供的堆栈跟踪不是很完整,也不意味着堆栈上仍然没有有用的信息.打开CPU视图并查看堆栈窗格; 对于每个CALL操作码,返回地址都被压入堆栈.由于堆栈向下增长,您将在当前堆栈位置上方找到这些返回地址,即通过在堆栈窗格中向上滚动.

主线程的堆栈大约为$ 00120000或$ 00180000(Vista中的地址空间随机化以及更高的随机性使其更随机).主可执行文件的代码大约为$ 00400000.您可以通过右键单击堆栈条目并选择Follow - > Near Code来推测性地调查堆栈上看起来不像整数数据(低值)或堆栈地址($ 00120000 +范围)的元素,这将导致反汇编窗口跳转到该代码地址.如果它看起来像无效代码,它可能不是堆栈跟踪中的有效条目.如果它是有效的代码,它可能是操作系统代码(通常在7700万美元以上),在这种情况下你将没有有意义的符号,但每隔一段时间你就会遇到一个实际的正确堆栈条目.

这种技术虽然有些费力,但在调试器无法跟踪事物时可以为您提供有意义的堆栈跟踪信息.但是,如果ESP(堆栈指针)被拧紧,它对你没有帮助.幸运的是,这种情况非常罕见.