非托管第一次机会异常会导致 crash/restart 吗?

Can unmanaged first chance exception cause a crash/restart?

后续问题: "When investigating a crash, should I only investigate second chance exceptions? What are the cases when I also need to investigate a first chance exception dump?"

我的问题有点宽泛,但我很好奇真正的答案是什么。我读过很多文章说第一次机会异常不太可能导致应用程序崩溃;这是导致它的第二次机会异常。简单的 google 搜索不能直接回答我的问题。

编辑:这里是示例文章,但还有更多其他文章:

What is a First Chance Exception?:

"For code without exception handling, the debugger will receive a second chance exception notification and will stop with a unhandled exception. "

Program crashes, but Debug Diag says it's a first chance exception, is that correct?

Surely by definition, only a 2nd chance exception can make code crash, i.e. one that has NOT been handled by the code?

我遇到间歇性问题,我的应用程序重启或崩溃(事件查看器中没有错误),但在它重启之前,Adplus 生成了一些第一次机会 AccessViolation 异常。没有第二次机会例外。

以下是 WinDbg.exe 上 FULLDUMP_FirstChance_av_AccessViolation 的片段:

PROBLEM_CLASSES: 
HEAP_CORRUPTION
    Tid    [0x16e8]
    Frame  [0x02]: ntdll!RtlAllocateHeap
HEAP_CORRUPTION
    Tid    [0x16e8]
    Frame  [0x02]: ntdll!RtlAllocateHeap
INVALID_POINTER_READ
    Tid    [0x16e8]
    Frame  [0x00]: ntdll!ExpInterlockedPopEntrySListFault
NOSOS
    Tid    [0x16e8]
BUGCHECK_STR:  HEAP_CORRUPTION_HEAP_CORRUPTION_INVALID_POINTER_READ_NOSOS

下面的示例调用堆栈:

# ChildEBP RetAddr  Args to Child              
00 085aec28 7c91020e 00000007 00c407d8 00c40000 ntdll!ExpInterlockedPopEntrySListFault (FPO: [0,2,0])
01 085aec58 7c91019b 00c407d8 00000030 00000000 ntdll!RtlpAllocateFromHeapLookaside+0x1d (FPO: [Non-Fpo])
02 085aee84 78134d83 00c40000 00000000 00000030 ntdll!RtlAllocateHeap+0x1c2 (FPO: [Non-Fpo])
03 085aeea4 78160e30 00000030 0000002f 085aeecc msvcr80!malloc(unsigned int size = 0x30)+0x7a (FPO: [1,0,0]) (CONV: cdecl) [f:\dd\vctools\crt_bld\self_x86\crt\src\malloc.c @ 163]
04 085aeebc 7c4221b3 00000030 00000003 7c422f20 msvcr80!operator new(unsigned int size = 0x30)+0x1d (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt_bld\self_x86\crt\src\new.cpp @ 59]
05 085aeed4 7c423315 00000030 00000000 ae218f51 msvcp80!std::_Allocate<char>(unsigned int _Count = 0x30, char * __formal = 0x00000000 "")+0x15 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt_bld\self_x86\crt\src\xmemory @ 44]
06 085aef0c 7c4233c4 0000002a 00000000 085af028 msvcp80!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Copy(unsigned int _Newsize = 0x2a, unsigned int _Oldlen = 0)+0x55 (FPO: [Non-Fpo]) (CONV: thiscall) [f:\dd\vctools\crt_bld\self_x86\crt\src\xstring @ 2020]
07 085aef20 7c423779 0000002a 00000000 085af200 msvcp80!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Grow(unsigned int _Newsize = 0x2a, bool _Trim = false)+0x22 (FPO: [2,0,0]) (CONV: thiscall) [f:\dd\vctools\crt_bld\self_x86\crt\src\xstring @ 2050]
08 085aef3c 7c425e55 0000002a 00000000 0000002a msvcp80!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::append(class std::basic_string<char,std::char_traits<char>,std::allocator<char> > * _Right = 0x0000002a, unsigned int _Roff = 0, unsigned int _Count = 0x2a)+0x58 (FPO: [Non-Fpo]) (CONV: thiscall) [f:\dd\vctools\crt_bld\self_x86\crt\src\xstring @ 969]
09 085aef4c 60baed1e 085af028 ae262fd2 085af1a4 msvcp80!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::append(class std::basic_string<char,std::char_traits<char>,std::allocator<char> > * _Right = 0x085af028 " S1 S1 Card number:    ************8706  
")+0xd (FPO: [1,0,0]) (CONV: thiscall) [f:\dd\vctools\crt_bld\self_x86\crt\src\xstring @ 956]
0a 085af1a4 7c802662 00000100 00000000 00000000 aipoptrv19!DllUnregisterServer+0x1f15e
0b 085af234 7c42317a 00000000 00000000 0000000f kernel32!WaitForSingleObject+0x12 (FPO: [Non-Fpo])
0c 085af274 60bc1fd8 60baa1cb 0865d680 0000001c msvcp80!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >(void)+0x11 (FPO: [0,0,4]) (CONV: thiscall) [f:\dd\vctools\crt_bld\self_x86\crt\src\xstring @ 576]
0d 085af278 60baa1cb 0865d680 0000001c 00000002 aipoptrv19!DllUnregisterServer+0x32418
0e 085af2e4 60bb227c 00000001 085af420 0865d648 aipoptrv19!DllUnregisterServer+0x1a60b
0f 085af34c 7c425e45 085af404 00000000 ffffffff aipoptrv19!DllUnregisterServer+0x226bc
10 085af35c 60b97724 72506f44 69746e69 0000676e msvcp80!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign(class std::basic_string<char,std::char_traits<char>,std::allocator<char> > * _Right = 0x72506f44)+0xd (FPO: [1,0,0]) (CONV: thiscall) [f:\dd\vctools\crt_bld\self_x86\crt\src\xstring @ 1044]
11 085af45c 78261414 00000002 403110f4 7824f516 aipoptrv19!DllUnregisterServer+0x7b64
12 085af468 7824f516 fffffffe 781f2c2e 0000001c mfc80!_AfxDispatchCall(<function> * __formal = 0x40b59c84, void * __formal = 0x085af6b8, unsigned int __formal = 0x85a0003)+0x10 (CONV: stdcall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\olecall.cpp @ 40]
13 085af470 781f2c2e 0000001c 7824f49b 00000008 mfc80!CCmdTarget::CallMemberFunc(struct AFX_DISPMAP_ENTRY * pEntry = 0x6d756e20, unsigned short wFlags = 0x6562, struct tagVARIANT * pvarResult = 0x20202020 Empty, struct tagDISPPARAMS * pDispParams = 0x2a2a2a2a, unsigned int * puArgErr = 0x2a2a2a2a)+0x1ad (CONV: thiscall) [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\oledisp1.cpp @ 1064]

错误与堆损坏和无效指针有关,我仍在研究这些问题。我是堆和 mallocs 的新手,我刚刚学会了使用 WinDbg 进行调试。我只是想知道我是否在浪费时间学习内存分配,而这不是我的首要任务并且不会真正解决我的问题。 (当然了解堆是件好事,但解决主要问题是重中之重)

我对我的 adplus 配置文件很有信心,我确信它会在所有第二次机会异常时生成完整转储。我在示例应用程序上试过了。

应用程序没有崩溃,它只是意外地间歇性地重新启动,没有事件查看器错误。它可以在使用特定服务时间歇性地重新创建。

如果转储文件不是问题的真正原因,以下是我可能的想法:

PS: 对不起,如果我没有指定一些细节和代码示例等,因为它是机密的。在不影响公司政策的情况下,我已尽力解释问题。

提前谢谢!

这个MSDN article about exception dispatching解释了这个过程:

When an exception occurs in user-mode code, the system uses the following search order to find an exception handler:

  1. If the process is being debugged, the system notifies the debugger. For more information, see Debugger Exception Handling.
  2. If the process is not being debugged, or if the associated debugger does not handle the exception, the system attempts to locate a frame-based exception handler by searching the stack frames of the thread in which the exception occurred. The system searches the current stack frame first, then searches through preceding stack frames in reverse order.
  3. If no frame-based handler can be found, or no frame-based handler handles the exception, but the process is being debugged, the system notifies the debugger a second time.
  4. If the process is not being debugged, or if the associated debugger does not handle the exception, the system provides default handling based on the exception type. For most exceptions, the default action is to call the ExitProcess function.

在第 1 步中,异常称为第一次机会异常,因为这是任何人都能捕获并处理异常的第一次机会。

在步骤 3 中,相同的异常称为第二次机会异常,因为这是第二次,调试器有机会捕获并处理异常。

只有进程继续到第4步程序才会崩溃或退出。因此,是的,只有第二次机会异常才能使进程崩溃。

Can unmanaged first chance exception cause a crash/restart?

没有。见前

When investigating a crash, should I only investigate second chance exceptions?

基本上是的。这就是每个人 (>90%) 在分析崩溃时所做的。

What are the cases when I also need to investigate a first chance exception dump?

案例一:

第二次机会异常可能是先前第一次机会异常的结果。由于第一次机会异常,值可能未初始化并导致不同的第二次机会异常。

这种情况的示例代码:

SomeObject o = null;
try {
    throw new Exception("First chance"); // consider this in some method
    o = new SomeObject();
}
catch (Exception)
{
    // make sure that the exception does not become a second chance exception
}
o.DoSomething(); // causes NullReferenceException first chance and second chance if uncaught

应用程序因 NullReferenceException 而崩溃,但真正的原因是之前的异常。但是,这种情况通常很容易识别,而无需查看第一次机会异常。

案例二:

异常有很高的开销,即它们会花费 CPU 个周期,从而影响性能。如果您真的有很多第一次机会异常,您可能想要摆脱它们。