.net core 里面处理异常无论是在linux 或者是widnows都有一个异常入口函数processclrexception()
core里面的异常分为用户异常和硬件异常,前者是代码里面引发的异常,后面是CPU寄存器硬件等引发的异常
两者处理不同在于VEH扩展,当硬件异常的时候,会调用注册的VEH处理异常。而后进入processclrexception,用户异常则省略了VEH部分直接进入
当引发异常的时候,Jithelpers类会调用IL_throw进行处理。然后调用windows api raiseexception抛出异常,通过Windows处理机制进入异常入口函数
这个函数在 linux和windwos上通用,捕捉到异常之后,需要获取异常函数和调用来源,可以通过函数栈来递推循环处理得到来源。当获取到调用的
来源之后,就可以获取core clr 的异常处理,在 .net 里面每个函数都有一个异常处理表。获取到之后,可以枚举异常处理表一次调用 try ,catch ,finally
快的数据。以便处理异常。
IL_Throw 代码如下,主要是引入异常类的对象,然后RaiseTheExceptionInternalOnly函数,在这个函数里了调用了RaiseException(code, flags, argCount, args),
抛出异常代码,被processclrexception捕捉到。然后流程进入processclrexception。
IL_Throw代码如下:
1 HCIMPL1(void, IL_Throw, Object* obj) 2 { 3 FCALL_CONTRACT; 4 5 // This "violation" isn't a really a violation. 6 // We are calling a assembly helper that can't have an SO Tolerance contract 7 CONTRACT_VIOLATION(SOToleranceViolation); 8 /* Make no assumptions about the current machine state */ 9 ResetCurrentContext(); 10 11 FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC 12 13 HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame 14 15 OBJECTREF oref = ObjectToOBJECTREF(obj); 16 17 #if defined(_DEBUG) && defined(_TARGET_X86_) 18 __helperframe.InsureInit(false, NULL); 19 g_ExceptionEIP = (LPVOID)__helperframe.GetReturnAddress(); 20 #endif // defined(_DEBUG) && defined(_TARGET_X86_) 21 22 23 if (oref == 0) 24 COMPlusThrow(kNullReferenceException); 25 else 26 if (!IsException(oref->GetMethodTable())) 27 { 28 GCPROTECT_BEGIN(oref); 29 30 WrapNonCompliantException(&oref); 31 32 GCPROTECT_END(); 33 } 34 else 35 { // We know that the object derives from System.Exception 36 if (g_CLRPolicyRequested && 37 oref->GetMethodTable() == g_pOutOfMemoryExceptionClass) 38 { 39 EEPolicy::HandleOutOfMemory(); 40 } 41 42 // If the flag indicating ForeignExceptionRaise has been set, 43 // then do not clear the "_stackTrace" field of the exception object. 44 if (GetThread()->GetExceptionState()->IsRaisingForeignException()) 45 { 46 ((EXCEPTIONREF)oref)->SetStackTraceString(NULL); 47 } 48 else 49 { 50 ((EXCEPTIONREF)oref)->ClearStackTracePreservingRemoteStackTrace(); 51 } 52 } 53 54 #ifdef FEATURE_CORRUPTING_EXCEPTIONS 55 if (!g_pConfig->LegacyCorruptedStateExceptionsPolicy()) 56 { 57 // Within the VM, we could have thrown and caught a managed exception. This is done by 58 // RaiseTheException that will flag that exception's corruption severity to be used 59 // incase it leaks out to managed code. 60 // 61 // If it does not leak out, but ends up calling into managed code that throws, 62 // we will come here. In such a case, simply reset the corruption-severity 63 // since we want the exception being thrown to have its correct severity set 64 // when CLR's managed code exception handler sets it. 65 66 ThreadExceptionState *pExState = GetThread()->GetExceptionState(); 67 pExState->SetLastActiveExceptionCorruptionSeverity(NotSet); 68 } 69 #endif // FEATURE_CORRUPTING_EXCEPTIONS 70 71 //这个函数进入,调用抛出异常 72 73 RaiseTheExceptionInternalOnly(oref, FALSE); 74 75 HELPER_METHOD_FRAME_END(); 76 }
.Net Core C++ 异常模型
1 #define WIN32_LEAN_AND_MEAN 2 3 #include4 #include 5 DWORD scratch; 6 EXCEPTION_DISPOSITION 7 __cdecl 8 processclrexception(struct _EXCEPTION_RECORD* ExceptionRecord, 9 void* EstablisherFrame, 10 struct _CONTEXT* ContextRecord, 11 void* DispatcherContext) 12 { 13 unsigned i; 14 // 指明是我们让流程转到我们的异常处理程序的 15 printf("Hello from an exception handler\n"); 16 // 改变CONTEXT结构中EAX的值,以便它指向可以成功进写操作的位置 17 ContextRecord->Eax = (DWORD)&scratch; 18 // 告诉操作系统重新执行出错的指令 19 return ExceptionContinueExecution; 20 } 21 int main() 22 { 23 DWORD handler = (DWORD)processclrexception 24 __asm 25 { 26 // 创建EXCEPTION_REGISTRATION结构: 27 push handler // handler函数的地址 28 push FS : [0] // 前一个handler函数的地址 29 mov FS : [0] , ESP // 安装新的EXECEPTION_REGISTRATION结构 30 } 31 RaiseException(EXCEPTION_ACCESS_VIOLATION 32 , EXCEPTION_NONCONTINUABLE, 0, NULL); 33 //__asm 34 //{ 35 // mov eax, 0 // 将EAX清零 36 // mov[eax], 1 // 写EAX指向的内存从而故意引发一个错误 37 //} 38 printf("After writing!\n"); 39 __asm 40 { 41 // 移去我们的EXECEPTION_REGISTRATION结构 42 mov eax, [ESP] // 获取前一个结构 43 mov FS : [0] , EAX // 安装前一个结构 44 add esp, 8 // 将我们的EXECEPTION_REGISTRATION弹出堆栈 45 } 46 return 0; 47 48 }
下面是ProcessCLRException,这个函数非常长,长达几千行,只贴出部分有用的
1 ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord 2 WIN64_ARG(IN ULONG64 MemoryStackFp) 3 NOT_WIN64_ARG(IN ULONG MemoryStackFp), 4 IN OUT PCONTEXT pContextRecord, 5 IN OUT PDISPATCHER_CONTEXT pDispatcherContext 6 ) 7 { 8 9 DWORD dwLastError = GetLastError();//获取到当前的执行结果 10 11 ExceptionTracker* pTracker = ExceptionTracker::GetOrCreateTracker( // 这个函数里面会实例化一个异常的托管类,比如ExceptionArgument类等。 12 pDispatcherContext->ControlPc, 13 sf, 14 pExceptionRecord, 15 pContextRecord, 16 bAsynchronousThreadStop, 17 !(dwExceptionFlags & EXCEPTION_UNWINDING), 18 &STState); 19 20 status = pTracker->ProcessOSExceptionNotification( // 这个函数会调用 .net core里面的 异常的try块的代码 21 pExceptionRecord, 22 pContextRecord, 23 pDispatcherContext, 24 dwExceptionFlags, 25 sf, 26 pThread, 27 STState 28 #ifdef USE_PER_FRAME_PINVOKE_INIT 29 , (PVOID)pICFSetAsLimitFrame 30 #endif // USE_PER_FRAME_PINVOKE_INIT 31 ); 32 33 ClrUnwindEx(pExceptionRecord,// 这个函数调用了 windows api RtlUnwindEx 。RtlUnwindEx第二次调用ExceptionHandler,执行展开操作,清理资源等 34 (UINT_PTR)pThread, 35 INVALID_RESUME_ADDRESS, 36 pDispatcherContext->EstablisherFrame); 37 38 // .net core里面的异常try 块被执行了,如果有finally ,在这里会被执行。调用了windwos api RtlRestoreContext(pContextRecord, pExceptionRecord);执行finally 块 39 40 ExceptionTracker::ResumeExecution(pContextRecord,pExceptionRecord); 41 42 NULL 43 44 ); 45 46 }
以上就是大致的.net core 异常处理流程。有疑问可以一起交流 QQ群:676817308