首先让我说,我正在写几个月的试图找出在我们的应用程序发生崩溃的根源这个问题。 我会尽可能详细地说明我已经发现的情况。
关于应用程序
关于事故的事实
exception代码:c0000005 ACCESS_VIOLATION 地址:006a85b9 访问types:写入 访问地址:2e020fff 故障地址:006a85b9 01:002a75b9 C:\ MyDir \ MyApplication.exe ChildEBP RetAddr与孩子联系 警告:堆栈展开信息不可用。 以下框架可能是错误的。 030af6c8 7c9206eb 77bfc3c9 01a80000 00224bc3 MyApplication + 0x2a85b9 030af960 7c91e9c0 7c92901b 00000ab4 00000000 ntdll!RtlAllocateHeap + 0xeac(FPO:[Non-Fpo]) 030af98c 7c9205c8 00000001 00000000 00000000 ntdll!ZwWaitForSingleObject + 0xc(FPO:[3,0,0]) 030af9c0 7c920551 01a80898 7c92056d 313adfb0 ntdll!RtlpFreeToHeapLookaside + 0x22(FPO:[2,0,4]) 030afa8c 4ba3ae96 000307da 00130005 00040012 ntdll!RtlFreeHeap + 0x1e9(FPO:[Non-Fpo]) 030afacc 77bfc2e3 0214e384 3087c8d8 02151030 0x4ba3ae96 030afb00 7c91e306 7c80bfc1 00000948 00000001 msvcrt!free + 0xc8(FPO:[Non-Fpo]) 030afb20 0042965b 030afcc0 0214d780 02151218 ntdll!ZwReleaseSemaphore + 0xc(FPO:[3,0,0]) 030afb7c 7c9206eb 02e6c471 02ea0000 00000008 MyApplication + 0x2965b 030afe60 7c9205c8 02151248 030aff38 7c920551 ntdll!RtlAllocateHeap + 0xeac(FPO:[Non-Fpo]) 030afe74 7c92056d 0210bfb8 02151250 02151250 ntdll!RtlpFreeToHeapLookaside + 0x22(FPO:[2,0,4]) 030aff38 77bfc2de 01a80000 00000000 77bfc2e3 ntdll!RtlFreeHeap + 0x647(FPO:[Non-Fpo]) 7c92056d cffffffffffffffffffffff msvcrt!free + 0xc3(FPO:[Non-Fpo]) 7c920575 ff7c94be 00ffffff 12000000 907c94be 0xc5ffffff 7c920579 00ffffff 12000000 907c94be 90909090 0xff7c94be ***警告:无法validationxerces-c_2_7.dll的校验和 ***错误:无法find符号文件。 默认导出xerces-c_2_7.dll的符号 - 7c92057d 12000000 907c94be 90909090 8b55ff8b MyApplication + 0xbfffff 7c920581 907c94be 90909090 8b55ff8b 08458bec xerces_c_2_7 7c920585 90909090 8b55ff8b 08458bec 04408b66 0x907c94be 7c920589 8b55ff8b 08458bec 04408b66 0004c25d 0x90909090 7c92058d 08458bec 04408b66 0004c25d 90909090 0x8b55ff8b
我到目前为止所尝试过的
ntdll.dll!_RtlpCoalesceFreeBlocks@16()+ 0x124e字节 ntdll.dll!_RtlFreeHeap@12()+ 0x91f字节 msvcrt.dll!_free()+ 0xc3字节 MyApplication.exe!006a4fda() [下面的框架可能不正确和/或丢失,没有为MyApplication.exe加载符号] MyApplication.exe!0069f305() ntdll.dll!_NtFreeVirtualMemory@16()+ 0xc字节 ntdll.dll!_RtlpSecMemFreeVirtualMemory@16()+ 0x1b字节 ntdll.dll!_ZwWaitForSingleObject@12()+ 0xc个字节 ntdll.dll!_RtlpFreeToHeapLookaside@8()+ 0x26字节 ntdll.dll!_RtlFreeHeap@12()+ 0x114字节 msvcrt.dll!_free()+ 0xc3字节 c5ffffff()
可能的解决scheme(我知道),不能应用
我想现在我只记得,如果我忘记了一些东西,我会尽快join。 如果你能给我一些提示或者提出一些可能的解决办法,不要犹豫,回答!
提前感谢您的时间和build议。
您可以尝试用调试堆查看例程来查看代码是否可以找到更接近源代码的错误(您正在使用调试CRT来跟踪这个问题,对吧?):
使用Windows调试工具中的Application Verifier。 有时它有帮助。
尝试设置VS以下载OS调试符号,并确保OMIT FRAME POINTERS在应用程序中处于关闭状态。 也许堆栈跟踪会提供信息。
高度多线程
很久以前,我发现WinXP中每个进程的线程数是有限制的。 我的测试片段只能创建几条线程。 问题已由线程池解决。
编辑:
为了我的目的,只需在gflags.exe中检查“Application Verifier”复选框即可。 不幸的是,我没有其他选择的经验。 至于线程限制,测试片段很简单:
unsigned __stdcall ThreadProc(LPVOID) { _tprintf(_T("Thread started\n")); return 0; } int _tmain(int argc, _TCHAR* argv[]) { while (TRUE) { unsigned threadId = 0; _tprintf(_T("Start thread\n")); _beginthreadex( NULL, 0, &ThreadProc, NULL, 0, &threadId); } return 0; }
这一次我没有等很长时间,但是在任务管理器中处理的数量正在增长非常快。 我的真实世界的应用程序只有在12小时内得到这个效果 但是必须说这个问题不是在崩溃,新线程没有创建。
你可以发布你正在得到的例外吗?
如果这是一些内存损坏错误,那么在内存损坏之后的某个时候会发生崩溃,所以追踪根本原因将是一个挑战。 你应该:
您发布的调用堆栈看起来并不特别具有启发性。
由于你正在使用VS6和SP6,所以它的STL是OK的。
你能告诉生产系统上的应用程序是否泄漏了任何资源? 运行perfmon可以帮助这个。
另一件事,你不是很频繁地从不同的线程调用新/删除你? 我发现,如果你这样做的速度足够快,你会很快崩溃你的应用程序(在XP上这样做)。 我不得不使用VirtualAlloc(windows虚拟内存API)来替换我的应用程序中的新/删除调用,这对我非常有用。 当然,STL也可以从堆中分配。
使用可以挂钩到CPU事件的性能分析器,例如VTune。 在采样模式下进行设置,并告诉它等待与缓存行共享相关的事件。 这些由SNOOP阶段的HITM事件识别。
如果在具有真实工作负载的多处理器机器上运行这个工具,那么它将在您的代码中找到在单个数据的线程之间存在主动争用的地方。 您将需要分析这种方式发现的探查器热点,并尝试找到没有被适当的互斥锁包装的东西。
我不是CPU架构专家,但是我的理解是,当CPU要访问一段数据时,系统将检查是否有其他CPU正在访问相同的数据,这是通过观察内存从每个CPU中读取和写入,这个过程称为“窥探”。 Snooping可以确保,如果两个或更多CPU在其每个高速缓存中具有相同的数据,则在修改其中一个数据时,会删除数据的复制副本。 HIT修改事件意味着系统检测到这种情况,并不得不刷新其中一个CPU缓存行。
有关如何使用VTune的更多信息,请参阅此文档
我现在没有在我面前的VTune的副本,所以也许这不会工作,但它似乎是获取一些数据的影响最低的方式。 采样模式下的VTune不会引起性能问题。
这里的关键是,这只发生在多处理器机器上(内核和处理器一样)当一个线程程序运行在一个处理器上时,会发生什么情况:两个线程不能同时执行。 操作系统必须对每个处理器进行定时分片以模拟线程。 在多处理器系统中,多个线程可以同时运行。 您现在可能正在同时访问不同线程的共享资源。 这些资源可以连接到外部系统,甚至是全局变量和数据结构,甚至是Singleton类。 不幸的是,你现在有一个最难找到的问题。 如果你可以找到内存被损坏,那么你需要找到其他线程使用它,然后同步内存(信号量或CriticalSection)。 不幸的是,没有简单的方法来找到问题。
您可能会暂时将处理器亲和力设置为只能在一个处理器上运行,直到找到问题。 请参阅链接http://msdn.microsoft.com/en-us/library/ms684251(VS.85).aspx这是一个方法来设置亲和力对于Windows XP / Vista / 7,通过打开Windows任务管理器(CTL + ALT + DEL,或右键单击任务栏),选择“进程”选项卡,右键单击要分离的应用程序进程,然后选择“设置相关性”。 在“处理器相关性”对话框中,取消选中不需要使用的CPU /内核。 这有效地将应用程序隔离到选定的CPU /内核,以防止cashe跨越和减少过程切换,并简化了您监控多个程序的CPU /内核分配的能力。
当你的第二个堆栈跟踪显示,你的应用程序正在破坏堆。 堆块的头部被覆盖,因此当合并空闲块或者通过空闲列表时(在第一个堆栈跟踪中),崩溃发生在堆管理器中。 当前正在释放内存的代码可能是另一个代码溢出或下溢内存块的牺牲品。
调试这种类型的崩溃的最简单的方法是使用调试帮助,从窗口,通过pageheap或appverifier,但根据应用程序可能会减慢太多,或增加内存使用率太高,无法使用,这似乎是案子。 你可以尝试使用light pageheap,这会影响较小。
您需要确定应用程序的哪部分溢出。 做到这一点的一个方法是查看飞越区块中包含的信息。 如果你在RtlpCoalesceFreeBlocks中发生崩溃,我想我记得其中一个寄存器(@esi)指向已损坏块的开始(我在写这篇文章的时候没有在windows系统上,也无法检查)。 或者如果你有一个转储,使用windbg命令!堆-a将转储所有的内存并显示损坏的块(更好地登录到一个文件,因为整个堆列表可以很长)。 一旦知道了损坏的块,其内容可能有助于识别代码。
另一个帮助可以是启用堆栈回溯(使用gflags)。 这可以在生产中完成,因为它比pageheap更轻。 它会为堆块添加一些信息,并可能将崩溃移动到应用程序中的另一个地方,但堆栈跟踪将有助于确定哪些代码分配了正在溢出的块。
我将重点讨论如何使用适当的调试符号来构建这个问题,至少对于您的主应用程序来说。 你似乎用“抱歉,我们没有符号”来掩盖这个问题,但是当符号被应用时,栈轨迹可能会显示更多的信息。
这意味着什么:“我们不能生成符号,因为我们正在链接一个图书馆,如果我们正在使用它们,这个图书馆没有链接。”? 这似乎很奇怪。