调试Win32应用程序挂起

时间:2012-11-02 10:04:41

标签: c++ debugging exception-handling windbg visual-c++-2012

我无法在Win32应用程序中找到挂起的原因。该软件以紧凑的循环将一些数据呈现给OpenGL视觉效果:

std::vector<uint8_t> indices;
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_DOUBLE, 0, vertexDataBuffer);
while (...) {
    // get index type (1, 2, 4) and index count
    indices.resize(indexType * count);

    // get indices into "indices" buffer
    getIndices(indices.data(), indices.size()); //< seems to hang here!

    // draw (I'm using the correct parameters)
    glDrawElements(GL_TRIANGLES_*, count, GL_UNSIGNED_*);
}
glDisableClientState(GL_VERTEX_ARRAY);

使用VC11 Update 1(CTP 3)编译代码。在运行优化的二进制文件时,它会在几个循环之后挂起到getIndices()的调用内(下面有更多关于此内容)。我已经......

  • 三重验证了所有缓冲区,甚至是附加的CRC,以确保我没有任何缓冲区溢出
  • 在循环内添加了对HeapValidate()的调用以确保堆未损坏
  • 使用了ApplicationVerifier
  • 使用GFlags and PageHeap启用堆分配监控。
  • 在应用程序锁定时进入WinDbg

发现访问已分配缓冲区的代码有任何问题,也没有任何堆损坏。但是,如果我禁用low-fragmentation heap,问题就会消失。如果我为indices缓冲区使用单独的(低碎片)堆,它也会消失。

无论如何,这是导致死锁的堆栈跟踪:

0:000> kb
ChildEBP RetAddr  Args to Child              
0034e328 77b039c3 00000000 0034e350 00000000 ntdll!ZwWaitForKeyedEvent+0x15
0034e394 77b062bc 77b94724 080d36a8 0034e464 ntdll!RtlAcquireSRWLockExclusive+0x12e
0034e3c0 77aeb652 0034e464 0034e4b4 00000000 ntdll!RtlpCallVectoredHandlers+0x58
0034e3d4 77aeb314 0034e464 0034e4b4 77b94724 ntdll!RtlCallVectoredExceptionHandlers+0x12
0034e44c 77aa0133 0034e464 0034e4b4 0034e464 ntdll!RtlDispatchException+0x19
0034e44c 77b062c5 0034e464 0034e4b4 0034e464 ntdll!KiUserExceptionDispatcher+0xf
0034e7bc 77aeb652 0034e860 0034e8b0 00000000 ntdll!RtlpCallVectoredHandlers+0x61
0034e7d0 77aeb314 0034e860 0034e8b0 0034ec28 ntdll!RtlCallVectoredExceptionHandlers+0x12
0034e848 77aa0133 0034e860 0034e8b0 0034e860 ntdll!RtlDispatchException+0x19
0034e848 1c43c666 0034e860 0034e8b0 0034e860 ntdll!KiUserExceptionDispatcher+0xf
0034ebe8 1c43c4e5 0034ec28 080d35d0 080d35d6 lcdb4!lc::db::PackedIndices::unpackIndices<unsigned char>+0x86
0034ec14 1c45922d 0034ec28 080d35d0 00000006 lcdb4!lc::db::PackedIndices::unpack+0xb5
...
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx getIndices

为了完整起见,我将lc::db::PackedIndices::unpackIndices()的代码(包括为调试添加的所有代码)发布到http://ideone.com/sVVXX7

触发对KiUserExceptionDispatcher的调用的代码为(*p++) = static_cast<T>(index);mov dword ptr [esp+10h],eax)。

我似乎无法弄清楚发生了什么。似乎抛出了一个异常,但我的异常处理程序都没有被调用。该应用程序只是挂起。我检查了任何死锁的关键部分(!lock),但没有找到。此外,我不明白为什么应该引发异常,因为内存位置都是有效的。有人能给我一些提示吗?

更新

我试图找到抛出的异常类型:

0:000> s -d esp L1000 1003f
0028ebdc  0001003f 00000000 00000000 00000000  ?...............
0028efd8  0001003f 00000000 00000000 00000000  ?...............
0:000> .cxr 0028ebdc
eax=77b94724 ebx=0804be30 ecx=00000002 edx=00000004 esi=77b94724 edi=0804be28
eip=77b062c5 esp=0028eec4 ebp=0028eee4 iopl=0         nv up ei ng nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010287
ntdll!RtlpCallVectoredHandlers+0x61:
77b062c5 ff03            inc     dword ptr [ebx]      ds:002b:0804be30=00000001
0:000> .cxr 0028efd8
eax=0000003b ebx=00000001 ecx=0804bd98 edx=0028f340 esi=0028f340 edi=04b77580
eip=1c43c296 esp=0028f2c0 ebp=0028f2fc iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
lcdb4!lc::db::PackedIndices::unpackIndices<unsigned char>+0x36:
1c43c296 8801            mov     byte ptr [ecx],al          ds:002b:0804bd98=3e

3 个答案:

答案 0 :(得分:2)

堆栈跟踪讲述了这个故事。您的程序崩溃,很有可能这是一个访问冲突异常,C ++代码的典型故障模式,通常由堆损坏触发。 Windows然后尝试调用异常过滤器以查找愿意处理异常的任何代码。首先是AddVectoredExceptionHandler()安装的处理程序。必须锁定才能防止其中一个处理程序导致崩溃时重新进入。

这就是降压停止的地方。究竟为什么从堆栈跟踪中不清楚。这可能是因为另一个线程也因堆损坏而崩溃,并且正在忙于处理异常并已采取锁定。使用Debug + Windows + Threads来查看它们。但更可能的是,进程状态被破坏,锁定对象本身也被破坏了。不太可能,但确实发生了。

是的,关闭低碎片堆有一个隐藏堆损坏的诀窍。内存布局将是非常不同的,所以无论什么代码导致腐败现在可能已经打乱了一些无辜的东西。这当然不是解决方案。

Debug + Exception,勾选“Win32 Exceptions”的Thrown复选框。调试器现在将在抛出异常时停止。至少你会知道抛出什么异常。最终,您需要找出堆损坏发生的位置。从来没有找到崩溃的代码,祝你好运调试。

答案 1 :(得分:2)

线程挂起等待属于OS异常处理代码的SRW(超薄读写锁定)的独占锁定。而这个异常是由您的代码引起的。可以使用以下堆栈帧找到它的确切异常和细节。 0034e848 77aa0133 0034e860 0034e8b0 0034e860 ntdll!RtlDispatchException + 0x19 - RtlDispatchException 的参数是指向 EXCEPTION_RECORD 的指针。因此,如果您键入 .exr 0034e860 ,则可以看到异常记录。从异常记录中,您将知道访问哪个地址导致异常(如果异常是访问冲突异常)。

在执行这些步骤之后,您发现由于写入您在堆上正确分配的地址而发生访问冲突 - 您可以通过命令找到包含该地址的虚拟页面的保护属性!地址“虚拟地址”

正如您发现页面保护属性已更改为(通过某些代码) PAGE_READONLY 在这些堆地址上,并且在看到其他线程的调用堆栈后,我有以下猜想,我想想可能会帮助你找到根本原因。

我猜测Windows堆管理器在引发异常之前更改页面属性以指示堆损坏。在ole堆中似乎也存在一些损坏 - 来自您展示的其他线程的调用堆栈。问题的根源可能是破坏堆的代码 - 堆随后发现并引发异常,之后OS的异常机制实现代码启动并在它能够调用之前挂起在SWR锁上您或其他库代码中的异常处理程序。在此之后,代码中的另一个无知线程正确地触及堆内存,堆已经由于已经发现的损坏而已经受到保护,导致异常并使异常机制代码启动并落入其中僵局。鉴于你曾经说过,当程序在调试器下运行时,问题是不可重现的,任何人都会猜测问题有一些时间问题或竞争条件。

答案 2 :(得分:1)

如果你正在使用ATI显卡(带有ATI驱动程序),那么一个已知的问题就是你不能泄漏任何其他状态,否则会在以后发生内存损坏。

尝试禁用所有状态(glDisableClientState),使用API​​Trace找出你忘记了哪一个。

在图形驱动程序中测试内存损坏的一种简单方法是在另一个板/驱动程序上进行测试,或者强制进行软件渲染。